下载

0下载券

加入VIP
  • 专属下载特权
  • 现金文档折扣购买
  • VIP免费专区
  • 千万文档免费下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 3ds文件结构

3ds文件结构.doc

3ds文件结构

00001z
2011-05-25 0人阅读 举报 0 0 0 暂无简介

简介:本文档为《3ds文件结构doc》,可适用于IT/计算机领域

DS文件结构(ds)文档版本:–作者:MartinvanVelsen(重写)RobinFercoq(重写)JimPitts(原版)AlbertSzilvasy(原代码)翻译:樱From:GameReshttp:wwwgamerescom还有许多“块”我并没有写入本文档中这是因为我并不知道他们有什么用如果你知道的话请来信告诉我。当我ds文件介绍DS文件格式介绍ds文件是基于“块”存储的这些块描述了诸如场景数据每个编辑窗口(Viewport)的状态材质网格数据(我们最关心的就是这个)等等数据。每个块都包含一个ID和块长度的块头(这里原文写的是下一个块的偏移量我认为不精确)如果你对该块的信息不感兴趣的话可以直接跳过该块读取下一个块。跟许多文件格式类似为了读取的方便ds文件中数据的存储方式是Intel式的也就是说是高位放在后面低位放在前面。比如:网格块的块头IDx在文件里是以存放的对于windows程序员来说无需做任何转换。每个块都以这样的块头开始:开始结束长度作用块的ID该块的长度……………………块数据ds文件是严格按照块来划分、分层的通常一个块会包含下级子块作为自己的数据而子块又有孙块孙块亦有子块子子孙孙无穷尽也……如果你从一个一级块开始按照跳过每块长度找寻下一块的做法无疑是无法访问到二级子块的相反的从二级子块开始却有可能回到一个一级块。按ds文件的划分方式有一个块是其它所有的块共同的祖宗也就是其他所有块的根块我们称之为主块(就是下图的MAINDS块)。主块说白了就是整个文件。所有的ds文件都是以他开始的他总是位于整个文件的最开始(你可以把它的块ID当作识别ds文件的标志)延伸到整个文件结束(多么庞大的东西啊)。他的作用………………也就是存在而已。你只要知道有这么个块存在并了解他的逻辑结构就可以了。MAINDS(xDD)|(注意此处并不是紧接着EDIT块的还有一些描述文件版本信息的块)EDITDS(xDD)|||EDITMATERIAL(xAFFF)|||||MATNAME(xA)(SeemliDoc)|||EDITCONFIG(x)|EDITCONFIG(xED)|EDITVIEWP(x)|||||TOP(x)||BOTTOM(x)||LEFT(x)||RIGHT(x)||FRONT(x)||BACK(x)||USER(x)||CAMERA(xFFFF)||LIGHT(x)||DISABLED(x)||BOGUS(x)|||EDITVIEWP(x)|||||TOP(x)||BOTTOM(x)||LEFT(x)||RIGHT(x)||FRONT(x)||BACK(x)||USER(x)||CAMERA(xFFFF)||LIGHT(x)||DISABLED(x)||BOGUS(x)|||EDITVIEWP(x)|EDITVIEW(x)|EDITBACKGR(x)|EDITAMBIENT(x)|EDITOBJECT(x)|||||OBJTRIMESH(x)|||||||TRIVERTEXL(x)|||TRIVERTEXOPTIONS(x)|||TRIMAPPINGCOORS(x)|||TRIMAPPINGSTANDARD(x)|||TRIFACEL(x)|||||||TRISMOOTH(x)|||TRIMATERIAL(x)|||(原文SMOOTH和MATERIAL是属于TRIFACE的我手头上的|||源代码的在这个地方的处理有误如果有问题可以E我)|||TRILOCAL(x)|||TRIVISIBLE(x)|||||OBJLIGHT(x)|||||||LITOFF(x)|||LITSPOT(x)|||LITUNKNWN(xA)|||||OBJCAMERA(x)|||||||CAMUNKNWN(x)|||CAMUNKNWN(x)|||||OBJUNKNWN(x)||OBJUNKNWN(x)|||EDITUNKNW(x)|EDITUNKNW(x)|EDITUNKNW(x)|EDITUNKNW(x)|EDITUNKNW(x)|EDITUNKNW(x)|EDITUNKNW(x)|EDITUNKNW(x)|EDITUNKNW(x)|EDITUNKNW(x)|EDITUNKNW(x)|EDITUNKNW(x)|EDITUNKNW(x)|EDITUNKNW(xAFFF)|KEYFDS(xB)|KEYFUNKNWN(xBA)(x)(viewport,sameaseditor)KEYFFRAMES(xB)KEYFUNKNWN(xB)KEYFOBJDES(xB)|KEYFOBJHIERARCH(xB)KEYFOBJDUMMYNAME(xB)KEYFOBJUNKNWN(xB)KEYFOBJUNKNWN(xB)KEYFOBJUNKNWN(xB)KEYFOBJPIVOT(xB)KEYFOBJUNKNWN(xB)KEYFOBJUNKNWN(xB)另外还有一些块是在整个文件中都会经常出现的那就是颜色块COLRGBx以float存放个分量(这里的float是IEEE定义的float)COLRGBx以char存放个分量COLRGBxsobad不知道是什么格式如果你知道的话可以Email我。我手头上的两分源代码在这里有不一样的地方一个写的是x,一个是x.主编辑块主块下包含两个块他们是一级子块:一个描述场景数据的主编辑块(ID==xdd)和一个描述关键帧数据的关键帧块(ID==xb)。相对于关键帧块主编辑块对我们更重要。它包含了场景中使用的材质(纹理是材质的一部分)配置视口的定义方式背景颜色物体的数据……等等一系列数据可以说他就表示了我们当前编辑场景的状况和当前窗口的配置数据。这里主编辑块的子块虽然是按照一定的次序存放但其中有些块并不是一定存在的(比如如果你没有定义材质使用缺省材质这里将不存在材质块)。所以不要试图找某个块因为说不定找着找着你不仅没找到他还把其他有用的块都给“找”过去了中国人管这个叫偷鸡不成蚀把米。这里并不是教你真地去偷一只鸡看看是不是真的很相似而是说明在这种情况下最好的做法是被动的碰到什么块就读取什么块全部读完了再来看哪些数据被读取了哪些数据没有。另外为了保证文件操作的正确性必须保证读取操作不会超出块的范围这些都给我们的读取带来了麻烦。不过满足吧聊胜于无。你也不想每次都想象着无数的顶点坐标和纹理坐标、凭空建立一个复杂的模型或者场景吧这样虽然对你的空间想象力很有帮助但肯定不会让你长寿。我这么啰嗦说这些只是让你明白我为什么会把源代码写得像一锅粥前前后后都一团糟。当然如果你看到我手里头的另外一份源代码如何把setjmp和longjmp用得出神入化的时候你就会感叹跟他们比起来我们把源代码搞得乱七八糟、让别人都看不懂的功夫还远没有到家。材质块材质块定义了使用于物体上的材质的属性包括我们很熟悉的AmbientDiffuse等分量遗憾的是我只明白AmbientDiffuseSpecualr三个分量的属性和读取的方法。在ds文件定义的材质上似乎没有Emision属性。Shininess分量是有的按Mli文档的解释至少有两个块跟Shininess有关但我还不明白这两个块中数据代表的含义和他的计算方法。除了我们通常意义上的材质之外材质块还定义了一个非常重要的属性那就是该材质所使用的纹理。如果你有DSMax的使用经验你应该能够记得能够将一个Map附加到材质的某个属性上但通常我们使用的都是Diffuse属性定义的纹理。DS中纹理坐标的计算是分成两个部分的。材质中U、V、W方向的Offset、Tiling和Rotate等属性定义了在这个材质上定义的纹理在整张纹理文件中的位置(具体的计算方法在后面物体块的时候再给出)和缠绕的方法而后面网格块中存储的纹理坐标均是在这张材质定义的局部纹理中相应的纹理坐标。所以如果你不读取材质块就想取得纹理坐标是不可能的那样渲染出来的物体就会花花绿绿的一团糟想象一下把一个岁大的孩子和一大堆七七八八的颜料放在同一个房间里半个小时后的情景就和你在你渲染窗口里看到的情况一样样了。对于材质块xAFFF来说他的一级子块包括:xA这是一个以结尾的字符串(以下统称TeriminatedString)存储该材质的名称这个名称在整个文件定义的材质中是唯一的可以通过它确定使用的是哪个材质对象。很不幸的是没有任何表示该字符串长度的量所以你要做的就是“读、判断、读、判断……”天想象一下一个名字有天文数字长度的材质(会有这样的BT吗?)读取这样一个材质名称所耗费的时间可以把人类送上火星。对于一个听到“效率”就会流口水跟巴甫若夫的狗极为相似的CC程序员来说(ScottMeyers语)简直无法容忍。如果你是上面所说的那类人的话我劝你做好思想准备后面还有一些的块的数据都是这种该死的TerminatedString。xA–xA分别是Ambient块Diffuse块Specular块和Shininess块。这里前三个块存放数据的方式是一样的一个Color块就搞定了。Shininess块上面已经说过了暂时无法解决。xA定义了该材质的类型(比如Phong、Flat等等)xA这是纹理块Diffuse定义的纹理就存储在这个块中从xA开始的一系列子块是这个块的一级子块。xA(String)又一个TerminatedString该纹理的文件名注意这里的文件名不包含路径。xA(WORDBytes)纹理选项块按Mli文档的说法位有意义分别表示几个纹理的属性(比如是否tiling是否Mirror等等)这部分我不准备详细讲因为只要建模的时候稍微注意一下就可以忽略这个块。比如Mirror就可以用把两个Tiling换成负数的方法解决。xA(floatBytes)记得那个mapfilteringblur吗?就是他表示我对DS不是很熟悉不太清楚这个分量的含义在源码终直接跳过去了。xA(floatBytes)U方向的Tiling值xA(floatBytes)V方向的Tiling值xA(floatBytes)U方向的Offset值xAA(floatBytes)V方向的Offset值注意这里读到的Voffset的值跟ds里面看到的值相差一个符号。xAC(floatBytes)W方向的rotate值同样的也相差一个符号。物体块你不会把整个场景都作为同一个物体是吧?你不会为了做一个机器人就把它的头身子……反正能动的东西都做成一个独立的模型然后存储在不同的文件中是吧?如果答案是“不”请看下一段答案如果是“是”……首先对你表示敬佩但你还是要看下一段。如果你的答案是“不”那就意味着在绝大部分情况下你要处理的文件中包含许多个物体。不过如你前面所见一个连字符串长度都舍不得纪录的文件格式你是不能指望太多的。除非真是非用不可了他才会扭扭捏捏半天挤出那么几个字节的空间用来存放其后面某些对象的数目后面你马上就能看到不过目前门都没有。因此放弃确定物体数目的努力吧毫无疑问判断是否将所有物体块都读入的唯一办法是判断你是否已经到达Edit块的块尾了。事实上前面的Material块也是一样的。当然如果你固执地要先遍历一遍整个文件先把所有的物体块都揪出来一遍以确定其数目我只能说您真是太特立独行了“流口水的C程序员”俱乐部不适合您。(事实上没这么夸张这样做得并不见得会慢多少因为我们每次做的都是跳过去而不是读他但这样做除了能够确定数目之外也不会带来多少方便)当然物体块(ID==x)是无辜的他并不知道他的上级给我们带来了怎样的麻烦咱们还是很有必要认识一下他的。对于所有的物体块在惯例的六个字节的块头之后均有一个TerminatedString用来存放他的名称(比如Box、CameraEtc)。随后以一个子块来表示这个物体:包括该物体的种类(就是子块的ID啦)、属性和描述该物体的数据。这里的子块可以是网格(x)、光源(x)或者摄像机(x)等等他们都是你定义的场景中的物体。.网格块网格块是物体块的一个一级子块他描述的其实就是你在场景里面搭建的一个物体比如正方体圆柱甚至更为复杂的模型。如果你对D引擎比较熟悉的话当然知道通常描述一个网格物体需要怎样的数据和数据结构。是的无非是顶点有时候包含顶点法向量各个顶点的纹理坐标面信息有些时候还有边信息以及其他的一些什么东西。这其中根据顶点法向量的定义法向量可以由面数据和顶点数据生成(如果你不明白可以自己看看书这份文档不是D引擎的教程或者D解析几何教程)边信息则是包含于面信息里面的。Autodesk的工程师不比你笨他们也清楚地知道必须将哪些量写入文件中以下是网格块包含的一级子块的作用:id对应块x顶点列表块x顶点选择表块(可忽略)x面信息块x纹理坐标数据列表块x物体信息块(用来表示物体姿态和位置)x物体是否可见xStandardMapping我们将读取顶点块(x)面块(x)纹理块(x)必要的时候还包括变换矩阵块(x)这些数据足够在我们的渲染窗口上把物体重新生成出来了。其他有些块描述的是该物体在DS编辑窗口里被编辑的状态(如顶点选择块和x的visiable块)有些的含义stillunknow所以这些块我都直接跳过去。注意这里并不一定按照DS文件排放块的次序来介绍所有块而是……依据我的心情(立刻被无数砖头砸死)。(顽强地站起来擦干身上的血迹)我前面已经说过了虽然块的存放的确存在一定的次序但这里不要有次序的概念。因为有些块并非一定存在比如纹理坐标块如果你没有让该物体使用纹理的话他就不存在你一定要按顺序找所有的块的话就好像在一片浑浊不堪的水塘里面找几条不知道到底存不存在的鱼一样费劲。所以最合适的做法是我上面说的用被动的方式来读取让鱼自己跑到你的渔网里来。当你一觉醒来发现网里并没有那条叫“纹理坐标块”的大鱼你就应该明白根本没有这条鱼的存在。首先是顶点列表(x)在这里吝啬的ds文件终于必须屈服了没有任何可用于表示顶点块结束的量因此这里必须有一个表示顶点数目的量他紧接在块头后面。但很不幸他只有区区两个字节……这意味着一个物体最多只能有个顶点。啧啧……W个顶点……要知道现在的机器如果你不把它绑在桌子上他就会把自己飞到太阳系外。前几年我还在为一块Voodoo卡沾沾自喜过了两年我就发现我落伍了马上换了Voodoo可是没过几天我就看到了GF(不是GirlFriend更不是FF的召唤兽)现在用GF还觉得低人一等呢……区区W多个顶点怎么能把那些动不动就号称几千万几亿多边形秒的显卡喂饱呢?顶多塞个牙缝。唯一的方法就是多生成几个Object然后自己把他们拼接起来。当然我们能够理解ds已经是非常古老的软件了如今连Max都已经到了。那时候的机器估计经不起几个这么几万个多边形组成的物体折腾几下。闲话不提顶点块后面是两个字节的表示该物体顶点数目的量。紧接着毫无疑问就是顶点列了他以float型三元组(XYZ)的方式给出我想你只要不是对文件操作一无所知就应该明白怎么把他取出来。以下是一个例子:数目Bytesf第一个顶点的XBytesf第一个顶点的YBytesf第一个顶点的ZBytesf第二个顶点的XBytesf第二个顶点的YBytesf第二个顶点的ZBytesf第个顶点的XBytesf第个顶点的YBytesf第个顶点的ZBytes一点需要注意的是坐标系的问题。DS采用的是右手系坐标其Z轴是向上的(即OpenGL和DX引擎里面Y的方向)而Y轴则是垂直XZ平面向内的。对于同样是右手系的OpenGL引擎只需要将整个场景绕X轴正方向顺时针旋转可以了。而左手系的DX引擎变换之后还需要将Z的值取一个相反数。这里源代码输出的顶点是右手系的。纹理坐标块(x)这里先介绍纹理块面信息块比较复杂放在最后讲。这个块就像我上面说的不一定存在。纹理块的组织方式跟顶点列表很相似同样以表示数目的WORD打头这个值应该跟顶点块对应的值相等。如果他们不相等先给你的电脑几个嘴巴再不相等给你装的ds或者dsmax几个嘴巴如果还不相等我就没辙了。因为纹理坐标是元组(为什么?请您给自己几个嘴巴)所以坐标列是以float二元组的方式给出的。数目Bytesf第一个顶点的UBytesf第一个顶点的VBytesf第二个顶点的UBytesf第二个顶点的VBytesf第个顶点的UBytesf第个顶点的VBytes物体信息块(x)物体信息块是用来描述物体姿态的。Unfortunately我没有这部分的代码(我手里头的源代码这部分没有读取)无从判断正误。所以只好照抄原文的:这个块的内容包括四个float*组(就是个三维向量)头三个float*的块用来表示物体自身坐标系(相对于全局)的XYZ坐标轴。最后一个float*的块是物体相对的中心坐标(这里同样要注意坐标系的问题)。在源代码里我读取了中心坐标但未作处理根据我使用的经验物体的中心坐标对你可能一点用处都没有因为它大多数时候都不是你想要的那个。举个最简单的例子来说一个正方体你一定认为这个坐标就是他的几何中心了很不幸不是的他可能跟你开始画这个正方体时的位置有关系。所以想要确定中心的话还是要自己来一般来说在DS导出的文件和你自己使用的文件之间还需要有一层处理。面信息块(x)我相信你不想看到自己读出来的模型只有一堆顶点虽然在你的渲染窗口里显示这么一堆东西的确很酷很铉也很后现代但不是每个观众(比如我)都有欣赏后现代艺术的大脑所以难保你不会被投诉的EMail淹死。OK放下你艺术家的大脑仔细地读下面这几段吧。面信息块正如名字所表明的那样是用来记录网格对象中所有面的信息的。ds创建的模型全部以角形的方式存在这就意味着记录每个面都只要纪录他的个顶点就可以了另外一个模型可以使用多个材质这就还需要一个专门纪录每个面使用的究竟是哪个材质的块面材质块这很重要因为纹理坐标的变换需要知道该顶点使用的材质面材质块(x)是面信息块的一个一级子块。(这里我手头上的一份源代码跟我的理解不一样他将面材质块作为和顶点块、面信息块同一级的块来处理我也不清楚到底是我错了还是他错了不过我的代码可以很顺利地读出模型并正确地显示出来)对于面信息块来说最重要的显然是纪录每个面使用的是哪几个顶点了。在记录所有面的之前照例是一个记录面数目的WORD(因此面最多也只能有个)。然后是每个面的信息跟你想象的一样这里记录顶点是采用纪录定点索引的方式记录该顶点在顶点列表中所处的位置顶点的数目不可能超过个所以这里每个顶点索引用一个WORD来存放也就足够了三个表示顶点的索引值之后还有一个WORD用来记录面的一些其他信息。因此一个面需要个WORD来记录。数目Bytes第一个顶点的索引Bytes第二个顶点的索引Bytes第三个顶点的索引Bytes面的信息Bytes第一个顶点的索引Bytes第二个顶点的索引Bytes第三个顶点的索引Bytes面的信息Bytes按照原文的说法面的信息中包含了该面是否被选中如何被选中和该面顶点的排列方式是顺时针还是逆时针的信息。这跟我使用的结果有一些出入下面是原文中面信息值中各个位数代表的信息:bitAC边方向bitBC方向bitAB方向bitMapping(ifthereismappingforthisfacebit(notused?)??bitx(chaotic)???bit(notused)????!!!!!bitfaceselectedinselectionbitfaceselectedinselectionbitfaceselectedinselection最重要的是前三位信息标示了整个面的正方向(按法向量的不同面的正方向可以有两个方向)位说明了AC边的方向如果该位为则由A指向C反之由C指向A。位是BC的方向位是AB的方向。举例来说如果该值为=则表明排列的方法是A>B>C>A是逆时针方式存放的。以上这些斜体字都是按照原文翻译的但实际使用过程中与上面的描述有出入:如果忽略排列信息的话模型倒是正确的如果按上述文档处理了结果反而有错(打开cullface发现一些面被cull掉了)。所以我的源代码里面这边便没有处理直接拷贝到面数组里面了。接下来的数据可能包含两个子块:SMOOTH块(x)和面材质块(x)SMOOTH块保存的是一组WORD的数据表明那些面是属于Smooth组的。我不清楚Smooth组是干什么用的也许你知道可以告诉我。面材质块(x)描述了哪些材质被使用和使用这种材质的面。这些对于计算正确的纹理坐标很重要。记得前面材质块都有一个唯一的名称吗?忘了的话请再看一遍材质块部分。在这里材质的名称用得上了。每个面材质块均以一个TerminatedString开始通过这个唯一的名称你可以确定当前的材质是哪个。当然仅仅知道使用的是什么材质是远远不够的所以接下来存放的是有多少个面使用了这个材质和这些面的索引这些数据都使用WORD存放。使用该材质面的数目不言自明索引则是这些面在上头那个面列表中的位置(因此如果你要确定使用该材质的某个面上某个顶点的坐标你至少要查两次表先找到面然才能找到对应的顶点)。纹理坐标变换:有了上述的信息我们终于可以把局部的纹理坐标映射到全局的纹理坐标、再不用忍受那个花花绿绿的模型了。在这之前需要注意一点:有少数顶点可能被复数的材质引用如果你不做任何处理的话显然就会发现这个顶点在两个材质中都被处理了一次。毫无疑问只有最后那个材质对应的面上的纹理才是正确的前面那些材质上面乱七八糟的纹理则会显示出浪漫主义者或者意识流主义者的高傲以一种无比嘲弄的眼光冷眼旁观一边焦头烂额的你。让他们回到现实很容易*这个顶点保证这样的顶点在每个引用他的材质中都有一份拷贝。然后看看恢复理性的世界是多么美好吧。很明显纹理坐标变换必须基于这个物体上使用的每个材质。对于每个材质我们能够取得他UV方向的Offset和Tiling以及W方向上的Rotate剩下的问题是如何将原坐标的(XY)根据这几个值映射到全局纹理上。DS处理纹理的做法是先绕纹理中心旋转W角度然后根据offset和tiling圈定矩形区域。完整的公式如下:x=(cosW*(xouf)sinW*(youf))*tufy=(sinW*(xouf)cosW*(youf))*tvf(此处我在之前的文档里犯了一个错误旋转不是绕着圈定的中心来的而是绕着纹理的中心原文写的是绕这圈定的中心旋转的如果按照原文的说法提供的计算方法也错了事实上这个计算方法是正确的是我调试得出的结论但如何解释却错了这也说明了我还没有深刻理解“知之为知之不知为不知是知也”这句话对调试得出的结论没有好好理解)这里ou和ov分别表示UV方向的Offsettu和tv则表示UV方向上的Tiling。加减是因为Offset采用的比较奇特的表示方法它并不是将中心点而是左上角当作原点。经过这两步处理纹理坐标块定义的局部纹理坐标就被映射到全局的纹理上了。我们关于模型块的介绍也到此为止。在介绍物体块的时候我说过物体不仅包括网格物体还有其他如摄像机光源等等。这部分我实在没有精力去深入了解而且我并不需要他们。如果你有兴趣的话可以参照那份英文文档自己写读取的代码。在编辑块的后面是关键帧块。关键帧虽然对我们读取静态模型一点用处也没有但如果你做的东西包含动画的话他就显得很重要了。这部分我还在看如果有时间的话我会把它写出来不过目前我最应该做的其实是好好学学如何使用ds才对。

用户评价(0)

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

评分:

/10

VIP

在线
客服

免费
邮箱

爱问共享资料服务号

扫描关注领取更多福利