3D模型格式解析(OBJ)

文章目录

  • OBJ格式解析
  • 一、OBJ格式
  • 二、vtkOBJReader源码解析
    • 1.主要部分
  • 总结


OBJ格式解析

通过解读vtk源码中对OBJ文件的读取代码,讲解如何获取obj模型的顶点(vertex)信息,法线信息,纹理信息以及点(Point)、线、面等主要元素的提取。

一、OBJ格式

1.OBJ文件示例

# 一些注释mtllib cube.mtl
g default
v -0.500000 -0.500000 0.500000
v 0.500000 -0.500000 0.500000
v -0.500000 0.500000 0.500000
v 0.500000 0.500000 0.500000
v -0.500000 0.500000 -0.500000
v 0.500000 0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
vt 0.001992 0.001992
vt 0.998008 0.001992
vt 0.001992 0.998008
vt 0.998008 0.998008
vt 0.001992 0.001992
vt 0.998008 0.001992
vt 0.001992 0.998008
vt 0.998008 0.998008
vt 0.001992 0.001992
vt 0.998008 0.001992
vt 0.001992 0.998008
vt 0.998008 0.998008
vt 0.001992 0.001992
vt 0.998008 0.001992
vt 0.001992 0.998008
vt 0.998008 0.998008
vt 0.001992 0.001992
vt 0.998008 0.001992
vt 0.001992 0.998008
vt 0.998008 0.998008
vt 0.998008 0.998008
vt 0.001992 0.998008
vt 0.998008 0.001992
vt 0.001992 0.001992
vn 0.000000 0.000000 1.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 1.000000 0.000000 0.000000
vn 1.000000 0.000000 0.000000
vn 1.000000 0.000000 0.000000
vn 1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
s 1
g pCube1
usemtl file1SG
f 1/1/1 2/2/2 3/3/3
f 3/3/3 2/2/2 4/4/4
s 2
f 3/13/5 4/14/6 5/15/7
f 5/15/7 4/14/6 6/16/8
s 3
f 5/21/9 6/22/10 7/23/11
f 7/23/11 6/22/10 8/24/12
s 4
f 7/17/13 8/18/14 1/19/15
f 1/19/15 8/18/14 2/20/16
s 5
f 2/5/17 8/6/18 4/7/19
f 4/7/19 8/6/18 6/8/20
s 6
f 7/9/21 1/10/22 5/11/23
f 5/11/23 1/10/22 3/12/24

2.格式分析

  • 注释以#开头;
  • v表示模型的顶点坐标,表示为: v x y z
  • vn:法线坐标,表示为:vn x y z
  • vt:纹理坐标,一般每个坐标包含两个值,表示为:vt u v w
  • p:Point元素;
  • l:线
  • f:面,可以有多个顶点表示;
    • f v1 v2 v3 … ,仅由三个以上顶点索引组成;
    • f v1/vt1 v2/vt2 v3/vt3 …,由顶点和纹理索引组成;
    • f v1//vn1 v2//vn2 v3//vn3 …,由顶点和法线索引组成;
    • f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 …,由顶点,纹理和法线索引组成;
      索引的形成是按照顶点、法线,纹理信息在文件中的先后顺序从1开始编号,可以采用负索引,表示倒数第几个顶点,法线,纹理,一般由三个顶点组成一个面,也有多个顶点的情况。

其它更多信息请查看补充部分。

补充:
http://netghost.narod.ru/gff/graphics/summary/waveobj.htm#WAVEOBJ-DMYID.3
https://blog.csdn.net/qq_26900671/article/details/81739977
https://www.cnblogs.com/daofaziran/p/11540517.html

二、vtkOBJReader源码解析

1.主要部分

