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

时间:2012-06-02 17:29:12

标签: java opengl lwjgl

我正在写一个.smd导入器,而且我被困在骨骼动画部分。问题是我不确切知道它是如何工作的。我正在使用此this来编写导出器,但它没有显示如何使用存储在文件中的信息。

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

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

1 个答案:

答案 0 :(得分:12)

我并不熟悉SMD格式,但这里有......

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

骨骼和动画

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

局部节点变换:节点的局部变换是从其位置和旋转构造的4×4矩阵,它将点从其局部空间转换到其父节点的空间:如果表示节点空间中的位置的向量,则将其与矩阵相乘导致父对象空间中的该向量。有关如何执行此操作的详细信息,请参阅this linkGoogle a bit了解更多信息。

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

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

获取静止骨骼变换

在预处理中(当加载网格时),你需要计算所谓的" at-rest"骨转换。这些是每个骨骼的模型到骨骼空间变换,当它位于参考位置时。这就是原因:

所有顶点位置都在模型空间中定义,但最终必须从骨骼空间开始进行顶点变换,因为您希望顶点随单个骨骼移动。因此,必须首先将顶点位置转换为骨骼空间。这是静止骨骼变换的用武之地。

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

transform = root * spine * shoulderR * upperArmR

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

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

顶点/骨骼关联

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

triangles
my_material
bone_id    x y z    nx ny nz    u v    bone_links

这定义了(x, y, z)处的顶点,并将其与骨骼bone_id(我假设的权重为1)进行了初步关联。 bone_links部分可以(排序)覆盖它并指定多个关联,如下所示:

bone_links = num_links bone_id[0] weight[0] bone_id[1] weight[1] ... etc.

如果权重不等于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

顶点转换

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

boneToWorld = root * spine * shoulderR * upperArmR

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

vertexPosAnimated = boneToWorld * boneAtRest * vertexPosModel

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

观察到,当骨骼当前处于静止位置时,boneAtRestboneToWorld相反,因此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];

最后的想法

这些是一些广泛的描述,我甚至没有讨论着色器的实现(如果这是你将要做的事情),但我认为我已经涵盖了所有的原则,并且它已经是一个很长的答案。

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

此外,尝试理解使用 at-rest 骨骼变换将顶点从模型转换为骨骼空间的概念,然后再使用其当前变换返回。这是整个事情的关键。

祝你好运!