Revamped GLTF model loading as it was wrong by default. Also updated some comments.

GLTF models were loaded only by mesh but they should be loaded recursively by hierarchical nodes because tehre are some static node transformations that are to be applied to the vertices. It also resulted in more meshes being included in some models.

It is the correct way of loading GLTF and what is suggested in the official examples.

Currenlty limiting to only one scene but more can be included later.
This commit is contained in:
Hristo Stamenov 2021-06-19 15:10:55 +03:00
parent 718f83c0b1
commit 3501c86b33

View File

@ -4158,6 +4158,253 @@ static bool GLTFReadValue(cgltf_accessor* acc, unsigned int index, void *variabl
return true; return true;
} }
static void ProcessGLTFMesh(cgltf_data *data, cgltf_mesh* mesh, Model* outModel, Matrix currentTransform, int* primitiveIndex, const char* fileName)
{
for (unsigned int p = 0; p < mesh->primitives_count; p++)
{
for (unsigned int j = 0; j < mesh->primitives[p].attributes_count; j++)
{
if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_position)
{
cgltf_accessor *acc = mesh->primitives[p].attributes[j].data;
outModel->meshes[(*primitiveIndex)].vertexCount = (int)acc->count;
int bufferSize = outModel->meshes[(*primitiveIndex)].vertexCount*3*sizeof(float);
outModel->meshes[(*primitiveIndex)].vertices = RL_MALLOC(bufferSize);
outModel->meshes[(*primitiveIndex)].animVertices = RL_MALLOC(bufferSize);
if (acc->component_type == cgltf_component_type_r_32f)
{
for (unsigned int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, outModel->meshes[(*primitiveIndex)].vertices + (a*3), 3, sizeof(float));
}
}
else if (acc->component_type == cgltf_component_type_r_32u)
{
int readValue[3];
for (unsigned int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, readValue, 3, sizeof(int));
outModel->meshes[(*primitiveIndex)].vertices[(a*3) + 0] = (float)readValue[0];
outModel->meshes[(*primitiveIndex)].vertices[(a*3) + 1] = (float)readValue[1];
outModel->meshes[(*primitiveIndex)].vertices[(a*3) + 2] = (float)readValue[2];
}
}
else
{
// TODO: Support normalized unsigned byte/unsigned short vertices
TRACELOG(LOG_WARNING, "MODEL: [%s] glTF vertices must be float or int", fileName);
}
for (unsigned int v = 0; v < outModel->meshes[(*primitiveIndex)].vertexCount; v++)
{
Vector3 vertex = {
outModel->meshes[(*primitiveIndex)].vertices[(v * 3 + 0)],
outModel->meshes[(*primitiveIndex)].vertices[(v * 3 + 1)],
outModel->meshes[(*primitiveIndex)].vertices[(v * 3 + 2)],
};
vertex = Vector3Transform(vertex, currentTransform);
outModel->meshes[(*primitiveIndex)].vertices[(v * 3 + 0)] = vertex.x;
outModel->meshes[(*primitiveIndex)].vertices[(v * 3 + 1)] = vertex.y;
outModel->meshes[(*primitiveIndex)].vertices[(v * 3 + 2)] = vertex.z;
}
memcpy(outModel->meshes[(*primitiveIndex)].animVertices, outModel->meshes[(*primitiveIndex)].vertices, bufferSize);
}
else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_normal)
{
cgltf_accessor *acc = mesh->primitives[p].attributes[j].data;
int bufferSize = (int)(acc->count*3*sizeof(float));
outModel->meshes[(*primitiveIndex)].normals = RL_MALLOC(bufferSize);
outModel->meshes[(*primitiveIndex)].animNormals = RL_MALLOC(bufferSize);
if (acc->component_type == cgltf_component_type_r_32f)
{
for (unsigned int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, outModel->meshes[(*primitiveIndex)].normals + (a*3), 3, sizeof(float));
}
}
else if (acc->component_type == cgltf_component_type_r_32u)
{
int readValue[3];
for (unsigned int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, readValue, 3, sizeof(int));
outModel->meshes[(*primitiveIndex)].normals[(a*3) + 0] = (float)readValue[0];
outModel->meshes[(*primitiveIndex)].normals[(a*3) + 1] = (float)readValue[1];
outModel->meshes[(*primitiveIndex)].normals[(a*3) + 2] = (float)readValue[2];
}
}
else
{
// TODO: Support normalized unsigned byte/unsigned short normals
TRACELOG(LOG_WARNING, "MODEL: [%s] glTF normals must be float or int", fileName);
}
for (unsigned int v = 0; v < outModel->meshes[(*primitiveIndex)].vertexCount; v++)
{
Vector3 normal = {
outModel->meshes[(*primitiveIndex)].normals[(v * 3 + 0)],
outModel->meshes[(*primitiveIndex)].normals[(v * 3 + 1)],
outModel->meshes[(*primitiveIndex)].normals[(v * 3 + 2)],
};
normal = Vector3Transform(normal, currentTransform);
outModel->meshes[(*primitiveIndex)].normals[(v * 3 + 0)] = normal.x;
outModel->meshes[(*primitiveIndex)].normals[(v * 3 + 1)] = normal.y;
outModel->meshes[(*primitiveIndex)].normals[(v * 3 + 2)] = normal.z;
}
memcpy(outModel->meshes[(*primitiveIndex)].animNormals, outModel->meshes[(*primitiveIndex)].normals, bufferSize);
}
else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_texcoord)
{
cgltf_accessor *acc = mesh->primitives[p].attributes[j].data;
if (acc->component_type == cgltf_component_type_r_32f)
{
outModel->meshes[(*primitiveIndex)].texcoords = RL_MALLOC(acc->count*2*sizeof(float));
for (unsigned int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, outModel->meshes[(*primitiveIndex)].texcoords + (a*2), 2, sizeof(float));
}
}
else
{
// TODO: Support normalized unsigned byte/unsigned short texture coordinates
TRACELOG(LOG_WARNING, "MODEL: [%s] glTF texture coordinates must be float", fileName);
}
}
else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_joints)
{
cgltf_accessor *acc = mesh->primitives[p].attributes[j].data;
LoadGLTFBoneAttribute(outModel, acc, data, *primitiveIndex);
}
else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_weights)
{
cgltf_accessor *acc = mesh->primitives[p].attributes[j].data;
outModel->meshes[(*primitiveIndex)].boneWeights = RL_MALLOC(acc->count*4*sizeof(float));
if (acc->component_type == cgltf_component_type_r_32f)
{
for (unsigned int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, outModel->meshes[(*primitiveIndex)].boneWeights + (a*4), 4, sizeof(float));
}
}
else if (acc->component_type == cgltf_component_type_r_32u)
{
unsigned int readValue[4];
for (unsigned int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, readValue, 4, sizeof(unsigned int));
outModel->meshes[(*primitiveIndex)].boneWeights[(a*4) + 0] = (float)readValue[0];
outModel->meshes[(*primitiveIndex)].boneWeights[(a*4) + 1] = (float)readValue[1];
outModel->meshes[(*primitiveIndex)].boneWeights[(a*4) + 2] = (float)readValue[2];
outModel->meshes[(*primitiveIndex)].boneWeights[(a*4) + 3] = (float)readValue[3];
}
}
else
{
// TODO: Support normalized unsigned byte/unsigned short weights
TRACELOG(LOG_WARNING, "MODEL: [%s] glTF normals must be float or int", fileName);
}
}
else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_color)
{
cgltf_accessor *acc = mesh->primitives[p].attributes[j].data;
outModel->meshes[(*primitiveIndex)].colors = RL_MALLOC(acc->count*4*sizeof(unsigned char));
if (acc->component_type == cgltf_component_type_r_8u)
{
for (int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, outModel->meshes[(*primitiveIndex)].colors + (a*4), 4, sizeof(unsigned char));
}
}
if (acc->component_type == cgltf_component_type_r_16u)
{
TRACELOG(LOG_WARNING, "MODEL: [%s] converting glTF colors to unsigned char", fileName);
for (int a = 0; a < acc->count; a++)
{
unsigned short readValue[4];
for (int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, readValue, 4, sizeof(unsigned short));
// 257 = 65535/255
outModel->meshes[(*primitiveIndex)].colors[(a*4) + 0] = (unsigned char)(readValue[0]/257);
outModel->meshes[(*primitiveIndex)].colors[(a*4) + 1] = (unsigned char)(readValue[1]/257);
outModel->meshes[(*primitiveIndex)].colors[(a*4) + 2] = (unsigned char)(readValue[2]/257);
outModel->meshes[(*primitiveIndex)].colors[(a*4) + 3] = (unsigned char)(readValue[3]/257);
}
}
}
else
{
TRACELOG(LOG_WARNING, "MODEL: [%s] glTF colors must be uchar or ushort", fileName);
}
}
}
cgltf_accessor *acc = mesh->primitives[p].indices;
LoadGLTFModelIndices(outModel, acc, *primitiveIndex);
if (mesh->primitives[p].material)
{
// Compute the offset
outModel->meshMaterial[(*primitiveIndex)] = (int)(mesh->primitives[p].material - data->materials);
}
else
{
outModel->meshMaterial[(*primitiveIndex)] = outModel->materialCount - 1;
}
BindGLTFPrimitiveToBones(outModel, data, *primitiveIndex);
(*primitiveIndex) = (*primitiveIndex) + 1;
}
}
static void ProcessGLTFNode(cgltf_data *data, cgltf_node* node, Model* outModel, Matrix currentTransform, int* primitiveIndex, const char* fileName)
{
Matrix nodeTransform = { node->matrix[0], node->matrix[4], node->matrix[8], node->matrix[12],
node->matrix[1], node->matrix[5], node->matrix[9], node->matrix[13],
node->matrix[2], node->matrix[6], node->matrix[10], node->matrix[14],
node->matrix[3], node->matrix[7], node->matrix[11], node->matrix[15] };
currentTransform = MatrixMultiply(nodeTransform, currentTransform);
if(node->mesh != NULL)
{
ProcessGLTFMesh(data, node->mesh, outModel, currentTransform, primitiveIndex, fileName);
}
for(unsigned int i = 0; i < node->children_count; i++)
{
ProcessGLTFNode(data, node->children[i], outModel, currentTransform, primitiveIndex, fileName);
}
}
static void GetPrimitiveCount(cgltf_node* node, int* outCount)
{
if(node->mesh != NULL)
{
*outCount += node->mesh->primitives_count;
}
for(unsigned int i = 0; i < node->children_count; i++)
{
GetPrimitiveCount(node->children[i], outCount);
}
}
// LoadGLTF loads in model data from given filename, supporting both .gltf and .glb // LoadGLTF loads in model data from given filename, supporting both .gltf and .glb
static Model LoadGLTF(const char *fileName) static Model LoadGLTF(const char *fileName)
{ {
@ -4201,10 +4448,13 @@ static Model LoadGLTF(const char *fileName)
result = cgltf_load_buffers(&options, data, fileName); result = cgltf_load_buffers(&options, data, fileName);
if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load mesh/material buffers", fileName); if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load mesh/material buffers", fileName);
int primitivesCount = 0; if(data->scenes_count > 1) TRACELOG(LOG_INFO, "MODEL: [%s] Has multiple scenes but only the first one will be loaded", fileName);
for (unsigned int i = 0; i < data->meshes_count; i++) int primitivesCount = 0;
primitivesCount += (int)data->meshes[i].primitives_count; for (unsigned int i = 0; i < data->scene->nodes_count; i++)
{
GetPrimitiveCount(data->scene->nodes[i], &primitivesCount);
}
// Process glTF data and map to model // Process glTF data and map to model
model.meshCount = primitivesCount; model.meshCount = primitivesCount;
@ -4220,190 +4470,10 @@ static Model LoadGLTF(const char *fileName)
LoadGLTFMaterial(&model, fileName, data); LoadGLTFMaterial(&model, fileName, data);
int primitiveIndex = 0; int primitiveIndex = 0;
for(unsigned int i = 0; i < data->scene->nodes_count; i++)
for (unsigned int i = 0; i < data->meshes_count; i++)
{ {
for (unsigned int p = 0; p < data->meshes[i].primitives_count; p++) Matrix staticTransform = MatrixIdentity();
{ ProcessGLTFNode(data, data->scene->nodes[i], &model, staticTransform, &primitiveIndex, fileName);
for (unsigned int j = 0; j < data->meshes[i].primitives[p].attributes_count; j++)
{
if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_position)
{
cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
model.meshes[primitiveIndex].vertexCount = (int)acc->count;
int bufferSize = model.meshes[primitiveIndex].vertexCount*3*sizeof(float);
model.meshes[primitiveIndex].vertices = RL_MALLOC(bufferSize);
model.meshes[primitiveIndex].animVertices = RL_MALLOC(bufferSize);
if (acc->component_type == cgltf_component_type_r_32f)
{
for (unsigned int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, model.meshes[primitiveIndex].vertices + (a*3), 3, sizeof(float));
}
}
else if (acc->component_type == cgltf_component_type_r_32u)
{
int readValue[3];
for (unsigned int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, readValue, 3, sizeof(int));
model.meshes[primitiveIndex].vertices[(a*3) + 0] = (float)readValue[0];
model.meshes[primitiveIndex].vertices[(a*3) + 1] = (float)readValue[1];
model.meshes[primitiveIndex].vertices[(a*3) + 2] = (float)readValue[2];
}
}
else
{
// TODO: Support normalized unsigned byte/unsigned short vertices
TRACELOG(LOG_WARNING, "MODEL: [%s] glTF vertices must be float or int", fileName);
}
memcpy(model.meshes[primitiveIndex].animVertices, model.meshes[primitiveIndex].vertices, bufferSize);
}
else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_normal)
{
cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
int bufferSize = (int)(acc->count*3*sizeof(float));
model.meshes[primitiveIndex].normals = RL_MALLOC(bufferSize);
model.meshes[primitiveIndex].animNormals = RL_MALLOC(bufferSize);
if (acc->component_type == cgltf_component_type_r_32f)
{
for (unsigned int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, model.meshes[primitiveIndex].normals + (a*3), 3, sizeof(float));
}
}
else if (acc->component_type == cgltf_component_type_r_32u)
{
int readValue[3];
for (unsigned int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, readValue, 3, sizeof(int));
model.meshes[primitiveIndex].normals[(a*3) + 0] = (float)readValue[0];
model.meshes[primitiveIndex].normals[(a*3) + 1] = (float)readValue[1];
model.meshes[primitiveIndex].normals[(a*3) + 2] = (float)readValue[2];
}
}
else
{
// TODO: Support normalized unsigned byte/unsigned short normals
TRACELOG(LOG_WARNING, "MODEL: [%s] glTF normals must be float or int", fileName);
}
memcpy(model.meshes[primitiveIndex].animNormals, model.meshes[primitiveIndex].normals, bufferSize);
}
else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_texcoord)
{
cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
if (acc->component_type == cgltf_component_type_r_32f)
{
model.meshes[primitiveIndex].texcoords = RL_MALLOC(acc->count*2*sizeof(float));
for (unsigned int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, model.meshes[primitiveIndex].texcoords + (a*2), 2, sizeof(float));
}
}
else
{
// TODO: Support normalized unsigned byte/unsigned short texture coordinates
TRACELOG(LOG_WARNING, "MODEL: [%s] glTF texture coordinates must be float", fileName);
}
}
else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_joints)
{
cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
LoadGLTFBoneAttribute(&model, acc, data, primitiveIndex);
}
else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_weights)
{
cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
model.meshes[primitiveIndex].boneWeights = RL_MALLOC(acc->count*4*sizeof(float));
if (acc->component_type == cgltf_component_type_r_32f)
{
for (unsigned int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, model.meshes[primitiveIndex].boneWeights + (a*4), 4, sizeof(float));
}
}
else if (acc->component_type == cgltf_component_type_r_32u)
{
unsigned int readValue[4];
for (unsigned int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, readValue, 4, sizeof(unsigned int));
model.meshes[primitiveIndex].boneWeights[(a*4) + 0] = (float)readValue[0];
model.meshes[primitiveIndex].boneWeights[(a*4) + 1] = (float)readValue[1];
model.meshes[primitiveIndex].boneWeights[(a*4) + 2] = (float)readValue[2];
model.meshes[primitiveIndex].boneWeights[(a*4) + 3] = (float)readValue[3];
}
}
else
{
// TODO: Support normalized unsigned byte/unsigned short weights
TRACELOG(LOG_WARNING, "MODEL: [%s] glTF normals must be float or int", fileName);
}
}
else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_color)
{
cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
model.meshes[primitiveIndex].colors = RL_MALLOC(acc->count*4*sizeof(unsigned char));
if (acc->component_type == cgltf_component_type_r_8u)
{
for (int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, model.meshes[primitiveIndex].colors + (a*4), 4, sizeof(unsigned char));
}
}
if (acc->component_type == cgltf_component_type_r_16u)
{
TRACELOG(LOG_WARNING, "MODEL: [%s] converting glTF colors to unsigned char", fileName);
for (int a = 0; a < acc->count; a++)
{
unsigned short readValue[4];
for (int a = 0; a < acc->count; a++)
{
GLTFReadValue(acc, a, readValue, 4, sizeof(unsigned short));
// 257 = 65535/255
model.meshes[primitiveIndex].colors[(a*4) + 0] = (unsigned char)(readValue[0]/257);
model.meshes[primitiveIndex].colors[(a*4) + 1] = (unsigned char)(readValue[1]/257);
model.meshes[primitiveIndex].colors[(a*4) + 2] = (unsigned char)(readValue[2]/257);
model.meshes[primitiveIndex].colors[(a*4) + 3] = (unsigned char)(readValue[3]/257);
}
}
}
else
{
TRACELOG(LOG_WARNING, "MODEL: [%s] glTF colors must be uchar or ushort", fileName);
}
}
}
cgltf_accessor *acc = data->meshes[i].primitives[p].indices;
LoadGLTFModelIndices(&model, acc, primitiveIndex);
if (data->meshes[i].primitives[p].material)
{
// Compute the offset
model.meshMaterial[primitiveIndex] = (int)(data->meshes[i].primitives[p].material - data->materials);
}
else
{
model.meshMaterial[primitiveIndex] = model.materialCount - 1;
}
BindGLTFPrimitiveToBones(&model, data, primitiveIndex);
primitiveIndex++;
}
} }
cgltf_free(data); cgltf_free(data);
@ -4606,12 +4676,12 @@ static void LoadGLTFBoneAttribute(Model *model, cgltf_accessor *jointsAccessor,
} }
static void BindGLTFPrimitiveToBones(Model* model, const cgltf_data* data, int primitiveIndex) static void BindGLTFPrimitiveToBones(Model* model, const cgltf_data* data, int primitiveIndex)
{
if (model->meshes[primitiveIndex].boneIds == NULL && data->nodes_count > 0)
{ {
for (unsigned int nodeId = 0; nodeId < data->nodes_count; nodeId++) for (unsigned int nodeId = 0; nodeId < data->nodes_count; nodeId++)
{ {
if (data->nodes[nodeId].mesh == &(data->meshes[primitiveIndex])) if (data->nodes[nodeId].mesh == &(data->meshes[primitiveIndex]))
{
if (model->meshes[primitiveIndex].boneIds == NULL)
{ {
model->meshes[primitiveIndex].boneIds = RL_CALLOC(4 * model->meshes[primitiveIndex].vertexCount, sizeof(int)); model->meshes[primitiveIndex].boneIds = RL_CALLOC(4 * model->meshes[primitiveIndex].vertexCount, sizeof(int));
model->meshes[primitiveIndex].boneWeights = RL_CALLOC(4 * model->meshes[primitiveIndex].vertexCount, sizeof(float)); model->meshes[primitiveIndex].boneWeights = RL_CALLOC(4 * model->meshes[primitiveIndex].vertexCount, sizeof(float));
@ -4628,48 +4698,6 @@ static void BindGLTFPrimitiveToBones(Model* model, const cgltf_data* data, int p
model->meshes[primitiveIndex].boneIds[b] = 0; model->meshes[primitiveIndex].boneIds[b] = 0;
model->meshes[primitiveIndex].boneWeights[b] = 0.0f; model->meshes[primitiveIndex].boneWeights[b] = 0.0f;
} }
}
Vector3 boundVertex = { 0 };
Vector3 boundNormal = { 0 };
Vector3 outTranslation = { 0 };
Quaternion outRotation = { 0 };
Vector3 outScale = { 0 };
int vCounter = 0;
int boneCounter = 0;
int boneId = 0;
for (int i = 0; i < model->meshes[primitiveIndex].vertexCount; i++)
{
boneId = model->meshes[primitiveIndex].boneIds[boneCounter];
outTranslation = model->bindPose[boneId].translation;
outRotation = model->bindPose[boneId].rotation;
outScale = model->bindPose[boneId].scale;
// Vertices processing
boundVertex = (Vector3){ model->meshes[primitiveIndex].vertices[vCounter], model->meshes[primitiveIndex].vertices[vCounter + 1], model->meshes[primitiveIndex].vertices[vCounter + 2] };
boundVertex = Vector3Multiply(boundVertex, outScale);
boundVertex = Vector3RotateByQuaternion(boundVertex, outRotation);
boundVertex = Vector3Add(boundVertex, outTranslation);
model->meshes[primitiveIndex].vertices[vCounter] = boundVertex.x;
model->meshes[primitiveIndex].vertices[vCounter + 1] = boundVertex.y;
model->meshes[primitiveIndex].vertices[vCounter + 2] = boundVertex.z;
// Normals processing
if (model->meshes[primitiveIndex].normals != NULL)
{
boundNormal = (Vector3){ model->meshes[primitiveIndex].normals[vCounter], model->meshes[primitiveIndex].normals[vCounter + 1], model->meshes[primitiveIndex].normals[vCounter + 2] };
boundNormal = Vector3RotateByQuaternion(boundNormal, outRotation);
model->meshes[primitiveIndex].normals[vCounter] = boundNormal.x;
model->meshes[primitiveIndex].normals[vCounter + 1] = boundNormal.y;
model->meshes[primitiveIndex].normals[vCounter + 2] = boundNormal.z;
}
vCounter += 3;
boneCounter += 4;
} }
} }
} }
@ -4765,9 +4793,9 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCo
for (unsigned int a = 0; a < data->animations_count; a++) for (unsigned int a = 0; a < data->animations_count; a++)
{ {
// gltf animation consists of the following structures: // gltf animation consists of the following structures:
// - nodes - bones // - nodes - bones are part of the node system (the whole node system is animatable)
// - channels - single transformation type on a single bone // - channels - single transformation type on a single bone
// - node - bone // - node - animatable node
// - transformation type (path) - translation, rotation, scale // - transformation type (path) - translation, rotation, scale
// - sampler - animation samples // - sampler - animation samples
// - input - points in time this transformation happens // - input - points in time this transformation happens
@ -4816,10 +4844,16 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCo
for (unsigned int i = 0; i < output->boneCount; i++) for (unsigned int i = 0; i < output->boneCount; i++)
{ {
output->framePoses[frame][i].translation = Vector3Zero(); if (data->nodes[i].has_translation) memcpy(&output->framePoses[frame][i].translation, data->nodes[i].translation, 3 * sizeof(float));
output->framePoses[frame][i].rotation = QuaternionIdentity(); else output->framePoses[frame][i].translation = Vector3Zero();
if (data->nodes[i].has_rotation) memcpy(&output->framePoses[frame][i], data->nodes[i].rotation, 4 * sizeof(float));
else output->framePoses[frame][i].rotation = QuaternionIdentity();
output->framePoses[frame][i].rotation = QuaternionNormalize(output->framePoses[frame][i].rotation); output->framePoses[frame][i].rotation = QuaternionNormalize(output->framePoses[frame][i].rotation);
output->framePoses[frame][i].scale = Vector3One();
if (data->nodes[i].has_scale) memcpy(&output->framePoses[frame][i].scale, data->nodes[i].scale, 3 * sizeof(float));
else output->framePoses[frame][i].scale = Vector3One();
} }
} }
@ -4891,8 +4925,7 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCo
if (success) if (success)
{ {
output->framePoses[frame][boneId].rotation = QuaternionLerp(rotationStart, rotationEnd, lerpPercent); output->framePoses[frame][boneId].rotation = QuaternionNlerp(rotationStart, rotationEnd, lerpPercent);
output->framePoses[frame][boneId].rotation = QuaternionNormalize(output->framePoses[frame][boneId].rotation);
} }
} }
if (channel->target_path == cgltf_animation_path_type_scale) if (channel->target_path == cgltf_animation_path_type_scale)