3.11 分页细节层次节点
分页细节层次节点(osg::PagedLOD)继承自osg::LOD 节点,它也是一个细节层次节点,用于实现动态分页加载,根据视点来加载所需要的,分页细节层次节点中还可以包含LOD节点。它与osg::LOD节点的区别是:osg::LOD节点存在于一个文件之中,osg::PagedLOD 的每个节点都是盘中的文件,可以根据需要来加载这些文件,加载过程中有单独的线程负责实时调度及加载。
分页细节层次节点主要是用来处理大规模的数据,在地形和GIS方面有广泛的应用。可以把模型进行预处理,在渲染场景时,再根据需要来实时加载需要的数据及卸载无用的数据。
在示例程序中我们会把PagedLOD节点写到磁盘上,以便与3.3.10节的程序输出的LOD节点相比较。通过比较可以看出,osg::LOD 节点存在于一个文件中,而PagedLOD节点只提供索引的作用,每个LOD节点存在于磁盘文件中,但不把文件存在一个文件中。
osg::PagedLOD的继承关系图如图3-18所示。
图3-18 osg::PagedLOD的继承关系图
3.12 分页细节层次节点示例
分页细节层次节点(osg::PagedLOD)示例的代码如程序清单3-7所示
- osg::ref_ptr<osg::Group> createPagedLOD(const string &strDataFolder)// 创建三级PagedLOD场景
- {
- // 创建PagedLOD对象
- osg::ref_ptr<osg::PagedLOD> page = new osg::PagedLOD();
- page->setCenter(osg::Vec3(0.0f, 0.0f, 0.0f));// 设置中心位置
- // 添加节点,设置0级的内容为cow.osg
- string strDataPath = strDataFolder + "cow.osg";
- page->setFileName(0, strDataPath);
- page->setRange(0, 0.0f, 50.0f);// 设置可视变化范围为0.0f-50.0f
- // 添加节点,设置0级的内容为spaceship.osg
- strDataPath = strDataFolder + "glider.osg";
- page->setFileName(1, strDataPath);
- page->setRange(1, 50.0f, 100.0f);// 设置可视变化范围50.0f-100.0f
- // 添加节点,设置0级的内容为cessna.osg
- strDataPath = strDataFolder + "cessna.osg";
- page->setFileName(2, strDataPath);
- page->setRange(2, 100.0f, 200.0f);// 设置可视变化范围100.0f-200.0f
- return page.get();
- }
- void pagedLOD_3_7(const string &strDataFolder)
- {
- osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
- osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
- traits->x = 40;
- traits->y = 40;
- traits->width = 600;
- traits->height = 480;
- traits->windowDecoration = true;
- traits->doubleBuffer = true;
- traits->sharedContext = 0;
- osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
- osg::ref_ptr<osg::Camera> camera = new osg::Camera;
- camera->setGraphicsContext(gc.get());
- camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
- GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
- camera->setDrawBuffer(buffer);
- camera->setReadBuffer(buffer);
- viewer->addSlave(camera.get());
- osg::ref_ptr<osg::Group> root = new osg::Group();
- // 创建矩阵变换节点
- osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform();
- mt->addChild(createPagedLOD(strDataFolder));// 添加子节点
- osg::Matrix m;
- m.makeRotate(60.0f, 0.0f, 0.0f, 1.0f);// 旋转一下,调整一下合适的角度
- mt->setMatrix(m);// 设置矩阵
- root->addChild(mt.get());//添加PagedLOD场景
- // 写入PagedLOD
- string strDataPath = strDataFolder + "page.osg";
- osgDB::writeNodeFile(*root, strDataPath);
- // 优化场景数据
- osgUtil::Optimizer optimizer;
- optimizer.optimize(root.get());
- viewer->setSceneData(root.get());
- viewer->realize();
- viewer->run();
- }
运行序,截图如图3-19 示(可以进行各种层次之间的切换)。
3-19分页细节层次节点示例截图
3.13 替代节点
替代节点(osgSim::Impostor)继承自osg::LOD节点。替代节点也是一种布告板,它是通过从当前视点将一个复杂物体对象绘制到一幅图像纹理上来创建的,其中渲染的图像纹理映射到布告板上。值得注意的是,绘制过程与替代物在屏幕上覆盖的像素点数而不是顶点数或者物体的复杂程度成正比。替代节点可用于物体的一些实例或图形的一些画面,可以加速图像的绘制及渲染。替代节点的另一个优点是可以对纹理图像进行低通滤波,从而生成一张可用于景深效果的模糊图像。
在实际运用中,绘制一个替代节点应该比绘制其所表示的物体要快很多,而且替代物应该和标识的物体非常相像。同样重要的是,替代节点应该可以重复用于挨在一起的多个视点,从而可以有效地利用帧与帧之间的相关性。随着视点距离的加大,物体的投影图像会随之变小,所以通常这种情形意味着可以使用替代节点方法来处理距离视点较远的缓慢运动的物体。就是距离视点比较近,而且运动时有一面总是面向视点的物体也比较适合用替代节点。
但很多时候替代节点对当前几何形状的近似不是很好,当错误超过某个范围时,替代节点就会变得无效。通常,对替代物的有效限制就是它的分辨率。当一个远处的替代节点慢慢靠近时,纹理中的单个像素将变得越来越明显,原来的视觉效果就会慢慢消失,这时就需要根据视点和替代节点的当前位置生成新的替代物。这种情况下,图形纹理的分辨率绝对不能超过屏幕的当前分辨率。
osgSim::Impostor的继承关系图如图3-20所示。
图3-20 osgSim::Impostor 的继承关系图
osgSim::Impostor 的成员函数如下
void setImpostorThreshold (float distance);
这个函数用于设置开始启用Impostor 的距离,参数可以根据情况适当调节。它继承自LOD节点因此它同样可以有多个子节点来控制模型的层次显示。
3.14 替代节点示例
替代节点(osgSim::Impostor)示例的代码如程序清单3-8所示。
- void impostor_3_8(const string &strDataFolder)
- {
- // 创建Viewer对象,场景浏览器
- osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
- osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
- traits->x = 40;
- traits->y = 40;
- traits->width = 600;
- traits->height = 480;
- traits->windowDecoration = true;
- traits->doubleBuffer = true;
- traits->sharedContext = 0;
- osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
- osg::ref_ptr<osg::Camera> camera = new osg::Camera;
- camera->setGraphicsContext(gc.get());
- camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
- GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
- camera->setDrawBuffer(buffer);
- camera->setReadBuffer(buffer);
- viewer->addSlave(camera.get());
- osg::ref_ptr<osg::Group> root = new osg::Group();
- // 创建一个节点,读取牛的模型
- string strDataPath = strDataFolder + "cow.osg";
- osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(strDataPath);
- osg::ref_ptr<osgSim::Impostor> impostor = new osgSim::Impostor();// 创建一个替代节点
- impostor->setImpostorThreshold(50.0f);// 设置50.0f以后开始使用贴图
- impostor->addChild(node.get(), 0, 10000.0f);// 设置模型的显示的范围在0-10000内
- root->addChild(impostor.get());// 添加到场景
- // 优化场景数据
- osgUtil::Optimizer optimizer;
- optimizer.optimize(root.get());
- viewer->setSceneData(root.get());
- viewer->realize();
- viewer->run();
- }
运行程序,截图如图3-21 所示。
图3-21替代节点示例截图
3.15 遮挡裁剪节点
遮挡裁剪节点 (osg::OccluderNode)继承自 osg::Group 节点。osg::OccluderNode 节点的主要作用是裁剪掉被遮挡的物体,也就是场景中被其他物体所遮挡的物体。最优的遮挡裁剪算法只选择其中可见的物体。从某种意义上来说,Z缓冲器只选择并绘制那些可见的物体,但是必须将这些物体都送入管线的大部分阶段。高效的逛挡算法背后的思想是:提前执行一些简单的测试,从而避免将所有的数据送入管线的大部分阶段。
目前遮挡裁剪算法主要有两种,分别是基于点的遮挡裁剪和基于单元的遮挡裁剪。图 3-22将可以清晰地表达出这两种算法的区别。基于点的可见性通常用于绘制,也就是从单个视点位置看到所有的物体。基于单元的可见性判断主要针对一个方体或者圆体单元进行可见性判断,利用这种方法判断出的不可见物体在单元内部的所有点看上去是不可见的,它的优点是:一旦计算出一个单元的可见性只要视点在这个单元中,通常就可以将可见性判断结果用于很多真实画面。但这种算法有一个缺陷,就是所需计算的开销要比基于视点的可见性大得多。因此,通常将其作为一个预处理步骤。可以用这样一个形象的比喻来说明这两种算法,那就是基于点的可见性和基于单元的可见性可以认为分别是一个点光源和区域光源。
图 3-22基于点的可见性和基于单元的可见性遮挡
osg::OccluderNode 主要采用的是基于点的遮挡算法,但节点本身不具备挡能力,因此,在判断时需要指定一个遮挡面,可以调用下面的函数:
void setOccluder(ConvexPlanarOccluder*occluder)
指定平面时需要注意,该平面应该为一个凸多边形。
osg::OccluderNode 的继承关系图如图3-23 所示。
图3-23 osg::OccluderNode 的继承关系图
3.16 遮挡裁剪节点示例
遮挡裁剪节点(osg::OccluderNode)示例的代码如程序清单3-9所示。
- osg::ref_ptr<osg::Node> createOccluder(const string &strDataFolder, const osg::Vec3& v1,
- const osg::Vec3& v2, const osg::Vec3& v3, const osg::Vec3& v4)// 创建遮挡节点
- {
- // 创建遮挡节点对象
- osg::ref_ptr<osg::OccluderNode> occluderNode = new osg::OccluderNode();
- // 创建遮挡平面
- osg::ref_ptr<osg::ConvexPlanarOccluder> cpo = new osg::ConvexPlanarOccluder;
- // 关联遮挡板平面
- string strDataPath = strDataFolder + "occluder";
- occluderNode->setOccluder(cpo.get());
- occluderNode->setName(strDataPath);
- // 初始化一个遮挡平面
- osg::ConvexPlanarPolygon& occluder = cpo->getOccluder();
- occluder.add(v1);
- occluder.add(v2);
- occluder.add(v3);
- occluder.add(v4);
- // 为这当面画一个四边形
- osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
- osg::ref_ptr<osg::Vec3Array> coords = new osg::Vec3Array(occluder.getVertexList().begin(), occluder.getVertexList().end());
- geom->setVertexArray(coords);
- osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(1);
- (*colors)[0].set(1.0f, 1.0f, 1.0f, 0.5f);
- geom->setColorArray(colors.get());
- geom->setColorBinding(osg::Geometry::BIND_OVERALL);
- geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));
- osg::ref_ptr<osg::Geode> geode = new osg::Geode;
- geode->addDrawable(geom.get());
- osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
- stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);// 关闭光照
- stateset->setMode(GL_BLEND, osg::StateAttribute::ON);// 使用混合,以保证Alpha纹理正确
- stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);// 设置透明渲染元
- geom->setStateSet(stateset.get());
- // 添加四边形作为遮挡节点,遮挡节点本身不具备遮挡能力
- occluderNode->addChild(geode.get());
- return occluderNode.get();
- }
- osg::ref_ptr<osg::Group> createOccludersAroundModel(const string &strDataFolder, osg::ref_ptr<osg::Node> model)// 创建绕模型的遮挡场景
- {
- // 创建场景组节点
- string strDataPath = strDataFolder + "OccluderScene";
- osg::ref_ptr<osg::Group> scene = new osg::Group();
- scene->setName(strDataPath);
- // 添加子节点
- strDataPath = strDataFolder + "cow.osg";
- scene->addChild(model.get());
- model->setName(strDataPath);
- // 计算模型的包围盒
- const osg::BoundingSphere bs = model->getBound();
- // 根据包围盒来创建几个前后左右几个遮挡面
- osg::BoundingBox bb;
- bb.expandBy(bs);
- // 前遮挡面
- scene->addChild(createOccluder(strDataFolder, bb.corner(0),
- bb.corner(1),
- bb.corner(5),
- bb.corner(4)));
- //右遮挡面
- scene->addChild(createOccluder(strDataFolder, bb.corner(1),
- bb.corner(3),
- bb.corner(7),
- bb.corner(5)));
- //左遮挡面
- scene->addChild(createOccluder(strDataFolder, bb.corner(2),
- bb.corner(0),
- bb.corner(4),
- bb.corner(6)));
- //后遮挡面
- scene->addChild(createOccluder(strDataFolder, bb.corner(3),
- bb.corner(2),
- bb.corner(6),
- bb.corner(7)));
- return scene.get();
- }
- void occluderNode_3_9(const string &strDataFolder)
- {
- osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
- osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
- traits->x = 40;
- traits->y = 40;
- traits->width = 600;
- traits->height = 480;
- traits->windowDecoration = true;
- traits->doubleBuffer = true;
- traits->sharedContext = 0;
- osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
- osg::ref_ptr<osg::Camera> camera = new osg::Camera;
- camera->setGraphicsContext(gc.get());
- camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
- GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
- camera->setDrawBuffer(buffer);
- camera->setReadBuffer(buffer);
- viewer->addSlave(camera.get());
- osg::ref_ptr<osg::Group> root = new osg::Group();
- string strDataPath = strDataFolder + "cow.osg";
- osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(strDataPath);
- // 添加遮挡场景
- root->addChild(createOccludersAroundModel(strDataFolder, node.get()));
- // 优化场景数据
- osgUtil::Optimizer optimizer;
- optimizer.optimize(root.get());
- viewer->setSceneData(root.get());
- viewer->realize();
- viewer->run();
- }
运行程序,截图如图3-24所示
图3-24 挡剪节点示例截图
3.17 坐标系节点
坐标系节点(osg::CoordinateSystemNode)继承自osg::Group 节点,它的主要作用是使一个场景的对象关联一个坐标系统。通常的坐标系统类型有 WKT、PROJ4和USGS。它通常与osg::EllipsoidModel节点一起使用,osg::EllipsoidModel节点(圆体模型节点)主要用来模拟天体,如太阳、行星和月亮等,其默认情况下是地球体。osg::EllipsoidModel 节点还有一个作用就是实现经纬度与坐标之间的转换,这样可以实现天体椭圆体模型子节点的精确定位。当读者建立地球数据库时,这个节点会非常有用,可以根据地球椭圆体模型实现子节点的精确定位。
对于WKT坐标系,可能很多人都不理解,它的定义包括以下几个方面:
- 一个全局的坐标系统名称。
- 一个地理坐标系统的名称。
- 一个数据识别器。
- 一个椭球体的名称、长半轴、扁率。
- 本初子午线的名称和它到格林尼治的偏移量。
- 投影类型(如横轴墨卡托影)。
- 投影参数(如中央子午线)。
- 单位名称,到米或弧度的转换因数。
- 各轴的名称和顺序。
- 由授权单位(如EPSG)预先定义的大多数坐标系统的代码。
这样解释可能比较好理解,关于地理坐标系的知识,读者可以参看本书第13.5.2节。
osg::CoordinateSystemNode 的继承关系图如图3-25 所示。
3-25 osg::CoordinateSystemNode 的继承关系图
3.18 坐标系节点示例
坐标系节点(osg::CoordinateSystemNode)示例的代码如程序清单3-10所示
- osg::ref_ptr<osg::Node> createEarth(const string &strDataFolder)// 绘制一个地球
- {
- // 创建一个球体
- osg::ref_ptr<osg::ShapeDrawable> sd = new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0, 0.0, 0.0), osg::WGS_84_RADIUS_POLAR));
- // 添加到叶节点
- osg::ref_ptr<osg::Geode> geode = new osg::Geode;
- geode->addDrawable(sd.get());
- // 设置纹理
- std::string filename = strDataFolder + "Images/land_shallow_topo_2048.jpg";
- geode->getOrCreateStateSet()->setTextureAttributeAndModes(0, new osg::Texture2D(osgDB::readImageFile(filename)));
- // 创建坐标系节点
- osg::ref_ptr<osg::CoordinateSystemNode> csn = new osg::CoordinateSystemNode;
- csn->setEllipsoidModel(new osg::EllipsoidModel());// 设置椭圆体模型,默认的坐标系WGS_84
- csn->addChild(geode.get());//添加子节点
- return csn.get();
- }
- void coordinateSystemNode_3_10(const string &strDataFolder)
- {
- //创建Viewer对象,场景浏览器
- osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
- osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
- traits->x = 40;
- traits->y = 40;
- traits->width = 600;
- traits->height = 480;
- traits->windowDecoration = true;
- traits->doubleBuffer = true;
- traits->sharedContext = 0;
- osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
- osg::ref_ptr<osg::Camera> camera = new osg::Camera;
- camera->setGraphicsContext(gc.get());
- camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
- GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
- camera->setDrawBuffer(buffer);
- camera->setReadBuffer(buffer);
- viewer->addSlave(camera.get());
- osg::ref_ptr<osg::Group> root = new osg::Group();
- // 添加到场景
- root->addChild(createEarth(strDataFolder));
- // 优化场景数据
- osgUtil::Optimizer optimizer;
- optimizer.optimize(root.get());
- viewer->setSceneData(root.get());
- viewer->realize();
- viewer->run();
- }
运行程序,截图如图 3-26 所示
图3-26 坐标系节点示例截图
4. 场景中节点的拷贝 — osg::CopyOp 类
osg::CopyOp类是一个拷贝类,主要负责场景中节点的拷贝,根据不同的需要,控制使用深拷贝或者浅拷贝来拷贝场景中的节点。关于深拷贝和浅拷贝在C++里面已经有很详细的介绍。这里再简单介绍一下。如果读者是熟练的C++程序员,则可以跳过下面这段文件。
深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响,例如,一头牛的节点被拷贝了,对原来牛的节点做矩阵变换并不会影响拷贝对象。浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同,对其中任何一个对象的改动都会影响另外一个对象,例如,一头牛的节点被拷贝了,对原来牛的节点做矩阵变换,拷贝对象的牛也会随之做相应的矩阵变换。深拷贝和浅拷贝的定义可以简单理解成:如果一个类拥有资源(堆或其他系统资源),这个类的对象发生复制的过程就可以称为深拷贝:反之,对象存在资源但复制过程并未复制资源的情况视为浅拷贝。
浅拷贝资源后,在释放资源时会产生资源归属不清的情况,这种情况会导致程序运行出错,所以定要注意。当在场景中共享一个节点时,确定使用深拷贝或浅拷贝时,尤其要注意。
还有一个问题是,当场景中多父节点共享一个子节点时,进行深拷贝时也会出现问题,即默认情况下会改变原来树的结构,这时需要自定义控制子节点的拷贝,以避免多次重复拷贝而改变场景中的树的结构。通过图 3-27,读者可以清晰地看到这种情况下存在的问题,所以没有正确设置拷贝结构会导致场景树拷贝后的改变及场景树结构的变化,这种做法是明显错误的。
图3-27场景拷贝结构图
4.1 自定义场景拷贝示例(一)
自定义场景拷贝示例主要演示如何拷贝场景中的一个或多个节点,了解深拷贝与浅拷贝的区别。通过自定义拷贝类,读者将深入了解节点拷贝的过程,也将明显看到深拷贝和浅拷贝之间的区别。
代码如程序清单3-11所示。
- class MyCopyOp : public osg::CopyOp// 自定义Copy类,用于输出拷贝信息
- {
- public:
- inline MyCopyOp(CopyFlags flags = SHALLOW_COPY) :
- osg::CopyOp(flags),
- _blank(2),
- _number(5)
- {
- //
- }
- //内联函数用于控制输出信息格式,实现下三角规则输出
- inline void moveIn() const
- {
- _blank += _number;
- }
- inline void moveOut() const
- {
- _blank -= _number;
- }
- inline void writeIndent() const
- {
- for (int i = 0; i < _blank; ++i)
- {
- std::cout << " ";
- }
- }
- //引用计数器copy
- virtual osg::Referenced* operator() (const osg::Referenced* ref) const
- {
- writeIndent();
- std::cout << "copying Referenced " << ref << std::endl;
- moveIn();
- osg::Referenced* ret_ref = CopyOp::operator()(ref);
- moveOut();
- return ret_ref;
- }
- //对象copy
- virtual osg::Object* operator() (const osg::Object* obj) const
- {
- writeIndent();
- std::cout << "copying Object " << obj;
- if (obj)
- {
- std::cout << " " << obj->className();
- }
- std::cout << std::endl;
- moveIn();
- osg::Object* ret_obj = CopyOp::operator()(obj);
- moveOut();
- return ret_obj;
- }
- //节点copy
- virtual osg::Node* operator() (const osg::Node* node) const
- {
- writeIndent();
- std::cout << "copying Node " << node;
- if (node)
- {
- std::cout << " " << node->className() << " '" << node->getName() << "'";
- }
- std::cout << std::endl;
- moveIn();
- osg::Node* ret_node = CopyOp::operator()(node);
- moveOut();
- return ret_node;
- }
- //Drawable copy
- virtual osg::Drawable* operator() (const osg::Drawable* drawable) const
- {
- writeIndent();
- std::cout << "copying Drawable " << drawable;
- if (drawable)
- {
- std::cout << " " << drawable->className();
- }
- std::cout << std::endl;
- moveIn();
- osg::Drawable* ret_drawable = CopyOp::operator()(drawable);
- moveOut();
- return ret_drawable;
- }
- //状态集copy
- virtual osg::StateSet* operator() (const osg::StateSet* stateset) const
- {
- writeIndent();
- std::cout << "copying StateSet " << stateset;
- if (stateset)
- {
- std::cout << " " << stateset->className();
- }
- std::cout << std::endl;
- moveIn();
- osg::StateSet* ret_stateset = CopyOp::operator()(stateset);
- moveOut();
- return ret_stateset;
- }
- //状态属性copy
- virtual osg::StateAttribute* operator() (const osg::StateAttribute* attr) const
- {
- writeIndent();
- std::cout << "copying StateAttribute " << attr;
- if (attr)
- {
- std::cout << " " << attr->className();
- }
- std::cout << std::endl;
- moveIn();
- osg::StateAttribute* ret_attr = CopyOp::operator()(attr);
- moveOut();
- return ret_attr;
- }
- //纹理信息copy
- virtual osg::Texture* operator() (const osg::Texture* text) const
- {
- writeIndent();
- std::cout << "copying Texture " << text;
- if (text)
- {
- std::cout << " " << text->className();
- }
- std::cout << std::endl;
- moveIn();
- osg::Texture* ret_text = CopyOp::operator()(text);
- moveOut();
- return ret_text;
- }
- //贴图copy
- virtual osg::Image* operator() (const osg::Image* image) const
- {
- writeIndent();
- std::cout << "copying Image " << image;
- if (image)
- {
- std::cout << " " << image->className();
- }
- std::cout << std::endl;
- moveIn();
- osg::Image* ret_image = CopyOp::operator()(image);
- moveOut();
- return ret_image;
- }
- protected:
- //此处两个必须是可变型变量,因为在const函数中使用,需要突破const的限制
- //空格增减变量
- mutable int _number;
- //空格总数数
- mutable int _blank;
- };
- void copyOp1_3_1(const string &strDataFolder)
- {
- osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
- osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
- traits->x = 40;
- traits->y = 40;
- traits->width = 600;
- traits->height = 480;
- traits->windowDecoration = true;
- traits->doubleBuffer = true;
- traits->sharedContext = 0;
- osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
- osg::ref_ptr<osg::Camera> camera = new osg::Camera;
- camera->setGraphicsContext(gc.get());
- camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
- GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
- camera->setDrawBuffer(buffer);
- camera->setReadBuffer(buffer);
- viewer->addSlave(camera.get());
- string strDataPath = strDataFolder + "glider.osg";
- osg::ref_ptr<osg::Node> rootnode = osgDB::readNodeFile(strDataPath);
- // 优化场景数据
- osgUtil::Optimizer optimzer;
- optimzer.optimize(rootnode.get());
- // 浅拷贝一个场景
- osg::ref_ptr<osg::Node> shallowCopy = dynamic_cast<osg::Node*>(rootnode->clone(osg::CopyOp::SHALLOW_COPY));
- std::cout << std::endl << "完成浅拷贝一个场景" << std::endl << std::endl;
- // 深拷贝一个场景
- osg::ref_ptr<osg::Node> deepCopy = dynamic_cast<osg::Node*>(rootnode->clone(osg::CopyOp::DEEP_COPY_ALL));
- std::cout << std::endl << "完成深拷贝一个场景" << std::endl << std::endl;
- // 自定义输出信息浅拷贝场景
- osg::ref_ptr<osg::Node> myShallowCopy = dynamic_cast<osg::Node*>(rootnode->clone(MyCopyOp(osg::CopyOp::SHALLOW_COPY)));
- std::cout << std::endl << "完成一个自定义输出信息浅拷贝场景" << std::endl << std::endl;
- // 自定义输出信息深拷贝场景
- osg::ref_ptr<osg::Node> myDeepCopy = dynamic_cast<osg::Node*>(rootnode->clone(MyCopyOp(osg::CopyOp::DEEP_COPY_ALL)));
- std::cout << std::endl << "完成一个自定义输出信息深拷贝场景" << std::endl << std::endl;
- viewer->setSceneData(rootnode.get());
- viewer->realize();
- viewer->run();
- }
运行程序,截图如图3-28 所示。
图3-28自定场景示例(一)截图
4.2 自定义场景拷贝示例(二)
通过第 3.4.1 节中的示例,相信读者已经理解了场景中节点的拷贝及拷贝应该注意的事项。本节的示例将更进一步向读者展示如何拷贝场景中多父节点的场景,这是读者在场景中拷贝节点树时非常值得注意的。
代码如程序清单3-12 所示。
- class GraphCopyOp : public osg::CopyOp // 自定义Copy类,用于输出拷贝信息
- {
- public:
- inline GraphCopyOp(CopyFlags flags = SHALLOW_COPY) : osg::CopyOp(flags)
- {
- _nodeCopyMap.clear();
- }
- // 节点的copy
- virtual osg::Node* operator() (const osg::Node* node) const
- {
- //判断是否为深拷贝
- if (node && _flags&DEEP_COPY_NODES)
- {
- //判断节点的父节点是否有多个,如果只有一个,则直接深拷贝
- if (node->getNumParents() > 1)
- {
- //判断是否已经拷贝
- if (_nodeCopyMap.find(node) != _nodeCopyMap.end())
- {
- std::cout << "Copy of node " << node << " " << node->getName() << ", "
- << _nodeCopyMap[node] << ", will be reused" << std::endl;
- //直接返回拷贝对象的地址
- return (osg::Node*)(_nodeCopyMap[node]);
- }
- else
- {
- //进行拷贝并在映射表中保存拷贝对象
- osg::Node* newNode = dynamic_cast<osg::Node*>(node->clone(*this));
- _nodeCopyMap[node] = newNode;
- //返回地址
- return newNode;
- }
- }
- else
- {
- //直接拷贝
- return dynamic_cast<osg::Node*>(node->clone(*this));
- }
- }
- else
- {
- //直接返回地址
- return const_cast<osg::Node*>(node);
- }
- }
- protected:
- //节点映射表,用来保存child和拷贝,分别作为原象和象
- mutable std::map<const osg::Node*, osg::Node*> _nodeCopyMap;
- };
- void copyOp2_3_2(const string &strDataFolder)
- {
- osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
- osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
- traits->x = 40;
- traits->y = 40;
- traits->width = 600;
- traits->height = 480;
- traits->windowDecoration = true;
- traits->doubleBuffer = true;
- traits->sharedContext = 0;
- osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
- osg::ref_ptr<osg::Camera> camera = new osg::Camera;
- camera->setGraphicsContext(gc.get());
- camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
- GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
- camera->setDrawBuffer(buffer);
- camera->setReadBuffer(buffer);
- viewer->addSlave(camera.get());
- // 创建一个多父节点共享一个子节点的场景
- osg::ref_ptr<osg::Group> rootnode = new osg::Group();
- string strDataPath = strDataFolder + "glider.osg";
- osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(strDataPath);
- // 子节点pat,共享node
- osg::ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform();
- pat->setPosition(osg::Vec3(5.0f, 0.0f, 0.0f));
- pat->addChild(node.get());
- // 子节点mt,共享node
- osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform();
- osg::Matrix m;
- m.makeScale(2.0f, 2.0f, 2.0f);
- mt->setMatrix(m);
- mt->addChild(node.get());
- // 添加子节点
- rootnode->addChild(pat.get());
- rootnode->addChild(mt.get());
- // 优化场景数据
- osgUtil::Optimizer optimzer;
- optimzer.optimize(rootnode.get());
- // 一个多父节点一个子节点的场景的默认深拷贝
- osg::ref_ptr<osg::Node> deepCopy = dynamic_cast<osg::Node*>(rootnode->clone(osg::CopyOp::DEEP_COPY_ALL));
- std::cout << std::endl << "完成一个多父节点一个子节点的场景的默认深拷贝" << std::endl << std::endl;
- osgDB::writeNodeFile(*(deepCopy.get()), "deepCopy.osg");
- // 完成一个多父节点一个子节点的场景的浅拷贝
- osg::ref_ptr<osg::Node> myShallowCopy = dynamic_cast<osg::Node*>(rootnode->clone(GraphCopyOp(osg::CopyOp::SHALLOW_COPY)));
- std::cout << std::endl << "完成一个多父节点一个子节点的场景的浅拷贝" << std::endl << std::endl;
- osgDB::writeNodeFile(*(myShallowCopy.get()), "myShallowCopy.osg");
- // 完成一个多父节点一个子节点的场景的深拷贝
- osg::ref_ptr<osg::Node> myDeepCopy = dynamic_cast<osg::Node*>(rootnode->clone(GraphCopyOp(osg::CopyOp::DEEP_COPY_ALL)));
- std::cout << std::endl << "完成一个多父节点一个子节点的场景的深拷贝" << std::endl << std::endl;
- osgDB::writeNodeFile(*(myDeepCopy.get()), "myDeepCopy.osg");
- viewer->setSceneData(rootnode.get());
- viewer->realize();
- viewer->run();
- }
运行程序,截图如图3-29所示。
3-29自定义场景拷贝示例(二)截图