osgcallback程序注解及CallBack回调的介绍(转)

下面的文章虽然写的较早,但是对比例子代码还是可用的,我已经对比了osg3.5.1版本的例子。觉得不错。感谢原作者。

osgcallback程序注解及CallBack回调的介绍(转)

http://starspiritray.blogcn.com/diary,16867050.shtml
   本文的目的是通过对示例程序osgcallback的详细注解和分析,学习OSG节点回调的方法和执行过程,并进行程序的适当注解和知识总结。
详细的源代码请参阅OSG 2.0发行版的附带示例程序examples/osgcallback.cpp。本文只是针对其中的重点代码进行注解和分析,文中所述的程序代码并不完整,基本无法直接执行。


观察main函数中场景调用和加载节点回调的语句:
// 加载文件读取回调MyReadFileCallBack。
osgDB::Registry::instance()->setReadFileCallback(new MyReadFileCallback());
// 读取命令行指定的场景图形模型文件,或者读取缺省的cow.osg。
osgViewer::Viewer viewer;
osg::Node* rootnode = osgDB::readNodeFiles(arguments);
if (!rootnode) rootnode = osgDB::readNodeFile("cow.osg");
if (!rootnode)
{
    osg::notify(osg::NOTICE)<<"Please specify a file on the command line"<<std::endl;
    return 1;
}


// 优化场景图形树结构。
// run optimization over the scene graph
osgUtil::Optimizer optimzer;
optimzer.optimize(rootnode.get());


// 从根节点rootnode开始,遍历所有节点并执行节点访问器icv。
// insert all the callbacks
InsertCallbacksVisitor icv;
rootnode->accept(icv);


// 加载摄像机更新回调CameraUpdateCallBack,以及摄像机事件回调CameraEventCallBack。


viewer.getCamera()->setUpdateCallback(new CameraUpdateCallback());
viewer.getCamera()->setEventCallback(new CameraEventCallback());


// 设置视口的场景图形根节点为rootnode。
// set the scene to render
viewer.setSceneData(rootnode.get());


这其中涉及到一些用户自定义的结构体和类,分别是     
MyReadFileCallback,
InsertCallbacksVisitor,
CameraUpdateCallback和
CameraEventCallback。
观察MyReadFileCallback结构体的内容,可以发现它继承自osgDB::Registry::ReadFileCallback,并重载了一个函数readNode,分析源代码可知,该函数在osgDB::readNodeFile函数中被调用,即,在加载模型文件时,即会调用 MyReadFileCallback结构体所重载的readNode函数并执行相应的内容。示例程序中该函数的内容如下:
class MyReadFileCallback : public osgDB::Registry::ReadFileCallback
{
public:
virtual osgDB::ReaderWriter::ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options)
{
std::cout<<"before readNode"<<std::endl;
// note when calling the Registry to do the read you have to call readNodeImplementation NOT readNode, as this will
// cause on infinite recusive loop.
osgDB::ReaderWriter::ReadResult result = osgDB::Registry::instance()->readNodeImplementation(fileName,options);
std::cout<<"after readNode"<<std::endl;
return result;
}
};
其中较为重要的是readNodeImplementation一行,这是模型文件读取的实际执行函数,如果在回调中对于这一语句的使用有错,那么模型无论怎样都不能被加载。




InsertCallbacksVisitor的内容比较复杂,它继承自osg::NodeVisitor,属于用户自定义的节点访问器。换句话说,当执行到:
InsertCallbacksVisitor icv;
rootnode->accept(icv);
节点访问器将从根节点rootnode开始,遍历所有的子节点并执行相应的操作。
节点访问器InsertCallbacksVisitor的构造函数中,可以传递一个遍历方式的枚举量TraversalMode ,它的取值意义如下:
TRAVERSE_NONE:仅仅传递到当前节点,然后停止传递; 
TRAVERSE_PARENTS:传递给当前节点及其父节点,注意,如果当前节点有不止一个父节点,那么访问器将列举出所有的传递路线,其中可能有多个重复出现的节点; 
TRAVERSE_ALL_CHILDREN:传递给当前节点及其所有子节点,同样,对于多个子节点的情况,访问器将列举所有的传递路线; 
TRAVERSE_ACTIVE_CHILDREN:传递给当前节点及所有被激活的子节点,例如遇到LOD节点和Switch节点时,将不会传递给当前无法显示的子节点。


