OSG中读取模型插件的单独使用
模型插件单独使用的原因
因为我的任务的需求,我需要把诸如3ds,flt等多种格式的模型添加到数据库里,这个时候我需要在把模型添加到数据库的时候,自动的把模型的纹理也拷贝或者移动到相对应的磁盘目录中去。这个时候就必须要获取模型的纹理文件列表,读取模型的内部信息。而 OSG的插件机制只是返回Node节点,或者ReadWriter类,缺少更为精细的模型信息。所以我就借助OSG的3DS,FLT的模型插件源码,对这两个插件进行了扩展,获取源码中的纹理路径名,并导出具体的DLL类,重新得到Node节点,和原有的OSG程序进行了融合。这里写下我的方法和走的一些弯路,希望能给有这方面的需求的朋友一点经验,。也希望能为OSG社区做一点小小的贡献。我的水平不高,对osg的理解和使用也很简单,请大家见谅!
插件的单独使用方法:
测试环境的搭建
我先使用CMake配置了osg的源程序,然后打开工程,如图一
图一OSG源程序
把Plugins 3ds目录下的文件都拷贝到了Examples Huds里面,这样做的原因有两个
第一:因为我自己不知道模型文件里面的纹理列表在哪里,需要进行多次尝试和验证,而Plugins 3ds生成的是dll,不方便测试。需要在一个exe 程序进行测试。生成的容器的测试环境不如Examples 方便。
第二:如果自己新建一个工程,在拷贝Plugins 3ds文件的,因为Plugins 3ds里工程里有依赖原始的OS
G源码,新的工程里依赖的却是别人编译好的OSG dll,这样程序非常容易失败,我开始的时候就这么做的,可是总在创建纹理函数时抛异常。因为上面两点原因,所以我就把文件放到了Examples Huds,需要注意的是:别忘了修改“Example”链接的lib文件,因为有可能插件库里的链接的lib和Example Huds不一样,这样测试环境就配置好了。
具体的插件的修改
我在Examples Huds里,添加了:ReaderWriter3DS.h头文件,用于后来的导出类使用。把ReaderWriter3DS作为导出类,就是像这样加上:
#ifdef DLL_API
#else
#define DLL_API _declspec(dllimport)
#endif
#pragma once
3dsclass DLL_API ReaderWriter3DS: public osgDB::ReaderWriter这种修饰符。
同时我修改了:
virtual ReadResult readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) ; const函数,
因为 const函数里不能对类成员作修改,而我想把Lib3dsFile *f; 作为ReaderWriter3DS的成员变量,所以就把这个函数的声明给就改了。3DS的模型格式比较简单,我很快到了模型文件的列表,在
osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(const
std::string& file, const osgDB::ReaderWriter::Options* options) 函数里添加了获取纹理列表的方法。这样获取模型的纹理列表。
for (Lib3dsMaterial *mat=f->materials; mat; mat=mat->next)
{
drawStateMap[mat->name] = ateStateSet(mat, options);
string file=mat->texture1_map.name;
myTexfileList.push_back(file);
}
这里我想补充说明的是:我发现Lib3dsMaterial里其实有很多纹理文件,如图二所示:
图二:材质结构体的部分信息
可是不知道为什么,只有texture1_map真正使用了,所以我就只获取了这个纹理文件的名字,然后用专门的模型查看器(3D MAX之类)验证获取的外部纹理文件的名字是否正确。正确后就可以导出如下三
个文件。
图三:导出dll,lib截图
导出的dll与OSG程序结合
这里在网友“礼拜六”的指点下,我才知道原来可以这么用(见笑了!),具体的代码片段如下:
ReaderWriter3DS *rw=new ReaderWriter3DS();
osgDB::ReaderWriter::ReadResult
rr=rw->readNode("E:\\Plane\\z9.3DS",NULL);
osg::Node *Node();
这样就可以获得Node节点了,以后的方法就是OSG的方法了,这样在Example hud里面测试成功后,我就对Plugins 3ds 进行了真正的修改,导出类库,以后再OSG里,如果事先知道是3ds的模型,就可以直接调用这个ReaderWriter3DS 这个类来读了。
后来,按照我自己的需求,在阿威的指点下,我也到了OpenFlight的纹理列表的获取方法和位置(这里注意:如果是把OpenFlight的源文件放到Example 里的时候,别忘了在工程里设置链接库,否则会报链接错误的),因为OpenFlight 是按照节点来读取的,我测试了在PaletteRecords.cpp文件里面可以获得纹理列表。具体代码片段如下:
class TexturePalette
{
virtual void readRecord(RecordInputStream& in, Document& document)
}
readRecord这个方法里的std::string pathname
=osgDB::findDataFile(Options());就是FLT模型的纹理路径,这样按照扩展3ds的方法,对OpenFlight进行扩展,就可以实现我的目的了。
后来我在读取FLT的模型源码也看到了:FltExportVisitor这个类,里面其实已经获得了flt模型的点,面片,纹理列表信息,用于写到文件里,想想我自己可能做得多余了,后来发现这个似乎只有在设置了节
点回调的时候,才能使用,而我的方式相当于仅仅读取模型,并不进行渲染,所以这样速度会比较快,同样的方法也可以用于3DS的模型,即可以不渲染模型,直接使用Lib3dsFile这个结构体,就可以获得纹理列表,面片信息,可以进一步对Plugin -3ds进行裁剪,不过因为时间原因,我没有进行这样的尝试。以后如果对批量模型进行入库的时
候,我觉得这会是一个比较好的方法。同时因为osg插接件库是需要搜索插件,如果已经知道了读取模型的格式,直接调用对应的插件也许更快一些。这个是我的猜测。
以上就是我对 OSG插件库按照自己的方式进行的小小的修改,非常的简单和浅显,鉴于水平有限,第一次写文档,很多意思都表达不清楚,请大家见谅和理解。也谢谢osg里的阿威和礼拜六。
By Bruno