/*---------------------------------------------------------------------------*\This is only partial support for the OBJ format, which is quite complicated.
To find a full specification, search the net for "OBJ format", eg.:http://en.wikipedia.org/wiki/Objhttp://netghost.narod.ru/gff/graphics/summary/waveobj.htmWe support the following types:v <x> <y> <z>vertexvn <x> <y> <z>vertex normalvt <x> <y>texture coordinatef <v_a> <v_b> <v_c> ...polygonal face linking vertices v_a, v_b, v_c, etc. whichare 1-based indices into the vertex listf <v_a>/<t_a> <v_b>/<t_b> ...polygonal face as above, but with texture coordinates foreach vertex. t_a etc. are 1-based indices into the texturecoordinates list (from the vt lines)f <v_a>/<t_a>/<n_a> <v_b>/<t_b>/<n_b> ...polygonal face as above, with a normal at each vertex, as a1-based index into the normals list (from the vn lines)f <v_a>//<n_a> <v_b>//<n_b> ...polygonal face as above but without texture coordinates.Per-face tcoords and normals are supported by duplicatingthe vertices on each face as necessary.l <v_a> <v_b> ...lines linking vertices v_a, v_b, etc. which are 1-basedindices into the vertex listp <v_a> <v_b> ...points located at the vertices v_a, v_b, etc. which are 1-basedindices into the vertex list\*---------------------------------------------------------------------------*///部分代码float xyz[3];//按行来解析数据...// in the OBJ format the first characters determine how to interpret the line://为定点信息if (strcmp(cmd, "v") == 0){// this is a vertex definition, expect three floats, separated by whitespace://获取坐标值if (sscanf(pLine, "%f %f %f", xyz, xyz + 1, xyz + 2) == 3){points->InsertNextPoint(xyz);}else{vtkErrorMacro(<< "Error reading 'v' at line " << lineNr);everything_ok = false;}}//纹理坐标else if (strcmp(cmd, "vt") == 0){// this is a tcoord, expect two floats, separated by whitespace:// 获取坐标if (sscanf(pLine, "%f %f", xyz, xyz + 1) == 2){tcoords->InsertNextTuple(xyz);}else{vtkErrorMacro(<< "Error reading 'vt' at line " << lineNr);everything_ok = false;}}//法线坐标else if (strcmp(cmd, "vn") == 0){// this is a normal, expect three floats, separated by whitespace://获取坐标if (sscanf(pLine, "%f %f %f", xyz, xyz + 1, xyz + 2) == 3){normals->InsertNextTuple(xyz);hasNormals = true;}else{vtkErrorMacro(<< "Error reading 'vn' at line " << lineNr);everything_ok = false;}}//点元素else if (strcmp(cmd, "p") == 0){// this is a point definition, consisting of 1-based indices separated by whitespace and /pointElems->InsertNextCell(0); // we don't yet know how many points are to comeint nVerts = 0; // keep a count of how many there arewhile (everything_ok && pLine < pEnd){// find next non-whitespace characterwhile (isspace(*pLine) && pLine < pEnd) { pLine++; }if (pLine < pEnd)         // there is still data left on this line{int iVert;if (sscanf(pLine, "%d", &iVert) == 1){pointElems->InsertCellPoint(iVert - 1);nVerts++;}else if (strcmp(pLine, "\\\n") == 0){// handle backslash-newline continuationif (fgets(rawLine, MAX_LINE, in) != NULL){lineNr++;pLine = rawLine;pEnd = rawLine + strlen(rawLine);continue;}else{vtkErrorMacro(<< "Error reading continuation line at line " << lineNr);everything_ok = false;}}else{vtkErrorMacro(<< "Error reading 'p' at line " << lineNr);everything_ok = false;}// skip over what we just sscanf'd// (find the first whitespace character)while (!isspace(*pLine) && pLine < pEnd) { pLine++; }}}if (nVerts < 1){vtkErrorMacro(<< "Error reading file near line " << lineNr<< " while processing the 'p' command");everything_ok = false;}// now we know how many points there were in this cellpointElems->UpdateCellCount(nVerts);}//线元素else if (strcmp(cmd, "l") == 0){// this is a line definition, consisting of 1-based indices separated by whitespace and /lineElems->InsertNextCell(0); // we don't yet know how many points are to comeint nVerts = 0; // keep a count of how many there arewhile (everything_ok && pLine < pEnd){// find next non-whitespace characterwhile (isspace(*pLine) && pLine < pEnd) { pLine++; }if (pLine < pEnd)         // there is still data left on this line{int iVert, dummyInt;if (sscanf(pLine, "%d/%d", &iVert, &dummyInt) == 2){// we simply ignore texture informationlineElems->InsertCellPoint(iVert - 1);nVerts++;}else if (sscanf(pLine, "%d", &iVert) == 1){lineElems->InsertCellPoint(iVert - 1);nVerts++;}else if (strcmp(pLine, "\\\n") == 0){// handle backslash-newline continuationif (fgets(rawLine, MAX_LINE, in) != NULL){lineNr++;pLine = rawLine;pEnd = rawLine + strlen(rawLine);continue;}else{vtkErrorMacro(<< "Error reading continuation line at line " << lineNr);everything_ok = false;}}else{vtkErrorMacro(<< "Error reading 'l' at line " << lineNr);everything_ok = false;}// skip over what we just sscanf'd// (find the first whitespace character)while (!isspace(*pLine) && pLine < pEnd) { pLine++; }}}if (nVerts < 2){vtkErrorMacro(<< "Error reading file near line " << lineNr<< " while processing the 'l' command");everything_ok = false;}// now we know how many points there were in this celllineElems->UpdateCellCount(nVerts);}//面元素else if (strcmp(cmd, "f") == 0){// this is a face definition, consisting of 1-based indices separated by whitespace and /polys->InsertNextCell(0); // we don't yet know how many points are to cometcoord_polys->InsertNextCell(0);normal_polys->InsertNextCell(0);int nVerts = 0, nTCoords = 0, nNormals = 0; // keep a count of how many of each there arewhile (everything_ok && pLine < pEnd){// find the first non-whitespace characterwhile (isspace(*pLine) && pLine < pEnd) { pLine++; }if (pLine < pEnd)         // there is still data left on this line{int iVert, iTCoord, iNormal;//1.获取顶点、纹理、法线索引if (sscanf(pLine, "%d/%d/%d", &iVert, &iTCoord, &iNormal) == 3){polys->InsertCellPoint(iVert - 1); // convert to 0-based indexnVerts++;tcoord_polys->InsertCellPoint(iTCoord - 1);nTCoords++;normal_polys->InsertCellPoint(iNormal - 1);nNormals++;if (iTCoord != iVert)tcoords_same_as_verts = false;if (iNormal != iVert)normals_same_as_verts = false;}//2.获取顶点、法线索引else if (sscanf(pLine, "%d//%d", &iVert, &iNormal) == 2){polys->InsertCellPoint(iVert - 1);nVerts++;normal_polys->InsertCellPoint(iNormal - 1);nNormals++;if (iNormal != iVert)normals_same_as_verts = false;}//3.获取顶点、纹理索引else if (sscanf(pLine, "%d/%d", &iVert, &iTCoord) == 2){polys->InsertCellPoint(iVert - 1);nVerts++;tcoord_polys->InsertCellPoint(iTCoord - 1);nTCoords++;if (iTCoord != iVert)tcoords_same_as_verts = false;}//4.获取顶点索引else if (sscanf(pLine, "%d", &iVert) == 1){polys->InsertCellPoint(iVert - 1);nVerts++;}else if (strcmp(pLine, "\\\n") == 0){// handle backslash-newline continuationif (fgets(rawLine, MAX_LINE, in) != NULL){lineNr++;pLine = rawLine;pEnd = rawLine + strlen(rawLine);continue;}else{vtkErrorMacro(<< "Error reading continuation line at line " << lineNr);everything_ok = false;}}else{vtkErrorMacro(<< "Error reading 'f' at line " << lineNr);everything_ok = false;}// skip over what we just read// (find the first whitespace character)while (!isspace(*pLine) && pLine < pEnd) { pLine++; }}}// count of tcoords and normals must be equal to number of vertices or zeroif (nVerts < 3 ||(nTCoords > 0 && nTCoords != nVerts) ||(nNormals > 0 && nNormals != nVerts)){vtkErrorMacro(<< "Error reading file near line " << lineNr<< " while processing the 'f' command");everything_ok = false;}// now we know how many points there were in this cellpolys->UpdateCellCount(nVerts);tcoord_polys->UpdateCellCount(nTCoords);normal_polys->UpdateCellCount(nNormals);// also make a note of whether any cells have tcoords, and whether any have normalsif (nTCoords > 0) { hasTCoords = true; }if (nNormals > 0) { hasNormals = true; }}else{//vtkDebugMacro(<<"Ignoring line: "<<rawLine);}

