天天看点

osgDB加载机制

  osgDB库允许用户程序加载、使用和写入3D数据库,它采用插件管理的架构,可以支持大量常见的2D图形和3D图形文件格式。osgDB负责维护插件的信息注册表,并负责检查将要被载入的OSG插件接口的合法性。由于大型3D地形数据通常是多段数据块的组合体,因此,应用程序从文件中读取各部分数据库信息时,需要在不干扰当前渲染的前提下以后台线程的方式进行,osgDB::DatabaseParger提供了这样的功能。

  插件的不足之处在于,其设计过程中只能遵循固定的格式和工作模式进行编程,它的一切行为都无法超出主系统所提供的公共接口规范。

  osg插件是一组动态链接库,其中实现了osgDB头文件ReaderWriter定义的接口。OSG不可能查找并加载所有的插件以获取它们支持的文件格式,这样,在程序启动时将会是一个很大的开销,因此,OSG使用职责链(Chain of Responsibility)的设计模式,以加载尽量少的插件。当用户程序尝试使用osgDB读取或写入文件时,OSG将按照如下步骤来查找合适的插件:

  1. OSG搜索已注册的插件列表,查找支持文件格式的插件。开始时已注册插件列表仅包含了Registry类构造函数中注册的插件。如果OSG找到了可以支持此文件格式的插件,并成功执行了I/O操作,那么它将返回相应的数据。

  2. 如果没有发现可以支持此格式的已注册插件,或者I/0操作失败,那么OSG将根据前面所述的文件命名规则创建插件文件的名称,并尝试读取相应的插件库。如果读取成功,OSG将添加此插件到已注册插件列表中。

  3. OSG将重复步骤1,如果文件I/O的操作再次失败,OSG将返回失败信息。

  就earth格式模型做一个简要说明,在加载earth文件时,会根据格式在电脑的所有环境变量中找到对应的dll文件(osgdb_earth.dll),并试着读取osgdb_earth.dll,加载并成功执行osgdb_earth.dll的宏定义,就会成功解析文件。

  代码中具体查找读取插件流程如图所示:

  

  函数执行顺序为:

  osg::Node *node=osgDB::readNodeFile(某种类型的文件路径),这个语句是osg读取节点数据常用的函数,下面对这个函数进行跟踪,简单介绍下数据的处理过程,方便自己对osgDB的插件进行简单的修改。

  1.  osg::Node *readNodeFile(const std::string &filename) 

  2. ①的具体实现,该函数的作用是调用osgDB/Registry的单例,进行数据的解析,参数options默认为空。

  3. ②的具体实现,由于options参数为空,并且_readFileCallback为空,所以函数直接执行③

 ReaderWriter::ReadResult readNode(const std::string& fileName,const Options* options, bool buildKdTreeIfRequired=true)

 {

   ReaderWriter::ReadResult result;

   if (options && options->getReadFileCallback()) result = options->getReadFileCallback()->readNode(fileName,options);

   else if (_readFileCallback.valid()) result = _readFileCallback->readNode(fileName,options);

   else result = readNodeImplementation(fileName,options); ③

   if (buildKdTreeIfRequired) _buildKdTreeIfRequired(result, options);

   return result;

}

  4. ③的具体实现,直接执行函数④

  5. ④的具体实现,由于readFunctor的options为空,所以函数在中间没有做实际的数据处理,直接执行⑤

  6. ⑤的具体实现,主要的思想是:

先从_rwList查找是否可以解析当前类型的数据,如果能解析,直接返回解析结果;

如果不能解析,根据当前文件名,创建一个跟当前名称相关的动态库的名称,然后使用loadLibrary加载该动态库,加载成功后,_rwList这里面存储的对象的数目会增加。

工程中没有发现有对_rwList做增加操作的代码,那么_rwList是怎么增加的?关键点是,对于每一个插件dll的注册,都需要定义全局变量,方法为REGISTER_OSGPLUGIN(VR, ReaderWriterVR),这个宏调用生成了一个静态的对象osgDB::RegistryReaderWriterProxy,这个对象的作用就是增加_rwList的对象数目,这是注册机制的关键。

  这里说一下"#" "##"的含义:"#"将后面跟的变量由引号包含,如#value会解析成"value"。 "##"将前后两个值连接,去掉空格,如A##B会解析成AB。

  7. ⑥doRead函数就是利用osgDB机制,第三方插件库osgDB_earth中的读取方式,正式开始读取本地earth文件,(为什么是本地,因为读取服务器似乎是另外一种处理方案)!

  8. 继续实现进入ReaderWriterOsgEarth的readNode函数中,看到序列化函数,关于序列化,请看另一篇博客。

转载链接:[osg]osgDB的加载机制,使用3DS插件做参考(转,整理现有osgDB资料) - 南水之源