Browse Source

[models] Animation System Fix Part 1 (#2009)

* Fixed gltf missing transforms on load

mend

* extracted Matrix calculation in to static method and added skinning check

* fixed formatting

* Fixed write to access to nullptr when animation has no normals

* Refactored UpdateModelAnimation to only update changed vertices when needed (allows for multi animation playing)

* add check for models that were missed during BindGLTFPrimitiveToBones to not segfault the program

* fixed id mismatch between animation and model

* draft on fixing the mesh to skin mapping

* dont look at this

* removing debug info
Tristan Schulz 3 years ago
parent
commit
b7063ab879
1 changed files with 95 additions and 72 deletions
  1. 95 72
      src/rmodels.c

+ 95 - 72
src/rmodels.c

@@ -134,13 +134,14 @@ static ModelAnimation *LoadIQMModelAnimations(const char *fileName, unsigned int
 static Model LoadGLTF(const char *fileName);    // Load GLTF mesh data
 static Model LoadGLTF(const char *fileName);    // Load GLTF mesh data
 static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, unsigned int *animCount);    // Load GLTF animation data
 static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, unsigned int *animCount);    // Load GLTF animation data
 static void LoadGLTFMaterial(Model *model, const char *fileName, const cgltf_data *data);
 static void LoadGLTFMaterial(Model *model, const char *fileName, const cgltf_data *data);
-static void LoadGLTFMesh(cgltf_data *data, cgltf_mesh *mesh, Model *outModel, Matrix currentTransform, int *primitiveIndex, const char *fileName);
+static void LoadGLTFMesh(cgltf_data *data, cgltf_node *node, Model *outModel, Matrix currentTransform, int *primitiveIndex, const char *fileName);
 static void LoadGLTFNode(cgltf_data *data, cgltf_node *node, Model *outModel, Matrix currentTransform, int *primitiveIndex, const char *fileName);
 static void LoadGLTFNode(cgltf_data *data, cgltf_node *node, Model *outModel, Matrix currentTransform, int *primitiveIndex, const char *fileName);
 static void InitGLTFBones(Model *model, const cgltf_data *data);
 static void InitGLTFBones(Model *model, const cgltf_data *data);
-static void BindGLTFPrimitiveToBones(Model *model, const cgltf_data *data, int primitiveIndex);
+static void BindGLTFPrimitiveToBones(Model *model, cgltf_node *node, const cgltf_data *data, int primitiveIndex);
 static void GetGLTFPrimitiveCount(cgltf_node *node, int *outCount);
 static void GetGLTFPrimitiveCount(cgltf_node *node, int *outCount);
 static bool ReadGLTFValue(cgltf_accessor *acc, unsigned int index, void *variable);
 static bool ReadGLTFValue(cgltf_accessor *acc, unsigned int index, void *variable);
 static void *ReadGLTFValuesAs(cgltf_accessor *acc, cgltf_component_type type, bool adjustOnDownCasting);
 static void *ReadGLTFValuesAs(cgltf_accessor *acc, cgltf_component_type type, bool adjustOnDownCasting);
+static Matrix GetNodeTransformationMatrix(cgltf_node *node, Matrix current);
 #endif
 #endif
 #if defined(SUPPORT_FILEFORMAT_VOX)
 #if defined(SUPPORT_FILEFORMAT_VOX)
 static Model LoadVOX(const char *filename);     // Load VOX mesh data
 static Model LoadVOX(const char *filename);     // Load VOX mesh data