总结

熟悉vtk框架,将obj转换为其他模型文件以及提取有效信息。

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

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

相关文章

《动手学深度学习》第一天 (2.1+2.2)

2.1 获取和运行本书的代码 2.1.1获取代码并安装运行环境 第一步&#xff1a; 安装Miniconda &#xff0c;由于我的电脑已经安装过Anaconda&#xff0c;所以就没有再安装Miniconda了。 第二步&#xff1a;下载代码&#xff0c;地址 https://zh.d2l.ai/d2l-zh-1.0.zip 第三步…

从技术到科学,中国AI向何处去?

来源&#xff1a; 科学网编辑&#xff1a;宗华排版&#xff1a;华园作者&#xff1a;金榕&#xff08;阿里巴巴达摩院副院长、原密歇根州立大学终身教授&#xff09;● AI时代序幕刚拉开&#xff0c;AI目前还处于初级阶段&#xff0c;犹如法拉第刚刚发现了交流电&#xff0c;还…

QT高级编程之基本函数用法

1.QString类函数介绍 toInt()函数&#xff0c;可以将整型按照不同进制转换为QString对象&#xff1b;number()函数&#xff0c;可以将QString对象转换为不同进制的数字&#xff1b; 2.文档查询方法&#xff1a;通过Qt Creator编辑器通过帮助->索引->QString查看QString…

