osg读取文件的原理(插件工作机制)

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

1、自定义文件插件:

自定义文件插件主要是自定义一个插件读写类,继承osgDB::ReaderWriter类,然后根据需求重写如readNode等函数方法即可,如下,这些函数的返回值均为枚举变量。 
 virtual ReadResult readNode(std::istream& /*fin*/,const Options* =NULL) const { return ReadResult(ReadResult::NOT_IMPLEMENTED); }
在建立插件读写类时需要注意:

  • 需要建立一个dll项目工程,输出的dll必须为osgdb_扩展名.dll或osgdb_扩展名d.dll的形式
  • 为了实现插件注册,需要定义全局变量,方法如下REGISTER_OSGPLUGIN(VR, ReaderWriterVR),在该全局变量的初始化过程中,会使用Registery::addReaderWriter函数自动注册插件所对应的扩展名。
    REGISTER_OSGPLUGIN是一个宏,在Registry.h中如下定义
    #define REGISTER_OSGPLUGIN(ext, classname) \
    extern "C" void osgdb_##ext(void) {} \
    static osgDB::RegisterReaderWriterProxy<classname> g_proxy_##classname;
这里说一下"#" "##"的含义:"#"将后面跟的变量由引号包含,如#value会解析成"value"。 "##"将前后两个值连接,去掉空格,如A##B会解析成AB。
  • 在应用程序使用中,需要注册插件,方法如下 osgDB::Registry::instance->addFileExtensionAlias("VR", "VR")
void Registry::addFileExtensionAlias(const std::string mapExt, const std::string toExt)
{
    _extAliasMap[mapExt] = toExt;
}
2、插件的工作机制
osg插件是一组动态链接库,其中实现了osgDB头文件ReaderWriter定义的接口。OSG不可能查找并加载所有的插件以获取它们支持的文件格式,这样,在程序启动时将会是一个很大的开销。因此, OSG使用职责链(Chain of Responsibility)的设计模式,以加载尽量少的插件。当用户程序尝试使用osgDB读取或写入文件时,OSG将按照如下步骤来查找合适的插件。
  • OSG搜索已注册的插件列表,查找支持文件格式的插件。开始时已注册插件列表仅包含了Registry类构造函数中注册的插件。如果OSG找到了可以支持此文件格式的插件,并成功执行了I/O操作,那么它将返回相应的数据。
  • 如果没有发现可以支持此格式的已注册插件,或者I/0操作失败,那么OSG将根据前面所述的文件命名规则创建插件文件的名称,并尝试读取相应的插件库。如果读取成功,OSG将添加此插件到已注册插件列表中。
  • OSG将重复步骤(1),如果文件I/O的操作再次失败,OSG将返回失败信息。
总的来说,用户不必了解OSG内部如何实现文件I/O操作,就可以使用插件顺利工作。反之,如果文件I/O操作失败,用户也可以根据给出的错误信息跟踪插件源代码中的相关内容。其在程序中代码的实现顺序如下图所示:

osg读取文件的原理(插件工作机制)_第1张图片

主要的实现的代码是在ReaderWriter::ReadResult Registry::read(const ReadFunctor& readFunctor)函数中

ReaderWriter::ReadResult Registry::read(const ReadFunctor& readFunctor)
{
    // first attempt to load the file from existing ReaderWriter's
    //看是否有可用的ReaderWriter,对当前的数据进行解析,如果解析成功,就返回结果
    AvailableReaderWriterIterator itr(_rwList, _pluginMutex);
    for(;itr.valid();++itr)
    {
        ReaderWriter::ReadResult rr = readFunctor.doRead(*itr);
        if (readFunctor.isValid(rr)) return rr;
        else results.push_back(rr);
    }


    // now look for a plug-in to load the file. 
    //根据文件名称创建新的动态库名称,然后加载动态库,增加_rwList对象个数
    std::string libraryName = createLibraryNameForFile(readFunctor._filename);
    if (loadLibrary(libraryName)!=NOT_LOADED)
    {
        //重新遍历一下,使用新的ReaderWriter进行数据的解析
        for(;itr.valid();++itr)
        {
            ReaderWriter::ReadResult rr = readFunctor.doRead(*itr);
            if (readFunctor.isValid(rr)) return rr;
            else results.push_back(rr);
        }
    }
}
接下去需要研究createLibraryNameForFile()。

你可能感兴趣的:(osg读取文件的原理(插件工作机制))