You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

osgEarth::ModelNode动态更新顶点数据及圆锥高度修改失效咨询

解决osgEarth中修改圆锥高度不生效的问题

问题根源

核心问题在于ModelNode会对传入的模型进行内部克隆/实例化,你在外部修改_cone_coneDrawable的参数,并不会同步到ModelNode内部实际渲染的副本上。另外你设置了_coneDrawable->setUseDisplayList(false),此时调用dirtyDisplayList()本身就不会起作用。

修复方案

方案1:重构节点结构,直接控制渲染节点(推荐)

放弃通过ModelSymbol::setModel给ModelNode传入模型,改用GeoTransform处理地形贴合和位置管理,这样你直接操作的就是场景中实际渲染的节点,修改参数后能立即生效:

修改构造函数:

Cone::Cone(osgEarth::MapNode* mapNode) {
    if(!mapNode){
        return;
    }
    _height = 700000.0;
    _cone = new osg::Cone(osg::Vec3(0, 0, 0), 300000.0, _height);
    _coneDrawable = new osg::ShapeDrawable(_cone);
    _coneDrawable->setColor(osg::Vec4d(0.0,1.0,0.0,0.5));
    osg::ref_ptr<osg::Geode> geode = new osg::Geode();
    geode->addDrawable(_coneDrawable);
    geode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);

    osgEarth::ShaderGenerator shg;
    geode->accept(shg);

    _mt = new osg::MatrixTransform;
    _mt->setMatrix(
        osg::Matrix::rotate(0.0, osg::X_AXIS,
                            0.0, osg::Y_AXIS,
                            0.0, osg::Z_AXIS)
        *osg::Matrix::translate(osg::Vec3d(0,  0, -_height*3/4)));
    _mt->addChild(geode);

    // 改用GeoTransform处理地形贴合
    osgEarth::GeoTransform* geoXform = new osgEarth::GeoTransform();
    osgEarth::Style style;
    style.getOrCreate<osgEarth::AltitudeSymbol>()->clamping() = osgEarth::AltitudeSymbol::CLAMP_TO_TERRAIN; 
    style.getOrCreate<osgEarth::AltitudeSymbol>()->binding() = osgEarth::AltitudeSymbol::BINDING_CENTROID;
    geoXform->setStyle(style);
    geoXform->addChild(_mt);

    // 设置初始位置
    osgEarth::GeoPoint position(
        mapNode->getMapSRS(),
        -122.0, 37.0, 1000.0, 
        osgEarth::ALTMODE_RELATIVE);
    geoXform->setPosition(position);
    geoXform->setLocalRotation(osg::Quat(osg::DegreesToRadians(90.0),osg::Vec3(0.0,0.0,0.0)));

    this->addChild(geoXform);
    // 保存GeoTransform指针,方便后续更新
    _geoXform = geoXform;
}

修改updateCone方法:

void Cone::updateCone(osgEarth::GeoPoint gp,double height,double r,osg::Quat quat){
    _height = height;

    // 更新圆锥参数
    _cone->setHeight(_height);
    _cone->setRadius(r);

    // 更新MatrixTransform的平移
    _mt->setMatrix(
        osg::Matrix::rotate(quat)
        *osg::Matrix::translate(osg::Vec3d(0,  0, -_height*3/4)));

    // 通知Drawable更新
    _coneDrawable->dirtyBound(); // 更新包围盒
    _coneDrawable->dirtyVertexBufferObjects(); // 更新VBO数据
    
    // 更新位置和旋转
    _geoXform->setPosition(gp);
    _geoXform->setLocalRotation(quat);
}

方案2:获取ModelNode内部的实际渲染节点(不推荐,耦合性高)

若坚持使用ModelNode,可通过遍历子节点找到内部的ShapeDrawable修改,但该方式依赖ModelNode内部实现,版本更新后可能失效:

void Cone::updateCone(osgEarth::GeoPoint gp,double height,double r,osg::Quat quat){
    _height = height;

    // 遍历ModelNode子节点查找目标Drawable
    struct FindConeDrawable : public osg::NodeVisitor {
        osg::ShapeDrawable* result = nullptr;
        FindConeDrawable() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
        void apply(osg::Geode& geode) override {
            for(auto drawable : geode.getDrawableList()){
                if(auto sd = dynamic_cast<osg::ShapeDrawable*>(drawable.get())){
                    if(dynamic_cast<osg::Cone*>(sd->getShape())){
                        result = sd;
                        break;
                    }
                }
            }
        }
    };

    FindConeDrawable finder;
    _modelNode->accept(finder);
    if(finder.result){
        auto cone = dynamic_cast<osg::Cone*>(finder.result->getShape());
        if(cone){
            cone->setHeight(height);
            cone->setRadius(r);
            finder.result->dirtyBound();
            finder.result->dirtyVertexBufferObjects();
        }
    }

    _modelNode->setPosition(gp);
    _modelNode->setLocalRotation(quat);
}

关键注意点

  • 修改osg::Shape参数后,必须调用ShapeDrawabledirtyBound()更新包围盒,否则视锥体剔除可能出错;若使用VBO(默认开启),需调用dirtyVertexBufferObjects()更新顶点数据。
  • 关闭DisplayList后,dirtyDisplayList()不再生效,无需调用。

内容的提问来源于stack exchange,提问作者user30325735

火山引擎 最新活动