SkinMeshes的思路和代码
资源共享——用DX8实现Skin Meshes(翻译版)
主题:懒人的实现类似CS的人物动画效果的方法
版权所有:kane_peng 原作 提交时间:21:07:59 11月29日
首先,读读这个:http://cgd.pages.com.cn/cvbb/showthread.php?s=&threadid=1116是不是一头雾水?hehe看不懂也没关系,把它的源代码下下来,把所有文件(不过main.cpp之类的显然不用)硬拼进自己的程序,反复调试至无ERROR!(最最多1hour)然后,去http://www.milkshape3d.com下一个MS3D 1.6.4,破了它http://www.andr.net/andrnet-crc/m/pgms164.zip(找了好久才找到的破解...),用它把CS的MDL破了,再Import,然后用JT的.x输出插件导出成.x,然后用你的程序调入,OK!各位懒人先凑合着用别人的代码,有空再研究具体原理吧(其实very simple,只是做起来有点烦),hehe我也懒得自己写慢慢调试了,反正没什么技巧,知道原理就算了,hehe
源文件地址
http://www.gamedev.net/reference/ar...article1835.asp
VC的。还有源代码……http://www.gamedev.net/reference/pr...mesh/source.zip
以下为翻译:
Implementing Skin Meshes with DirectX 8
by Sarmad Kh Abdulla
Skin meshes或者骨骼meshes在3D世界里是大多数重要课题之一。骨骼动画总是受注视为重要的角色让他们有组成织的活动。骨骼动画是建立在对象的外形是由多个骨架并且能由改变骨架的位置和方向作简单的活动思想基础上的。你能找到许多讨论这个课题的理论资源,但这个文章讨论用D3D8与D3DX来实现骨骼meshes。但在我开始讨论实现之前,我在后面的skin meshes在数学模块上来一个快速的回顾。
Skin Meshes的概述
Skin meshes是分层场景(hierarchical scene)的一种类型。分层场景(hierarchical scene)被用于对象的相互连接。例如,手指属于手掌,手掌属于前臂等等。每个对象坐标的给予是相对于属于对象的局部空间,因此,旋转前臂将也引起手掌和手指的移动和旋转。下列插图显示如何人体使用一个分层场景如何构造。
用数学公式能非常简单的表示这个场景;矩阵代数是关键。给予场景,如果我们考虑前臂矩阵到前臂的变换矩阵,手掌矩阵到手掌的变换矩阵与手指矩阵到手指的变换矩阵,然后我们能简单的计算手指相对于世界的变换矩阵,由下列公式:
FingerWorldMat = FingerMat * PalmMat * ForearmMat * ... * BodyMat
在上面的公式里,我们考虑了分层场景的基础是身体,因此,它的矩阵是相对于世界空间。有我们的mesh拆分进入局部在那里每个部分是被一个特定骨架的世界矩阵变换了的,将使我们能够容易的活动mesh。
然而,有的人物是由分开的固定子对象组成现在不接受这种做法,因为在关节处它会引起裂缝。今后上升到skin meshes。Skin meshes克服了这个问题由人物根据位置和骨架的方位改变较小的部分的已有的外形。Skin meshes同样有骨架体系,但一个skin mesh的单个部分能被不止一个骨架变换。下列一个线性插值公式,顶点能有它们的位置被两个(或更多)骨架代替一个骨架影响。下列方法,我们能克服裂缝的问题。这个技术我们叫作顶点混合(Vertex Blending)。顶点混合要求每个顶点有一个混合权以便顶点描影能确定骨架如何影响顶点。例如,如果我们有一个顶点被两个骨架影响,下列公式将给予顶点的世界坐标作为被两个骨架影响:
Vw = Vm * M1 * w + Vm * M2 * (1-w)
在上面Vw是顶点在世界坐标里的位置。Vm是顶点相对于模型的局部空间位置。M1和M2是两个骨架的变换矩阵。w是混合权。注意混合权要求骨架数字小于1。
你能参考DX8文档看关于顶点混合更详细的信息。
Skin Meshes and DirectX 8
重要的是在开始实现我们的skin mesh代码以前应该知道DX如何处理skin mesh。当然,有许多文件格式能存储skin mesh但在此时最容易的一个是X文件。X文件能存储标准的静态meshes以外还能存储skin meshes。在我们讨论skin meshes以前,让我们对X文件有一个一般概念。我将讨论X文件的通用设计并且你能参考DX文档获得更详细的信息。X文件存储数据作为一套模版。象C语音的结构一样,模版有定义决定数据将如何存储在模版的实例里。模版的每个类型,你能有一个或更多的实例。有模版的许多类型,每一个想象成存储一个专用的数据类型。模版能有子类模版,使我们能够构造分层场景。虽然它不是强制性的,模版的实例能有名字。我们不需要用模版的所有类型直接处理,DX将为我们处理大多数工作,但在我们能开始工作以前我们需要有一些关于下列模版的概念。
l Frame
模版用于存储一个框架。框架是建造分层场景的成份。框架有它们的自己的变换矩阵并且它们能包含子类对象。框架也能包含子类框架。在skin mesh里,一个骨架参照一个框架。
l FrameTransformationMatrix
与名字暗示一样,这个是为框架的变换矩阵。它在框架模版内被用于实例的说明。
l Mesh
模版存储单个静态mesh和它的材质一起。在skin mesh里,整个人物将用一个mesh并且s皮肤信息规定mesh的每个部分如何被骨架影响。Mesh将在内部的分裂进入子对象;每个子对象将被一个骨架的特定设置影响。
l XSkinMeshHeader
这个模版存储关于皮肤的自然信息,信息用mesh输出。这个模版包含在Mesh模版内。
l SkinWeights
真实的皮肤信息存储在这里。模版定义一个特定的骨架如何能影响mesh。也就是,这个模版为影响mesh的每个骨架具体说明一次。如果有十二个骨架影响mesh,mesh模版将有十二个SkinWeights模版在内部说明。
在skin mesh与静态mesh之间的差别只是XSkinMeshHeader和SkinWeights模版的存在。从任何skin mesh的mesh模版移去这两个模版就把它变成了一个静态mesh。
其它的模版我们将也处理活动数据。我将在文章后面提涉它们。
有好消息也有坏消息。好消息是DX将处理所有的工作读取mesh需要它的材质和皮肤信息。坏消息是我们必须做余下的。我们将需要读取框架和构造分层场景。我们将也需要连接skin mesh到骨架。通常,X文件将包含一个框架分层结构并且包含一个(或更多)与皮肤信息一起的mesh模版。我们将独立的读取每个框架分层结构与mesh模版,然后手动连接mesh到它的骨架(框架)。用一个skin mesh建立一个X文件是制作模型者的任务。在用任何商业软件制作人物后,制作模型者能容易使用为这个目的设计特殊的插件导出它的模型到一个X文件。在Microsoft的网址上,你能找到为3DMAX和MAYA导出为X文件的插件。你甚至能找到支持建立X文件的应用程序。
现在我们知道数据在X文件里是如何组织的。为了读取数据,我们需要使用X文件库。IDirectXFile是库的主要接口。IDirectXFile有一个方法为创建IDirectXFileEnumObject。IDirectXFileEnumObject方法是被用于从一个指定的X文件里检索数据。IDirectXFileEnumObject::GetNextDataObject将循环通过在X文件里所有的顶层模版并且返回一个IDirectXFileData接口。稍后在X文件内部被用于检索单个模版的数据。相似于IDirectXFileEnumObject::GetNextDataObject,方法IDirectXFileData::GetNextObject循环通过所有的子类模版并且返回一个IDirectXFileData接口。方法IDirectXFileData::GetData被用于从模版里检索数据,但在我们能检索数据以前,我们需要知道模版的类型。方法IDirectXFileData::GetID返回模版的GUID。例如,如果模版是一个框架模版,GetID将返回TID_D3DRMFrame,它被预先定义在DX的头文件里。假如你需要模版的实例的名字(象是用框架的情况一样),方法GetName把名字将给你。
在我们进行通过X文件模版期间,我们将寻找一个模版与一个GUID等于TID_D3DRMMesh,它意味着它包含一个mesh。现在是DX给我们一些帮助的时候了。函数D3DXLoadSkinMeshFromXof将读取skin mesh与所有的余下的数据。仅给它一个指向IDirectXFileData接口的指针并且它将做余下的工作。
函数D3DXLoadSkinMeshFromXof将给予我们一个指向ID3DXSkinMesh对象的指针。这个对象包含skin mesh。在内部,这个对象包含mesh数据作为组(group)。每个组(group)被一个不同骨架的设置变换。函数将也返回一个材质数据由skin mesh使用。与我先前提到的一样,我们的工作是连接skin mesh到骨架。D3DXLoadSkinMeshFromXof给予一个缓冲区包含影响mesh的所有骨架的名字。它也给予其它缓冲区包含它们的变换。我们将使用名字为指定的骨架查遍我们的框架分层结构。骨架变换有点让人混乱。假定变换不在这里被包含在框架里。事实上,这个变换是骨架偏移(bone offset)。那么骨架偏移又是什么呢?重要的知道skin mesh的所有顶点是被存储在相对于一个原点,它是mesh的原点并不是骨架的局部原点。这意味着在mesh上为了有骨架的影响,我们应该使mesh变形由位于骨架的当前变换与骨架的原点变换之间的差别。或者换句话说,我们应该变换顶点到骨架的局部空间,然后变换它们回到mesh的空间使用新的骨架的变换。为了我们有一个更清楚的概念,让我们示范举例。让我们说说我们有一个骨架被放置在(0,50,0)与一个顶点被放置在(0,51,0)并且让我们假定这个顶点是只由一个骨架影响。如果我们移动骨架从它的原始位置到这个新的位置(0,51,0),顶点应该被移动到位置(0,52,0),但如果我们简单的由骨架的变换乘顶点,顶点将有新的位置等于(0,102,0)它是错误的坐标。因此,我们使用骨架的偏移矩阵到变换顶点从它的原始位置到一个相对于骨架的位置。新的位置将是(0,1,0)它将是被骨架的当前矩阵到新的位置变换,它是(0,52,0)。简单步骤如下:当你使用一个骨架,由偏移矩阵乘以它的当前变换矩阵并且使用结果作为世界矩阵。