3D模型格式解析(OBJ)


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/Obj
    http://netghost.narod.ru/gff/graphics/summary/waveobj.htm

We support the following types:

v <x> <y> <z>

    vertex

vn <x> <y> <z>

    vertex normal

vt <x> <y>

    texture coordinate

f <v_a> <v_b> <v_c> ...

    polygonal face linking vertices v_a, v_b, v_c, etc. which
    are 1-based indices into the vertex list

f <v_a>/<t_a> <v_b>/<t_b> ...

    polygonal face as above, but with texture coordinates for
    each vertex. t_a etc. are 1-based indices into the texture
    coordinates 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 a
    1-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 duplicating
    the vertices on each face as necessary.

l <v_a> <v_b> ...

    lines linking vertices v_a, v_b, etc. which are 1-based
    indices into the vertex list

p <v_a> <v_b> ...

    points located at the vertices v_a, v_b, etc. which are 1-based
    indices 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 come

				int nVerts = 0; // keep a count of how many there are
				
				while (everything_ok && pLine < pEnd)
				{
					// find next non-whitespace character
					while (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 continuation
							if (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 cell
				pointElems->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 come

				int nVerts = 0; // keep a count of how many there are

				while (everything_ok && pLine < pEnd)
				{
					// find next non-whitespace character
					while (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 information
							lineElems->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 continuation
							if (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 cell
				lineElems->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 come
				tcoord_polys->InsertNextCell(0);
				normal_polys->InsertNextCell(0);

				int nVerts = 0, nTCoords = 0, nNormals = 0; // keep a count of how many of each there are

				while (everything_ok && pLine < pEnd)
				{
					// find the first non-whitespace character
					while (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 index
							nVerts++;
							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 continuation
							if (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 zero
				if (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 cell
				polys->UpdateCellCount(nVerts);
				tcoord_polys->UpdateCellCount(nTCoords);
				normal_polys->UpdateCellCount(nNormals);

				// also make a note of whether any cells have tcoords, and whether any have normals
				if (nTCoords > 0) { hasTCoords = true; }
				if (nNormals > 0) { hasNormals = true; }
			}
			else
			{
				//vtkDebugMacro(<<"Ignoring line: "<<rawLine);
			}

总结

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

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秘境之眼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值