Linux命令整理 —— vi 和 vim 编辑器

一、目录结构 二 、vim 和 vim 编辑器 2.1【基本介绍】 所有的 Linux 系统都会内建 vi 文本编辑器。 vim 具有程序编辑的能力&#xff0c;可以看做是 vi 的增强版本&#xff0c;可以主动的以字体颜色辨别语法的正确性&#xff0c;方便程序设计。代码补完、编译及错误跳转等方…

网络版ATM项目的实现——客户端

网络版的ATM实现思路 客户端 1 #include "customer.h"2 3 static int sockfd;4 5 /*int create_msg_queue_c(int key)6 {7 //创建消息队列8 int msgid msgget(key,IPC_CREAT|0644);9 if(0 > msgid)10 {11 perror("msgget");12…

为什么航空发动机那么难造?看看3D工作原理

来源&#xff1a;网络目前大部分航空发动机都是属于燃气涡轮型&#xff0c;民用客机的发动机突出的安全性和可靠性&#xff0c;而军用发动机在这个基础上还追求更大的推力&#xff0c;以及开加力时的最大推力。由此可见&#xff0c;航空发动机领域中最强者必然是军用航发&#…

OSGB格式解析

主要介绍倾斜摄影数据OSGB二进制数据的解析&#xff0c;首先简单介绍一下OpenSceneGraph相关技术&#xff0c;并介绍倾斜摄影数据的组织结构&#xff0c;着重分析OSGB格式的相关字段&#xff0c;最后逐字节对OSGB数据进行解析并通过OpenSceneGraph对数据进行可视化&#xff0c;…

《动手学深度学习》 第二天 (自动求梯度)

2.3、自动求梯度 MXNet提供的autograd模块可以用来自动求梯度。 2.3.1 一个简单的栗子 这里我们要求对函数 y 2xTx &#xff08;2乘以x的转秩乘以X&#xff09;求关于列向量 x 的梯度。(使用jupyter notebook) 1、 创建变量x,并赋初值 x nd.arange(4).reshape((4,1)) #…

JS DOM节点增删改查 属性设置

一.节点操作增createElement(name)创建元素 appendChild();将元素添加 删获得要删除的元素 获得它的父元素 使用removeChild()方法删除 改第一种方式:使用上面增和删结合完成修改第二中方式:使用setAttribute();方法修改属性 使用innerHTML属性修改元素的内容 查<s…

CMake基本用法与项目编译

1.简介 1.1 CMake编译项目主要是 通过编写CMakeList.txt文件项目的每个子文件夹下分别编写CMakeList.txt&#xff0c;通过CMake工具来根据CMakeList文件生成makefile&#xff0c;最后编译成可执行文件。 1.2 通过CMake可以编译不同版本的vs项目&#xff0c;方便在不同版本的vs…

