• osg实现自定义插件读取自定义格式的模型文件到场景


    目录

    1. 前言

    2. 预备知识

    3. 工具、原料

    4. 代码实现


    1. 前言

            osg提供了很多插件来读取模型文件到场景中,这些插件支持大约70种格式类型的文件,但现实中的文件是各式各样,osg不可能囊括所有类型文件,当osg不支持某种类型格式文件时,就需要自己根据文件的格式开发插件来对这些文件定制解析、读取。

             本文讲解在osg中,如何实现自定义插件读取自定义格式的模型文件到场景。在读本博文之前,强烈建议先看osgDB::readNodeFile等函数源码剖析博文,该篇博文对osg读取文件的核心函数osgDB::readNodeFile的源码及原理机制进行了深入的剖析,看完并理解后,更加深对本博文的理解。

    2. 预备知识

            在osgDB::readNodeFile等函数源码剖析 博文提到,如果想要读取一个自定义格式的文件,则:

    1. 必须开发一个读写器类,该读写器类从ReaderWriter类派生,并重写相应的虚函数。
    2. 利用REGISTER_OSGPLUGIN宏将自定义读写器注册到系统内部,该宏的第1个参数为该类型文件的扩展名,第2个参数是自定义的读写器类名称。
    3. 将1、2步用dll来封装实现,dll文件名格式为:osgdb_ext.dll或osgdb_extd.dll,其中ext为文件扩展名,d表示debug版,没d的表示release版,并将生成的dll放到osgPlugins-xx.yy.zz目录,其中xx.yy.zz为osg版本号。

    3. 工具、原料

          本次用到的开发环境如下:

    • osg 3.6.2。
    • Visual Studio 2022。
    • Windows 11 操作系统。

    4. 代码实现

             现在假设有一个名称为sphere.csdn 格式的文件,文件内容如下:

    图1

    这个文件包含创建一个地球所需的数据,其中第1个数值表示球半径;接下来三个数值表示球心位置x、y、z的值,即球位于何处;最后一个表示贴在球上的纹理图片,注意,该纹理图片位于工程的当前目录下。这几个数据之间用空格隔开,空格的个数无所谓。问题的提出:怎么解析这个文件,将其数据在视景器中展现出来?

    步骤如下:

    1. 先用Visual Studio 2022建立一个dll工程,并将该dll编译生成的输出目录设置为:osg插件目录下,如:E:\osg\build_osg3.6.2\bin\osgPlugins-3.6.2,请根据你本机实际更改此目录。
    2. 将debug版的dll目标文件名称改为osgdb_csdnd,release版为osgdb_csdn,其中csdn为文件扩展名。
    3. 在Visual Studio 2022中请自行设置该工程的头文件和lib文件,这是osg开发基础,在此不赘述。
    4. 新建一个readMy3DModel类。

    如下为该插件在Visual Studio 2022的设置:

    图2 

    readMy3DModel类的.h文件和cpp文件如下:

    .h文件:

    1. #ifndef _READMY3DFMODEL_EXPORT_H
    2. #define _READMY3DFMODEL_EXPORT_H
    3. #include
    4. #include
    5. using namespace osgDB;
    6. class readMy3DModel : public ReaderWriter
    7. {
    8. public:
    9. readMy3DModel();
    10. ~readMy3DModel();
    11. public:
    12. // 返回类名
    13. virtual const char* className() const override;
    14. // 读节点
    15. virtual ReaderWriter::ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const override;
    16. private:
    17. osg::Node* makeSphere(const std::string& strFileDirPath, std::ifstream& stream) const;
    18. void doTexture(const std::string& strTextureImageFilePath, osg::Geode* pShereGeode) const;
    19. };
    20. REGISTER_OSGPLUGIN(csdn, readMy3DModel)
    21. #endif

    .cpp文件:

    1. #include "readMy3DModel.h"
    2. #include // 这里有很多文件操作相关的工具类,如:获取文件扩展名、文件目录名等
    3. #include // 这里有很多文件操作相关的工具类,如:文件是否存在,查找文件、文件类型等
    4. #include
    5. #include
    6. #include
    7. readMy3DModel::readMy3DModel()
    8. {
    9. supportsExtension("csdn", "sphere csdn model");
    10. }
    11. readMy3DModel::~readMy3DModel()
    12. {
    13. }
    14. ReaderWriter::ReadResult readMy3DModel::readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
    15. {
    16. ReaderWriter::ReadResult result;
    17. auto strExt = osgDB::getLowerCaseFileExtension(fileName);
    18. // 在构造函数里必须先通过supportsExtension函数将文件的扩展名加入,否则这里会返回false
    19. if (!acceptsExtension(strExt))
    20. {
    21. OSG_WARN << fileName << "Not Found!";
    22. result = ReaderWriter::ReadResult::FILE_NOT_FOUND;
    23. return result;
    24. }
    25. auto strFileFullPath = osgDB::findDataFile(fileName, options); // 获取文件的绝对路径
    26. if (!osgDB::fileExists(strFileFullPath))
    27. {
    28. OSG_WARN << strFileFullPath << "is not exist!";
    29. result = ReaderWriter::ReadResult::FILE_NOT_FOUND;
    30. return result;
    31. }
    32. auto strFileDirPath = osgDB::getFilePath(strFileFullPath); // 获取模型文件所在的目录
    33. std::ifstream stream(strFileFullPath.c_str(), std::ios::in | std::ios::binary);
    34. if (!stream)
    35. {
    36. result = ReaderWriter::ReadResult::ERROR_IN_READING_FILE;
    37. return result;
    38. }
    39. // 构建球体
    40. result = makeSphere(strFileDirPath, stream);
    41. return result;
    42. }
    43. const char* readMy3DModel::className() const
    44. {
    45. return "sphere csdn model";
    46. }
    47. osg::Node* readMy3DModel::makeSphere(const std::string& strFileDirPath, std::ifstream& stream) const
    48. {
    49. osg::Node* pShereNode{nullptr};
    50. osgDB::Input fr;
    51. fr.attach(&stream);
    52. // 以下解析sphere.csdn文件
    53. auto radius = 0.0f;
    54. auto center = osg::Vec3d(0.0, 0.0, 0.0);
    55. auto textureImageFilePath = std::string();
    56. /* 读取%f表示读取数值,%s表示读取字符串。
    57. 注意:%f与%f或%f与%s之间的分隔符即空格个数不需要和sphere.csdn文件完全一致
    58. sphere.csdn中要是字符串,必须用英文双引号括起来
    59. */
    60. if (!fr.matchSequence("%f %f %f %f %s"))
    61. {
    62. return pShereNode;
    63. }
    64. fr.readSequence(radius);
    65. auto x = 0.0f, y = 0.0f, z = 0.0f;
    66. fr.readSequence(x);
    67. fr.readSequence(y);
    68. fr.readSequence(z);
    69. center.set(x, y, z);
    70. fr.readSequence(textureImageFilePath);
    71. textureImageFilePath = strFileDirPath + R"(\)" + textureImageFilePath;
    72. bool bTextureImageFilePathIsExist = osgDB::fileExists(textureImageFilePath);
    73. if (!bTextureImageFilePathIsExist)
    74. {
    75. OSG_WARN << textureImageFilePath << "is not exist!";
    76. }
    77. auto pShereGeode = new osg::Geode();
    78. osg::TessellationHints* pHints = new osg::TessellationHints;
    79. pHints->setDetailRatio(5.0f); // 球的精细度设置
    80. auto pSpere = new osg::ShapeDrawable(new osg::Sphere(center, radius), pHints);
    81. pShereGeode->addDrawable(pSpere);
    82. pShereNode = pShereGeode;
    83. if (bTextureImageFilePathIsExist)
    84. {
    85. doTexture(textureImageFilePath, pShereGeode);
    86. }
    87. return pShereNode;
    88. }
    89. // 为地球贴纹理
    90. void readMy3DModel::doTexture(const std::string& strTextureImageFilePath, osg::Geode* pShereGeode) const
    91. {
    92. /*注意:这里需要读取jpg,故请保证jpg插件存在,否则读取jpg会失败。
    93. 关于怎么编译jpg插件到osg,请参见:https://blog.csdn.net/danshiming/article/details/115412956
    94. */
    95. auto pImage = osgDB::readImageFile(strTextureImageFilePath);
    96. if (nullptr == pImage)
    97. {
    98. OSG_WARN << "read " << strTextureImageFilePath << "Failed!";
    99. return;
    100. }
    101. // 构造二维纹理对象
    102. auto pTexture2D = new osg::Texture2D;
    103. pTexture2D->setImage(pImage);
    104. // 设置二维纹理过滤器属性
    105. pTexture2D->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
    106. pTexture2D->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
    107. pTexture2D->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
    108. pTexture2D->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
    109. // 将纹理贴到地球上
    110. pShereGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0, pTexture2D, osg::StateAttribute::ON);
    111. }

    说明:

    • 在.h文件中需要通过如下宏,将该插件类即该读写器注册到osg内核:
    REGISTER_OSGPLUGIN(csdn, readMy3DModel)

    关于这个宏的作用及参数含义,请参见前提提到的那篇博客。

    • 本类重写了父类的className和readNode函数。
    • osgDB::Input从osgDB:: FieldReaderIterator类派生而来,是一个字段迭代器,通过该对象可以遍历sphere.csdn中每个字段。当然,你也可以自己实现读取一行然后按空格解析出每个数据。
    • 因为贴纹理时,用的是jpg图片,所以本机需要有读取jpg的插件并挂接到osg中,关于怎么编译jpg插件到osg,请参见:osg第三方插件的编译方法(以jpeg插件来讲解)博文。
    • 其它的内容,请参见注释。

    请确保sphere.csdn和land_shallow_topo_2048.jpg文件在当前工程目录下,编写主程序以便调用该dll,如下:

    1. #include
    2. #include
    3. #include
    4. int main(int argc, char *argv[])
    5. {
    6. osgViewer::Viewer viewer;
    7. osg::ref_ptrspOptions = new osgDB::Options();
    8. spOptions->setDatabasePath("."); // 设置文件搜索目录,即在当前目录下看是否存在sphere.csdn文件
    9. auto pSphereNode = osgDB::readNodeFile("sphere.csdn", spOptions);
    10. auto pRoot = new osg::Group;
    11. pRoot->addChild(pSphereNode);
    12. viewer.setSceneData(pRoot);
    13. return viewer.run();
    14. }

    运行结果如下:

    图3

    也可以在命令行中输入如下命令验证是否加载成功:

    osgViewerd sphere.csdn

     至此,一个自定义的读写插件开发完成。

  • 相关阅读:
    力扣记录:Hot100(1)——1-19
    【Python百日进阶-数据分析】Day326 - plotly.express.scatter_geo():地理散点图
    YOLOv8+PyQt5农作物杂草检测系统完整资源集合(yolov8模型,从图像、视频和摄像头三种路径识别检测,包含登陆页面、注册页面和检测页面)
    深入理解磁盘阵列(RAID)和逻辑卷管理(LVM)技术及应用指南
    自考本科和成人高考有什么区别?
    阿里云、腾讯云、华为云:从内卷到外卷
    Idea操作Git合并另一个分支的部分提交
    【10】Spring源码-分析篇-AOP源码分析
    网页端IM即时通讯开发:短轮询、长轮询、SSE、WebSocket
    DoozyUI⭐️十三 、UIToggle:开关讲解
  • 原文地址:https://blog.csdn.net/danshiming/article/details/133603046