在节点访问器中,使用apply函数来实现对各种类型的节点的访问。当遍历过程中遇到与某个重载的apply的输入参数相符合的节点时,将执行这一 apply函数的内容。示例代码中重载了Node,Geode和Transform节点的访问函数。在Geode节点的访问函数中,实现了对场景图形结构中每个节点的回调指定:
// 指定Geode节点的更新回调。
geode.setUpdateCallback(new UpdateCallback());


// 指定Geode节点中所有关联几何体(Drawables)的绘制回调,拣选回调和更新回调。
for(unsigned int i=0;i<geode.getNumDrawables();++i)
{
     geode.getDrawable(i)->setUpdateCallback(new DrawableUpdateCallback());
     geode.getDrawable(i)->setCullCallback(new DrawableCullCallback());
     geode.getDrawable(i)->setDrawCallback(new DrawableDrawCallback());
}


在Geode之外的其它节点的apply函数中,往往包含有下面的语句:
traverse(node);
这表示对场景图形节点的遍历将继续进行。换句话说,如果某种类型的节点的apply函数中不包含这一语句,那么场景图形遍历到这一类型的节点时,当前遍历的路线将自动结束,以下的子节点均被忽略。而对Geode节点来说,因为不存在子节点,因此也不必添加这一语句。


使用节点访问器为场景图形节点或几何体添加的回调主要有以下几种:
几何体绘制遍历(DrawableDrawCallback):重载函数drawImplementation,当几何体进行绘制时,函数的内容被调用,注意需要添加Drawable::drawImplementation函数以实现几何体的实际绘制,否则将无法画出当前的几何形状。可以参阅 include/osg/Drawable,draw函数中的调用过程。
几何体拣选遍历(DrawableCullCallback):重载函数cull,当几何体进行拣选优化时,函数的内容被调用。可以参阅src/osgUtil/CullVisitor,apply(Geode&)函数中的调用过程。
几何体更新遍历(DrawableUpdateCallback):重载函数update,当几何体更新时,函数的内容被调用。可以参阅include/osgUtil/UpdateVisitor,handle_geode_callbacks函数中的调用过程。
拣选遍历(CullCallBack):重载操作符operator(),当场景执行拣选优化时,operator()的内容被调用,注意需要添加 traverse函数以实现节点的继续遍历。可以参阅include/osgUtil /CullVisitor,handle_cull_callbacks_and_traverse函数中的调用过程。
更新遍历(UpdateCallBack):重载操作符operator(),当场景更新时,operator()的内容被调用,注意需要添加 traverse函数以实现节点的继续遍历。可以参阅src/osg/StateSet.cpp,runUpdateCallbacks函数中的调用过程。


最后,观察CameraUpdateCallback和CameraEventCallback结构体的内容,可以发现它们均继承自 osg::NodeCallback,并重载了操作符operator(),当执行摄像机更新或者事件回调时,operator()的内容将被调用,添加 traverse函数可以实现节点的继续遍历。


此外,还可以使用Node::setEventCallback来设置节点的事件回调,其输入参数为继承自osg::NodeCallback的用户类,且需要重载操作符operator()以实现回调过程的处理。


综上,可以得出回调的编写方法:
1、编写用户结构体,继承OSG中相应的虚函数结构体,如osg::NodeCallBack;
2、重载回调执行函数,根据回调种类的不同,执行函数的名称也不同,可以参考osgcallback中的设置;
3、注意在回调执行的过程中,有一些必要的系统操作需要交由用户来完成,例如readNodeImplementation,Drawable::drawImplementation,traverse等,否则系统本身的渲染工作可能会不正常。


编译运行osgcallback之后,可以看到控制台不断输出各种回调的执行结果,用户可以根据自己的需要在不同的时间段进入不同的回调来完成所需的工作。



//osg自带例子:
#include <osgViewer/Viewer>

