admin管理员组文章数量:1539746
2024年1月20日发(作者:)
从3DS文件中导入网格数据
这份文档是根据英文文当翻译的。
1. 介绍
======================
3ds文件是基于“块”存储的,这些块描述了诸如场景数据,每个编辑窗口(Viewport)的状态,材质,网格数据(我们最关心的就是这个)等等数据。每个块都包含一个ID和块长度的块头(这里原文写的是下一个块的偏移量,我认为不精确),如果你对该块的信息不感兴趣的话,可以直接跳过该块读取下一个块。跟许多文件格式类似,为了读取的方便,3ds文件中数据的存储方式是Intel式的,也就是说是高位放在后面,低位放在前面。比如:网格块的块头ID,0x4000在文件里是以00 40存放的,对于windows程序员来说,无需做任何转换。
每个块都以这样的块头开始:
开始 结束 长度 作用
0 1 2 块的ID
2 5 4 该块的长度
6…………………… 块数据
3ds文件是严格按照块来划分、分层的,通常一个块包含其他子块作为自己的数据。
所有的3ds文件都是以一个ID是0x4d4d主块开始的,他总是位于整个文件的最开始(可以把它当作识别3ds文件的标志),长度就是整个文件的长度。以下是一个描述块组织方式的图表。
MAIN3DS (0x4D4D)
|(注意,此处并不是紧接着EDIT块的,还有一些描述文件版本信息的块)
+--EDIT3DS (0x3D3D)
| |
| +--EDIT_MATERIAL (0xAFFF)
| | |
| | +--MAT_NAME01 (0xA000)
| |
| +--EDIT_CONFIG1 (0x0100)
| +--EDIT_CONFIG2 (0x3E3D)
| +--EDIT_VIEW_P1 (0x7012)
| | |
| | +--TOP (0x0001)
| | +--BOTTOM (0x0002)
| | +--LEFT (0x0003)
| | +--RIGHT (0x0004)
| | +--FRONT (0x0005)
| | +--BACK (0x0006)
| | +--USER (0x0007)
| | +--CAMERA (0xFFFF)
| | +--LIGHT (0x0009)
| | +--DISABLED (0x0010)
| | +--BOGUS (0x0011)
| |
| +--EDIT_VIEW_P2 (0x7011)
| | |
| | +--TOP (0x0001)
| | +--BOTTOM (0x0002)
| | +--LEFT (0x0003)
| | +--RIGHT (0x0004)
| | +--FRONT (0x0005)
| | +--BACK (0x0006)
| | +--USER (0x0007)
| | +--CAMERA (0xFFFF)
| | +--LIGHT (0x0009)
| | +--DISABLED (0x0010)
| | +--BOGUS (0x0011)
| |
| +--EDIT_VIEW_P3 (0x7020)
| +--EDIT_VIEW1 (0x7001)
| +--EDIT_BACKGR (0x1200)
| +--EDIT_AMBIENT (0x2100)
| +--EDIT_OBJECT (0x4000)
| | |
| | +--OBJ_TRIMESH (0x4100)
| | | |
| | | +--TRI_VERTEXL (0x4110)
| | | +--TRI_VERTEXOPTIONS (0x4111)
| | | +--TRI_MAPPINGCOORS (0x4140)
| | | +--TRI_MAPPINGSTANDARD (0x4170)
| | | +--TRI_FACEL1 (0x4120)
| | | +--TRI_SMOOTH (0x4150)
| | | +--TRI_MATERIAL (0x4130)
| | | |(原文SMOOTH和MATERIAL是属于TRI_FACE的,但观察源代码
| | | 发现应该是属于TRIMESH的,如果有问题可以E我)
| | | +--TRI_LOCAL (0x4160)
| | | +--TRI_VISIBLE (0x4165)
| | |
| | +--OBJ_LIGHT (0x4600)
| | | |
| | | +--LIT_OFF (0x4620)
| | | +--LIT_SPOT (0x4610)
| | | +--LIT_UNKNWN01 (0x465A)
| | |
| | +--OBJ_CAMERA (0x4700)
| | | |
| | | +--CAM_UNKNWN01 (0x4710)
| | | +--CAM_UNKNWN02 (0x4720)
| | |
| | +--OBJ_UNKNWN01 (0x4710)
| | +--OBJ_UNKNWN02 (0x4720)
| |
| +--EDIT_UNKNW01 (0x1100)
| +--EDIT_UNKNW02 (0x1201)
| +--EDIT_UNKNW03 (0x1300)
| +--EDIT_UNKNW04 (0x1400)
| +--EDIT_UNKNW05 (0x1420)
| +--EDIT_UNKNW06 (0x1450)
| +--EDIT_UNKNW07 (0x1500)
| +--EDIT_UNKNW08 (0x2200)
| +--EDIT_UNKNW09 (0x2201)
| +--EDIT_UNKNW10 (0x2210)
| +--EDIT_UNKNW11 (0x2300)
| +--EDIT_UNKNW12 (0x2302)
| +--EDIT_UNKNW13 (0x2000)
| +--EDIT_UNKNW14 (0xAFFF)
|
+--KEYF3DS (0xB000)
|
+--KEYF_UNKNWN01 (0xB00A)
+--............. (0x7001) ( viewport, same as editor )
+--KEYF_FRAMES (0xB008)
+--KEYF_UNKNWN02 (0xB009)
+--KEYF_OBJDES (0xB002)
|
+--KEYF_OBJHIERARCH (0xB010)
+--KEYF_OBJDUMMYNAME (0xB011)
+--KEYF_OBJUNKNWN01 (0xB013)
+--KEYF_OBJUNKNWN02 (0xB014)
+--KEYF_OBJUNKNWN03 (0xB015)
+--KEYF_OBJPIVOT (0xB020)
+--KEYF_OBJUNKNWN04 (0xB021)
+--KEYF_OBJUNKNWN05 (0xB022)
另外还有一些块是在整个文件中都会经常出现的,那就是颜色块
COL_RGB1 0x0010 以float存放3个分量
COL_RGB2 0x0011 以char存放3个分量
COL_RGB3 0x0012 不知道是什么格式。我手头上的两分源代码在这里有不一样的地方,一个写的是0x0012,一个是0x0013
2.主编辑块
主块下包含两个块:一个描述场景数据的主编辑块和一个描述关键帧数据的关键帧块。我们主要关心的是主编辑块的数据,其中包含了网格数据。
主编辑块的ID是0x3d3d,关键帧块的ID是0xb000。
编辑块包含了场景中使用的材质(纹理是材质的一部分),配置,视口的定义方式,背景颜色,物体的数据„„等等一系列数据,其中对我们有用的是材质块(0xafff)和物体块(0x4000)。
这里从物体块开始。
一个场景中可以有许多物体,这意味着物体块可以有许多个,3ds文件中并不包含描述物体数目这样的量,所以你必须根据块的长度判断是否已经读取所有的块(不要简单的使用文件读取函数的返回值来判断,这样可能会导致该文件对象在下面的操作中产生不可预知的错误)。
每个物体块都以0x4000开始,然后是一个表示他长度的DWORD。之后,接着是一个NULL Terminated String用来存放他的名称(比如Box-01)。以下是两个例子。
0x4000 dwLength “Box-01” 数据„„
0x4000 dwLength “Camera01” 数据„„
随后放置的是相应的块数据,可以是网格(0x4100)、光源(0x4600)或者摄像机(0x4700)等等。
对于网格块(0x4100)来说,存储的有该网格的顶点数据,面数据,贴图座标数据,变换数据,他们对应的ID分别是:
id 对应块
0x4110 顶点列表
0x4111 顶点选择表
0x4120 面列表
0x4130 面材质
0x4140 纹理坐标数据列表
0x4150 Face smoothing group
0x4160 变换矩阵块(用来表示物体姿态和位置)
0x4165 物体是否可见
0x4170 Standard Mapping
其中0x4150、0x4160、0x4170三个块的意义不明,对我们真正有用的块是0x4110、0x4120、0x4130、0x4140、0x4160这几个块
顶点列表(0x4110)
每个顶点列表在块头之后的两个字节给出了该物体顶点的数目的,因此,一个物体最多只能有2^16-1=65535个顶点。在顶点数目之后是对应的顶点列,以float三元组的方式给出。以下是一个例子:
50 数目,2Bytes
1.0f 第一个顶点的X,4Bytes
1.0f 第一个顶点的Y,4Bytes
1.0f 第一个顶点的Z,4Bytes
1.0f 第二个顶点的X,4Bytes
1.0f 第二个顶点的Y,4Bytes
1.0f 第二个顶点的Z,4Bytes
1.0f 第三个顶点的X,4Bytes
1.0f 第三个顶点的Y,4Bytes
1.0f 第三个顶点的Z,4Bytes
1.0f 第四个顶点的X,4Bytes
1.0f 第四个顶点的Y,4Bytes
1.0f 第四个顶点的Z,4Bytes
.
.
.
1.0f 第50个顶点的X,4Bytes
1.0f 第50个顶点的Y,4Bytes
1.0f 第50个顶点的Z,4Bytes
顺序读取所有顶点就可以了。
顶点列表之后是顶点选择列表,这个块跟我们需要的数据没有关系。
紧跟在顶点选择列表后面的是纹理坐标列表——如果有的话,假如定义的物体并没有给出纹理,那么这个块不存在。
纹理块的组织方式跟顶点列表很相似,同样以表示数目的WORD打头,对应的坐标列是float型的2元组:
50 数目,2Bytes
1.0f 第一个顶点的U,4Bytes
1.0f 第一个顶点的V,4Bytes
1.0f 第二个顶点的U,4Bytes
1.0f 第二个顶点的V,4Bytes
1.0f 第三个顶点的U,4Bytes
1.0f 第三个顶点的V,4Bytes
1.0f 第四个顶点的U,4Bytes
1.0f 第四个顶点的V,4Bytes
.
.
.
1.0f 第50个顶点的U,4Bytes
1.0f 第50个顶点的V,4Bytes
之后存放的是一个叫standard mapping的块,意义不明,似乎不影响使用网格。
接下来就是重要性仅次于VertexList的FaceList了,面列表块以定点索引的方式存放所有面片的信息。与前面的块一样,面块以一个表示面的数目的WORD开始,之后是4个
以WORD(因为顶点数目以WORD存放,所以使用WORD足够)方式存放的量,分别表示第一个顶点,第二个顶点,第三个顶点和面的信息。
50 数目,2Bytes
1 第一个顶点的索引,2Bytes
2 第二个顶点的索引,2Bytes
3 第三个顶点的索引,2Bytes
4 面的信息,2Bytes
1 第一个顶点的索引,2Bytes
2 第二个顶点的索引,2Bytes
3 第三个顶点的索引,2Bytes
4 面的信息,2Bytes
1 第一个顶点的索引,2Bytes
2 第二个顶点的索引,2Bytes
3 第三个顶点的索引,2Bytes
4 面的信息,2Bytes
.
.
.
1 第一个顶点的索引,2Bytes
2 第二个顶点的索引,2Bytes
3 第三个顶点的索引,2Bytes
4 面的信息,2Bytes
面信息中包含了该面是否被选中,如何被选中,和该面顶点的排列方式是顺时针还是逆时针的信息。一下是其各个位数代表的信息:
bit 0 AC 边方向
bit 1 BC 方向
bit 2 AB 方向
bit 3 Mapping (if there is mapping for this face)?
bit 4-8 0 (not used ?)
bit 9-10 x (chaotic ???)
bit 11-12 0 (not used ?)
bit 13 face selected in selection 3
bit 14 face selected in selection 2
bit 15 face selected in selection 1
这里根据观察3ds文件和读取之后的数据,绝大部分数据(事实上没有发现例外)都是只使用0-2三位。
最重要的是前三位信息,标示了整个面的正方向(按法向量的不同,面的正方向可以有两个方向)
0位说明了AC边的方向,如果该位为1则由A指向C,反之由C指向A。
1位是BC的方向,2位是AB的方向。
举例来说,如果该值为110=6,则表明排列的方法是A->B->C->A,是逆时针方式存放的(和OpenGL缺省的正方向一致)。
(这部分是按原文翻译的,在我实际操作上发现有问题,我写的代码在这里进行了简单的处理,将所有面都认为是逆时针的,事实上判断排列方式的时候遇到问题,如果全部按逆时针处理,就是全部直接拷贝的话,图像是正确的,反之如果做了判断,当开启cull的时候会出现一些面丢失,很明显这些面的排列方式原来是正确的,但经过转换之后就错了)
接下来是SMOOTH(0x4150)和MATERIAL(0x4130)块,SMOOTH块保存的是一组DWORD的数据,表明那些面是属于Smooth组的。(Smooth组的意义不明)
Material(0x4130)块描述了那种材质被使用和使用这种材质的面。所有的材质块都是以一个Null Terminated String描述的材质名(和前面定义的材质名称一样)开始的。紧接着是一个WORD描述有多少个面是用了这个材质,之后是一个WORD数组描述使用了这个材质的面的索引。
然后是Local Axis块,这个块是用来描述物体状态的。按照原文的的解释(没有读取这部分的源代码)。这个块的内容包括四个float*3组(就是4个三维向量),头三个float*3的块用来表示物体自身坐标系(相对于全局)的X,Y,Z坐标轴。最后一个float*3的块是物体象对中心坐标。
最后,是一个Visiable块,对网格读取似乎没有影响。原文也没有这部分。
材质块
to be continue
版权声明:本文标题:3DS文件格式 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/xitong/1705749415a153921.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论