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

news2024/10/6 8:27:35

目录

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文件:

#ifndef _READMY3DFMODEL_EXPORT_H
#define _READMY3DFMODEL_EXPORT_H
#include<osgDB/ReaderWriter>
#include<osgDB/Registry>
using namespace osgDB;
class readMy3DModel : public ReaderWriter
{
public:
    readMy3DModel();
    ~readMy3DModel();
public:

    // 返回类名
    virtual const char* className() const override;

    // 读节点
    virtual ReaderWriter::ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const override;

private:
    osg::Node* makeSphere(const std::string& strFileDirPath, std::ifstream& stream) const;

    void doTexture(const std::string& strTextureImageFilePath, osg::Geode* pShereGeode) const;
};

REGISTER_OSGPLUGIN(csdn, readMy3DModel)

#endif 

.cpp文件:

#include "readMy3DModel.h"
#include<osgDB/FileNameUtils> // 这里有很多文件操作相关的工具类,如:获取文件扩展名、文件目录名等
#include<osgDB/FileUtils> // 这里有很多文件操作相关的工具类,如:文件是否存在,查找文件、文件类型等
#include<osgDB/ReadFile>
#include<osg/ShapeDrawable>
#include<osg/Texture2D>
readMy3DModel::readMy3DModel()
{
	supportsExtension("csdn", "sphere csdn model");
}

readMy3DModel::~readMy3DModel()
{

}

ReaderWriter::ReadResult readMy3DModel::readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
{
	ReaderWriter::ReadResult result;

	auto strExt = osgDB::getLowerCaseFileExtension(fileName);

	// 在构造函数里必须先通过supportsExtension函数将文件的扩展名加入,否则这里会返回false
	if (!acceptsExtension(strExt)) 
	{
		OSG_WARN << fileName << "Not Found!";
		result = ReaderWriter::ReadResult::FILE_NOT_FOUND;
		return result;
	}

	auto strFileFullPath = osgDB::findDataFile(fileName, options); // 获取文件的绝对路径
	if (!osgDB::fileExists(strFileFullPath))
	{
		OSG_WARN << strFileFullPath << "is not exist!";
		result = ReaderWriter::ReadResult::FILE_NOT_FOUND;
		return result;
	}

	auto strFileDirPath = osgDB::getFilePath(strFileFullPath); // 获取模型文件所在的目录
	std::ifstream stream(strFileFullPath.c_str(), std::ios::in | std::ios::binary);
	if (!stream)
	{
		result = ReaderWriter::ReadResult::ERROR_IN_READING_FILE;
		return result;
	}

	// 构建球体
	result = makeSphere(strFileDirPath, stream);

	return result;
}

const char* readMy3DModel::className() const
{
	return "sphere csdn model";
}

osg::Node* readMy3DModel::makeSphere(const std::string& strFileDirPath, std::ifstream& stream) const
{
	osg::Node* pShereNode{nullptr};
	osgDB::Input fr;
	fr.attach(&stream);

	// 以下解析sphere.csdn文件
	auto radius = 0.0f;
	auto center = osg::Vec3d(0.0, 0.0, 0.0);
	auto textureImageFilePath = std::string();

	/* 读取%f表示读取数值,%s表示读取字符串。
	   注意:%f与%f或%f与%s之间的分隔符即空格个数不需要和sphere.csdn文件完全一致
	         sphere.csdn中要是字符串,必须用英文双引号括起来
    */
	if (!fr.matchSequence("%f %f %f %f %s"))
	{
		return pShereNode;
	}

	fr.readSequence(radius);
	auto x = 0.0f, y = 0.0f, z = 0.0f;
	fr.readSequence(x);
	fr.readSequence(y);
	fr.readSequence(z);
	center.set(x, y, z);

	fr.readSequence(textureImageFilePath);
	textureImageFilePath = strFileDirPath + R"(\)" + textureImageFilePath;
	bool bTextureImageFilePathIsExist = osgDB::fileExists(textureImageFilePath);
	if (!bTextureImageFilePathIsExist)
	{
		OSG_WARN << textureImageFilePath << "is not exist!";
	}

	auto pShereGeode = new osg::Geode();
	osg::TessellationHints* pHints = new osg::TessellationHints;
	pHints->setDetailRatio(5.0f); // 球的精细度设置
	auto pSpere = new osg::ShapeDrawable(new osg::Sphere(center, radius), pHints);
	pShereGeode->addDrawable(pSpere);
	pShereNode = pShereGeode;

	if (bTextureImageFilePathIsExist)
	{
		doTexture(textureImageFilePath, pShereGeode);
	}
	return pShereNode;
}