#include <osg/Transform>
#include <osg/Billboard>
#include <osg/Geode>
#include <osg/Group>
#include <osg/Notify>

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

#include <osgGA/TrackballManipulator>
#include <osgGA/FlightManipulator>
#include <osgGA/DriveManipulator>

#include <osgUtil/Optimizer>

#include <iostream>
#include <windows.h>  

class UpdateCallback : public osg::NodeCallback
{
	virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
	{
		std::cout<<"update callback - pre traverse"<<node<<std::endl;
		traverse(node,nv);
		std::cout<<"update callback - post traverse"<<node<<std::endl;
	}
};

class CullCallback : public osg::NodeCallback
{
	virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
	{
		std::cout<<"cull callback - pre traverse"<<node<<std::endl;
		traverse(node,nv);
		std::cout<<"cull callback - post traverse"<<node<<std::endl;
	}
};

class DrawableDrawCallback : public osg::Drawable::DrawCallback
{
	virtual void drawImplementation(osg::RenderInfo& renderInfo,const osg::Drawable* drawable) const
	{
		std::cout<<"draw call back - pre drawImplementation"<<drawable<<std::endl;
		drawable->drawImplementation(renderInfo);
		std::cout<<"draw call back - post drawImplementation"<<drawable<<std::endl;
	}
};

struct DrawableUpdateCallback : public osg::Drawable::UpdateCallback
{
	virtual void update(osg::NodeVisitor*, osg::Drawable* drawable)
	{
		std::cout<<"Drawable update callback "<<drawable<<std::endl;
	}
};

struct DrawableCullCallback : public osg::Drawable::CullCallback
{
	/** do customized cull code.*/
	virtual bool cull(osg::NodeVisitor*, osg::Drawable* drawable, osg::State* /*state*/) const
	{
		std::cout<<"Drawable cull callback "<<drawable<<std::endl;
		return false;
	}
};

class InsertCallbacksVisitor : public osg::NodeVisitor
{

public:

	InsertCallbacksVisitor():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
	{
	}

	virtual void apply(osg::Node& node)
	{
		node.setUpdateCallback(new UpdateCallback());
		node.setCullCallback(new CullCallback());
		traverse(node);
	}

	virtual void apply(osg::Geode& geode)
	{
		geode.setUpdateCallback(new UpdateCallback());

		//note, it makes no sense to attach a cull callback to the node
		//at there are no nodes to traverse below the geode, only
		//drawables, and as such the Cull node callbacks is ignored.
		//If you wish to control the culling of drawables
		//then use a drawable cullback...

		for(unsigned int i=0;i<geode.getNumDrawables();++i)
		{
			geode.getDrawable(i)->setUpdateCallback(new DrawableUpdateCallback());
			geode.getDrawable(i)->setCullCallback(new DrawableCullCallback());
			geode.getDrawable(i)->setDrawCallback(new DrawableDrawCallback());
		}
	}

	virtual void apply(osg::Transform& node)
	{
		apply((osg::Node&)node);
	}
};

class MyReadFileCallback : public osgDB::Registry::ReadFileCallback
{
public:
	virtual osgDB::ReaderWriter::ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options)
	{
		std::cout<<"before readNode"<<std::endl;
		// note when calling the Registry to do the read you have to call readNodeImplementation NOT readNode, as this will
		// cause on infinite recusive loop.
		osgDB::ReaderWriter::ReadResult result = osgDB::Registry::instance()->readNodeImplementation(fileName,options);
		std::cout<<"after readNode"<<std::endl;
		return result;
	}
};

class CameraUpdateCallback : public osg::NodeCallback
{
	virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
	{
		std::cout<<"Camera update callback - pre traverse"<<node<<std::endl;
		traverse(node,nv);
		std::cout<<"Camera update callback - post traverse"<<node<<std::endl;
	}
};

class CameraEventCallback : public osg::NodeCallback
{
	virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
	{
		std::cout<<"Camera event callback - pre traverse"<<node<<std::endl;
		traverse(node,nv);
		std::cout<<"Camera event callback - post traverse"<<node<<std::endl;
	}
};