@@ -1782,72 +1783,93 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame)
 
 
         for (int m = 0; m < model.meshCount; m++)
         for (int m = 0; m < model.meshCount; m++)
         {
         {
+            Mesh mesh = model.meshes[m];
+            if (mesh.boneIds == NULL || mesh.boneWeights == NULL)
+            {
+                TRACELOG(LOG_WARNING, "MODEL: UpdateModelAnimation Mesh %i has no connection to bones",m);
+                continue;
+            }
+
+            bool updated = false; // set to true when anim vertex information is updated
             Vector3 animVertex = { 0 };
             Vector3 animVertex = { 0 };
             Vector3 animNormal = { 0 };
             Vector3 animNormal = { 0 };
 
 
             Vector3 inTranslation = { 0 };
             Vector3 inTranslation = { 0 };
             Quaternion inRotation = { 0 };
             Quaternion inRotation = { 0 };
-            //Vector3 inScale = { 0 };      // Not used...
+            // Vector3 inScale = { 0 };
 
 
             Vector3 outTranslation = { 0 };
             Vector3 outTranslation = { 0 };
             Quaternion outRotation = { 0 };
             Quaternion outRotation = { 0 };
             Vector3 outScale = { 0 };
             Vector3 outScale = { 0 };
 
 
-            int vCounter = 0;
-            int boneCounter = 0;
             int boneId = 0;
             int boneId = 0;
+            int boneCounter = 0;
             float boneWeight = 0.0;
             float boneWeight = 0.0;
 
 
-            for (int i = 0; i < model.meshes[m].vertexCount; i++)
+            const int vValues = mesh.vertexCount*3;
+            for (int vCounter = 0; vCounter < vValues; vCounter+=3)
             {
             {
-                model.meshes[m].animVertices[vCounter] = 0;
-                model.meshes[m].animVertices[vCounter + 1] = 0;
-                model.meshes[m].animVertices[vCounter + 2] = 0;
+                mesh.animVertices[vCounter] = 0;
+                mesh.animVertices[vCounter + 1] = 0;
+                mesh.animVertices[vCounter + 2] = 0;
 
 
-                model.meshes[m].animNormals[vCounter] = 0;
-                model.meshes[m].animNormals[vCounter + 1] = 0;
-                model.meshes[m].animNormals[vCounter + 2] = 0;
+                if (mesh.animNormals!=NULL)
+                {
+                    mesh.animNormals[vCounter] = 0;
+                    mesh.animNormals[vCounter + 1] = 0;
+                    mesh.animNormals[vCounter + 2] = 0;
+                }
 
 
-                for (int j = 0; j < 4; j++)
+                // Iterates over 4 bones per vertex
+                for (int j = 0; j < 4; j++, boneCounter++)
                 {
                 {
-                    boneId = model.meshes[m].boneIds[boneCounter];
-                    boneWeight = model.meshes[m].boneWeights[boneCounter];
+                    boneWeight = mesh.boneWeights[boneCounter];
+                    // early stop when no transformation will be applied
+                    if (boneWeight == 0.0f)
+                    {
+                        continue;
+                    }
+                    boneId = mesh.boneIds[boneCounter];
+                    int boneIdParent = model.bones[boneId].parent;
                     inTranslation = model.bindPose[boneId].translation;
                     inTranslation = model.bindPose[boneId].translation;
                     inRotation = model.bindPose[boneId].rotation;
                     inRotation = model.bindPose[boneId].rotation;
-                    //inScale = model.bindPose[boneId].scale;
+                    // inScale = model.bindPose[boneId].scale;
                     outTranslation = anim.framePoses[frame][boneId].translation;
                     outTranslation = anim.framePoses[frame][boneId].translation;
                     outRotation = anim.framePoses[frame][boneId].rotation;
                     outRotation = anim.framePoses[frame][boneId].rotation;
                     outScale = anim.framePoses[frame][boneId].scale;
                     outScale = anim.framePoses[frame][boneId].scale;
 
 
                     // Vertices processing
                     // Vertices processing
                     // NOTE: We use meshes.vertices (default vertex position) to calculate meshes.animVertices (animated vertex position)
                     // NOTE: We use meshes.vertices (default vertex position) to calculate meshes.animVertices (animated vertex position)
-                    animVertex = (Vector3){ model.meshes[m].vertices[vCounter], model.meshes[m].vertices[vCounter + 1], model.meshes[m].vertices[vCounter + 2] };
+                    animVertex = (Vector3){ mesh.vertices[vCounter], mesh.vertices[vCounter + 1], mesh.vertices[vCounter + 2] };
                     animVertex = Vector3Multiply(animVertex, outScale);
                     animVertex = Vector3Multiply(animVertex, outScale);
                     animVertex = Vector3Subtract(animVertex, inTranslation);
                     animVertex = Vector3Subtract(animVertex, inTranslation);
                     animVertex = Vector3RotateByQuaternion(animVertex, QuaternionMultiply(outRotation, QuaternionInvert(inRotation)));
                     animVertex = Vector3RotateByQuaternion(animVertex, QuaternionMultiply(outRotation, QuaternionInvert(inRotation)));
                     animVertex = Vector3Add(animVertex, outTranslation);
                     animVertex = Vector3Add(animVertex, outTranslation);
-                    model.meshes[m].animVertices[vCounter] += animVertex.x*boneWeight;
-                    model.meshes[m].animVertices[vCounter + 1] += animVertex.y*boneWeight;
-                    model.meshes[m].animVertices[vCounter + 2] += animVertex.z*boneWeight;
+//                     animVertex = Vector3Transform(animVertex, model.transform);
+                    mesh.animVertices[vCounter] += animVertex.x*boneWeight;
+                    mesh.animVertices[vCounter + 1] += animVertex.y*boneWeight;
+                    mesh.animVertices[vCounter + 2] += animVertex.z*boneWeight;
+                    updated = true;
 
 
                     // Normals processing
                     // Normals processing
                     // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
                     // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
-                    if (model.meshes[m].normals != NULL)
+                    if (mesh.normals != NULL)
                     {
                     {
-                        animNormal = (Vector3){ model.meshes[m].normals[vCounter], model.meshes[m].normals[vCounter + 1], model.meshes[m].normals[vCounter + 2] };
+                        animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] };
                         animNormal = Vector3RotateByQuaternion(animNormal, QuaternionMultiply(outRotation, QuaternionInvert(inRotation)));
                         animNormal = Vector3RotateByQuaternion(animNormal, QuaternionMultiply(outRotation, QuaternionInvert(inRotation)));
-                        model.meshes[m].animNormals[vCounter] += animNormal.x*boneWeight;
-                        model.meshes[m].animNormals[vCounter + 1] += animNormal.y*boneWeight;
-                        model.meshes[m].animNormals[vCounter + 2] += animNormal.z*boneWeight;
+                        mesh.animNormals[vCounter] += animNormal.x*boneWeight;
+                        mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight;
+                        mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight;
                     }
                     }
-                    boneCounter += 1;
                 }
                 }
-                vCounter += 3;
             }
             }
 
 
             // Upload new vertex data to GPU for model drawing
             // Upload new vertex data to GPU for model drawing