// 为地球贴纹理
void readMy3DModel::doTexture(const std::string& strTextureImageFilePath, osg::Geode* pShereGeode) const
{

	/*注意:这里需要读取jpg,故请保证jpg插件存在,否则读取jpg会失败。
	关于怎么编译jpg插件到osg,请参见:https://blog.csdn.net/danshiming/article/details/115412956
     */
	auto pImage = osgDB::readImageFile(strTextureImageFilePath);
	if (nullptr == pImage)
	{
		OSG_WARN << "read " << strTextureImageFilePath << "Failed!";
		return;
	}

	// 构造二维纹理对象
	auto pTexture2D = new osg::Texture2D;
	pTexture2D->setImage(pImage);

	// 设置二维纹理过滤器属性
	pTexture2D->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
	pTexture2D->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
	pTexture2D->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
	pTexture2D->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);

	// 将纹理贴到地球上
	pShereGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0, pTexture2D, osg::StateAttribute::ON);
}

说明:

  • 在.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,如下:

#include<osgViewer/Viewer>
#include<osgDB/readFile>
#include<osgDB/FileUtils>

int main(int argc, char *argv[])
{
    osgViewer::Viewer viewer;
    osg::ref_ptr<osgDB::Options>spOptions = new osgDB::Options();
    spOptions->setDatabasePath(".");  // 设置文件搜索目录,即在当前目录下看是否存在sphere.csdn文件
    auto pSphereNode = osgDB::readNodeFile("sphere.csdn", spOptions);
    auto pRoot = new osg::Group;
    pRoot->addChild(pSphereNode);
    viewer.setSceneData(pRoot);
    return viewer.run();
}

运行结果如下:

图3

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

osgViewerd sphere.csdn

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1063184.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

林沛满-Wireshark的提示

本文整理自&#xff1a;《Wireshark网络分析的艺术 第1版》 作者&#xff1a;林沛满 著 出版时间&#xff1a;2016-02 最近有不少同事开始学习 Wireshark&#xff0c;他们遇到的第一个困难就是理解不了主界面上的提示信息&#xff0c;于是跑来问我。问的人多了&#xff0c;我也…

海洋测绘外业总结(2023年9月)

本次外业大约历时2个多月&#xff0c;中间遇到了多波束噪声、电火花电源箱冒烟、侧扫声纳绞车承重头内部线路中断&#xff0c;连接拖鱼的硫化头烧坏问题。 1、多波束噪声的问题 在外业采集过程中&#xff0c;EM2040D多波束水体图呈现了明显三段&#xff0c;意思就是&#xff…

SSM 注解配置方式整合

文章目录 Bean注解使用注解方式代替 web.xml 文件Spring注解方式整合Mybatis事务管理器配置请求与响应EnableWebMvc 注解REST 风格RestControllerComponentScan静态资源放行表现层数据封装EnableAspectJAutoProxy&#xff08;AOP注解方式&#xff09;AOP 通知获取数据&#xff…

AUTOSAR COM模块框架梳理

框架&#xff1a; COM的功能主要就是两个&#xff1a; 把IPDU内的signal提取出来提供给SWC使用&#xff0c;把SWC发送的signal拷贝到IPDU buffer内 所以&#xff0c;COM的关键字是 signal, signal group, IPDU, IPDU group Signal group 是为了保证 Complex Data Types 的数…

NodeMCU ESP8266开发流程详解(图文并茂)

文章目录 整体架构打开软件setuploop 连接开发板CP2102版本CH340版本 下载结论 整体架构 NodeMCU ESP8266基于Arduino IDE的开发相对来说还是比较容易上手的&#xff0c;我们基本需要以下几个东西&#xff1b; 一台安装好Arduino IDE的PC&#xff0c;并且已经部署环境&#x…

unity脚本_transform父子物体

运行 就没有父子关系了 当我们需要设置父物体时 通常我们用方式二 说明&#xff1a; 参数一&#xff1a;父物体 参数二&#xff1a;是否保留世界坐标的 位置 角度 缩放 信息 true 会保留 世界坐标系下的状态 和 父物体 进行计算 得到本地坐标系信息 false 不会保留 会直…

点餐小程序实战教程04-用户注册

我们上一篇讲解了自定义方法以及生命周期函数的概念&#xff0c;本节我们讲解一下数据源操作的方法。 1 查询单条数据 在我们的业务逻辑里&#xff0c;是要根据用户的登录信息来去数据源获取数据&#xff0c;根据返回结果来判断是否注册过。这里就涉及到要如何过滤数据的问题…

