osg_0">osg系列文章目录
文章目录
- osg系列文章目录
- 前言
- 一、分析原因
- 二、最终解决方法
- 三、完整代码
前言
osg3.6.5 osgEarth3.2中,鼠标操作地球放大、缩小、旋转发生屏闪,如下图所示,感觉晃眼睛,这谁能忍?
一、分析原因
1.垂直同步(VSync)未启用:
如果垂直同步未启用,图形的刷新速率可能和显示器的刷新速率不同步,导致屏幕撕裂和闪烁。
解决方法:无效
typedef BOOL(WINAPI* PFNWGLSWAPINTERVALEXTPROC)(int interval);
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr;
wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
if (wglSwapIntervalEXT)
{wglSwapIntervalEXT(1); // Enable VSync
}
2.双缓冲(Double Buffering)未启用:
双缓冲可以避免在渲染过程中直接绘制到屏幕上,从而减少屏幕闪烁。
解决方法:无效
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
traits->doubleBuffer = true;
3.绘制循环中的重复绘制:
如果绘制循环中存在不必要的重复绘制操作,可能会导致性能问题和屏幕闪烁。
解决方法:无效
void COSGObject::Render(void* ptr)
{COSGObject* osg = static_cast<COSGObject*>(ptr);if (!osg){std::cerr << "Error: osg is nullptr!" << std::endl;return;}osgViewer::Viewer* viewer = osg->GetViewer();if (!viewer){std::cerr << "Error:viewer is nullptr!" << std::endl;return;}while (!viewer->done()){osg->PreFrameUpdate();viewer->frame();osg->PostFrameUpdate();}_endthread();
}
4.图形驱动程序或硬件问题:
图形驱动程序或硬件问题也可能导致屏幕闪烁。
更新过显卡驱动:无效
5.透明度和混合设置问题:
透明度和混合设置不当可能会影响渲染性能,导致屏幕闪烁。
解决方法:无效
osg::ref_ptr<osg::BlendFunc> blendFunc = new osg::BlendFunc;
blendFunc->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_CONSTANT_ALPHA);
stateSet->setAttributeAndModes(blendFunc, osg::StateAttribute::ON);
stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
6.窗口重绘频率过高:
窗口重绘频率过高可能导致屏幕闪烁。
解决方法:无效
m_viewer->setUpThreadingModel(osgViewer::Viewer::SingleThreaded);
二、最终解决方法
透明对象需要特别的处理来保证它们以正确的顺序进行渲染(通常是从远到近),以避免混合错误。这行代码将该节点及其子节点放入一个专门的渲染通道(Render Bin)中,以确保正确处理透明度。
显式地设置了渲染通道的详细信息。
//启动混合
osg::ref_ptr<osg::BlendFunc> blendFunc = new osg::BlendFunc;
blendFunc->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_CONSTANT_ALPHA);
stateSet->setAttributeAndModes(blendFunc, osg::StateAttribute::ON);stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);//启动透明度写入
stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
stateSet->setRenderBinDetails(0, "RenderBin");
三、完整代码
1.头文件
#pragma once#include <osg/Group>
#include <osg/CullFace>
#include <osg/BlendFunc>
#include <osgviewer/Viewer>
#include <osgDB/ReadFile>
#include <osgviewer/api/Win32/GraphicsWindowWin32>
#include <osgGA/TrackballManipulator>
#include <osgEarth/MapNode>
#include <osgEarth/Utils>
#include <osgEarth/EarthManipulator>
#include <osgEarth/SkyView>
#include <osgEarth/Units>
//#include <osgEarth/Util/SkyNode> // 确保包含 SkyNode 的定义
#include <osgEarth/Sky>
#include <osgEarth/Ephemeris>
#include <osgEarth/ImageLayer>
#include <osgEarth/AnnotationUtils>#include <osgText/Text>
#include <osgEarth/GeoTransform>
#include <osgEarth/AnnotationNode>
#include <osgEarth/MapInfo>
#include <osgEarth/Symbol>#include <osgEarth/GeoMath>
#include <osgEarth/SpatialReference>
#include <osgEarth/TextSymbol>
#include <osgEarth/Feature>
#include <osgEarth/PlaceNode>
#include <osgEarth/Style>
#include <osgEarth/OGRFeatureSource>
#include <osgEarth/FeatureModelLayer>
#include <osgEarth/FeatureImageLayer>#include <mutex>
#include <memory>
#include <tuple>#include "CLabelTools.h"using namespace osgEarth;
using namespace osgEarth::Util;class COSGObject
{
public:COSGObject(HWND hWnd);~COSGObject();void InitOSG();void InitSceneGraph();void InitCameraConfig();void PreFrameUpdate();void PostFrameUpdate();static void Render(void* ptr);void InitOsgEarth();osgViewer::Viewer* GetViewer();void setChinaBoundariesOpacity(double opt);double getChinaBoundariesOpacity();void rmvChinaBoundaryes();void addChinaBoundaryes();//新增地标void addLabel();void loadSharp();std::wstring MultiByteToWideChar(const std::string& str);std::string WideChar2MultiByte(const std::wstring& wstr);std::string MultiByteToUTF8(const std::string& str);//1:ptr COSGObject//2:ptr 文件名//3:ptr 地标数量static void ReadLabelThread(void* ptr);//static void CreateLabelThread(void* ptr);//void CreateLabelThread(std::shared_ptr<std::vector<void*>> params);static void CreateLabelThread(void* ptr);//static COSGObject* m_osg;unsigned int m_processI;osg::ref_ptr<osg::Image> m_province;osg::ref_ptr<osg::Image> m_cityCenter;osg::ref_ptr<osg::Image> m_city;osg::ref_ptr<osg::Image> m_countyCity;osg::ref_ptr<osg::Image> m_county;osg::ref_ptr<osg::Image> m_town;osg::ref_ptr<osg::Image> m_vi;//osgEarth::Style m_style;std::shared_ptr<std::tuple<COSGObject*, std::shared_ptr<std::string>, unsigned int>> m_shaanXiParam;std::shared_ptr<std::string> m_shaanXiTxt;std::shared_ptr<std::tuple<COSGObject*, std::shared_ptr<std::string>, unsigned int>> m_shanXiParam;std::shared_ptr<std::string> m_shanXiTxt;/*unsigned int shaanXiParam[3];std::string shaanXiTxt;std::string shanXiTxt;*/public:HWND m_hWnd;osg::ref_ptr<osgViewer::Viewer> m_viewer;osg::ref_ptr<osg::Group> m_root;static osg::ref_ptr<osgEarth::MapNode> m_mapNode;osg::ref_ptr<osgEarth::EarthManipulator> m_em;osg::ref_ptr<osgEarth::SkyNode> m_skyNode;static osgEarth::Style *m_style;static std::mutex m_mutex;time_t m_nowTime;tm* m_tm;//国界线图层//osg::ref_ptr<osgEarth::Layer> m_chinaBoundaries;osg::ref_ptr<osgEarth::ImageLayer> m_chinaBoundaries;osg::ref_ptr<osg::Node> m_world;//地标osg::ref_ptr<osg::Group> m_earthLabel;
};
2.实现文件
#include "pch.h"
#include "COSGObject.h"
#include "DigitalEarth.h"osg::ref_ptr<osgEarth::MapNode> COSGObject::m_mapNode;
osgEarth::Style* COSGObject::m_style = new osgEarth::Style;
std::mutex COSGObject::m_mutex;
//COSGObject* COSGObject::m_osg = nullptr;COSGObject::COSGObject(HWND hWnd)
{m_hWnd = hWnd;//m_osg = nullptr;m_viewer = nullptr;m_root = nullptr;m_processI = 0;
}COSGObject::~COSGObject() {}void COSGObject::InitOSG()
{InitSceneGraph();InitCameraConfig();InitOsgEarth();
}void COSGObject::InitSceneGraph()
{// osg::ref_ptr<osg::Group> rr = new osg::Group;try{osgEarth::initialize();m_root = new osg::Group;// m_root->addChild(osgDB::readNodeFile("../../vs2022_64bit_3rdParty_osg365_oe32/runtime/test/earthFile/china-simple.earth"));//启动背面剔除osg::ref_ptr<osg::CullFace> cullFace = new osg::CullFace(osg::CullFace::BACK);osg::ref_ptr<osg::StateSet> stateSet = m_root->getOrCreateStateSet();stateSet->setAttributeAndModes(cullFace, osg::StateAttribute::ON);// osg::ref_ptr<osg::Node> mp = osgDB::readNodeFile("../../vs2022_64bit_3rdParty_osg365_oe32/runtime/test/earth/TestCommon10/output.ive");osg::ref_ptr<osg::Node> mp = osgDB::readNodeFile("../../vs2022_64bit_3rdParty_osg365_oe32/runtime/test/earthFile/china-simple.earth");m_root->addChild(mp);// 注意:osgEarth::MapNode只能加载earth文件,加载ive会失败m_mapNode = dynamic_cast<osgEarth::MapNode*>(mp.get());//设置透明度、材质osg::ref_ptr<osg::Material> material = dynamic_cast<osg::Material*>(stateSet->getAttribute(osg::StateAttribute::MATERIAL));if (!material){material = new osg::Material;stateSet->setAttribute(material);}//设置透明度0.2material->setAlpha(osg::Material::FRONT_AND_BACK, 0.2f);//启动混合osg::ref_ptr<osg::BlendFunc> blendFunc = new osg::BlendFunc;blendFunc->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_CONSTANT_ALPHA);stateSet->setAttributeAndModes(blendFunc, osg::StateAttribute::ON);stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);//启动透明度写入stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);stateSet->setRenderBinDetails(0, "RenderBin");// 地标初始化m_earthLabel = new osg::Group;m_root->addChild(m_earthLabel);}catch (const std::exception& e){AfxMessageBox(CString("Failed to create osg::Group in constructor: ") + CString(e.what()));throw std::runtime_error(std::string("Failed to create osg::Group in constructor: ") + e.what());}
}void COSGObject::InitCameraConfig()
{RECT rect;m_viewer = new osgViewer::Viewer;// osg::ref_ptr<osgViewer::View> viewer = new osgViewer::Viewer;// osg::ref_ptr<osgViewer::Viewer> m_vv = new osgViewer::Viewer;::GetWindowRect(m_hWnd, &rect);osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;osg::ref_ptr<osg::Referenced> winData = new osgViewer::GraphicsWindowWin32::WindowData(m_hWnd);traits->x = 0;traits->y = 0;traits->width = rect.right - rect.left;traits->height = rect.bottom - rect.top;traits->windowDecoration = false;traits->doubleBuffer = true;traits->sharedContext = 0;traits->setInheritedWindowPixelFormat = true;traits->inheritedWindowData = winData;osg::GraphicsContext* gc = osg::GraphicsContext::createGraphicsContext(traits);osg::ref_ptr<osg::Camera> camera = new osg::Camera;camera->setGraphicsContext(gc);camera->setViewport(new osg::Viewport(traits->x, traits->y, traits->width, traits->height));camera->setProjectionMatrixAsPerspective(30.0f, static_cast<double>(traits->width) / static_cast<double>(traits->height),1.0f, 1000.f);m_viewer->setCamera(camera);// m_viewer->setCameraManipulator(new osgGA::TrackballManipulator);m_viewer->setSceneData(m_root);m_viewer->realize();// 启用垂直同步m_viewer->getCamera()->setComputeNearFarMode(osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);m_viewer->getCamera()->setNearFarRatio(0.000003f);
}void COSGObject::PreFrameUpdate()
{// std::lock_guard<std::mutex> lock(m_mutex); // 加锁if (m_viewer == nullptr || m_root == nullptr){std::cerr << "Error: Viewer or root is nullptr in PreFrameUpdate!" << std::endl;return;}while (theApp.m_needModify)Sleep(1);theApp.m_canModify = FALSE;
}void COSGObject::PostFrameUpdate()
{// std::lock_guard<std::mutex> lock(m_mutex); // 加锁if (m_viewer == nullptr || m_root == nullptr){std::cerr << "Error: Viewer or root is nullptr in PostFrameUpdate!" << std::endl;return;}if (theApp.m_needModify)theApp.m_canModify = TRUE;
}void COSGObject::Render(void* ptr)
{COSGObject* osg = static_cast<COSGObject*>(ptr);if (!osg){std::cerr << "Error: osg is nullptr!" << std::endl;return;}osgViewer::Viewer* viewer = osg->GetViewer();if (!viewer){std::cerr << "Error:viewer is nullptr!" << std::endl;return;}while (!viewer->done()){osg->PreFrameUpdate();viewer->frame();osg->PostFrameUpdate();}_endthread();
}osgViewer::Viewer* COSGObject::GetViewer()
{return m_viewer;
}void COSGObject::InitOsgEarth()
{// 初始化操作器m_em = new osgEarth::EarthManipulator;if (m_mapNode.valid()){m_em->setNode(m_mapNode);}m_em->getSettings()->setArcViewpointTransitions(true);//解决视口在南北极时,鼠标无法上下旋转视口m_em->getSettings()->setMinMaxPitch(-90.0, 90.0);// Allow free rotation around the polesm_em->getSettings()->setThrowingEnabled(false);m_em->getSettings()->setLockAzimuthWhilePanning(false);m_viewer->setCameraManipulator(m_em);// 获取当前时间m_nowTime = time(0);m_tm = localtime(&m_nowTime);osgEarth::DateTime currTime(m_nowTime);osg::ref_ptr<osgEarth::Ephemeris> ephemeris = new osgEarth::Ephemeris;// 初始化天空osgEarth::Util::SkyOptions skyOptions;m_skyNode = osgEarth::SkyNode::create(skyOptions);// osgEarth::Util::SkyOptions skyOptions;osgEarth::Config skyConf;double hours = skyConf.value("hours", 12.0);m_skyNode->setName("skyNode");m_skyNode->setEphemeris(ephemeris);m_skyNode->setDateTime(currTime);m_viewer->setLightingMode(osg::View::SKY_LIGHT);m_skyNode->attach(m_viewer, 0);m_skyNode->setLighting(true);m_skyNode->addChild(m_mapNode);m_root->addChild(m_skyNode);// 获取国界线图层// m_chinaBoundaries = m_mapNode->getMap()->getImageLayerByName("china_boundaries");osg::ref_ptr<osgEarth::Layer> layer = m_mapNode->getMap()->getLayerByName("province_boundaries");if (layer){auto imageLayer = dynamic_cast<osgEarth::ImageLayer*>(layer.get());if (imageLayer){m_chinaBoundaries = imageLayer;}}setChinaBoundariesOpacity(1.0);m_world = osgDB::readNodeFile("../../vs2022_64bit_3rdParty_osg365_oe32/runtime/test/data/image/globel/world.ive");//m_mapNode->addChild(m_world);m_root->addChild(m_world);// 新增地标addLabel();loadSharp();
}void COSGObject::setChinaBoundariesOpacity(double opt)
{// std::lock_guard<std::mutex> lock(m_mutex);if (m_chinaBoundaries){m_chinaBoundaries->setOpacity(opt);}
}double COSGObject::getChinaBoundariesOpacity()
{// std::lock_guard<std::mutex> lock(m_mutex);if (m_chinaBoundaries){return m_chinaBoundaries->getOpacity();}else{return -1.0;}
}void COSGObject::rmvChinaBoundaryes()
{// std::lock_guard<std::mutex> lock(m_mutex);theApp.m_needModify = TRUE;while (!theApp.m_canModify)Sleep(1);if (m_chinaBoundaries){m_mapNode->getMap()->removeLayer(m_chinaBoundaries);}// theApp.m_needModify = FALSE;
}void COSGObject::addChinaBoundaryes()
{// std::lock_guard<std::mutex> lock(m_mutex);// theApp.m_needModify = TRUE;// while (!theApp.m_canModify) Sleep(1);if (m_chinaBoundaries){m_mapNode->getMap()->addLayer(m_chinaBoundaries);}// theApp.m_needModify = FALSE;
}void COSGObject::addLabel()
{// 创建样式osgEarth::Style pm;// m_style = new osgEarth::Style;osg::ref_ptr<osgEarth::IconSymbol> iconSymbol = m_style->getOrCreate<osgEarth::IconSymbol>();iconSymbol->declutter() = true;osg::ref_ptr<osgEarth::TextSymbol> textSymbol = m_style->getOrCreate<osgEarth::TextSymbol>();textSymbol->halo()->color() = osgEarth::Color("#E2E200");textSymbol->font() = "simsun.ttc"; // 确保字体文件存在且路径正确textSymbol->size() = 40.0;textSymbol->encoding() = osgEarth::TextSymbol::ENCODING_UTF8; // 确保使用 UTF-8 编码textSymbol->fill()->color() = osgEarth::Color("white"); // 文本填充颜色textSymbol->pixelOffset() = osg::Vec2s(100.0, 100.0);// 成都定义位置(纬度、经度、高度)double lon = 104.065735; // 经度double lat = 30.659462; // 纬度double alt = 0; // 高度// 西安108.9466 34.2474lon = 108.9466;lat = 34.2474;// 使用 GeoPoint 并指定绝对高度模式const SpatialReference* spatialReference = m_mapNode->getMapSRS()->getGeographicSRS();osgEarth::GeoPoint geoPoint(spatialReference, lon, lat, alt, osgEarth::ALTMODE_RELATIVE);// 加载图标图片osg::ref_ptr<osg::Image> pImage = osgDB::readImageFile("F:/osg/yangShiXing/019.Earth/builder/data/image/label/chinaIcon.jpg");if (pImage.valid()){pImage->scaleImage(64, 62, 1);}// 创建 PlaceNode// 注意:确保传递给 PlaceNode 的文本是以 UTF-8 编码的 C++ 字符串字面量std::string label = "中国"; // 使用 u8 前缀来确保字符串是 UTF-8 编码的std::string utf8Label = MultiByteToUTF8(label);osgEarth::PlaceNode* pNode = new osgEarth::PlaceNode(geoPoint, label, *m_style);if (pImage.valid()){pNode->setIconImage(pImage);}// 添加到场景中m_earthLabel->addChild(pNode);// 添加陕西地名std::fstream file("F:/osg/yangShiXing/019.Earth/builder/data/label/txt/shaanxi.txt", std::ios::in);char name[128] = {};wchar_t wname[128] = {};char area[256] = {};int level = 0;float tempLon = 0.0;float tempLat = 0.0;alt = 0.0;textSymbol->halo()->color() = osgEarth::Color(" #FFB6C1");textSymbol->size() = 20.0; osg::ref_ptr<osg::Image> tempImg = 0;//m_cityCenter = osgDB::readImageFile("F:/osg/yangShiXing/019.Earth/builder/data/image/label/icon25.png");//m_city = osgDB::readImageFile("F:/osg/yangShiXing/019.Earth/builder/data/image/label/icon26.png");//m_countyCity = osgDB::readImageFile("F:/osg/yangShiXing/019.Earth/builder/data/image/label/icon27.png");//m_county = osgDB::readImageFile("F:/osg/yangShiXing/019.Earth/builder/data/image/label/icon28.png");//m_town = osgDB::readImageFile("F:/osg/yangShiXing/019.Earth/builder/data/image/label/icon29.png");//m_vi = osgDB::readImageFile("F:/osg/yangShiXing/019.Earth/builder/data/image/label/icon31.png");//m_province = osgDB::readImageFile("F:/osg/yangShiXing/019.Earth/builder/data/image/label/icon32.png");m_cityCenter = osgDB::readImageFile("E:/osgEarth/vs2022_64bit_3rdParty_osg365_oe32/runtime/test/data/image/label/icon25.png");m_city = osgDB::readImageFile("E:/osgEarth/vs2022_64bit_3rdParty_osg365_oe32/runtime/test/data/image/label/icon26.png");m_countyCity = osgDB::readImageFile("E:/osgEarth/vs2022_64bit_3rdParty_osg365_oe32/runtime/test/data/image/label/icon27.png");m_county = osgDB::readImageFile("E:/osgEarth/vs2022_64bit_3rdParty_osg365_oe32/runtime/test/data/image/label/icon28.png");m_town = osgDB::readImageFile("E:/osgEarth/vs2022_64bit_3rdParty_osg365_oe32/runtime/test/data/image/label/icon29.png");m_vi = osgDB::readImageFile("E:/osgEarth/vs2022_64bit_3rdParty_osg365_oe32/runtime/test/data/image/label/icon31.png");m_province = osgDB::readImageFile("E:/osgEarth/vs2022_64bit_3rdParty_osg365_oe32/runtime/test/data/image/label/icon32.png");if (!m_cityCenter->valid() || !m_city->valid() || !m_countyCity->valid() || !m_county->valid() || !m_town->valid() || !m_vi->valid()){std::cerr << "Failed to load one or more image." << std::endl;return;}// 添加陕西地名std::string tempStr = "F:/osg/yangShiXing/019.Earth/builder/data/label/ive/ShaanXi/";std::string centerFile = tempStr + "center.center";char tempChar[256];std::fstream fIn(centerFile, std::ios::in);for (size_t index = 0; index < 37937; index++){sprintf(tempChar, "%d.ive", index);std::string temp = tempStr;temp += tempChar;osg::ref_ptr<osg::PagedLOD> pLod = new osg::PagedLOD;pLod->setFileName(0, temp);// 读取center.center和distint x = 0, y = 0, z = 0;long dist = 0;fIn >> x >> y >> z >> dist;pLod->setCenter(osg::Vec3(x, y, z));pLod->setRange(0, 0, dist);pLod->setCenterMode(osg::LOD::USER_DEFINED_CENTER);m_earthLabel->addChild(pLod);}fIn.close();{// 增加全国省地名std::string tempStr = "F:/osg/yangShiXing/019.Earth/builder/data/label/ive/Sheng/";std::string centerFile = tempStr + "center.center";char tempChar[256];std::fstream fin(centerFile, std::ios::in);for (int index = 0; index < 32; index++){sprintf(tempChar, "%d.ive", index);std::string temp = tempStr;temp += tempChar;osg::ref_ptr<osg::PagedLOD> plod = new osg::PagedLOD;plod->setFileName(0, temp);// 读取center和distint x, y, z;long dist;fin >> x >> y >> z >> dist;plod->setCenter(osg::Vec3(x, y, z));plod->setRange(0, 0, dist);plod->setCenterMode(osg::LOD::USER_DEFINED_CENTER);m_earthLabel->addChild(plod);}fin.close();}/*******************************************************************************************/// 多线程加载城市村镇地表引起数据同步竞争问题std::lock_guard<std::mutex> lock(m_mutex);// m_shaanXiTxt = std::make_shared<std::string>("F:/osg/yangShiXing/019.Earth/builder/data/label/txt/shaanxi.txt");// m_shaanXiParam = std::make_shared<std::tuple<COSGObject*, std::shared_ptr<std::string>, unsigned int>>(this, m_shaanXiTxt, 37937);//(HANDLE)_beginthread(&COSGObject::ReadLabelThread, 0, m_shaanXiParam.get());// m_shanXiTxt = std::make_shared<std::string>("F:/osg/yangShiXing/019.Earth/builder/data/label/txt/shanxi.txt");// m_shanXiParam = std::make_shared<std::tuple<COSGObject*, std::shared_ptr<std::string>, unsigned int>>(this, m_shanXiTxt, 33199);//(HANDLE)_beginthread(&COSGObject::ReadLabelThread, 0, m_shanXiParam.get());/*******************************************************************************************/
}void COSGObject::loadSharp()
{srand(static_cast<unsigned int>(time(nullptr)));// Load the base map (globe)osg::ref_ptr<osg::Node> globe = osgDB::readNodeFile("../vs2022_64bit_3rdParty_osg365_oe32/runtime/test/earthFile/china-simple.earth");if (!globe){std::cerr << "Failed to load earth file." << std::endl;return;}osg::ref_ptr<osgEarth::MapNode> mapNode = osgEarth::MapNode::get(globe);if (!mapNode){std::cerr << "Failed to get MapNode." << std::endl;return;}osg::ref_ptr<osgEarth::Map> map = mapNode->getMap();// Load the Shapefileosg::ref_ptr<osgEarth::OGRFeatureSource> features = new osgEarth::OGRFeatureSource;features->setURL("F:/osg/yangShiXing/019.Earth/builder/data/shpFile/world.shp"); // china.shp world.shpif (!features->open()){std::cerr << "Failed to load shapefile: " << features->getStatus().message() << std::endl;return;}else{std::cout << "Shapefile loaded successfully." << std::endl;}// Initialize the query objectosgEarth::Query query;query.expression() = "1=1"; // A simple query that matches all features// Log the query expression if it existsif (query.expression().isSet()){std::cout << "Query expression: " << query.expression().value() << std::endl;}// Create a feature cursor with the queryosg::ref_ptr<osgEarth::FeatureCursor> cursor = features->createFeatureCursor(query, nullptr);if (!cursor){std::cerr << "Failed to create feature cursor." << std::endl;return;}else{std::cout << "Feature cursor created successfully." << std::endl;}// Map to store unique colors for each countrystd::map<std::string, osgEarth::Style> countryStyles;// 遍历每个要素并为其应用颜色的样式while (cursor->hasMore()){osg::ref_ptr<osgEarth::Feature> feature = cursor->nextFeature();std::string countryName = feature->getString("CNTRY_NAME");// Generate a unique random color for each country if not already generatedif (countryStyles.find(countryName) == countryStyles.end()){osg::Vec4 randomColor(static_cast<float>(rand()) / RAND_MAX,static_cast<float>(rand()) / RAND_MAX,static_cast<float>(rand()) / RAND_MAX,1.0f);// Define the style for the countryosgEarth::Style featureStyle;featureStyle.setName(countryName); // Set the style name to the country nameosg::ref_ptr<osgEarth::RenderSymbol> featureRender = featureStyle.getOrCreate<osgEarth::RenderSymbol>();featureRender->depthTest() = false;osg::ref_ptr<osgEarth::AltitudeSymbol> featureAlt = featureStyle.getOrCreate<osgEarth::AltitudeSymbol>();featureAlt->clamping() = featureAlt->CLAMP_TO_TERRAIN;featureAlt->technique() = featureAlt->TECHNIQUE_DRAPE;osg::ref_ptr<osgEarth::LineSymbol> featureLine = featureStyle.getOrCreate<osgEarth::LineSymbol>();featureLine->stroke()->color() = randomColor;featureLine->stroke()->width() = 2.0f;featureLine->tessellationSize()->set(1000.0f, osgEarth::Units::KILOMETERS);countryStyles[countryName] = featureStyle;}// Apply the style to the featurefeature->style() = countryStyles[countryName];}// 创建样式表并添加所有国家的样式osg::ref_ptr<osgEarth::StyleSheet> sheet = new osgEarth::StyleSheet;for (const auto& entry : countryStyles){sheet->addStyle(entry.second);}// 创建 FeatureImageLayer 并设置样式表osg::ref_ptr<osgEarth::FeatureImageLayer> layer = new osgEarth::FeatureImageLayer;layer->setFeatureSource(features);layer->setStyleSheet(sheet);map->addLayer(layer);// Print the status of added layersosgEarth::LayerVector layers;map->getLayers(layers);for (osgEarth::LayerVector::const_iterator it = layers.begin(); it != layers.end(); it++){std::cout << "Layer Status: " << (*it)->getStatus().toString() << std::endl;} Print the status of added layers//osgEarth::LayerVector layers;//map->getLayers(layers);//for (osgEarth::LayerVector::const_iterator it = layers.begin(); it != layers.end(); it++)//{// std::cout << "Layer Status: " << (*it)->getStatus().toString() << std::endl;//}// Set up the viewer//osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;//viewer->setSceneData(mapNode); Use EarthManipulator to control the camera//osg::ref_ptr<osgEarth::EarthManipulator> manipulator = new osgEarth::EarthManipulator;//manipulator->getSettings()->setMinMaxPitch(-90.0, 90.0);//manipulator->getSettings()->setThrowingEnabled(false);//manipulator->getSettings()->setLockAzimuthWhilePanning(false);//viewer->setCameraManipulator(manipulator); Set the initial viewpoint to look at Chengdu//osgEarth::Viewpoint vp("Chengdu", 104.0668, 30.5728, 1000000.0, 0.0, -45.0, 25000000.0);//manipulator->setHomeViewpoint(vp); Run the viewer//viewer->setUpViewInWindow(100, 100, 1600, 1200);//viewer->run();
}// 将多字节字符转换为 UTF-8 编码的字符串
std::string COSGObject::MultiByteToUTF8(const std::string& str)
{// 获取转换为宽字符所需的缓冲区大小int size_needed = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, nullptr, 0);if (size_needed <= 0) {return std::string();}// 将多字节字符转换为宽字符std::wstring wstr(size_needed, 0);::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, &wstr[0], size_needed);// 获取转换为 UTF-8 所需的缓冲区大小size_needed = ::WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr);if (size_needed <= 0) {return std::string();}// 将宽字符转换为 UTF-8 字符std::string utf8str(size_needed, 0);::WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &utf8str[0], size_needed, nullptr, nullptr);// 移除字符串末尾的空字符utf8str.pop_back();return utf8str;
}// 将多字节字符转换为宽字符
std::wstring COSGObject::MultiByteToWideChar(const std::string& str)
{int size_needed = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, nullptr, 0);std::wstring wstr(size_needed, 0);::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, &wstr[0], size_needed);return wstr;
}// 将宽字符转换为多字节字符
std::string COSGObject::WideChar2MultiByte(const std::wstring& wstr)
{int size_needed = ::WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr);std::string str(size_needed, 0);::WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &str[0], size_needed, nullptr, nullptr);return str;
}void COSGObject::ReadLabelThread(void* ptr)
{// std::lock_guard<std::mutex> lock(m_mutex);// COSGObjectauto params = static_cast<std::tuple<COSGObject*, std::shared_ptr<std::string>, unsigned int>*>(ptr);// 参数0COSGObject* osg = std::get<0>(*params);// 参数1std::shared_ptr<std::string> fileFullPath = std::get<1>(*params);// 参数2unsigned int count = std::get<2>(*params);std::cout << "fileFullPath address: " << fileFullPath.get() << std::endl;if (fileFullPath){std::cout << "fileFullPath content: = " << *fileFullPath << std::endl;}else{std::cerr << "ERROR: fileFullPath is null!" << std::endl;return;}if (fileFullPath->empty()){std::cerr << "Error: fileFullPath is empty!" << std::endl;return;}const char* tempPtr = fileFullPath->c_str();std::fstream fStream(fileFullPath->c_str(), std::ios::in);if (!fStream.is_open()){std::cerr << "Error failed to open file!" << *fileFullPath << std::endl;return;}// 参数2// unsigned int count = reinterpret_cast<unsigned int>(tempArray[2]);std::string* str1 = new std::string(*fileFullPath);// const char* tempPtr = fileFullPath->c_str();// std::fstream fStream(fileFullPath->c_str(), std::ios::in);char name[128] = {};wchar_t wName[128] = {};char area[256] = {};int level = 0;float lon = 0.0;float lat = 0.0;float alt = 0.0;osg::ref_ptr<osg::Image> tempImage = nullptr;for (int i = 0; i < count;){int j = 0;osg::ref_ptr<osg::Group> gp = new osg::Group;while (j < 100 && i < count){j++;i++;fStream >> name >> area >> level >> lon >> lat;if (strlen(name) == 0){std::cerr << "Error:Invalid name!" << std::endl;return;}if (!m_mapNode->getMapSRS()->getGeographicSRS()){std::cerr << "Error: Invalid spatial reference!" << std::endl;return;}// 将经纬度转换为全球坐标点osg::Vec3d center;// const SpatialReference// std::lock_guard<std::mutex> lock(m_mutex);osg::ref_ptr<const osgEarth::SpatialReference> spatialRef = m_mapNode->getMapSRS()->getGeographicSRS();osgEarth::GeoPoint geoPoint(spatialRef, lon, lat, alt, osgEarth::ALTMODE_ABSOLUTE);if (!geoPoint.isValid()){std::cerr << "Error:Invalid geoPoint!" << std::endl;return;}osg::Vec3d worldPos;geoPoint.toWorld(worldPos);osg::ref_ptr<osg::LOD> lod = new osg::LOD;lod->setCenterMode(osg::LOD::USER_DEFINED_CENTER);lod->setCenter(worldPos);m_mapNode->getMap()->getWorldSRS()->getGeocentricSRS();// 根据级别选择图标和可见距离long dist = 0;std::string strLabel;switch (level){case 8:{dist = 5000000;tempImage = osg->m_province;}break;case 16:{dist = 500000;tempImage = osg->m_cityCenter;}break;case 64:{dist = 100000;tempImage = osg->m_city;}break;case 256:{dist = 50000;tempImage = osg->m_countyCity;}break;case 512:{dist = 25000;tempImage = osg->m_county;}break;case 1024:{dist = 12000;tempImage = osg->m_town;}break;case 4096:{dist = 6000;tempImage = osg->m_vi;}break;default:{dist = 10;}break;}if (strlen(name) == 0){std::cerr << "Error:Invalid name!" << std::endl;return;}// 将地名取出并转换成宽字符,再转换成 UTF-8 编码的字符串std::wstring wName(128, L'\0');::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name, -1, &wName[0], 128);int sizeNeed = ::WideCharToMultiByte(CP_UTF8, 0, wName.c_str(), -1, nullptr, 0, nullptr, nullptr);strLabel.resize(sizeNeed - 1);::WideCharToMultiByte(CP_UTF8, 0, wName.c_str(), -1, &strLabel[0], sizeNeed, nullptr, nullptr);if (strLabel.empty()){std::cerr << "Error: Invalid strLabel!" << std::endl;continue;}// 创建placeNode并设置图标// osg::ref_ptr<osgEarth::PlaceNode> placeNodeImage = new osgEarth::PlaceNode(geoPoint, strLabel, m_style);osg::ref_ptr<osgEarth::PlaceNode> placeNodeImage = new osgEarth::PlaceNode(geoPoint, strLabel, *m_style);if (tempImage->valid()){placeNodeImage->setIconImage(tempImage);}lod->addChild(placeNodeImage, 0.0, static_cast<double>(dist));gp->addChild(lod);}theApp.m_needModify = TRUE;while (!theApp.m_canModify){Sleep(1);// std::lock_guard<std::mutex> lock(m_mutex);osg->m_earthLabel->addChild(gp);theApp.m_needModify = FALSE;Sleep(10);}}fStream.close();_endthread();
}void COSGObject::CreateLabelThread(void* ptr)
{theApp.m_needModify = TRUE;while (!theApp.m_canModify){Sleep(1);}// unsigned int* tempArray = (unsigned int*)ptr;参数0// COSGObject* osg = (COSGObject*)tempArray[0];参数1,地标文件路径// std::string* fileFullPath = (std::string*)tempArray[1];参数2,地标数量// unsigned int count = tempArray[2];参数3,地标输出文件路径// std::string* fileOutputPath = (std::string*)tempArray[3];auto params = static_cast<std::tuple<COSGObject*, std::string*, unsigned int, std::string*>*>(ptr);// 参数0COSGObject* osg = std::get<0>(*params);// 参数1std::string* fileFullPath = std::get<1>(*params);// 参数2unsigned int count = std::get<2>(*params);// 参数3std::string* fileOutputPath = std::get<3>(*params);// 临时变量std::string centerFile = *fileOutputPath;std::string outputPath = *fileOutputPath;centerFile += "center.center";std::fstream fIn(fileFullPath->c_str(), std::ios::in);std::fstream fOut(centerFile.c_str(), std::ios::out);// F:\osg\yangShiXing\019.Earth\builder\data\label\txt\shaanxi_label.txt// E:\osgEarth\vs2022_64bit_3rdParty_osg365_oe32\runtime\test\data\label\ive\ShaanXichar name[128] = {};wchar_t wName[128] = {};char area[256] = {};int level = 0;float lon = 0.0;float lat = 0.0;float alt = 0.0;fIn >> count;osg::ref_ptr<osg::Image> tempImage = nullptr;char iTemp[10];char iFout[256];for (size_t i = 0; i < count; i++){osg->m_processI = i;// char iNumber[10];outputPath = *fileOutputPath;osg::ref_ptr<osg::Group> gp = new osg::Group;fIn >> name >> area >> level >> lon >> lat;if (strlen(name) == 0){std::cerr << "Error:Invalid name!" << std::endl;return;}if (!m_mapNode->getMapSRS()->getGeographicSRS()){std::cerr << "Error: Invalid spatial reference!" << std::endl;return;}// 将经纬度转换为全球坐标点osg::Vec3d center;osg::ref_ptr<const osgEarth::SpatialReference> spatialRef = m_mapNode->getMapSRS()->getGeographicSRS();osgEarth::GeoPoint* geoPoint = new osgEarth::GeoPoint(spatialRef, lon, lat, alt, osgEarth::ALTMODE_RELATIVE);if (!geoPoint->isValid()){std::cerr << "Error::Invalid geoPoint!" << std::endl;return;}// osg::Vec3d worldPos;geoPoint->toWorld(center);/*osg::ref_ptr<osg::LOD> lod = new osg::LOD;lod->setCenterMode(osg::LOD::USER_DEFINED_CENTER);lod->setCenter(center);*///m_mapNode->getMap()->getWorldSRS()->getGeocentricSRS();m_mapNode->getMap()->getWorldSRS()->getGeocentricSRS();// 根据级别选择图标和可见距离long dist = 0;std::string strLabel;switch (level){case 8:{dist = 5000000;tempImage = osg->m_province;}break;case 16:{dist = 500000;tempImage = osg->m_cityCenter;}break;case 64:{dist = 100000;tempImage = osg->m_city;}break;case 256:{dist = 50000;tempImage = osg->m_countyCity;}break;case 512:{dist = 25000;tempImage = osg->m_county;}break;case 1024:{dist = 12000;tempImage = osg->m_town;}break;case 4096:{dist = 6000;tempImage = osg->m_vi;}break;default:{dist = 10;}break;}/*osg::ref_ptr<osgEarth::PlaceNode> placeNodeImage = new osgEarth::PlaceNode(geoPoint, strLabel, *m_style);if (tempImage->valid()){placeNodeImage->setIconImage(tempImage);}*/sprintf(iFout, "%.0f, %.0f, %.0f, %ld\n", center.x(), center.y(), center.z(), dist);fOut << iFout;sprintf(iTemp, "%d", i);outputPath += iTemp;outputPath += ".ive";// memset(iTemp, 0, 10);// 将地名取出并转换成宽字符,再转换成 UTF-8 编码的字符串std::wstring wName(128, L'\0');::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name, -1, &wName[0], 128);int sizeNeed = ::WideCharToMultiByte(CP_UTF8, 0, wName.c_str(), -1, nullptr, 0, nullptr, nullptr);strLabel.resize(sizeNeed - 1);::WideCharToMultiByte(CP_UTF8, 0, wName.c_str(), -1, &strLabel[0], sizeNeed, nullptr, nullptr);if (strLabel.empty()){std::cerr << "Error: Invalid strLabel!" << std::endl;continue;}// 创建placeNode并设置图标// osg::ref_ptr<osgEarth::PlaceNode> placeNodeImage = new osgEarth::PlaceNode(osg->m_mapNode, osg::Vec3d(lon, lat, alt), *m_style);//osg::ref_ptr<osgEarth::PlaceNode> placeNodeImage1 = new osgEarth::PlaceNode(*geoPoint, strLabel, *m_style);//osg::ref_ptr<osgEarth::PlaceNode> placeNodeImage = new osgEarth::PlaceNode(geoPoint, strLabel, *m_style);osg::ref_ptr<osgEarth::PlaceNode> placeNodeImage = new osgEarth::PlaceNode(*geoPoint, strLabel, *m_style);if (tempImage->valid()){placeNodeImage->setIconImage(tempImage);}osgDB::Registry::instance()->writeNode(*placeNodeImage, outputPath, osgDB::Registry::instance()->getOptions());outputPath.pop_back();outputPath.pop_back();outputPath.pop_back();outputPath.pop_back();}MessageBox(nullptr, _T("处理完成"), _T("完成"), MB_OK | MB_ICONINFORMATION);//theApp.m_needModify = TRUE;int widthiTemp = strlen(iTemp);while (widthiTemp > 0){widthiTemp--;outputPath.pop_back();}fIn.close();fOut.close();theApp.m_needModify = FALSE;_endthread();
}# 四、显示效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/16ba6118e7fa4aaaba87971278e2fe35.gif#pic_center)# 五、完整工程下载
[工程下载地址](https://download.csdn.net/download/aoxuestudy/90305307)