-            rlUpdateVertexBuffer(model.meshes[m].vboId[0], model.meshes[m].animVertices, model.meshes[m].vertexCount*3*sizeof(float), 0);    // Update vertex position
-            rlUpdateVertexBuffer(model.meshes[m].vboId[2], model.meshes[m].animNormals, model.meshes[m].vertexCount*3*sizeof(float), 0);     // Update vertex normals
+            // Only update data when values changed.
+            if (updated){
+                rlUpdateVertexBuffer(mesh.vboId[0], mesh.animVertices, mesh.vertexCount*3*sizeof(float), 0);    // Update vertex position
+                rlUpdateVertexBuffer(mesh.vboId[2], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0);     // Update vertex normals
+            }
         }
         }
     }
     }
 }
 }
@@ -5188,30 +5210,26 @@ static void LoadGLTFMaterial(Model *model, const char *fileName, const cgltf_dat
     model->materials[model->materialCount - 1] = LoadMaterialDefault();
     model->materials[model->materialCount - 1] = LoadMaterialDefault();
 }
 }
 
 
-static void BindGLTFPrimitiveToBones(Model *model, const cgltf_data *data, int primitiveIndex)
+static void BindGLTFPrimitiveToBones(Model *model, cgltf_node *node, const cgltf_data *data, int primitiveIndex)
 {
 {
-    for (unsigned int nodeId = 0; nodeId < data->nodes_count; nodeId++)
+    int nodeId = node - data->nodes;
+
+    if (model->meshes[primitiveIndex].boneIds == NULL)
     {
     {
-        if (data->nodes[nodeId].mesh == &(data->meshes[primitiveIndex]))
+        model->meshes[primitiveIndex].boneIds = RL_CALLOC(model->meshes[primitiveIndex].vertexCount*4, sizeof(int));
+        model->meshes[primitiveIndex].boneWeights = RL_CALLOC(model->meshes[primitiveIndex].vertexCount*4, sizeof(float));
+
+        for (int b = 0; b < model->meshes[primitiveIndex].vertexCount*4; b++)
         {
         {
-            if (model->meshes[primitiveIndex].boneIds == NULL)
+            if (b%4 == 0)
             {
             {
-                model->meshes[primitiveIndex].boneIds = RL_CALLOC(model->meshes[primitiveIndex].vertexCount*4, sizeof(int));
-                model->meshes[primitiveIndex].boneWeights = RL_CALLOC(model->meshes[primitiveIndex].vertexCount*4, sizeof(float));
-
-                for (int b = 0; b < model->meshes[primitiveIndex].vertexCount*4; b++)
-                {
-                    if (b%4 == 0)
-                    {
-                        model->meshes[primitiveIndex].boneIds[b] = nodeId;
-                        model->meshes[primitiveIndex].boneWeights[b] = 1.0f;
-                    }
-                    else
-                    {
-                        model->meshes[primitiveIndex].boneIds[b] = 0;
-                        model->meshes[primitiveIndex].boneWeights[b] = 0.0f;
-                    }
-                }
+                model->meshes[primitiveIndex].boneIds[b] = nodeId;
+                model->meshes[primitiveIndex].boneWeights[b] = 1.0f;
+            }
+            else
+            {
+                model->meshes[primitiveIndex].boneIds[b] = 0;
+                model->meshes[primitiveIndex].boneWeights[b] = 0.0f;
             }
             }
         }
         }
     }
     }
@@ -5383,13 +5401,16 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, unsigned in
                         translationStart.y = values[1];
                         translationStart.y = values[1];
                         translationStart.z = values[2];
                         translationStart.z = values[2];
 
 
-                        success = ReadGLTFValue(sampler->output, outputMax, values) || success;
+                        success = ReadGLTFValue(sampler->output, outputMax, values) && success;
 
 
                         translationEnd.x = values[0];
                         translationEnd.x = values[0];
                         translationEnd.y = values[1];
                         translationEnd.y = values[1];
                         translationEnd.z = values[2];
                         translationEnd.z = values[2];
 
 
-                        if (success) output->framePoses[frame][boneId].translation = Vector3Lerp(translationStart, translationEnd, lerpPercent);
+                        if (success)
+                        {
+                            output->framePoses[frame][boneId].translation = Vector3Lerp(translationStart, translationEnd, lerpPercent);
+                        }
                     }
                     }
                     if (channel->target_path == cgltf_animation_path_type_rotation)
                     if (channel->target_path == cgltf_animation_path_type_rotation)
                     {
                     {
@@ -5405,7 +5426,7 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, unsigned in
                         rotationStart.z = values[2];
                         rotationStart.z = values[2];
                         rotationStart.w = values[3];
                         rotationStart.w = values[3];
 
 
-                        success = ReadGLTFValue(sampler->output, outputMax, &values) || success;
+                        success = ReadGLTFValue(sampler->output, outputMax, &values) && success;
 
 
                         rotationEnd.x = values[0];
                         rotationEnd.x = values[0];
                         rotationEnd.y = values[1];
                         rotationEnd.y = values[1];
@@ -5430,13 +5451,16 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, unsigned in
                         scaleStart.y = values[1];
                         scaleStart.y = values[1];
                         scaleStart.z = values[2];
                         scaleStart.z = values[2];
 
 
-                        success = ReadGLTFValue(sampler->output, outputMax, &values) || success;
+                        success = ReadGLTFValue(sampler->output, outputMax, &values) && success;
 
 
                         scaleEnd.x = values[0];
                         scaleEnd.x = values[0];
                         scaleEnd.y = values[1];
                         scaleEnd.y = values[1];
                         scaleEnd.z = values[2];
                         scaleEnd.z = values[2];
 
 
-                        if (success) output->framePoses[frame][boneId].scale = Vector3Lerp(scaleStart, scaleEnd, lerpPercent);
+                        if (success)
+                        {
+                            output->framePoses[frame][boneId].scale = Vector3Lerp(scaleStart, scaleEnd, lerpPercent);
+                        }
                     }
                     }
                 }
                 }
             }
             }