Qt扩展-Advanced-Docking 简介及配置

Advanced-Docking 简介及配置 一、概述二、项目结构三、安装配置四、代码测试 一、概述 Advanced-Docking 是类似QDockWidget 功能的多窗口停靠功能的库。很像visual stdio 的 停靠功能&#xff0c;这个库对于停靠使用的比较完善。很多的软件都使用了这个框架。 项目源地址&a…

基于知识蒸馏的两阶段去雨去雪去雾模型学习记录(二)之知识收集阶段

前面学习了模型的构建与训练过程&#xff0c;然而在实验过程中&#xff0c;博主依旧对数据集与模型之间的关系有些疑惑&#xff0c;首先是论文说这是一个混合数据集&#xff0c;但事实上博主在实验时是将三个数据集分开的&#xff0c;那么在数据读取时是如何混合的呢&#xff0…

【前端开发---Vue3】前段开发之详细的Vue3入门教程,特别适合小白系统学习,入门到熟练使用Vue看这一篇就够了!

前言&#xff1a; 这篇文章更加侧重的是Vue3不同于Vue2的知识点&#xff0c;如果学习Vue2请看下面这篇文章 Vue2详细系统入门教程 11.2 Vue3 声明&#xff1a;图片资源来自于黑马程序员公开学习资料 本人在学习当中&#xff0c;详细整理了笔记&#xff0c;供大家参考学习 1…

基于最近电平逼近的开环MMC逆变器Simulink仿真模型

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

重新认识AUTOSAR Interface

核心&#xff1a; S/R interface: data elementC/S interface: operation (with arguement)M/S interface: mode group &#xff08;macro&#xff09; 其实 data element&#xff0c;operation&#xff0c;mode group 才是核心&#xff0c;他们可以看作是用户自定义的变量类…

柯桥实用口语学习,韩语口头禅系列短句-恋爱篇

사랑해.我爱你。 너한테 미치겠어.我为你疯狂。 난 니거야.我是你的。 넌 내거야.你是我的。 너 잘 생겼어.你很帅。 네가 뽀뽀/키스 해도 돼? 我可以吻你吗?

基于虚拟阻抗的下垂控制——孤岛双机并联Simulink仿真

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

[Spring] Spring5——事务简介

目录 一、事务概述 1、什么是事务 2、事务的四个特性&#xff08;ACID&#xff09; 二、搭建事务操作环境 1、dao、service 两层结构 2、示例 3、模拟异常&#xff08;事务场景引入&#xff09; 三、Spring 事务管理 1、事务管理介绍 2、声明式事务管理——注解方式 …

c++ 学习 之 继承的基本语法

继承可以减少重复的代码 语法&#xff1a; class 子类 : 继承方式 父类子类 也称为 派生类 父类 也称为 基类 class BasePage { public:void header(){cout << "首页&#xff0c;公开课&#xff0c;登录&#xff0c;注册。。。&#xff08;公共头部&#xff09…

C/C++——内存管理

1.为什么存在动态内存分配 灵活性 静态内存分配是在编译时确定的&#xff0c;程序执行过程中无法改变所分配的内存大小&#xff1b;动态内存分配可以根本程序的运行环境来动态分配和释放空间&#xff0c;提供了更大的灵活性 动态数据结构 有些数据结构的大小和结构在编译时…

input输入多行文本:删除“首先 其次 此外 总的来说”

input允许多行输入 233.3表示停止输入input输入多行文本文本 &#xff08;空行&#xff09; &#xff08;空行&#xff09; &#xff08;空行&#xff09; 正文 &#xff08;空行&#xff09; &#xff08;空行&#xff09; &#xff08;空行&#xff09; 正文 &#xff08;空行…

国庆节:不仅仅是庆祝,更是成长与体验

目录 国庆节&#xff1a;不仅仅是庆祝&#xff0c;更是成长与体验引言第一部分&#xff1a;旅途风景目的地选择旅行亮点与国庆的联系 技术主题完成的博文国庆与技术 第三部分&#xff1a;回家的路为什么回家艰难险阻家与国庆 结论 国庆节&#xff1a;不仅仅是庆祝&#xff0c;更…

【Spring笔记02】Spring中的IOC容器和DI依赖注入介绍

这篇文章&#xff0c;主要介绍一下Spring中的IOC容器和DI依赖注入两个概念。 目录 一、IOC控制反转 1.1、什么是IOC 1.2、两种IOC容器 &#xff08;1&#xff09;基于BeanFactory的IOC容器 &#xff08;2&#xff09;基于ApplicationContext的IOC容器 二、DI依赖注入 2.…