OSG之场景中节点的拷贝

248 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 28 天

1 场景中节点的拷贝--osg::CopyOp类

osg::CopyOp类是一个拷贝类,主要负责场景中节点的拷贝,根据不同的需要,控制使用深拷贝或者浅拷贝来拷贝场景中的节点。关于深拷贝和浅拷贝在C++里面已经有很详细的介绍。这里再简单介绍一下。如果读者是熟练的C++程序员,则可以跳过下面这段文件。

深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响,例如,一头牛的节点被拷贝了,对原来牛的节点做矩阵变换并不会影响拷贝对象。

浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同),对其中任何一个对象的改动都会影响另外一个对象,例如,一头牛的节点被拷贝了,对原来牛的节点做矩阵变换,拷贝对象的牛也会随之做相应的矩阵变换。

深拷贝和浅拷贝的定义可以简单理解成:如果一个类拥有资源(堆或其他系统资源),这个类的对象发生复制的过程就可以称为深拷贝:反之,对象存在资源但复制过程并未复制资源的情况视为浅拷贝。

浅拷贝资源后,在释放资源时会产生资源归属不清的情况,这种情况会导致程序运行出错,所以一定要注意。当在场景中共享一个节点时,确定使用深拷贝或浅拷贝时,尤其要注意。

还有一个问题是,当场景中多父节点共享一个子节点时,进行深拷贝时也会出现问题,即默认情况下会改变原来树的结构,这时需要自定义控制子节点的拷贝,以避免多次重复拷贝而改变场景中的树的结构。

通过图下图,读者可以清晰地看到这种情况下存在的问题,所以没有正确设置拷贝结构会导致场景树拷贝后的改变及场景树结构的变化,这种做法是明显错误的。

2.png

2 自定义场景拷贝示例

自定义场景拷贝示例主要演示如何拷贝场景中的一个或多个节点,了解深拷贝与浅拷贝的区别。通过自定义拷贝类,读者将深入了解节点拷贝的过程,也将明显看到深拷贝和浅拷贝之间的区别。

代码如程序清单所示。

#include <osgViewer/Viewer>
#include <osg/Node>
#include <osg/Geometry>
#include <osg/Image>
#include <osg/Geode>
#include <osg/Group>
#include <osg/Notify>
#include <osg/Texture>
#include <osg/StateSet>
#include <osg/StateAttribute>

#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>

#include <osgUtil/Optimizer>

#include <iostream>

#include "utility.h"

//自定义Copy类,用于输出拷贝信息
class MyCopyOp : public osg::CopyOp
{
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;
};

int main()
{
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();

	osg::ref_ptr<osg::Node> rootnode = osgDB::readNodeFile(GetCurrentPath() + "\\Data\\glider.osg");

	//优化场景数据
	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;

	setWindowSize(viewer.get(), 600, 400, "CopyOp");

	viewer->setSceneData(rootnode.get());

	viewer->realize();

	viewer->run();

	return 0;
}

效果图

3.PNG

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 28 天