struct TestDrawableUpdateCallback : public osg::Drawable::UpdateCallback
{
	TestDrawableUpdateCallback(const std::string &message): _message(message) {}

	virtual void update(osg::NodeVisitor*, osg::Drawable* drw) {
		printf("%s\n", _message.c_str());
	}
	std::string _message;
};

struct TestNodeUpdateCallback : public osg::NodeCallback
{
	TestNodeUpdateCallback(const std::string &message): _message(message) {}

	virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) {
		printf("%s\n", _message.c_str());
	}
	std::string _message;
};


int main( int argc, char **argv )
{
	// use an ArgumentParser object to manage the program arguments.
	osg::ArgumentParser arguments(&argc,argv);

	// set the osgDB::Registy read file callback to catch all requests for reading files.
	osgDB::Registry::instance()->setReadFileCallback(new MyReadFileCallback());

	// initialize the viewer.
	osgViewer::Viewer viewer;

	// load the nodes from the commandline arguments.
	osg::ref_ptr<osg::Node> rootnode;

	if (arguments.read("--test"))
	{
		osg::ref_ptr<osg::Group> root = new osg::Group();
		rootnode = root;

		osg::Node *test1 = new osg::Node();
		test1->setUpdateCallback(new TestNodeUpdateCallback("test1"));
		root->addChild(test1);

		osg::Drawable *test2 = new osg::Drawable();
		test2->osg::Node::setUpdateCallback(new TestNodeUpdateCallback("test2"));
		root->addChild(test2);

		osg::Drawable *test3 = new osg::Drawable();
		test3->setUpdateCallback(new TestDrawableUpdateCallback("test3"));
		root->addChild(test3);

		osg::Geode *test4 = new osg::Geode();
		osg::Drawable *drawable1 = new osg::Drawable();
		drawable1->osg::Node::setUpdateCallback(new TestNodeUpdateCallback("test4"));
		test4->addDrawable(drawable1);
		root->addChild(test4);

		osg::Geode *test5 = new osg::Geode();
		osg::Drawable *drawable2 = new osg::Drawable();
		drawable2->setUpdateCallback(new TestDrawableUpdateCallback("test5"));
		test5->addDrawable(drawable2);
		root->addChild(test5);

		osg::Geode *test6 = new osg::Geode();
		osg::Drawable *drawable3 = new osg::Drawable();
		drawable3->setUpdateCallback(new TestDrawableUpdateCallback("test6"));
		test6->addChild(drawable3);
		root->addChild(test6);

		osg::Geode *test7 = new osg::Geode();
		osg::Drawable *drawable4 = new osg::Drawable();
		drawable4->osg::Node::setUpdateCallback(new TestNodeUpdateCallback("test7"));
		test7->addChild(drawable4);
		root->addChild(test7);

		printf("Numchildren with updates %u\n", rootnode->getNumChildrenRequiringUpdateTraversal());

	}
	else
	{
		rootnode = osgDB::readRefNodeFiles(arguments);

		// if not loaded assume no arguments passed in, try use default mode instead.
		if (!rootnode) rootnode = osgDB::readRefNodeFile("cow.osgt");

		if (!rootnode)
		{
			osg::notify(osg::NOTICE)<<"Please specify a file on the command line"<<std::endl;

			return 1;
		}

		// run optimization over the scene graph
		osgUtil::Optimizer optimzer;
		optimzer.optimize(rootnode.get());

		// insert all the callbacks
		InsertCallbacksVisitor icv;
		rootnode->accept(icv);
	}

	viewer.getCamera()->setUpdateCallback(new CameraUpdateCallback());
	viewer.getCamera()->setEventCallback(new CameraEventCallback());

	// set the scene to render
	viewer.setSceneData(rootnode.get());
	viewer.setUpViewOnSingleScreen(1);
	//viewer.setUpViewInWindow(0,0, 800, 600);
	viewer.setCameraManipulator(new osgGA::TrackballManipulator);

	viewer.realize();


	while(!viewer.done())
	{
		OSG_NOTICE<<std::endl<<"New Frame"<<std::endl;
		viewer.frame();
		Sleep(1000);
	}

	return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值