@@ -5451,7 +5475,10 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, unsigned in
                 {
                 {
                     for (int i = 0; i < output->boneCount; i++)
                     for (int i = 0; i < output->boneCount; i++)
                     {
                     {
-                        if (completedBones[i]) continue;
+                        if (completedBones[i])
+                        {
+                            continue;
+                        }
 
 
                         if (output->bones[i].parent < 0)
                         if (output->bones[i].parent < 0)
                         {
                         {
@@ -5460,7 +5487,10 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, unsigned in
                             continue;
                             continue;
                         }
                         }
 
 
-                        if (!completedBones[output->bones[i].parent]) 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);
@@ -5470,7 +5500,6 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, unsigned in
                         numberCompletedBones++;
                         numberCompletedBones++;
                     }
                     }
                 }
                 }
-
                 RL_FREE(completedBones);
                 RL_FREE(completedBones);
             }
             }
         }
         }
@@ -5484,8 +5513,9 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, unsigned in
     return animations;
     return animations;
 }
 }
 
 
-void LoadGLTFMesh(cgltf_data *data, cgltf_mesh *mesh, Model *outModel, Matrix currentTransform, int *primitiveIndex, const char *fileName)
+void LoadGLTFMesh(cgltf_data *data, cgltf_node *node, Model *outModel, Matrix currentTransform, int *primitiveIndex, const char *fileName)
 {
 {
+    cgltf_mesh *mesh = node->mesh;
     for (unsigned int p = 0; p < mesh->primitives_count; p++)
     for (unsigned int p = 0; p < mesh->primitives_count; p++)
     {
     {
         for (unsigned int j = 0; j < mesh->primitives[p].attributes_count; j++)
         for (unsigned int j = 0; j < mesh->primitives[p].attributes_count; j++)
@@ -5552,23 +5582,16 @@ void LoadGLTFMesh(cgltf_data *data, cgltf_mesh *mesh, Model *outModel, Matrix cu
                 cgltf_accessor *acc = mesh->primitives[p].attributes[j].data;
                 cgltf_accessor *acc = mesh->primitives[p].attributes[j].data;
                 unsigned int boneCount = acc->count;
                 unsigned int boneCount = acc->count;
                 unsigned int totalBoneWeights = boneCount*4;
                 unsigned int totalBoneWeights = boneCount*4;
-
                 outModel->meshes[(*primitiveIndex)].boneIds = RL_MALLOC(totalBoneWeights*sizeof(int));
                 outModel->meshes[(*primitiveIndex)].boneIds = RL_MALLOC(totalBoneWeights*sizeof(int));
                 short *bones = ReadGLTFValuesAs(acc, cgltf_component_type_r_16, false);
                 short *bones = ReadGLTFValuesAs(acc, cgltf_component_type_r_16, false);
+                // Find skin joint
                 for (unsigned int a = 0; a < totalBoneWeights; a++)
                 for (unsigned int a = 0; a < totalBoneWeights; a++)
                 {
                 {
                     outModel->meshes[(*primitiveIndex)].boneIds[a] = 0;
                     outModel->meshes[(*primitiveIndex)].boneIds[a] = 0;
                     if (bones[a] < 0) continue;
                     if (bones[a] < 0) continue;
-
-                    cgltf_node* skinJoint = data->skins->joints[bones[a]];
-                    for (unsigned int k = 0; k < data->nodes_count; k++)
-                    {
-                        if (data->nodes + k == skinJoint)
-                        {
-                            outModel->meshes[(*primitiveIndex)].boneIds[a] = k;
-                            break;
-                        }
-                    }
+                    cgltf_node* skinJoint = node->skin->joints[bones[a]];
+                    unsigned int skinJointId = skinJoint - data->nodes;
+                    outModel->meshes[(*primitiveIndex)].boneIds[a] = skinJointId;
                 }
                 }
                 RL_FREE(bones);
                 RL_FREE(bones);
             }
             }
@@ -5603,7 +5626,7 @@ void LoadGLTFMesh(cgltf_data *data, cgltf_mesh *mesh, Model *outModel, Matrix cu
         }
         }
         else outModel->meshMaterial[(*primitiveIndex)] = outModel->materialCount - 1;
         else outModel->meshMaterial[(*primitiveIndex)] = outModel->materialCount - 1;
 
 
-        BindGLTFPrimitiveToBones(outModel, data, *primitiveIndex);
+        BindGLTFPrimitiveToBones(outModel, node, data, *primitiveIndex);
 
 
         (*primitiveIndex) = (*primitiveIndex) + 1;
         (*primitiveIndex) = (*primitiveIndex) + 1;
     }
     }
