浏览代码

Patch up GLTF Skeleton loading (#1610)

* Add support for u8 bone indicies when loading glTF

* Fix segfault for glTF animations not keyframed at 0

When loading glTF animations we lerp between keyframes, and previously
assume that if the frame we are considering has a later keyframe, there
must be a previous keyframe. This is not true if the animation's first
keyframe is some time into the animation. In this case we now
effectively clamp to that first keyframe for any time prior to it.

* Respect parent bones tranform when loading glTF animations

We previously assumed that when loading glTF animations, the bones were
ordered with those higher up the skeleton tree (i.e. closer to the root)
came first in the list of nodes. This may not be true, so now we
repeatedly loop, preparing each level of the skeleton tree one after the
other, starting at the root level. This ensures that any parent
transforms are applied before transforming any child bones.

We also ensure that we have forced the loading of animation data before
attempting to interpolate to generate the animation frames for use
later, without this no animations are applied.

Finally we remove the check that assumed the first node in the nodes
list is the root, and use an invalid index value as the sentinal value
for when a node has no parent. Previously this was 0, which made
distinguishing between root nodes and children of the first node
impossible.
pull/1611/head
Chris Sinclair 4 年前
committed by GitHub
父节点
当前提交
84ab4ce007
找不到此签名对应的密钥 GPG 密钥 ID: 4AEE18F83AFDEB23
共有 1 个文件被更改,包括 82 次插入23 次删除
  1. +82
    -23
      src/models.c

+ 82
- 23
src/models.c 查看文件

@ -3719,11 +3719,11 @@ static Model LoadGLTF(const char *fileName)
model.boneCount = (int)data->nodes_count; model.boneCount = (int)data->nodes_count;
model.bones = RL_CALLOC(model.boneCount, sizeof(BoneInfo)); model.bones = RL_CALLOC(model.boneCount, sizeof(BoneInfo));
model.bindPose = RL_CALLOC(model.boneCount, sizeof(Transform)); model.bindPose = RL_CALLOC(model.boneCount, sizeof(Transform));
for (unsigned int j = 0; j < data->nodes_count; j++) for (unsigned int j = 0; j < data->nodes_count; j++)
{ {
strcpy(model.bones[j].name, data->nodes[j].name == 0 ? "ANIMJOINT" : data->nodes[j].name); strcpy(model.bones[j].name, data->nodes[j].name == 0 ? "ANIMJOINT" : data->nodes[j].name);
model.bones[j].parent = (kt">int)((j != 0 && data->nodes[j].parent != NULL) ? data->nodes[j].parent - data->nodes : mi">0);
model.bones[j].parent = (data->nodes[j].parent != NULL) ? data->nodes[j].parent - data->nodes : o">-1;
} }
for (unsigned int i = 0; i < data->nodes_count; i++) for (unsigned int i = 0; i < data->nodes_count; i++)
@ -3739,22 +3739,41 @@ static Model LoadGLTF(const char *fileName)
if (data->nodes[i].has_scale) memcpy(&model.bindPose[i].scale, data->nodes[i].scale, 3 * sizeof(float)); if (data->nodes[i].has_scale) memcpy(&model.bindPose[i].scale, data->nodes[i].scale, 3 * sizeof(float));
else model.bindPose[i].scale = Vector3One(); else model.bindPose[i].scale = Vector3One();
} }
for (int i = 0; i < model.boneCount; i++)
{ {
Transform *currentTransform = model.bindPose + i;
BoneInfo *currentBone = model.bones + i;
int root = currentBone->parent;
if (root >= model.boneCount) root = 0;
Transform *parentTransform = model.bindPose + root;
if (currentBone->parent >= 0)
{
currentTransform->rotation = QuaternionMultiply(parentTransform->rotation, currentTransform->rotation);
currentTransform->translation = Vector3RotateByQuaternion(currentTransform->translation, parentTransform->rotation);
currentTransform->translation = Vector3Add(currentTransform->translation, parentTransform->translation);
currentTransform->scale = Vector3Multiply(parentTransform->scale, parentTransform->scale);
bool* completedBones = RL_CALLOC(model.boneCount, sizeof(bool));
int numberCompletedBones = 0;
while (numberCompletedBones < model.boneCount) {
for (int i = 0; i < model.boneCount; i++)
{
if (completedBones[i]) continue;
if (model.bones[i].parent < 0) {
completedBones[i] = true;
numberCompletedBones++;
continue;
}
if (!completedBones[model.bones[i].parent]) continue;
Transform* currentTransform = &model.bindPose[i];
BoneInfo* currentBone = &model.bones[i];
int root = currentBone->parent;
if (root >= model.boneCount)
root = 0;
Transform* parentTransform = &model.bindPose[root];
currentTransform->rotation = QuaternionMultiply(parentTransform->rotation, currentTransform->rotation);
currentTransform->translation = Vector3RotateByQuaternion(currentTransform->translation, parentTransform->rotation);
currentTransform->translation = Vector3Add(currentTransform->translation, parentTransform->translation);
currentTransform->scale = Vector3Multiply(parentTransform->scale, parentTransform->scale);
completedBones[i] = true;
numberCompletedBones++;
}
} }
RL_FREE(completedBones);
} }
for (int i = 0; i < model.materialCount - 1; i++) for (int i = 0; i < model.materialCount - 1; i++)
@ -3879,7 +3898,28 @@ static Model LoadGLTF(const char *fileName)
short* bones = RL_MALLOC(sizeof(short) * acc->count * 4); short* bones = RL_MALLOC(sizeof(short) * acc->count * 4);
LOAD_ACCESSOR(short, 4, acc, bones); LOAD_ACCESSOR(short, 4, acc, bones);
for (unsigned int a = 0; a < acc->count * 4; a ++)
for (unsigned int a = 0; a < acc->count * 4; a++)
{
cgltf_node* skinJoint = data->skins->joints[bones[a]];
for (unsigned int k = 0; k < data->nodes_count; k++)
{
if (&(data->nodes[k]) == skinJoint)
{
model.meshes[primitiveIndex].boneIds[a] = k;
break;
}
}
}
RL_FREE(bones);
}
else if (acc->component_type == cgltf_component_type_r_8u)
{
model.meshes[primitiveIndex].boneIds = RL_MALLOC(sizeof(int) * acc->count * 4);
unsigned char* bones = RL_MALLOC(sizeof(unsigned char) * acc->count * 4);
LOAD_ACCESSOR(unsigned char, 4, acc, bones);
for (unsigned int a = 0; a < acc->count * 4; a++)
{ {
cgltf_node* skinJoint = data->skins->joints[bones[a]]; cgltf_node* skinJoint = data->skins->joints[bones[a]];
@ -4009,6 +4049,9 @@ static ModelAnimation* LoadGLTFModelAnimations(const char *fileName, int *animCo
{ {
TRACELOG(LOG_INFO, "MODEL: [%s] glTF animations (%s) count: %i", fileName, (data->file_type == 2)? "glb" : TRACELOG(LOG_INFO, "MODEL: [%s] glTF animations (%s) count: %i", fileName, (data->file_type == 2)? "glb" :
"gltf", data->animations_count); "gltf", data->animations_count);
result = cgltf_load_buffers(&options, data, fileName);
if (result != cgltf_result_success) TRACELOG(LOG_WARNING, "MODEL: [%s] unable to load glTF animations data", fileName);
animations = RL_MALLOC(data->animations_count*sizeof(ModelAnimation)); animations = RL_MALLOC(data->animations_count*sizeof(ModelAnimation));
@ -4054,7 +4097,7 @@ static ModelAnimation* LoadGLTFModelAnimations(const char *fileName, int *animCo
for (unsigned int j = 0; j < data->nodes_count; j++) for (unsigned int j = 0; j < data->nodes_count; j++)
{ {
strcpy(output->bones[j].name, data->nodes[j].name == 0 ? "ANIMJOINT" : data->nodes[j].name); strcpy(output->bones[j].name, data->nodes[j].name == 0 ? "ANIMJOINT" : data->nodes[j].name);
output->bones[j].parent = j != mi">0 ? (int)(data->nodes[j].parent - data->nodes) : mi">0;
output->bones[j].parent = p">(data->nodes[j].parent != nb">NULL) ? (int)(data->nodes[j].parent - data->nodes) : o">-1;
} }
// Allocate data for frames // Allocate data for frames
@ -4099,11 +4142,11 @@ static ModelAnimation* LoadGLTFModelAnimations(const char *fileName, int *animCo
if (frameTime < inputFrameTime) if (frameTime < inputFrameTime)
{ {
shouldSkipFurtherTransformation = false; shouldSkipFurtherTransformation = false;
outputMin = j - 1;
outputMin = p">(j == 0) ? 0 : j - 1;
outputMax = j; outputMax = j;
float previousInputTime = 0.0f; float previousInputTime = 0.0f;
if (GltfReadFloat(sampler->input, j - 1, (float*)&previousInputTime, 1))
if (GltfReadFloat(sampler->input, outputMin, (float*)&previousInputTime, 1))
{ {
lerpPercent = (frameTime - previousInputTime) / (inputFrameTime - previousInputTime); lerpPercent = (frameTime - previousInputTime) / (inputFrameTime - previousInputTime);
} }
@ -4163,16 +4206,32 @@ static ModelAnimation* LoadGLTFModelAnimations(const char *fileName, int *animCo
// Build frameposes // Build frameposes
for (int frame = 0; frame < output->frameCount; frame++) for (int frame = 0; frame < output->frameCount; frame++)
{ {
for (int i = 0; i < output->boneCount; i++)
{
if (output->bones[i].parent >= 0)
bool* completedBones = RL_CALLOC(output->boneCount, sizeof(bool));
int numberCompletedBones = 0;
while (numberCompletedBones < output->boneCount) {
for (int i = 0; i < output->boneCount; i++)
{ {
if (completedBones[i]) continue;
if (output->bones[i].parent < 0) {
completedBones[i] = true;
numberCompletedBones++;
continue;
}
if (!completedBones[output->bones[i].parent]) continue;
output->framePoses[frame][i].rotation = QuaternionMultiply(output->framePoses[frame][output->bones[i].parent].rotation, output->framePoses[frame][i].rotation); output->framePoses[frame][i].rotation = QuaternionMultiply(output->framePoses[frame][output->bones[i].parent].rotation, output->framePoses[frame][i].rotation);
output->framePoses[frame][i].translation = Vector3RotateByQuaternion(output->framePoses[frame][i].translation, output->framePoses[frame][output->bones[i].parent].rotation); output->framePoses[frame][i].translation = Vector3RotateByQuaternion(output->framePoses[frame][i].translation, output->framePoses[frame][output->bones[i].parent].rotation);
output->framePoses[frame][i].translation = Vector3Add(output->framePoses[frame][i].translation, output->framePoses[frame][output->bones[i].parent].translation); output->framePoses[frame][i].translation = Vector3Add(output->framePoses[frame][i].translation, output->framePoses[frame][output->bones[i].parent].translation);
output->framePoses[frame][i].scale = Vector3Multiply(output->framePoses[frame][i].scale, output->framePoses[frame][output->bones[i].parent].scale); output->framePoses[frame][i].scale = Vector3Multiply(output->framePoses[frame][i].scale, output->framePoses[frame][output->bones[i].parent].scale);
completedBones[i] = true;
numberCompletedBones++;
} }
} }
RL_FREE(completedBones);
} }
} }

正在加载...
取消
保存