这个例子演示了Callback功能。
Registry是一个单例,存储了不同类型的文件读取器(reader)和写入器(writer)。
下面让我们浅浅的看一下Registry和ReaderWriter这两个东西。
我们都知道OSG是通过插件的方式支持数据类型的扩展,如果想实现自己的数据类型,只需要写个插件继
承自ReaderWriter,然后重写readNode等方法就可以了,对了一定得需要REGISTER_OSGPLUGIN(osg,
OSGReaderWriter)这句话,这里的玄机在哪呢?进去看看吧!
REGISTER_OSGPLUGIN是个宏,在Registry中是这样定义的。
#define REGISTER_OSGPLUGIN(ext, classname) \
extern "C" void osgdb_##ext(void) {} \
static osgDB::RegisterReaderWriterProxy<classname> g_proxy_##classname;
这里说一下“#”“##”的含义:
“#”是只后面跟的变量由引号包含,如:#value会解析成“value”
“##”是只前后两个值连接,去掉空格,如:A##B会解析成AB
所以,如果上面ext是osg,classname是OSGReaderWriter,就会解析成g_proxy_OSGReaderWriter。
我们接着往下看,是在Registry.cpp中的ReaderWriter::ReadResult Registry::read(const
ReadFunctor& readFunctor)函数loadLibrary(libraryName)这里调用的,libraryName会是osgPlugins-
3.0.1/osgdb_osg.dll,loadLibrary这个函数如果再往下跟下去在windows下就会是调用windows API加载
动态链接库了,这里很明显加载了osgdb_osg.dll这个动态链接库了,这也就是为什么自己写的插件一定
要输出osgdb_xxx.dll格式,并且放入osgPlugins-3.0.1中的原因了,因为这样osg才会找到这个插件。
上面我们说了加载对于格式插件的过程,如果加载了osgdb_osg.dll这个动态链接库,在OSGReaderWriter
中我们写了REGISTER_OSGPLUGIN(osg, OSGReaderWriter),对应的是#define REGISTER_OSGPLUGIN(ext,
classname) \
extern "C" void osgdb_##ext(void) {} \
static
osgDB::RegisterReaderWriterProxy<classname> g_proxy_##classname;
看看这里面有个static,这个非常的关键,加载一个动态链接库的时候,static的会默认执行,因此
osgDB::RegisterReaderWriterProxy就做了初始化。
class RegisterReaderWriterProxy
{
public:
RegisterReaderWriterProxy()
{
if (Registry::instance())
{
_rw = new T;
Registry::instance()->addReaderWriter(_rw.get());
}
}
~RegisterReaderWriterProxy()
{
if (Registry::instance())
{
Registry::instance()->removeReaderWriter(_rw.get());
}
}
T* get() { return _rw.get(); }
protected:
osg::ref_ptr<T> _rw;
};
把这个new了这个ReaderWriter,然后放入了Registry进行注册。
因此,具有解析该种类型数据的插件的对象就会在OSG中保存了,如果下次再加载这个类型数据,直接去
这个对象就可以了,这里也解释了为什么插件要继承ReaderWriter,并且要REGISTER_OSGPLUGIN(osg,
OSGReaderWriter)进行注册,只有注册后才能实例化该插件对象,放入Registry中。
这个static是不是用的很巧妙,不同系统实例化ContextGraphic也同样用的该种方法,以后我们会进行研
究。
不知不觉已经离题万里,但相信这么走一趟我们是有收获的,回到例子中。
osgDB::Registry::instance()->setReadFileCallback(new MyReadFileCallback());
例子中这样设置了一个callback,我们还得去源码中看看为什么要这样设置。
看看Registry.h中的readNode,当加载一个数据的时候ReadFile中会调用它,看看它都干了什么,
if (options && options->getReadFileCallback()) result = options->getReadFileCallback()-
>readNode(fileName,options);
else if (_readFileCallback.valid()) result =
_readFileCallback->readNode(fileName,options);
else result =
readNodeImplementation(fileName,options);
三句话,如果options可用就执行它的readNode,否则执行_readFileCallback,都没有的话才执行自己的
readNodeImplementation。实际上面options,和_readFileCallback,中都执行了
readNodeImplementation这个函数。这里是OSG为扩展性考虑的,可以通过自定义options或
_readFileCallback来执行自己的read操作,因此,可以理解了例子中osgDB::Registry::instance()-
>setReadFileCallback(new MyReadFileCallback());就是通过自己写_readFileCallback实现自己的
readNode的。
看看继承关系MyReadFileCallback : public osgDB::Registry::ReadFileCallback
重写readNode方法,这里还是调用了 osgDB::ReaderWriter::ReadResult result =
osgDB::Registry::instance()->readNodeImplementation(fileName,options);
但是在这里做了打印的操
作,能够在控制台中看见调用,这里我们还可以扩展做很多事情。
接下来:
osgUtil::Optimizer optimzer;
optimzer.optimize(rootnode);
看看优化都做了什么了,这里不深究了,看看Oprimizer的optimize方法,一目了然:
StaticObjectDetectionVisitor
TessellateVisitor
RemoveLoadedProxyNodesVisitor
CombineLODsVisitor
TextureVisitor
StateVisitor
TextureAtlasVisitor
StateVisitor
CopySharedSubgraphsVisitor
FlattenStaticTransformsVisitor
CombineStaticTransformsVisitor
FlattenStaticTransformsDuplicatingSharedSubgraphsVisitor
MergeGeodesVisitor
CheckGeometryVisitor
MakeFastGeometryVisitor
MergeGeometryVisitor
TriStripVisitor
RemoveEmptyNodesVisitor
RemoveRedundantNodesVisitor
FlattenBillboardVisitor
SpatializeGroupsVisitor
IndexMeshVisitor
VertexCacheVisitor
VertexAccessOrderVisitor
这些都对根节点走了一遍,做了优化处理。
InsertCallbacksVisitor icv;
rootnode->accept(icv);
viewer.getCamera()-
>setUpdateCallback(new CameraUpdateCallback());
viewer.getCamera()->setEventCallback(new
CameraEventCallback());
这里通过InsertCallbacksVisitor 为根节点中的每个node,每个drawable都增加了callback,
节点需要继承NodeCallback,drawable需要继承 osg::Drawable::DrawCallback,重写相应的函数来实现
每一帧的回调,关于callback会以后单独的进行研究,包括 eventTraversal();
updateTraversal
();
renderingTraversals();三个重要的过程。
这里只顾着走源码了,没有强调callback的用途,Node和drawable的callback的作用就是在每一帧对节点
进行更新,实现节点的运动,状态的改变等操作。