在优诺科技实习的期间,修改了Emp项目中几个关于解析Obj文件的bug,记录下来当作实习的回忆吧。
1.Obj文件简介
Obj文件是Alias|Wavefront公司为3D建模和动画软件“Advanced Visualizer”开发的一种标准,适用于3D软件模型之间的互导,也可以通过Maya读写,其不需要文件头
2.Obj文件格式
2.1 项目中涉及的Obj文件行头定义
Obj文件几乎都是以行为单位进行排列的,每一行可以认为有两部分,第一部分我们不妨定义为“行头”(又可以称为关键字),另外的一个部分,我们不妨定义为“行数据”,举例来说有这么一行数据的话:
f 10026/10026/10026 10027/10027/100279553/9553/9553
“f”即为我们定义的行头,剩下的部分则代表行数据。
在EMP项目中所涉及到的行头如下:
关于顶点的行头如下:
v:几何体顶点(vertices),一般是3个数据,xyz
vt:贴图坐标点(texcoords),也成为纹理坐标,一般两个数据,xy
vn:顶点法线(normals),一般3个数据,xyz
关于面的行头如下:
f:面(face,代码中以triangle的形式表现
关于成组的行头如下:
g:组名称 group
2.2 项目中涉及的行数据形式
2.2.1点形式
v -0.798343598843.2453789791 20.8864974975
vt 1.669923126 0.089159741(对于Emaspro模型导入无实际意义)
vn0.19151529 0.9378334879 -0.2894653677
每一个点都由其对应的顶点坐标(v),贴图坐标(vt),vn(法线坐标)组成
2.2.2面形式
首先,我们需要明白,面其实是由点组成的,至少3个顶点就可以确定一个面,在Obj文件中,面也是由点进行索引。所谓索引的意思,是存入点的时候,Obj文件就为点隐式建立索引,举例来说:
v xx xx xx
v yy yy yy
v zz zz zz
则第一个点的索引值为1,第二个点的索引值为2,第三个点索引值为3,贴图坐标索引和法线坐标索引类似。
Emaspro导入的obj文件中主要为三角形面(当面为多边形面的时候转化为三角形面存入),所以以三角形面为例,讲述面在OBJ文件中存在的三种形式:
1.f 1/2/3 2/3/4 3/4/5
这种形式表示有一个面,其由三个点组成,三个点的坐标索引值是1,2,3(红色标注),即取第一个点,第二个点,第三个点的坐标为面上的三个点的坐标,同理,贴图索引为2,3,4(蓝色标注),法线坐标索引为3,4,5(紫色标注)。
2.f 1/3 2/5 3/4
这种形式表示这个面也有三个点,其中1,2,3为点索引值,3,5,4为贴图索引值。
3.f 1//3 2//5 3//4
这种形式表示的1,2,3为点索引值,3,5,4为法线索引
值得一提的是:面的索引值可以不为证书,当索引值为负数的时候(假设为-n),表示的意思是这个索引值为f上面的最后一个点(v或者vt或者vn)倒数n的那个点。
举例来说:
v 1 2 3
v 2 3 4
v 3 4 5
……(假设后面vt,vn也是3个)
f -1/-1/-1 -2/-2/-2 -3/-3/-3
这样的一组数据,我们可以得到三个坐标点,其索引值为1,2,3,三个贴图坐标,其索引值为1,2,3,三个法线坐标,其索引值为1,2,3。这里的索引明显是按照不同类型进行分类的索引。
则f所代表的第一个点,其点索引为点中倒着数的第一个点,也就是索引为3;其贴图坐标为贴图坐标中倒着数的第一个,索引也为3;法线索引也为法线中倒着数的第一个,索引也为3。之所以用负数相对索引,是因为有些时候(例如在点数目很多并频繁取倒数的几个点时)表达起来容易。
3.遇到的BUG以及解决方式
3.1Obj文件无法导入的问题
表现:导入obj文件,程序直接关闭
原因:obj文件虽然不支持对颜色的表示,但是支持导入材质库,在obj文件中以xxx.mtl形式出现,源代码中遇到mtl会去寻找这个mtl文件,如果找不到的话,会直接exit(1)。
修改:因为Obj文件中的mtl所需要的材质,在导入进EmasPro里面并不会涉及,所以直接将exit(1)改为return 即可。
3.2Obj文件导入部分部件缺失
表现:导入部分obj文件,部件缺失(tank.obj,su-37.obj)
原因:源代码中的ReadObjFile函数中,会判断当前部件中的面元数,如果为0,则会跳出while循环,因为存入部件的时候是以逆序链表存入的,也就是说,取部件是倒序取的,当obj部件中部存在空部件的时候,会跳出while循环而不是继续去读下一个部件直到全部部件读完。
修改:将while循环改为一直读到第一个部件为止(第一个部件的->next为空,不会继续读),遇到空部件的时候不是退出while循环,而是continue
3.3部分OBJ文件导入之后程序崩溃
表现:导入部分obj文件后,程序崩溃(F117.obj,m1a2.obj)
原因:源代码不支持相对坐标的obj文件,遇到索引为负数时崩溃
修改:添加当读到负数索引之后转换为正索引的代码
3.4部分obj文件读入之后面元丢失
表现:导入部分obj文件,部件不缺,但是部件中的部分面元丢失(tu-154.obj)
原因:部分obj文件中,在行末中有“\”符号,代表的是此行未完,原代码中没有进行处理,读到“\”直接就跳过了,由于下一行开始的时候读到的还是为1个数据,也就是数字开头,根据原代码,每行数据开头要为字母,所以也不会处理进行跳过,于是这个部件中的点没有读取完,面也没有读取完。
修改:添加了支持“\”相关的代码。
PS.因为”\”的出现情况相对复杂,另外一个合理的办法是用文字编辑软件打开OBJ文件,直接将其中的”\”替换成”\0”(替换时查找对象为\,替换对象为空),或者”\n”(替换时查找对象为\,替换对象为回车)。这种方法只针对Emaspro项目代码有效,并不代表一定有效。