@@ -5648,12 +5671,12 @@ void LoadGLTFNode(cgltf_data *data, cgltf_node *node, Model *outModel, Matrix cu
     {
     {
         // Check if skinning is enabled and load Mesh accordingly
         // Check if skinning is enabled and load Mesh accordingly
         Matrix vertexTransform = currentTransform;
         Matrix vertexTransform = currentTransform;
-        if((node->skin != NULL) && (node->parent != NULL))
+        if ((node->skin != NULL) && (node->parent != NULL))
         {
         {
             vertexTransform = localTransform;
             vertexTransform = localTransform;
             TRACELOG(LOG_WARNING,"MODEL: GLTF Node %s is skinned but not root node! Parent transformations will be ignored (NODE_SKINNED_MESH_NON_ROOT)",node->name);
             TRACELOG(LOG_WARNING,"MODEL: GLTF Node %s is skinned but not root node! Parent transformations will be ignored (NODE_SKINNED_MESH_NON_ROOT)",node->name);
         }
         }
-        LoadGLTFMesh(data, node->mesh, outModel, vertexTransform, primitiveIndex, fileName);
+        LoadGLTFMesh(data, node, outModel, vertexTransform, primitiveIndex, fileName);
     }
     }
     for (unsigned int i = 0; i < node->children_count; i++) LoadGLTFNode(data, node->children[i], outModel, currentTransform, primitiveIndex, fileName);
     for (unsigned int i = 0; i < node->children_count; i++) LoadGLTFNode(data, node->children[i], outModel, currentTransform, primitiveIndex, fileName);
 }
 }