《动手学深度学习》 第二天 (线性回归)

3.2 线性回归的从零开始实现 只利用NDArray和autograd来实现一个线性回归的训练。 首先&#xff0c;导入本节中实验所需的包或模块&#xff0c;其中的matplotlib包可用于作图&#xff0c;且设置成嵌入显示。 %matplotlib inline from IPython import display from matplotli…

Gartner发布2021年新兴技术成熟度曲线

来源&#xff1a;Gartner中国8月24日&#xff0c;Gartner 公司最新发布了“2021年新兴技术成熟度曲线”&#xff08;Hype Cycle for Emerging Technologies&#xff0c;2021&#xff09;。其中&#xff0c;建立信任&#xff0c;加速增长以及塑造变革将是三大主要趋势&#xff0…

Vue语法学习第一课——插值

学习关于Vue的插值语法 ① 文本值 &#xff1a; "Mustache"语法&#xff0c;即双大括号 1 <span>Message:{{msg}}</span> 注&#xff1a;双大括号中的msg值改变&#xff0c;插入的内容也会随之改变&#xff0c;可通过v-once指令限制&#xff0c;但会影响…

计算方法之方程求根、线性方程组求解、插值方法、数值积分简介

提示:本文章主要通过介绍方程求根、线性方程组求解、插值方法、数值积分等相关方法的理论知识,并运用相关方法来解决一个实际的问题,文章中简单介绍了二分法、不动点迭代,牛顿法、Scant Method等方程求根方法,Gauss-Seidel迭代,Jacobi迭代,SOR迭代,Gauss消元法等方程组…

hadoop 重新格式化 NameNode

【问题描述】 在安装配置hadoop的过程中&#xff0c;很可能发生错误导致datanode或者namenode 启动失败&#xff0c;这时我们可以选择重新格式化 namenode。 一、删除data数据和log日志 二 、使用命令 bin/dfs namenode -format 重新格式化 【注意事项】 为什么不能一直格式…

人工智能“上位”会让程序员消失吗?

大脑以及二进制代码&#xff08;图&#xff1a;Canva&#xff09;来源&#xff1a;Forbes作者&#xff1a;Nisha Talagala编译整理&#xff1a;科技行者写代码已经成了许多工作的一项关键技能。一些国家和学校甚至认为&#xff0c;编程语言是一种可以接受的外语。而在各种熙熙攘…

分类的IP地址

现有物理地址再有IP地址IP地址的表示方法为点分十进制法IP地址的设计思想&#xff1a;网络部分 主机部分 分类的IP地址 特征&#xff1a;根据不同特征的IP地址&#xff0c;事先约定好网络号所占的位数和主机号所占的位数。 A类地址 全球一共有27-2 个A类网络&#xff0c;每…

人工智能之深度优先,广度优先,贪婪最佳优先搜索,A*搜索以及爬山法与遗传算法

项目场景: 1. 分别用宽度优先、深度优先、贪婪算法和A*算法求解“罗马利亚度假问题”。 2. 分别用爬山法和GA算法求解n皇后问题。 文章目录 项目场景:一、度假场景1.1 问题描述2.1 问题分析:1.3 解决方案:1.4 运行结果二、N皇后问题2.1 问题描述2.2 数据存储结构2.3 算法思…

操作系统之多级队列调度算法,银行家算法,动态分区式存储区管理

题目描述: 1.对于多级队列调度算法,主要介绍轮转法,短进程优先算法;银行家算法主要介绍进程的资源分配策略; 2.对于动态分区式存储区管理,主要介绍首先适应法,最佳适应法,最坏适应法等调度算法。 文章目录 题目描述:程序功能及设计思路1. 多级队列调度算法函数设计2. …

卫星对于物联网来说是一个非常好的选择

ALAMY来源&#xff1a;IEEE电气电子工程师对许多人来说&#xff0c;“物联网”一词可能会让人想起智能城市的努力&#xff0c;比如配备交通摄像头和空气质量传感器的路灯&#xff0c;或者在自己家里连接设备。一个很自然的问题是&#xff0c;为什么你从没想使用卫星连接任何这些…