在这里插入图片描述

NodeVisitor(顶点访问器)

在 OSG(OpenSceneGraph)三维渲染引擎中,顶点访问器(Vertex Visitor) 是操作场景几何数据最核心、最常用的工具。

它基于访问者设计模式实现,完美实现了场景结构与操作逻辑的解耦,让开发者无需修改引擎源码,就能遍历、提取、修改场景中所有模型的顶点数据。

本文将从设计思想、继承关系、核心API、实战代码、使用场景五个维度,带你彻底掌握 OSG 顶点访问器。


什么是顶点访问器?

顶点访问器是专门用于遍历、处理场景中几何顶点数据的访问器。

OSG 场景是树形结构:
根节点 -> 组节点(Group) -> 几何节点(Geode) -> 几何体(Geometry) -> 顶点数组

顶点数据存储在最底层的 Geometry 中,顶点访问器的使命就是自动遍历整棵树,找到所有顶点并对其操作

核心设计思想

  1. 访问者模式:将“遍历逻辑”与“业务逻辑”分离
  2. 双重分发:运行时动态匹配节点类型,自动调用对应处理函数
  3. 非侵入式:不修改 Node、Geode、Geometry 源码
  4. 自动递归:无需手写递归遍历,引擎自动完成全场景遍历

类继承关系

理解继承关系,才能真正掌握访问器的运行机制。

1. 顶层基类:osg::NodeVisitor

所有访问器的祖宗类,提供遍历框架。

osg::Referenced
       ↓
osg::NodeVisitor  <-- 所有访问器必须继承它

2. 顶点访问器继承链

你自定义的顶点访问器继承结构:

osg::NodeVisitor
       ↓
你的顶点访问器(VertexVisitor)

3. 被访问节点继承链(顶点数据所在)

osg::Node
   ↓
osg::Geode      <-- 几何节点,存放模型
   ↓
osg::Geometry   <-- 真正存储顶点、法线、纹理坐标

4. 顶点数据继承链

osg::Array
   ↓
osg::Vec3Array  <-- 存储三维顶点坐标

四大核心 API

这是顶点访问器能工作的四大支柱

1. accept() —— 入口函数(节点提供)

  • 位置osg::Node
  • 作用:启动访问,允许访问器进入节点
  • 代码
    node->accept(visitor);
    

2. apply() —— 业务逻辑(访问器重写)

  • 位置osg::NodeVisitor
  • 作用:处理具体节点,顶点访问器必须重写 apply(Geode&)
  • 代码
    void apply(osg::Geode& geode) override;
    

3. traverse() —— 递归遍历

  • 作用:继续遍历子节点,必须调用,否则遍历中断
  • 代码
    traverse(geode);
    

4. validNodeMask() —— 节点过滤

  • 作用:跳过不需要的节点,提升性能

完整实战代码

这是工业级、可直接编译运行的顶点提取访问器,逐行解析。

完整代码

#include <osgViewer/Viewer>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Geometry>
#include <osgDB/readFile>
#include <fstream>
#include <iostream>

// ==============================================
// 顶点访问器:继承自 osg::NodeVisitor
// ==============================================
class VertexExtractor : public osg::NodeVisitor
{
public:
    // 存储提取的所有顶点
    osg::ref_ptr<osg::Vec3Array> vertices;

    // 构造:设置遍历所有子节点
    VertexExtractor()
        : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
    {
        vertices = new osg::Vec3Array;
    }

    // ==========================================
    // 核心:重写 apply,只处理 Geode 模型节点
    // ==========================================
    void apply(osg::Geode& geode) override
    {
        // 遍历 Geode 下的所有几何体
        for (unsigned i = 0; i < geode.getNumDrawables(); ++i)
        {
            // 转为 Geometry(只有它有顶点)
            osg::Geometry* geom = dynamic_cast<osg::Geometry*>(geode.getDrawable(i));
            if (!geom) continue;

            // 获取顶点数组
            osg::Vec3Array* verts = dynamic_cast<osg::Vec3Array*>(geom->getVertexArray());
            if (!verts) continue;

            // 把顶点追加到结果中
            vertices->insert(vertices->end(), verts->begin(), verts->end());
        }

        // 必须遍历子节点
        traverse(geode);
    }
};

// ==============================================
// 主函数:加载模型 + 使用访问器
// ==============================================
int main()
{
    // 1. 加载模型
    osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("glider.osg");

    // 2. 创建顶点访问器
    VertexExtractor extractor;

    // 3. 启动访问(核心:accept)
    model->accept(extractor);

    // 4. 输出顶点数量
    std::cout << "提取顶点数:" << extractor.vertices->size() << std::endl;

    // 5. 保存到文件
    std::ofstream out("vertices.txt");
    for (auto& v : *extractor.vertices)
        out << v.x() << " " << v.y() << " " << v.z() << "\n";
    out.close();

    // 显示
    osgViewer::Viewer viewer;
    viewer.setSceneData(model);
    return viewer.run();
}

在这里插入图片描述


代码执行流程

model->accept(extractor)
       ↓
extractor.apply(geode)  处理模型
       ↓
提取 Geometry 顶点
       ↓
traverse(geode)  继续遍历子节点
       ↓
遍历完成 → 得到所有顶点

应用场景

顶点访问器是 OSG 开发必备工具,覆盖 90% 几何操作:

1. 数据导出与格式转换

  • 导出顶点、法线、UV 坐标
  • 转成 JSON/CSV/自定义格式
  • 模型轻量化、数据清洗

2. 模型修改与实时形变

  • 水面波浪动画
  • 模型破碎、拉伸、扭曲
  • 顶点位移、缩放

3. 碰撞检测与拾取

  • 鼠标点击获取顶点
  • 检测交点所在三角面
  • 物理仿真碰撞计算

4. 网格优化与处理

  • 生成法线
  • 简化模型、减少三角面
  • 计算包围盒、中心点

5. 分析与统计

  • 统计三角面数量
  • 检查模型错误
  • 计算模型体积、面积

总结

  1. 解耦:场景结构 ↔ 操作逻辑
  2. 自动遍历:不用手写递归
  3. 非侵入:不修改引擎源码
  4. 灵活扩展:一个访问器实现一种功能
  5. 工业必备:仿真、车载、数字孪生必用

在这里插入图片描述

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