从.smd模型导入3D骨架动画

Qua*_*hey 3 java opengl lwjgl

我正在写一个.smd导入器,我被困在骨骼动画部分.问题是我不确切知道它是如何工作的.我用这写的出口国,但它并没有说明如何使用存储在文件中的信息.

我想所有具有相同骨骼id的顶点都应该被分组,平移和旋转,因为你无法旋转每个顶点.但我不知道我是否正确,即使我是,我仍然不知道如何通过脚本来做到这一点......

所以问题是:我如何使用存储在文件中的骨架动画信息?

Tor*_*ous 12

我并不特别熟悉SMD格式,但是这里......

注意:此答案假定您知道如何为对象/节点构造复合变换.这是结合其平移,旋转和缩放的矩阵(虽然看起来在SMD中没有使用比例).此外,使用矩阵乘法,矩阵求逆和矩阵*向量乘法.

骨头和动画

模型的骨节点形成树; 除了根骨(nodes部分)之外,每个骨骼都有一个父骨骼.每个节点都有自己的局部变换(位置和旋转).

局部节点变换:节点的局部变换是一个由其位置和旋转构成的4x4矩阵,它将点从其局部空间转换到其父节点的空间:如果一个向量表示节点空间中的位置,则将其与矩阵相乘得到父母的空间中的那个向量.有关如何执行此操作的详细信息,请参阅此链接.谷歌有点多了.

在SMD中,骨骼变换在动画中的关键帧中定义(该skeleton部分)."参考"SMD文件具有单帧动画; 模型参考位置中每个骨骼节点的位置和旋转.

动画SMD文件具有包含多个帧的动画序列,每个帧指定(某些)骨骼的不同变换.在播放动画时,您可以根据帧的时间和当前场景/游戏时间在帧之间进行插值,并为每个骨骼进行变换(位置+旋转).

获得静止骨骼变换

在预处理中(当加载网格时),您需要计算所谓的"静止"骨骼变换.这些是每个骨骼在参考位置时的模型到骨骼空间变换.原因如下:

所有顶点位置都在模型空间中定义,但最终,顶点变换必须从骨骼空间开始,因为您希望顶点与单个骨骼一起移动.因此,必须首先将顶点位置转换为骨骼空间.这是静止骨转换的来源.

因此,我们正在寻找的静止变换将顶点从模型空间转换为骨骼空间.将所有骨骼放在参考位置.从根节点开始遍历树,并连接变换矩阵.因此,例如,对于上臂节点,您将获得变换:

transform = root * spine * shoulderR * upperArmR
Run Code Online (Sandbox Code Playgroud)

然而,这是从上臂空间到模型空间的转换.因此,只需反转矩阵即可获得静止骨骼变换.为每个骨骼执行此操作并存储这些矩阵.

请注意,静止变换不会随时间变化; 它们是根据模型的参考位置修复的.

顶点/骨骼关联

每个顶点与一个或多个骨节点相关联.对于每个这样的关联,顶点具有相应的权重.通常,所有权重总和为1.在SMD中,这些关联在triangles段中定义.根据您链接的页面,格式为:

triangles
my_material
bone_id    x y z    nx ny nz    u v    bone_links
Run Code Online (Sandbox Code Playgroud)

这定义了一个顶点,(x, y, z)并将它与骨骼相关联bone_id(1假设权重为I).该bone_links部分可以(排序)覆盖它并指定多个关联,如下所示:

bone_links = num_links bone_id[0] weight[0] bone_id[1] weight[1] ... etc.
Run Code Online (Sandbox Code Playgroud)

如果权重不等于1,则剩余权重将与原始权重相关联bone_id.

因此,与骨骼0,1和2相关联的示例顶点将是:

0    x y z   nx ny nz   u v    3   0 0.15  1 0.35  2 0.5
Run Code Online (Sandbox Code Playgroud)

顶点变换

这是我们最终根据当前骨骼变换确定顶点位置的位置.如前所述; 根据当前时间和动画,您可以确定(插入)当前骨骼变换.对于每个骨骼,计算骨骼到世界的变换.示例(我们之前看到过):

boneToWorld = root * spine * shoulderR * upperArmR
Run Code Online (Sandbox Code Playgroud)

现在,对于与单个骨骼相关联的顶点,下面给出了它的动画/蒙皮位置:

vertexPosAnimated = boneToWorld * boneAtRest * vertexPosModel
Run Code Online (Sandbox Code Playgroud)

这首先将顶点位置从模型space(vertexPosModel)转换为与其关联的骨骼空间(此变换不会随时间变化).然后,使用骨骼的当前位置,顶点再次从骨骼转换为模型空间.这允许它随着变换的变化随着骨骼移动.

注意的是,当骨骼目前在其静止位置,boneAtRest是倒数boneToWorld,所以boneToWorld * boneAtRest是单位矩阵,所以顶点位置保持不变,这是正确的!

最后,由于顶点可以与多个骨骼相关联,而不是上面的,我们计算每个相关骨骼的上述加权和.例如,对于与3个骨骼关联的顶点:

vertexPosAnimated =
    boneToWorld[0] * boneAtRest[0] * vertexPosModel * weight[0] +
    boneToWorld[1] * boneAtRest[1] * vertexPosModel * weight[1] +
    boneToWorld[2] * boneAtRest[2] * vertexPosModel * weight[2];
Run Code Online (Sandbox Code Playgroud)

最后的想法

这些是一些广泛的笔触,我甚至没有讨论着色器实现(如果那是你要做的),但我认为我已经涵盖了所有原则,而且它已经是一个很长的答案.

我过去发现有助于3D引擎开发的一件事是M3G文档(旧的Java Mobile 3D API).条目SkinnedMesh基本上描述了我在这里发布的内容.

此外,尝试理解使用静止骨骼变换将顶点从模型转换为骨骼空间的概念,并使用其当前变换再次返回.这是整个事情的关键.

祝好运!