opengl 2.5D游戏遮挡测试

本教程通过实例演示如何使用OpenGL在Windows环境下绘制二维和三维图形,包括窗口创建、上下文设置、绘制矩形等基本操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#ifndef MODELMANGER_H #define MODELMANGER_H #include <QObject> #include <map> #include <vector> #include <osg/Node> #include <osg/Vec3d> #include <osgEarth/MapNode> #include <osg/observer_ptr> #include <osgEarthAnnotation/FeatureNode> namespace osg { class Group; class MatrixTransform; class Geode; } namespace osgEarth { class GeoPoint; class GeoTransform; } class ModelManger : public QObject { Q_OBJECT public: explicit ModelManger(QObject *parent = nullptr); /// 标记点数据结构 struct AnchorPoint { QString name; // 标记点名称 QString type; // 标记点类型 double x; // X坐标(模型局部坐标系) double y; // Y坐标 double z; // Z坐标 osgEarth::GeoPoint geoPoint; // 地理坐标(经纬度+高程) osg::ref_ptr<osg::Node> markerNode; // 标记点场景节点 }; bool loadModelTower(double lon, double lat, double alt, const QString& filePath, const QString& modelName, double angX, double angY, double angZ); bool loadModelFitForTower(const QString& filePath, const QString& towerName, const QString& pointName, double angX, double angY, double angZ); bool removeModel(const QString& modelName); Q_INVOKABLE void yflTest(QString modelName,double lon, double lat, double alt,double ang,const QString& modelPath); void setModelColor(const QString& modelName, const osg::Vec4& color); void setFitModelColor(const QString& towerName, const QString& pointName, const osg::Vec4& color); // 获取模型标记点信息 Q_INVOKABLE QVariantList getModelAnchorPoints(const QString& modelName) const; Q_INVOKABLE QVariantList getFitAnchorPoints(const QString& towerName, const QString& fitName) const; /// 两点连线 bool drawLine(const QString& lineName, const QString& startModel, const QString& startPoint, const QString& startType, const QString& endModel, const QString& endPoint, const QString& endType, const osg::Vec4& color, float width); bool removeLine(const QString& lineName); private: void applyColorToNode(osg::Node* node, const osg::Vec4& color); std::vector<ModelManger::AnchorPoint> getAnchorPointsFromJson(const QString &jsonPath, bool isTower); /// 塔模型信息结构 struct TowerModelInfo { osg::ref_ptr<osgEarth::GeoTransform> geoTransform; // 地理变换节点 osg::ref_ptr<osg::Group> modelGroup; // 主容器组 osg::ref_ptr<osg::MatrixTransform> modelXform; // 模型旋转节点 std::vector<AnchorPoint> anchorPoints; // 标记点列表 std::map<QString, osg::ref_ptr<osg::MatrixTransform>> fitModels; // 附属模型映射表 // std::map<QString, osg::ref_ptr<osg::MatrixTransform>> anchorTransforms; //存储每个标记点的变换矩阵 // 使用 observer_ptr 断开循环引用 std::map<QString, osg::observer_ptr<osg::MatrixTransform>> anchorTransforms; }; /// 附属模型信息结构 struct FitModelInfo { osg::ref_ptr<osg::MatrixTransform> transform; // 变换节点 std::vector<AnchorPoint> anchorPoints; // 标记点列表 QString parentTowerName; // 所属塔模型名称 QString pointName; // 安装点名称 }; // 计算所有标记点的地理坐标 void calculateAllAnchorPoints(); // 计算单个节点的地理坐标 osgEarth::GeoPoint calculateNodeGeoPoint(osg::Node* node) const; // 私有工具方法 osg::Node* createMarker(const QString& name, const osg::Vec3d& position); osg::Node* createBoundingBox(osg::Node* node); osg::Node* createCoordinateAxes(const osg::Vec3d& position); osg::observer_ptr<osgEarth::MapNode> m_mapNode; // 地图节点 std::map<QString, TowerModelInfo> m_towerModels; // 塔模型存储映射表 std::map<QString, FitModelInfo> m_fitModelsInfo; // 存储附属模型信息 // 添加QMutex保护 mutable QMutex m_dataMutex; /// 线段数据结构 struct LineSegment { osg::ref_ptr<osgEarth::Annotation::FeatureNode> featureNode; // 使用FeatureNode }; std::map<QString, LineSegment> m_lineSegments; // 存储所有线段 // 查找标记点坐标 bool findAnchorPoint(const QString& modelName, const QString& pointName, const QString& pointType, osgEarth::GeoPoint& outPoint) const; }; #endif // MODELMANGER_H #include "ModelManger.h" #include "utils/HkrCommon.h" #include <osgDB/ReadFile> #include <osgText/Text> #include <osgEarth/GeoTransform> #include <osg/ComputeBoundsVisitor> #include <osg/ShapeDrawable> #include <osg/MatrixTransform> #include <osg/AutoTransform> #include <osg/PositionAttitudeTransform> #include <osg/Geode> #include <osg/Geometry> #include <codecvt> #include <QDebug> #include <osg/StateSet> #include <osg/BlendFunc> #include <osg/LineWidth> #include <osgTools/OSGObject.h> #include <osgUtil/CullVisitor> #include <QVariantMap> #include <osgEarthFeatures/Feature> // 坐标单位转换宏(毫米→米) #define MM_TO_M(mm) ((mm) / 1000.0) ModelManger::ModelManger(QObject *parent) : QObject{parent} { m_mapNode = OSGObject::getInstance()->getMapNode(); } bool ModelManger::loadModelTower(double lon, double lat, double alt, const QString& filePath, const QString& modelName, double angX, double angY, double angZ) { // 检查模型名称是否已存在,存在就删除 if (m_towerModels.find(modelName) != m_towerModels.end()) { removeModel(modelName); } // 加载OBJ模型 osg::ref_ptr<osg::Node> towerModel = osgDB::readNodeFile(filePath.toStdString()); if (!towerModel) { qDebug() << "模型加载失败:" << filePath; return false; } // 输出模型原始包围盒信息 osg::ComputeBoundsVisitor cbv; towerModel->accept(cbv); osg::BoundingBox bb = cbv.getBoundingBox(); qDebug() << "模型原始包围盒(毫米):min(" << bb.xMin() << ", " << bb.yMin() << ", " << bb.zMin() << ") max(" << bb.xMax() << ", " << bb.yMax() << ", " << bb.zMax() << ")"; // 创建地理变换节点 osg::ref_ptr<osgEarth::GeoTransform> geoXform = new osgEarth::GeoTransform(); geoXform->setPosition(osgEarth::GeoPoint( m_mapNode->getMapSRS(), lon, lat, alt, osgEarth::ALTMODE_ABSOLUTE )); // 创建主容器组 osg::ref_ptr<osg::Group> modelGroup = new osg::Group(); // 添加固定旋转:绕X轴-90度(Y-up to Z-up) osg::Matrix fixRotation; fixRotation.makeRotate(osg::DegreesToRadians(-90.0), osg::X_AXIS); osg::Matrix rotationMatrix; rotationMatrix.makeRotate( osg::DegreesToRadians(angX), osg::X_AXIS, osg::DegreesToRadians(angY), osg::Y_AXIS, osg::DegreesToRadians(angZ), osg::Z_AXIS ); // 组合旋转:先应用初始修正,再应用用户旋转 osg::Matrix totalRotation = fixRotation * rotationMatrix; osg::ref_ptr<osg::MatrixTransform> modelXform = new osg::MatrixTransform(); modelXform->setMatrix(totalRotation); // 使用组合后的旋转矩阵 modelXform->addChild(towerModel); // 创建包围盒并添加到模型 osg::ref_ptr<osg::Node> bbox = createBoundingBox(towerModel); modelXform->addChild(bbox); modelGroup->addChild(modelXform); // 保存模型信息 TowerModelInfo info; // 创建标记点(添加到主容器组) QString jsonPath = filePath; jsonPath.replace(".obj", ".json"); // 获取对应的JSON文件路径 std::vector<AnchorPoint> towerPoints = getAnchorPointsFromJson(jsonPath, true); // 创建标记点节点 for (auto& pt : towerPoints) { osg::Vec3d position(MM_TO_M(pt.x), MM_TO_M(pt.y), MM_TO_M(pt.z)); qDebug() << "创建标记点" << pt.name << "坐标(米):" << position.x() << ", " << position.y() << ", " << position.z(); // 创建标记点节点 pt.markerNode = createMarker(pt.name, position); // 添加坐标轴 osg::ref_ptr<osg::Node> axes = createCoordinateAxes(position); // 创建变换节点 osg::Matrix posM; posM.makeRotate(osg::DegreesToRadians(angZ), osg::Z_AXIS); osg::ref_ptr<osg::MatrixTransform> posform = new osg::MatrixTransform(); posform->setMatrix(posM); posform->addChild(axes); posform->addChild(pt.markerNode); modelGroup->addChild(posform); // 记录标记点变换节点 info.anchorTransforms[pt.name] = posform; // 添加到标记点列表 info.anchorPoints.push_back(pt); } // 在原点添加坐标轴 modelGroup->addChild(createCoordinateAxes(osg::Vec3d(0,0,0))); // 将主容器组添加到地理变换节点 geoXform->addChild(modelGroup); // 保存模型信息 info.geoTransform = geoXform; info.modelGroup = modelGroup; info.modelXform = modelXform; info.anchorPoints = towerPoints; m_towerModels[modelName] = info; // 添加到场景 m_mapNode->addChild(geoXform); qDebug() << "塔模型加载完成:" << modelName; // 延迟计算地理坐标 calculateAllAnchorPoints(); return true; } bool ModelManger::loadModelFitForTower(const QString& filePath, const QString& towerName, const QString& pointName, double angX, double angY, double angZ) { // 查找塔模型 auto towerIt = m_towerModels.find(towerName); if (towerIt == m_towerModels.end()) { qDebug() << "未找到塔模型:" << towerName; return false; } TowerModelInfo& towerInfo = towerIt->second; // 查找标记点 auto pointIt = std::find_if(towerInfo.anchorPoints.begin(), towerInfo.anchorPoints.end(), [&](const AnchorPoint& pt) { return pt.name == pointName; }); if (pointIt == towerInfo.anchorPoints.end()) { qDebug() << "未找到标记点:" << pointName << "在塔模型" << towerName; return false; } osg::Vec3d towerPointPos(MM_TO_M(pointIt->x), MM_TO_M(pointIt->y), MM_TO_M(pointIt->z)); // 获取标记点变换节点 auto anchorTransformIt = towerInfo.anchorTransforms.find(pointName); if (anchorTransformIt == towerInfo.anchorTransforms.end()) { qDebug() << "未找到标记点变换节点:" << pointName << "在塔模型" << towerName; return false; } osg::ref_ptr<osg::MatrixTransform> anchorTransform = anchorTransformIt->second; // 加载fit模型 osg::ref_ptr<osg::Node> fitModel = osgDB::readNodeFile(filePath.toStdString()); if (!fitModel) { qDebug() << "fit模型加载失败:" << filePath; return false; } // 获取fit模型的JT点 QString fitJsonPath = filePath; fitJsonPath.replace(".obj", ".json"); std::vector<AnchorPoint> fitPoints = getAnchorPointsFromJson(fitJsonPath, false); osg::Vec3d jtPos(0, 0, 0); for (const auto& pt : fitPoints) { if (pt.type == "JT") { jtPos.set(MM_TO_M(pt.x), MM_TO_M(pt.y), MM_TO_M(pt.z)); break; } } // 创建fit模型的变换节点 osg::ref_ptr<osg::MatrixTransform> fitXform = new osg::MatrixTransform(); // 设置相对于标记点的变换 // 将JT点移动到原点 // osg::Matrix matrix = osg::Matrix::translate(-jtPos); // 应用fit模型自身旋转 osg::Matrix rotation; rotation.makeRotate( osg::DegreesToRadians(angX), osg::X_AXIS, osg::DegreesToRadians(angY), osg::Y_AXIS, osg::DegreesToRadians(angZ), osg::Z_AXIS ); // matrix = matrix * rotation; // matrix.setTrans(towerPointPos); // 移动到塔模型标记点 osg::Matrix matrix = osg::Matrix::translate(-jtPos) * rotation * osg::Matrix::translate(towerPointPos); fitXform->setMatrix(matrix); fitXform->addChild(fitModel); // 添加包围盒和坐标轴 fitXform->addChild(createBoundingBox(fitModel)); fitXform->addChild(createCoordinateAxes(osg::Vec3d(0, 0, 0))); // 创建FitModelInfo FitModelInfo fitInfo; fitInfo.transform = fitXform; fitInfo.parentTowerName = towerName; fitInfo.pointName = pointName; // 创建标记点节点 for (auto& pt : fitPoints) { osg::Vec3d position(MM_TO_M(pt.x), MM_TO_M(pt.y), MM_TO_M(pt.z)); qDebug() << "创建fit标记点" << pt.type << pt.name << "坐标(米):" << position.x() << ", " << position.y() << ", " << position.z(); // 创建标记点节点 pt.markerNode = createMarker(pt.type, position); // 添加坐标轴 osg::ref_ptr<osg::Node> axes = createCoordinateAxes(position); osg::Matrix posM; posM.makeRotate(osg::DegreesToRadians(-angX), osg::X_AXIS); osg::ref_ptr<osg::MatrixTransform> posform = new osg::MatrixTransform(); posform->setMatrix(posM); posform->addChild(axes); posform->addChild(pt.markerNode); fitXform->addChild(posform); // 添加到标记点列表 fitInfo.anchorPoints.push_back(pt); } // 存储fit模型信息 QString fitKey = towerName + "::" + pointName; m_fitModelsInfo[fitKey] = fitInfo; // 移除旧的fit模型(如果存在) auto fitIt = towerInfo.fitModels.find(pointName); if (fitIt != towerInfo.fitModels.end()) { anchorTransform->removeChild(fitIt->second); towerInfo.fitModels.erase(fitIt); } // 将fit模型节点附加到标记点变换节点 anchorTransform->addChild(fitXform); // 保存到塔模型信息中,以便后续删除 towerInfo.fitModels[pointName] = fitXform; // 调试输出 qDebug() << "fit模型加载到" << towerName << "的" << pointName << "位置, JT点: (" << jtPos.x() << "," << jtPos.y() << "," << jtPos.z() << ")" << "旋转角度: (" << angX << ", " << angY << ", " << angZ << ")"; // 计算JT点最终位置(相对于世界坐标系) osg::Matrix worldMatrix = fitXform->getWorldMatrices()[0]; osg::Vec3d jtFinal = jtPos * worldMatrix; qDebug() << "JT点最终位置: (" << jtFinal.x() << ", " << jtFinal.y() << ", " << jtFinal.z() << ")"; // 延迟计算地理坐标 calculateAllAnchorPoints(); return true; } bool ModelManger::removeModel(const QString& modelName) { auto it = m_towerModels.find(modelName); if (it == m_towerModels.end()) { qDebug() << "未找到要删除的模型:" << modelName; return false; } // 删除关联的fit模型信息 for (auto fitIt = m_fitModelsInfo.begin(); fitIt != m_fitModelsInfo.end(); ) { if (fitIt->second.parentTowerName == modelName) { fitIt = m_fitModelsInfo.erase(fitIt); } else { ++fitIt; } } // 从场景中移除整个塔模型 m_mapNode->removeChild(it->second.geoTransform); // 从管理列表中移除 m_towerModels.erase(it); qDebug() << "模型已删除:" << modelName; return true; } std::wstring string_to_wstring(const std::string& str) { std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; return converter.from_bytes(str); } // 创建坐标轴(辅助调试) osg::Node* ModelManger::createCoordinateAxes(const osg::Vec3d& position) { osg::ref_ptr<osg::Geode> geode = new osg::Geode(); // 创建几何体 osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry(); // 顶点 osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(); vertices->push_back(position); // 原点 vertices->push_back(position + osg::Vec3d(1.0, 0.0, 0.0)); // X轴 vertices->push_back(position); // 原点 vertices->push_back(position + osg::Vec3d(0.0, 1.0, 0.0)); // Y轴 vertices->push_back(position); // 原点 vertices->push_back(position + osg::Vec3d(0.0, 0.0, 1.0)); // Z轴 geometry->setVertexArray(vertices); // 颜色 osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(); colors->push_back(osg::Vec4(1.0, 0.0, 0.0, 1.0)); // 红色 - X轴 colors->push_back(osg::Vec4(1.0, 0.0, 0.0, 1.0)); colors->push_back(osg::Vec4(0.0, 1.0, 0.0, 1.0)); // 绿色 - Y轴 colors->push_back(osg::Vec4(0.0, 1.0, 0.0, 1.0)); colors->push_back(osg::Vec4(0.0, 0.0, 1.0, 1.0)); // 蓝色 - Z轴 colors->push_back(osg::Vec4(0.0, 0.0, 1.0, 1.0)); geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX); // 图元 geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, 6)); // 线宽 osg::ref_ptr<osg::LineWidth> lineWidth = new osg::LineWidth(3.0f); geometry->getOrCreateStateSet()->setAttributeAndModes(lineWidth); geode->addDrawable(geometry); return geode.release(); } bool ModelManger::findAnchorPoint(const QString &modelName, const QString &pointName, const QString &pointType, osgEarth::GeoPoint &outPoint) const { // // 1. 在塔模型中查找 // auto towerIt = m_towerModels.find(modelName); // if (towerIt != m_towerModels.end()) { // const TowerModelInfo& towerInfo = towerIt->second; // for (const auto& pt : towerInfo.anchorPoints) { // // 匹配名称或类型 // if ((!pointName.isEmpty() && pt.name == pointName) || // (!pointType.isEmpty() && pt.type == pointType)) { // if (pt.geoPoint.isValid()) { // outPoint = pt.geoPoint; // return true; // } // } // } // } // 2. 在附属模型中查找 for (const auto& fitPair : m_fitModelsInfo) { if(fitPair.first == modelName + "::" + pointName){ const FitModelInfo& fitInfo = fitPair.second; // 检查是否属于指定模型 if (fitInfo.parentTowerName == modelName && fitInfo.pointName == pointName) { for (const auto& pt : fitInfo.anchorPoints) { // 匹配名称或类型 if (!pointType.isEmpty() && pt.type == pointType) { if (pt.geoPoint.isValid()) { outPoint = pt.geoPoint; return true; } } } } } // const FitModelInfo& fitInfo = fitPair.second; // // 检查是否属于指定模型 // if (fitInfo.parentTowerName == modelName) { // for (const auto& pt : fitInfo.anchorPoints) { // // 匹配名称或类型 // if ((!pointName.isEmpty() && pt.name == pointName) || // (!pointType.isEmpty() && pt.type == pointType)) { // if (pt.geoPoint.isValid()) { // outPoint = pt.geoPoint; // return true; // } // } // } // } } // // 3. 在附属模型键值中查找 // QString fullKey = modelName + "::" + pointName; // auto fitIt = m_fitModelsInfo.find(fullKey); // if (fitIt != m_fitModelsInfo.end()) { // const FitModelInfo& fitInfo = fitIt->second; // for (const auto& pt : fitInfo.anchorPoints) { // // 匹配类型 // if (!pointType.isEmpty() && pt.type == pointType) { // if (pt.geoPoint.isValid()) { // outPoint = pt.geoPoint; // return true; // } // } // } // } qDebug() << "未找到标记点: model=" << modelName << ", point=" << pointName << ", type=" << pointType; return false; } // 创建标记点(红色小球+文字标签) osg::Node* ModelManger::createMarker(const QString& name, const osg::Vec3d& position) { osg::ref_ptr<osg::Group> group = new osg::Group(); // 创建小球 osg::Geode* sphereGeode = new osg::Geode(); osg::Sphere* sphere = new osg::Sphere(osg::Vec3d(0,0,0), 0.20); osg::ShapeDrawable* sphereDrawable = new osg::ShapeDrawable(sphere); sphereDrawable->setColor(osg::Vec4(1.0, 0.0, 0.0, 0.8));// 红色 sphereGeode->addDrawable(sphereDrawable); // 设置状态 osg::StateSet* ss = sphereGeode->getOrCreateStateSet(); ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF); ss->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON); ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); // 创建标签 osg::Geode* textGeode = new osg::Geode; osgText::Text* text = new osgText::Text; text->setFont(GISDATA"Fonts/simhei.ttf"); text->setColor(osg::Vec4(1,1,0,1)); // 黄色文本 text->setCharacterSize(0.2); // 文本尺寸 text->setPosition(osg::Vec3(-0.3,0,0.2)); // 文本位置 text->setAxisAlignment(osgText::Text::SCREEN); text->setText(string_to_wstring(name.toStdString()).c_str()); // text->setBackdropType(osgText::Text::OUTLINE); // text->setBackdropColor(osg::Vec4(0,0,0,1)); textGeode->addDrawable(text); // 创建定位节点(将标记移动到指定位置) osg::ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform(); pat->setPosition(position); // 设置模型局部坐标位置 pat->addChild(sphereGeode); pat->addChild(textGeode); // 直接返回定位变换PAT节点 return pat.release(); // group->addChild(pat); // return group.release(); } // 创建模型包围盒(绿色线框) osg::Node* ModelManger::createBoundingBox(osg::Node* node) { // 计算模型边界 osg::ComputeBoundsVisitor cbv; node->accept(cbv); osg::BoundingBox bb = cbv.getBoundingBox(); // 创建几何体 osg::ref_ptr<osg::Geometry> geom = new osg::Geometry(); // 添加8个顶点 osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(); vertices->push_back(osg::Vec3(bb.xMin(), bb.yMin(), bb.zMin())); vertices->push_back(osg::Vec3(bb.xMax(), bb.yMin(), bb.zMin())); vertices->push_back(osg::Vec3(bb.xMax(), bb.yMax(), bb.zMin())); vertices->push_back(osg::Vec3(bb.xMin(), bb.yMax(), bb.zMin())); vertices->push_back(osg::Vec3(bb.xMin(), bb.yMin(), bb.zMax())); vertices->push_back(osg::Vec3(bb.xMax(), bb.yMin(), bb.zMax())); vertices->push_back(osg::Vec3(bb.xMax(), bb.yMax(), bb.zMax())); vertices->push_back(osg::Vec3(bb.xMin(), bb.yMax(), bb.zMax())); geom->setVertexArray(vertices); // 添加边索引(12条边) osg::ref_ptr<osg::DrawElementsUInt> lines = new osg::DrawElementsUInt(osg::PrimitiveSet::LINES); const int indices[] = { 0,1, 1,2, 2,3, 3,0, // 底面 4,5, 5,6, 6,7, 7,4, // 顶面 0,4, 1,5, 2,6, 3,7 // 侧面 }; for (int i = 0; i < 24; ++i) lines->push_back(indices[i]); geom->addPrimitiveSet(lines); // 设置颜色(绿色) osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(); colors->push_back(osg::Vec4(0,1,0,1)); geom->setColorArray(colors, osg::Array::BIND_OVERALL); // 设置线宽 osg::ref_ptr<osg::LineWidth> lineWidth = new osg::LineWidth(2.0f); geom->getOrCreateStateSet()->setAttributeAndModes(lineWidth); // 创建几何节点 osg::ref_ptr<osg::Geode> geode = new osg::Geode(); geode->addDrawable(geom); return geode.release(); } std::vector<ModelManger::AnchorPoint> ModelManger::getAnchorPointsFromJson(const QString &jsonPath, bool isTower) { std::vector<AnchorPoint> points; QFile jsonFile(jsonPath); if (!jsonFile.open(QIODevice::ReadOnly)) { qWarning() << "无法打开JSON文件:" << jsonPath; return points; } QJsonDocument doc = QJsonDocument::fromJson(jsonFile.readAll()); if (doc.isNull()) { qWarning() << "无效的JSON格式:" << jsonPath; return points; } QJsonObject root = doc.object(); if (isTower) { // 解析塔模型的标记点 (来自"Tower.Head.HeadList[0].Property.Hanging_Points") QJsonObject tower = root.value("Tower").toObject(); QJsonObject head = tower.value("Head").toObject(); QJsonArray headList = head.value("HeadList").toArray(); if (!headList.isEmpty()) { QJsonObject firstHead = headList[0].toObject(); QJsonObject property = firstHead.value("Property").toObject(); QJsonArray hangingPoints = property.value("Hanging_Points").toArray(); for (const QJsonValue& ptVal : hangingPoints) { QJsonObject ptObj = ptVal.toObject(); AnchorPoint pt; pt.name = ptObj["name"].toString(); pt.type = ""; // 塔模型没有类型字段 pt.x = ptObj["x"].toDouble(); pt.y = ptObj["y"].toDouble(); pt.z = ptObj["z"].toDouble(); points.push_back(pt); } } } else { // 解析fit模型的标记点 (来自"Locations") QJsonArray locations = root.value("Locations").toArray(); for (const QJsonValue& locVal : locations) { QJsonObject locObj = locVal.toObject(); AnchorPoint pt; pt.name = locObj["name"].toString(); pt.type = locObj["Type"].toString(); // 保存类型 pt.x = locObj["x"].toString().toDouble(); // 注意:x,y,z是字符串类型 pt.y = locObj["y"].toString().toDouble(); pt.z = locObj["z"].toString().toDouble(); points.push_back(pt); } } // 添加原点作为参考点 AnchorPoint origin; origin.name = "原点"; origin.type = "origin"; origin.x = 0; origin.y = 0; origin.z = 0; points.push_back(origin); return points; } // 实现颜色设置方法 void ModelManger::applyColorToNode(osg::Node* node, const osg::Vec4& color) { if (!node) return; // 创建或获取状态集 osg::StateSet* stateSet = node->getOrCreateStateSet(); // 创建材质 osg::ref_ptr<osg::Material> material = new osg::Material; material->setAmbient(osg::Material::FRONT_AND_BACK, color); material->setDiffuse(osg::Material::FRONT_AND_BACK, color); material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4(0.1, 0.1, 0.1, 1.0)); material->setShininess(osg::Material::FRONT_AND_BACK, 20.0); // 设置材质 stateSet->setAttributeAndModes(material, osg::StateAttribute::ON); // 处理透明效果 if (color.a() < 1.0f) { stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); stateSet->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); } else { stateSet->setMode(GL_BLEND, osg::StateAttribute::OFF); } } void ModelManger::setModelColor(const QString& modelName, const osg::Vec4& color) { auto it = m_towerModels.find(modelName); if (it == m_towerModels.end()) { qDebug() << "未找到塔模型:" << modelName; return; } TowerModelInfo& info = it->second; applyColorToNode(info.modelXform, color); } void ModelManger::setFitModelColor(const QString& towerName, const QString& pointName, const osg::Vec4& color) { auto towerIt = m_towerModels.find(towerName); if (towerIt == m_towerModels.end()) { qDebug() << "未找到塔模型:" << towerName; return; } TowerModelInfo& info = towerIt->second; auto fitIt = info.fitModels.find(pointName); if (fitIt == info.fitModels.end()) { qDebug() << "未找到附属模型:" << pointName << "在塔模型" << towerName; return; } applyColorToNode(fitIt->second, color); } void ModelManger::yflTest(QString modelName, double lon, double lat, double alt, double ang, const QString &modelPath) { qDebug() << "开始测试:"; qDebug() << " 模型名称:" << modelName; qDebug() << " 位置:" << lon << "," << lat << "," << alt; qDebug() << " 旋转角度:" << ang << "度"; qDebug() << " 模型路径:" << modelPath; // 只加载塔模型,不加载fit模型 bool result = loadModelTower( lon, lat, alt, // 经纬度和高程 GISDATA"bimmodel/tower/21178.obj", // 模型路径 modelName, // 模型名称 0, 0, ang // 旋转角度 ); if (result) { qDebug() << "塔模型加载成功"; } else { qDebug() << "塔模型加载失败"; } // 设置塔模型为红色 // setModelColor(modelName, osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); //在塔模型的"导1"位置加载fit模型 loadModelFitForTower( GISDATA"bimmodel/fit/166817290949493765.obj", // fit模型路径 modelName, // 目标塔名称 "跳1-1", // 目标标记点 -90, 0, 90 // 旋转角度 ); loadModelFitForTower( GISDATA"bimmodel/fit/166817290949493765.obj", modelName, "跳1-2", -90, 0, 90 ); loadModelFitForTower( GISDATA"bimmodel/fit/166817290949493765.obj", modelName, "跳1-3", -90, 0, 90 ); loadModelFitForTower( GISDATA"bimmodel/fit/166817290986193925.obj", modelName, "前导1", -90, 0, 90 ); loadModelFitForTower( GISDATA"bimmodel/fit/166817290986193925.obj", modelName, "后导1", -90, 0, -90 ); // 设置模型为半透明蓝色 setFitModelColor(modelName, "前导1", osg::Vec4(0.0f, 0.0f, 1.0f, 0.7f)); setFitModelColor(modelName, "跳1-2", osg::Vec4(0.0f, 0.0f, 1.0f, 0.7f)); setFitModelColor(modelName, "后导1", osg::Vec4(0.0f, 0.0f, 1.0f, 0.7f)); // 获取塔模型标记点 QVariantList points = getModelAnchorPoints(modelName); for (int i = 0; i < points.size (); i++) { qDebug()<<"塔模型标记点:"<<points[i]; } // 获取fit模型标记点 QVariantList pointfits = getFitAnchorPoints(modelName, "前导1"); for (int j = 0; j < pointfits.size (); j++) { qDebug()<<"Fit模型标记点:"<<pointfits[j]; } //连线测试 xx塔的xx挂点上串子的xx点 line to xx塔的xx挂点上串子的xx点 // 添加连线 drawLine("line1", modelName, "前导1", "TX", modelName, "跳1-1", "DAX", osg::Vec4(1, 0, 0, 1), // 红色 3.0f); drawLine("line2", modelName, "跳1-1", "DAX", modelName, "跳1-2", "DAX", osg::Vec4(0, 1, 0, 1), // 绿色 2.0f); drawLine("line3", modelName, "跳1-2", "DAX", modelName, "跳1-3", "DAX", osg::Vec4(0, 0, 1, 1), // 蓝色 2.5f); drawLine("line4", modelName, "跳1-3", "DAX", modelName, "后导1", "TX", osg::Vec4(1, 1, 0, 1), // 黄色 3.0f); } // 计算所有标记点的地理坐标 void ModelManger::calculateAllAnchorPoints() { // 计算塔模型标记点 for (auto& towerPair : m_towerModels) { TowerModelInfo& towerInfo = towerPair.second; for (auto& anchorPoint : towerInfo.anchorPoints) { if (anchorPoint.markerNode) { anchorPoint.geoPoint = calculateNodeGeoPoint(anchorPoint.markerNode); if (anchorPoint.geoPoint.isValid()) { qDebug() << "塔模型标记点" << anchorPoint.name << "位置: " <<QString::number( anchorPoint.geoPoint.x(),'f',8) << ", " <<QString::number( anchorPoint.geoPoint.y(),'f',8) << ", " <<QString::number( anchorPoint.geoPoint.z(),'f',8); } } } } // 计算附属模型标记点 for (auto& fitPair : m_fitModelsInfo) { FitModelInfo& fitInfo = fitPair.second; for (auto& anchorPoint : fitInfo.anchorPoints) { if (anchorPoint.markerNode) { anchorPoint.geoPoint = calculateNodeGeoPoint(anchorPoint.markerNode); if (anchorPoint.geoPoint.isValid()) { qDebug() << "附属模型标记点" << anchorPoint.name << "位置: " <<QString::number( anchorPoint.geoPoint.x(),'f',8) << ", " <<QString::number( anchorPoint.geoPoint.y(),'f',8) << ", " <<QString::number( anchorPoint.geoPoint.z(),'f',8); } } } } } // // 计算节点的地理坐标 // osgEarth::GeoPoint ModelManger::calculateNodeGeoPoint(osg::Node* node) const { // if (!node || !m_mapNode) return osgEarth::GeoPoint::INVALID; // // 获取节点的父路径列表 // osg::NodePathList nodePaths = node->getParentalNodePaths(); // if (nodePaths.empty()) return osgEarth::GeoPoint::INVALID; // // 使用第一条父路径(通常只有一个) // const osg::NodePath& nodePath = nodePaths.front(); // osg::Matrix worldMatrix = osg::computeLocalToWorld(nodePath); // // // 计算原点在世界坐标系中的位置 // // osg::Vec3d worldPosition = osg::Vec3d(0, 0, 0) * worldMatrix; // // 获取节点的实际位置 // osg::Vec3d worldPosition = node->getBound().center() * worldMatrix; // // 转换为地理坐标 // osgEarth::GeoPoint geoPoint; // geoPoint.fromWorld(m_mapNode->getMapSRS(), worldPosition); // return geoPoint; // } osgEarth::GeoPoint ModelManger::calculateNodeGeoPoint(osg::Node* node) const { // if (!node || !m_mapNode) return osgEarth::GeoPoint::INVALID; // osg::NodePathList nodePaths = node->getParentalNodePaths(); // if (nodePaths.empty()) return osgEarth::GeoPoint::INVALID; // const osg::NodePath& nodePath = nodePaths.front(); // osg::Matrix worldMatrix = osg::computeLocalToWorld(nodePath); // // 关键修复:获取节点实际位置 // osg::Vec3d localPos = node->getBound().center(); // osg::Vec3d worldPosition = localPos * worldMatrix; // osgEarth::GeoPoint geoPoint; // geoPoint.fromWorld(m_mapNode->getMapSRS(), worldPosition); // return geoPoint; if (!node || !m_mapNode) return osgEarth::GeoPoint::INVALID; // 获取节点的世界变换矩阵 osg::NodePathList nodePaths = node->getParentalNodePaths(); if (nodePaths.empty()) return osgEarth::GeoPoint::INVALID; osg::Matrix worldMatrix = osg::computeLocalToWorld(nodePaths.front()); // 关键修改:获取标记点的实际位置(PAT节点设置的位置) osg::PositionAttitudeTransform* pat = dynamic_cast<osg::PositionAttitudeTransform*>(node); if (pat) { // 获取PAT节点设置的位置(模型局部坐标系) osg::Vec3d markerPos = pat->getPosition(); // 转换为世界坐标系 osg::Vec3d worldPosition = markerPos * worldMatrix; osgEarth::GeoPoint geoPoint; geoPoint.fromWorld(m_mapNode->getMapSRS(), worldPosition); return geoPoint; } return osgEarth::GeoPoint::INVALID; } // 获取塔模型标记点信息 QVariantList ModelManger::getModelAnchorPoints(const QString& modelName) const { QMutexLocker locker(&m_dataMutex); QVariantList points; auto it = m_towerModels.find(modelName); if (it == m_towerModels.end()) return points; for (const auto& pt : it->second.anchorPoints) { QVariantMap pointInfo; pointInfo["name"] = pt.name; pointInfo["type"] = pt.type; if (pt.geoPoint.isValid()) { pointInfo["lon"] = pt.geoPoint.x(); pointInfo["lat"] = pt.geoPoint.y(); pointInfo["alt"] = pt.geoPoint.z(); } else { pointInfo["lon"] = 0; pointInfo["lat"] = 0; pointInfo["alt"] = 0; } points.append(pointInfo); } return points; } // 获取附属模型标记点信息 QVariantList ModelManger::getFitAnchorPoints( const QString& towerName, const QString& fitName) const { QVariantList points; // 构建唯一标识符 QString fullId = towerName + "::" + fitName; auto it = m_fitModelsInfo.find(fullId); if (it == m_fitModelsInfo.end()) return points; for (const auto& pt : it->second.anchorPoints) { QVariantMap pointInfo; pointInfo["name"] = pt.name; pointInfo["type"] = pt.type; if (pt.geoPoint.isValid()) { pointInfo["lon"] = pt.geoPoint.x(); pointInfo["lat"] = pt.geoPoint.y(); pointInfo["alt"] = pt.geoPoint.z(); } else { pointInfo["lon"] = 0; pointInfo["lat"] = 0; pointInfo["alt"] = 0; } points.append(pointInfo); } return points; } //line // 绘制两点之间的线段 bool ModelManger::drawLine(const QString& lineName, const QString& startModel, const QString& startPoint, const QString& startType, const QString& endModel, const QString& endPoint, const QString& endType, const osg::Vec4& color, float width) { // 删除已存在的同名线段 removeLine(lineName); // 获取起点和终点的地理坐标 osgEarth::GeoPoint startGeo, endGeo; if (!findAnchorPoint(startModel, startPoint, startType, startGeo) || !findAnchorPoint(endModel, endPoint, endType, endGeo)) { return false; } qDebug() << "绘制线段: " << lineName << "\n 起点: (" << startGeo.x() << ", " << startGeo.y() << ", " << startGeo.z() << ")" << "\n 终点: (" << endGeo.x() << ", " << endGeo.y() << ", " << endGeo.z() << ")"; // 创建线特征 osgEarth::LineString* line = new osgEarth::LineString(); line->push_back(startGeo.x (),startGeo.y (),startGeo.z ()); line->push_back(endGeo.x (),endGeo.y (),endGeo.z ()); // 创建样式 osgEarth::Style style; style.getOrCreate<osgEarth::LineSymbol>()->stroke()->color() = color; style.getOrCreate<osgEarth::LineSymbol>()->stroke()->width() = width; style.getOrCreate<osgEarth::LineSymbol>()->stroke()->widthUnits() = osgEarth::Units::PIXELS; // 设置高度模式为绝对高度 style.getOrCreate<osgEarth::AltitudeSymbol>()->clamping() = osgEarth::AltitudeSymbol::CLAMP_ABSOLUTE; style.getOrCreate<osgEarth::AltitudeSymbol>()->technique() = osgEarth::AltitudeSymbol::TECHNIQUE_DRAPE; // 创建特征 osgEarth::Features::Feature* feature = new osgEarth::Features::Feature(line, m_mapNode->getMapSRS(), style); feature->geoInterp() = osgEarth::GEOINTERP_GREAT_CIRCLE; // 使用大圆航线 // 创建特征节点 osgEarth::Annotation::FeatureNode* featureNode = new osgEarth::Annotation::FeatureNode(feature); featureNode->setMapNode(m_mapNode.get ()); // 添加到场景 m_mapNode->addChild(featureNode); // 存储线段信息 LineSegment segment; segment.featureNode = featureNode; m_lineSegments[lineName] = segment; return true; } // 删除线段 bool ModelManger::removeLine(const QString& lineName) { auto it = m_lineSegments.find(lineName); if (it != m_lineSegments.end()) { m_mapNode->removeChild(it->second.featureNode); m_lineSegments.erase(it); qDebug() << "已删除线段: " << lineName; return true; } return false; } 上述代码运行ModelManger::yflTest后连线不准确,没有在对应的点位置,检查代码找出问题,并修改(输出完整.h .cpp)
最新发布
07-29
<think>根据用户的问题,他正在调试ModelManager类中的yflTest方法,该方法使用drawLine绘制连线,但位置不准确。用户希望得到调试和修复代码的建议。 首先,我们需要考虑可能的原因: 1. 坐标系统问题:绘制的坐标是否与视图或场景的坐标系统匹配? 2. 变换问题:是否在绘制时应用了不正确的变换(如视图变换、投影变换)? 3. 绘制参数问题:线宽、颜色等是否设置正确,导致视觉上的偏差? 4. 数据问题:传入drawLine的坐标值是否正确? 由于用户提到了osgEarth和Qt,我们需要考虑这两个框架的坐标系统差异。在Qt中,默认的坐标系统是左上角为原点(0,0),x向右增加,y向下增加。而在osgEarth中,使用的是三维场景,坐标系统可能是不同的(例如,OpenGL的坐标系统是中心为原点,y向上)。 在用户提供的引用[1]中,有一个使用QPainter绘制直线的例子,但这是在纯Qt的2D绘图环境中。如果用户是在osgEarth的3D场景中绘制2D叠加(例如使用HUD),那么可能需要将3D坐标转换为2D屏幕坐标。 另外,引用[2]提到了Qt的信号槽机制,但似乎与当前问题关系不大。 因此,我建议以下调试步骤: 1. **确认绘制环境**:确定drawLine是在什么上下文中调用的?是在Qt的2D绘图上下文中,还是在osgEarth的3D场景中?如果是3D场景,那么可能需要使用osgEarth的Annotation(注释)功能来绘制线,或者使用osg的绘图基元。 2. **检查坐标值**:在调用drawLine之前,打印出传入的坐标值,确保它们是正确的。如果这些坐标是3D场景中的坐标,那么需要将它们投影到2D屏幕坐标。可以使用osgEarth的Util::getViewpointMatrix或者osg的投影矩阵和视图矩阵来转换。 3. **检查绘制代码**:如果是在Qt的2D覆盖层上绘制,确保绘制的窗口大小和视口设置正确。例如,如果窗口大小改变了,是否重新调整了绘制区域? 4. **考虑变换**:在绘制之前,是否应用了任何变换(例如平移、缩放)?这些变换可能导致绘制位置偏移。 5. **使用简单的测试数据**:尝试在固定位置绘制一条线(例如从(0,0)到(100,100)),看是否显示正确。如果简单测试正确,那么问题可能出在坐标转换上。 6. **检查渲染顺序**:如果是叠加在3D场景上,确保2D绘制在3D场景之后渲染,否则可能被遮挡。 由于用户提到的是ModelManager类中的yflTest方法,我们假设这个方法是在Qt和osgEarth集成的环境中进行绘制。在osgEarth中,通常使用osg::Camera来创建HUD(平视显示器)进行2D绘制。如果用户是在这样的HUD中绘制,那么应该使用Qt的坐标系统(即原点在左上角)。 下面是一个可能的修复方向: - 如果绘制在HUD上,确保使用正确的投影矩阵(正交投影)和视图矩阵(单位矩阵)。通常,HUD的投影矩阵设置为从0到屏幕宽度和高度。 - 检查传入drawLine的坐标是否在屏幕坐标范围内(例如,x在0到屏幕宽度之间,y在0到屏幕高度之间)。 由于没有具体的代码,我无法给出确切的修复代码。但是,我可以提供一个基于osgEarth的HUD绘制直线的示例,以及一个在Qt中绘制直线的示例,供用户参考。 ### 在osgEarth中使用HUD绘制直线 ```cpp #include <osgEarth/MapNode> #include <osgEarth/AnnotationUtils> #include <osgEarth/GeoMath> #include <osgEarth/Viewpoint> #include <osgViewer/Viewer> #include <osg/Geode> #include <osg/Geometry> #include <osg/MatrixTransform> // 创建一个HUD相机 osg::Camera* createHUDCamera(double width, double height) { osg::Camera* camera = new osg::Camera; camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); camera->setViewMatrix(osg::Matrix::identity()); camera->setProjectionMatrix(osg::Matrix::ortho2D(0, width, 0, height)); camera->setViewport(0, 0, width, height); camera->setRenderOrder(osg::Camera::POST_RENDER); // 确保最后渲染 camera->setClearMask(GL_DEPTH_BUFFER_BIT); return camera; } // 创建一条线段 osg::Geometry* createLineGeometry(float x1, float y1, float x2, float y2, const osg::Vec4& color, float width) { osg::Geometry* geometry = new osg::Geometry; osg::Vec3Array* vertices = new osg::Vec3Array; vertices->push_back(osg::Vec3(x1, y1, 0)); vertices->push_back(osg::Vec3(x2, y2, 0)); geometry->setVertexArray(vertices); geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, 2)); osg::Vec4Array* colors = new osg::Vec4Array; colors->push_back(color); geometry->setColorArray(colors, osg::Array::BIND_OVERALL); osg::LineWidth* linewidth = new osg::LineWidth; linewidth->setWidth(width); geometry->getOrCreateStateSet()->setAttributeAndModes(linewidth, osg::StateAttribute::ON); return geometry; } // 在HUD上添加直线 void addLineToHUD(osg::Group* root, float x1, float y1, float x2, float y2, float screenWidth, float screenHeight) { osg::Camera* hudCamera = createHUDCamera(screenWidth, screenHeight); osg::Geode* geode = new osg::Geode; // 注意:在HUD相机中,原点在左下角,但通常我们希望原点在左上角,所以这里将y坐标翻转 // 如果希望原点在左上角,可以使用:y1 = screenHeight - y1; y2 = screenHeight - y2; // 但createHUDCamera设置的是左下角为原点,所以这里我们传入的y坐标应该是从底部开始计算的。 // 如果用户希望使用Qt的坐标系统(原点在左上角),那么需要将y坐标转换为底部为原点:y = screenHeight - y; geode->addDrawable(createLineGeometry(x1, screenHeight - y1, x2, screenHeight - y2, osg::Vec4(1,0,0,1), 2.0f)); hudCamera->addChild(geode); root->addChild(hudCamera); } ``` 在上面的代码中,我们创建了一个HUD相机,并在其上绘制了一条直线。注意,我们将传入的y坐标转换为从底部开始计算(因为OpenGL的窗口坐标系统是左下角为原点),这样如果传入的坐标是Qt坐标(左上角原点),就可以正确转换。 ### 在Qt中绘制直线(纯Qt) 如果用户是在Qt的QWidget中绘制,那么可以使用QPainter,如引用[1]所示。但要注意,如果这个QWidget是嵌入到osgEarth中的(例如通过QWidget嵌入到osg场景中),那么可能还需要处理坐标转换。 ### 针对yflTest方法的建议 1. 检查yflTest方法中绘制直线的代码部分,确认坐标值的来源和转换。 2. 确保在绘制之前,将3D坐标(如果有)转换为2D屏幕坐标。可以使用osgEarth的Util::getViewpointMatrix或者osg::Camera的帮助方法。 3. 如果是在HUD上绘制,确保正确转换y坐标(翻转)。 如果用户提供更多代码细节,我们可以给出更具体的建议。 ### 相关问题 1. 在osgEarth中如何正确创建HUD并绘制2D图形? 2. 如何将3D场景中的坐标转换为2D屏幕坐标? 3. Qt和osgEarth集成时,如何处理坐标系统的差异? 4. 在osgEarth中,除了HUD,还有哪些方法可以绘制2D叠加层?</think>### 调试与修复 ModelManager::yflTest() 连线位置不准确问题 #### 问题分析 连线位置不准确通常由以下原因导致: 1. **坐标系统不匹配**:Qt 屏幕坐标(左上角原点)与 osgEarth 世界坐标(三维空间)未正确转换 2. **投影矩阵错误**:绘制时未应用当前视图的投影矩阵 3. **视口计算偏差**:窗口尺寸变化时未更新视口参数 4. **绘制时机不当**:在场景更新前/后绘制导致位置偏移 #### 调试步骤 1. **验证坐标值** ```cpp // 在 yflTest() 中添加调试输出 void ModelManager::yflTest() { qDebug() << "Start point:" << startPoint.x() << startPoint.y(); qDebug() << "End point:" << endPoint.x() << endPoint.y(); // ...原有绘图代码... } ``` 2. **检查坐标转换** ```cpp // 确保使用正确的视图矩阵 osg::Matrixd viewMatrix = _viewer->getCamera()->getViewMatrix(); osg::Matrixd projMatrix = _viewer->getCamera()->getProjectionMatrix(); ``` 3. **添加参考基准线** ```cpp // 绘制绝对位置的参考线(验证坐标系) painter.drawLine(0, 0, 100, 100); // 应出现在窗口左上角 ``` #### 修复方案 ```cpp void ModelManager::yflTest() { // 1. 获取当前视口尺寸 osg::Viewport* viewport = _viewer->getCamera()->getViewport(); int screenWidth = viewport->width(); int screenHeight = viewport->height(); // 2. 坐标转换函数(世界坐标→屏幕坐标) auto toScreenCoords = [&](const osg::Vec3d& worldPos) -> QPoint { osg::Matrixd viewMatrix = _viewer->getCamera()->getViewMatrix(); osg::Matrixd projMatrix = _viewer->getCamera()->getProjectionMatrix(); osg::Matrixd viewProj = viewMatrix * projMatrix; // 投影变换 osg::Vec3d screenPos = worldPos * viewProj; // 归一化并转换到屏幕坐标 double x = (screenPos.x() + 1.0) * 0.5 * screenWidth; double y = (1.0 - (screenPos.y() + 1.0) * 0.5) * screenHeight; // Y轴翻转 return QPoint(static_cast<int>(x), static_cast<int>(y)); }; // 3. 转换坐标(示例点) QPoint screenStart = toScreenCoords(osg::Vec3d(0, 0, 0)); // 世界原点 QPoint screenEnd = toScreenCoords(targetPosition); // 4. 创建绘图表面 QPixmap pixmap(screenWidth, screenHeight); pixmap.fill(Qt::transparent); // 透明背景 // 5. 使用抗锯齿绘制 QPainter painter(&pixmap); painter.setRenderHint(QPainter::Antialiasing, true); painter.setPen(QPen(Qt::red, 2, Qt::SolidLine)); painter.drawLine(screenStart, screenEnd); // 使用转换后的坐标 // 6. 转换为OSG纹理并更新 osg::ref_ptr<osgQt::QPixmapImage> pixmapImage = new osgQt::QPixmapImage(pixmap); _overlayTexture->setImage(pixmapImage.get()); } ``` #### 关键修复点 1. **坐标系统同步**:通过 `toScreenCoords` 函数实现精确的世界坐标→屏幕坐标转换 2. **Y轴翻转处理**:补偿 Qt(左上角原点)与 OpenGL(左下角原点)的差异: $$ y_{screen} = (1.0 - \frac{y_{normalized} + 1.0}{2}) \times height $$ 3. **动态视口适应**:实时获取窗口尺寸,避免固定尺寸导致的错位 4. **抗锯齿支持**:启用 `QPainter::Antialiasing` 改善视觉精度 #### 验证方法 1. 在原点(0,0,0)绘制十字标记,验证是否对齐场景原点 2. 移动相机时检查连线是否持续连接目标对象 3. 调整窗口大小时确认连线位置自适应更新 ### 相关问题 1. 如何在 osgEarth 中实现动态对象的屏幕空间标注? 2. Qt 与 osgEarth 集成时有哪些常见的坐标转换陷阱? 3. 如何优化 OpenGL 与 Qt 混合渲染的性能? 4. 在三维场景中绘制二维叠加层的最佳实践是什么? [^1]: 引用自 Qt 绘图示例 [^2]: 参考 Qt 信号槽机制说明
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值