summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--examples/models/models_gltf_model.c3
-rw-r--r--examples/models/resources/gltf/LICENSE3
-rw-r--r--examples/models/resources/gltf/girl.glbbin0 -> 135000 bytes
-rw-r--r--src/models.c1152
4 files changed, 783 insertions, 375 deletions
diff --git a/examples/models/models_gltf_model.c b/examples/models/models_gltf_model.c
index 9889ad25..3f843c9d 100644
--- a/examples/models/models_gltf_model.c
+++ b/examples/models/models_gltf_model.c
@@ -21,7 +21,7 @@
#include <stdlib.h>
-#define MAX_MODELS 7
+#define MAX_MODELS 8
int main(void)
{
@@ -49,6 +49,7 @@ int main(void)
model[4] = LoadModel("resources/gltf/AnimatedTriangle.gltf");
model[5] = LoadModel("resources/gltf/AnimatedMorphCube.glb");
model[6] = LoadModel("resources/gltf/vertex_colored_object.glb");
+ model[7] = LoadModel("resources/gltf/girl.glb");
int currentModel = 0;
diff --git a/examples/models/resources/gltf/LICENSE b/examples/models/resources/gltf/LICENSE
index 8617385f..305079cf 100644
--- a/examples/models/resources/gltf/LICENSE
+++ b/examples/models/resources/gltf/LICENSE
@@ -15,6 +15,9 @@ Animated Triangle model is licensed as CC0 Universal Public Domain
Gearbox Assy model has been provided by Okino Computer Graphics, using Okino Polytrans Software.
no license information was provided
+Girl model has been provided by Hristo Stamenov (https://thatonegamedev.com/)
+and licensed as CC0 Universal Public Domain
+
Check for details on CC0: https://creativecommons.org/publicdomain/zero/1.0/
Check for details on CC4: http://creativecommons.org/licenses/by/4.0/
GLTF sample models for testing are taken from: https://github.com/KhronosGroup/glTF-Sample-Models/
diff --git a/examples/models/resources/gltf/girl.glb b/examples/models/resources/gltf/girl.glb
new file mode 100644
index 00000000..c368590c
--- /dev/null
+++ b/examples/models/resources/gltf/girl.glb
Binary files differ
diff --git a/src/models.c b/src/models.c
index b243a45e..4ad7d32b 100644
--- a/src/models.c
+++ b/src/models.c
@@ -50,6 +50,12 @@
#include <string.h> // Required for: memcmp(), strlen()
#include <math.h> // Required for: sinf(), cosf(), sqrtf(), fabsf()
+#if defined(_MSC_VER)
+ #include <climits> // Required for numerical limit constants like CHAR_MAX
+#else
+ #include <limits.h> // Required for numerical limit constants like CHAR_MAX
+#endif
+
#if defined(_WIN32)
#include <direct.h> // Required for: _chdir() [Used in LoadOBJ()]
#define CHDIR _chdir
@@ -117,11 +123,14 @@ static ModelAnimation *LoadIQMModelAnimations(const char *fileName, int *animCou
#if defined(SUPPORT_FILEFORMAT_GLTF)
static Model LoadGLTF(const char *fileName); // Load GLTF mesh data
static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCount); // Load GLTF animation data
-static void LoadGLTFModelIndices(Model *model, cgltf_accessor *indexAccessor, int primitiveIndex);
-static void BindGLTFPrimitiveToBones(Model *model, const cgltf_data *data, int primitiveIndex);
-static void LoadGLTFBoneAttribute(Model *model, cgltf_accessor *jointsAccessor, const cgltf_data *data, int primitiveIndex);
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 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 BindGLTFPrimitiveToBones(Model *model, const cgltf_data *data, int primitiveIndex);
+static void GetGLTFPrimitiveCount(cgltf_node* node, int* outCount);
+static bool GLTFReadValue(cgltf_accessor* acc, unsigned int index, void *variable);
+static void* GLTFReadValuesAs(cgltf_accessor* acc, cgltf_component_type type, bool adjustOnDownCasting);
#endif
//----------------------------------------------------------------------------------
@@ -844,7 +853,8 @@ void UploadMesh(Mesh *mesh, bool dynamic)
// NOTE: Attributes must be uploaded considering default locations points
// Enable vertex attributes: position (shader-location = 0)
- mesh->vboId[0] = rlLoadVertexBuffer(mesh->vertices, mesh->vertexCount*3*sizeof(float), dynamic);
+ void* vertices = mesh->animVertices != NULL ? mesh->animVertices : mesh->vertices;
+ mesh->vboId[0] = rlLoadVertexBuffer(vertices, mesh->vertexCount*3*sizeof(float), dynamic);
rlSetVertexAttribute(0, 3, RL_FLOAT, 0, 0, 0);
rlEnableVertexAttribute(0);
@@ -856,7 +866,8 @@ void UploadMesh(Mesh *mesh, bool dynamic)
if (mesh->normals != NULL)
{
// Enable vertex attributes: normals (shader-location = 2)
- mesh->vboId[2] = rlLoadVertexBuffer(mesh->normals, mesh->vertexCount*3*sizeof(float), dynamic);
+ void* normals = mesh->animNormals != NULL ? mesh->animNormals : mesh->vertices;
+ mesh->vboId[2] = rlLoadVertexBuffer(normals, mesh->vertexCount*3*sizeof(float), dynamic);
rlSetVertexAttribute(2, 3, RL_FLOAT, 0, 0, 0);
rlEnableVertexAttribute(2);
}
@@ -4132,272 +4143,611 @@ static Image LoadImageFromCgltfImage(cgltf_image *image, const char *texPath, Co
}
-static bool GLTFReadValue(cgltf_accessor* acc, unsigned int index, void *variable, unsigned int elements, unsigned int size)
+static bool GLTFReadValue(cgltf_accessor* acc, unsigned int index, void *variable)
{
+ unsigned int typeElements = 0;
+ switch(acc->type) {
+ case cgltf_type_scalar: typeElements = 1; break;
+ case cgltf_type_vec2: typeElements = 2; break;
+ case cgltf_type_vec3: typeElements = 3; break;
+ case cgltf_type_vec4:
+ case cgltf_type_mat2: typeElements = 4; break;
+ case cgltf_type_mat3: typeElements = 9; break;
+ case cgltf_type_mat4: typeElements = 16; break;
+ case cgltf_type_invalid: typeElements = 0; break;
+ }
+
+ unsigned int typeSize = 0;
+ switch(acc->component_type) {
+ case cgltf_component_type_r_8u:
+ case cgltf_component_type_r_8: typeSize = 1; break;
+ case cgltf_component_type_r_16u:
+ case cgltf_component_type_r_16: typeSize = 2; break;
+ case cgltf_component_type_r_32f:
+ case cgltf_component_type_r_32u: typeSize = 4; break;
+ case cgltf_component_type_invalid: typeSize = 0; break;
+ }
+
+ unsigned int singleElementSize = typeSize * typeElements;
+
if (acc->count == 2)
{
if (index > 1) return false;
- memcpy(variable, index == 0 ? acc->min : acc->max, elements*size);
+ memcpy(variable, index == 0 ? acc->min : acc->max, singleElementSize);
return true;
}
-
- unsigned int stride = size*elements;
- memset(variable, 0, stride);
+
+ memset(variable, 0, singleElementSize);
if (acc->buffer_view == NULL || acc->buffer_view->buffer == NULL || acc->buffer_view->buffer->data == NULL) return false;
-
- void* readPosition = ((char *)acc->buffer_view->buffer->data) + (index*stride) + acc->buffer_view->offset + acc->offset;
- memcpy(variable, readPosition, stride);
+
+ if(!acc->buffer_view->stride)
+ {
+ void* readPosition = ((char *)acc->buffer_view->buffer->data) + (index * singleElementSize) + acc->buffer_view->offset + acc->offset;
+ memcpy(variable, readPosition, singleElementSize);
+ }
+ else
+ {
+ void* readPosition = ((char *)acc->buffer_view->buffer->data) + (index * acc->buffer_view->stride) + acc->buffer_view->offset + acc->offset;
+ memcpy(variable, readPosition, singleElementSize);
+ }
+
return true;
}
-// LoadGLTF loads in model data from given filename, supporting both .gltf and .glb
-static Model LoadGLTF(const char *fileName)
+static void* GLTFReadValuesAs(cgltf_accessor* acc, cgltf_component_type type, bool adjustOnDownCasting)
{
- /***********************************************************************************
-
- Function implemented by Wilhem Barbier(@wbrbr), with modifications by Tyler Bezera(@gamerfiend) and Hristo Stamenov(@object71)
-
- Features:
- - Supports .gltf and .glb files
- - Supports embedded (base64) or external textures
- - Loads all raylib supported material textures, values and colors
- - Supports multiple mesh per model and multiple primitives per model
-
- Some restrictions (not exhaustive):
- - Triangle-only meshes
- - Not supported node hierarchies or transforms
- - Only supports unsigned short indices (no byte/unsigned int)
- - Only supports float for texture coordinates (no byte/unsigned short)
-
- *************************************************************************************/
-
- Model model = { 0 };
-
- // glTF file loading
- unsigned int dataSize = 0;
- unsigned char *fileData = LoadFileData(fileName, &dataSize);
-
- if (fileData == NULL) return model;
-
- // glTF data loading
- cgltf_options options = { 0 };
- cgltf_data *data = NULL;
- cgltf_result result = cgltf_parse(&options, fileData, dataSize, &data);
-
- if (result == cgltf_result_success)
+ unsigned int count = acc->count;
+ unsigned int typeSize = 0;
+ switch(type) {
+ case cgltf_component_type_r_8u:
+ case cgltf_component_type_r_8: typeSize = 1; break;
+ case cgltf_component_type_r_16u:
+ case cgltf_component_type_r_16: typeSize = 2; break;
+ case cgltf_component_type_r_32f:
+ case cgltf_component_type_r_32u: typeSize = 4; break;
+ case cgltf_component_type_invalid: typeSize = 0; break;
+ }
+
+ unsigned int typeElements = 0;
+ switch(acc->type) {
+ case cgltf_type_scalar: typeElements = 1; break;
+ case cgltf_type_vec2: typeElements = 2; break;
+ case cgltf_type_vec3: typeElements = 3; break;
+ case cgltf_type_vec4:
+ case cgltf_type_mat2: typeElements = 4; break;
+ case cgltf_type_mat3: typeElements = 9; break;
+ case cgltf_type_mat4: typeElements = 16; break;
+ case cgltf_type_invalid: typeElements = 0; break;
+ }
+
+ if(acc->component_type == type)
{
- TRACELOG(LOG_INFO, "MODEL: [%s] glTF meshes (%s) count: %i", fileName, (data->file_type == 2)? "glb" : "gltf", data->meshes_count);
- TRACELOG(LOG_INFO, "MODEL: [%s] glTF materials (%s) count: %i", fileName, (data->file_type == 2)? "glb" : "gltf", data->materials_count);
-
- // Read data buffers
- result = cgltf_load_buffers(&options, data, fileName);
- if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load mesh/material buffers", fileName);
-
- int primitivesCount = 0;
-
- for (unsigned int i = 0; i < data->meshes_count; i++)
- primitivesCount += (int)data->meshes[i].primitives_count;
-
- // Process glTF data and map to model
- model.meshCount = primitivesCount;
- model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh));
- model.materialCount = (int)data->materials_count + 1;
- model.materials = RL_MALLOC(model.materialCount*sizeof(Material));
- model.meshMaterial = RL_MALLOC(model.meshCount*sizeof(int));
- model.boneCount = (int)data->nodes_count;
- model.bones = RL_CALLOC(model.boneCount, sizeof(BoneInfo));
- model.bindPose = RL_CALLOC(model.boneCount, sizeof(Transform));
-
- InitGLTFBones(&model, data);
- LoadGLTFMaterial(&model, fileName, data);
-
- int primitiveIndex = 0;
-
- for (unsigned int i = 0; i < data->meshes_count; i++)
+ void* array = RL_MALLOC(count * typeElements * typeSize);
+
+ for(unsigned int i = 0; i < count; i++)
+ {
+ GLTFReadValue(acc, i, (char*)array + i * typeElements * typeSize);
+ }
+
+ return array;
+
+ } else {
+
+ unsigned int accTypeSize = 0;
+ switch(acc->component_type) {
+ case cgltf_component_type_r_8u:
+ case cgltf_component_type_r_8: accTypeSize = 1; break;
+ case cgltf_component_type_r_16u:
+ case cgltf_component_type_r_16: accTypeSize = 2; break;
+ case cgltf_component_type_r_32f:
+ case cgltf_component_type_r_32u: accTypeSize = 4; break;
+ case cgltf_component_type_invalid: accTypeSize = 0; break;
+ }
+
+ void* array = RL_MALLOC(count * typeElements * typeSize);
+ void* additionalArray = RL_MALLOC(count * typeElements * accTypeSize);
+
+ for(unsigned int i = 0; i < count; i++)
{
- for (unsigned int p = 0; p < data->meshes[i].primitives_count; p++)
+ GLTFReadValue(acc, i, (char*)additionalArray + i * typeElements * accTypeSize);
+ }
+
+ switch(acc->component_type) {
+ case cgltf_component_type_r_8u:
{
- 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)
+ unsigned char* typedAdditionalArray = (unsigned char*)additionalArray;
+ switch(type) {
+ case cgltf_component_type_r_8:
{
- 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)
+ char* typedArray = (char*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
{
- for (unsigned int a = 0; a < acc->count; a++)
+ if(adjustOnDownCasting)
{
- GLTFReadValue(acc, a, model.meshes[primitiveIndex].vertices + (a*3), 3, sizeof(float));
+ typedArray[i] = (char)(typedAdditionalArray[i] / (UCHAR_MAX / CHAR_MAX));
}
- }
- else if (acc->component_type == cgltf_component_type_r_32u)
- {
- int readValue[3];
- for (unsigned int a = 0; a < acc->count; a++)
+ else
{
- 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];
+ typedArray[i] = (char)typedAdditionalArray[i];
}
}
- else
+ break;
+ }
+ case cgltf_component_type_r_16u:
+ {
+ unsigned short* typedArray = (unsigned short*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
{
- // TODO: Support normalized unsigned byte/unsigned short vertices
- TRACELOG(LOG_WARNING, "MODEL: [%s] glTF vertices must be float or int", fileName);
+ typedArray[i] = (unsigned short)typedAdditionalArray[i];
}
-
- memcpy(model.meshes[primitiveIndex].animVertices, model.meshes[primitiveIndex].vertices, bufferSize);
+ break;
}
- else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_normal)
+ case cgltf_component_type_r_16:
{
- 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)
+ short* typedArray = (short*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
{
- for (unsigned int a = 0; a < acc->count; a++)
- {
- GLTFReadValue(acc, a, model.meshes[primitiveIndex].normals + (a*3), 3, sizeof(float));
- }
+ typedArray[i] = (short)typedAdditionalArray[i];
+ }
+ break;
+ }
+ case cgltf_component_type_r_32f:
+ {
+ float* typedArray = (float*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ typedArray[i] = (float)typedAdditionalArray[i];
+ }
+ break;
+ }
+ case cgltf_component_type_r_32u:
+ {
+ unsigned int* typedArray = (unsigned int*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ typedArray[i] = (unsigned int)typedAdditionalArray[i];
}
- else if (acc->component_type == cgltf_component_type_r_32u)
+ break;
+ }
+ default: {
+ RL_FREE(array);
+ RL_FREE(additionalArray);
+ return NULL;
+ }
+ }
+ break;
+ }
+ case cgltf_component_type_r_8:
+ {
+ char* typedAdditionalArray = (char*)additionalArray;
+ switch(type) {
+ case cgltf_component_type_r_8u:
+ {
+ unsigned char* typedArray = (unsigned char*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
{
- int readValue[3];
- for (unsigned int a = 0; a < acc->count; a++)
+ typedArray[i] = (unsigned char)typedAdditionalArray[i];
+ }
+ break;
+ }
+ case cgltf_component_type_r_16u:
+ {
+ unsigned short* typedArray = (unsigned short*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ typedArray[i] = (unsigned short)typedAdditionalArray[i];
+ }
+ break;
+ }
+ case cgltf_component_type_r_16:
+ {
+ short* typedArray = (short*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ typedArray[i] = (short)typedAdditionalArray[i];
+ }
+ break;
+ }
+ case cgltf_component_type_r_32f:
+ {
+ float* typedArray = (float*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ typedArray[i] = (float)typedAdditionalArray[i];
+ }
+ break;
+ }
+ case cgltf_component_type_r_32u:
+ {
+ unsigned int* typedArray = (unsigned int*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ typedArray[i] = (unsigned int)typedAdditionalArray[i];
+ }
+ break;
+ }
+ default: {
+ RL_FREE(array);
+ RL_FREE(additionalArray);
+ return NULL;
+ }
+ }
+ break;
+ }
+ case cgltf_component_type_r_16u:
+ {
+ unsigned short* typedAdditionalArray = (unsigned short*)additionalArray;
+ switch(type) {
+ case cgltf_component_type_r_8u:
+ {
+ unsigned char* typedArray = (unsigned char*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ if(adjustOnDownCasting)
+ {
+ typedArray[i] = (unsigned char)(typedAdditionalArray[i] / (USHRT_MAX / UCHAR_MAX));
+ }
+ else
{
- 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];
+ typedArray[i] = (unsigned char)typedAdditionalArray[i];
}
}
- else
+ break;
+ }
+ case cgltf_component_type_r_8:
+ {
+ char* typedArray = (char*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
{
- // TODO: Support normalized unsigned byte/unsigned short normals
- TRACELOG(LOG_WARNING, "MODEL: [%s] glTF normals must be float or int", fileName);
+ if(adjustOnDownCasting)
+ {
+ typedArray[i] = (char)(typedAdditionalArray[i] / (USHRT_MAX / CHAR_MAX));
+ }
+ else
+ {
+ typedArray[i] = (char)typedAdditionalArray[i];
+ }
}
-
- memcpy(model.meshes[primitiveIndex].animNormals, model.meshes[primitiveIndex].normals, bufferSize);
+ break;
}
- else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_texcoord)
+ case cgltf_component_type_r_16:
{
- cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
-
- if (acc->component_type == cgltf_component_type_r_32f)
+ short* typedArray = (short*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
{
- model.meshes[primitiveIndex].texcoords = RL_MALLOC(acc->count*2*sizeof(float));
-
- for (unsigned int a = 0; a < acc->count; a++)
+ if(adjustOnDownCasting)
+ {
+ typedArray[i] = (short)(typedAdditionalArray[i] / (USHRT_MAX / SHRT_MAX));
+ }
+ else
{
- GLTFReadValue(acc, a, model.meshes[primitiveIndex].texcoords + (a*2), 2, sizeof(float));
+ typedArray[i] = (short)typedAdditionalArray[i];
}
}
- else
+ break;
+ }
+ case cgltf_component_type_r_32f:
+ {
+ float* typedArray = (float*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
{
- // TODO: Support normalized unsigned byte/unsigned short texture coordinates
- TRACELOG(LOG_WARNING, "MODEL: [%s] glTF texture coordinates must be float", fileName);
+ typedArray[i] = (float)typedAdditionalArray[i];
}
+ break;
}
- else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_joints)
+ case cgltf_component_type_r_32u:
{
- cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
- LoadGLTFBoneAttribute(&model, acc, data, primitiveIndex);
+ unsigned int* typedArray = (unsigned int*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ typedArray[i] = (unsigned int)typedAdditionalArray[i];
+ }
+ break;
}
- else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_weights)
+ default: {
+ RL_FREE(array);
+ RL_FREE(additionalArray);
+ return NULL;
+ }
+ }
+ break;
+ }
+ case cgltf_component_type_r_16:
+ {
+ short* typedAdditionalArray = (short*)additionalArray;
+ switch(type) {
+ case cgltf_component_type_r_8u:
{
- 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)
+ unsigned char* typedArray = (unsigned char*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
{
- for (unsigned int a = 0; a < acc->count; a++)
+ if(adjustOnDownCasting)
+ {
+ typedArray[i] = (unsigned char)(typedAdditionalArray[i] / (SHRT_MAX / UCHAR_MAX));
+ }
+ else
{
- GLTFReadValue(acc, a, model.meshes[primitiveIndex].boneWeights + (a*4), 4, sizeof(float));
+ typedArray[i] = (unsigned char)typedAdditionalArray[i];
}
}
- else if (acc->component_type == cgltf_component_type_r_32u)
+ break;
+ }
+ case cgltf_component_type_r_8:
+ {
+ char* typedArray = (char*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
{
- unsigned int readValue[4];
- for (unsigned int a = 0; a < acc->count; a++)
+ if(adjustOnDownCasting)
{
- 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];
+ typedArray[i] = (char)(typedAdditionalArray[i] / (SHRT_MAX / CHAR_MAX));
+ }
+ else
+ {
+ typedArray[i] = (char)typedAdditionalArray[i];
}
}
- else
+ break;
+ }
+ case cgltf_component_type_r_16u:
+ {
+ unsigned short* typedArray = (unsigned short*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
{
- // TODO: Support normalized unsigned byte/unsigned short weights
- TRACELOG(LOG_WARNING, "MODEL: [%s] glTF normals must be float or int", fileName);
+ typedArray[i] = (unsigned short)typedAdditionalArray[i];
}
+ break;
}
- else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_color)
+ case cgltf_component_type_r_32f:
{
- 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)
+ float* typedArray = (float*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ typedArray[i] = (float)typedAdditionalArray[i];
+ }
+ break;
+ }
+ case cgltf_component_type_r_32u:
+ {
+ unsigned int* typedArray = (unsigned int*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ typedArray[i] = (unsigned int)typedAdditionalArray[i];
+ }
+ break;
+ }
+ default: {
+ RL_FREE(array);
+ RL_FREE(additionalArray);
+ return NULL;
+ }
+ }
+ break;
+ }
+ case cgltf_component_type_r_32f:
+ {
+ float* typedAdditionalArray = (float*)additionalArray;
+ switch(type) {
+ case cgltf_component_type_r_8u:
+ {
+ unsigned char* typedArray = (unsigned char*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ typedArray[i] = (unsigned char)typedAdditionalArray[i];
+ }
+ break;
+ }
+ case cgltf_component_type_r_8:
+ {
+ char* typedArray = (char*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
{
- for (int a = 0; a < acc->count; a++)
+ typedArray[i] = (char)typedAdditionalArray[i];
+ }
+ break;
+ }
+ case cgltf_component_type_r_16u:
+ {
+ unsigned short* typedArray = (unsigned short*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ typedArray[i] = (unsigned short)typedAdditionalArray[i];
+ }
+ break;
+ }
+ case cgltf_component_type_r_16:
+ {
+ short* typedArray = (short*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ typedArray[i] = (short)typedAdditionalArray[i];
+ }
+ break;
+ }
+ case cgltf_component_type_r_32u:
+ {
+ unsigned int* typedArray = (unsigned int*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ typedArray[i] = (unsigned int)typedAdditionalArray[i];
+ }
+ break;
+ }
+ default: {
+ RL_FREE(array);
+ RL_FREE(additionalArray);
+ return NULL;
+ }
+ }
+ break;
+ }
+ case cgltf_component_type_r_32u:
+ {
+ unsigned int* typedAdditionalArray = (unsigned int*)additionalArray;
+ switch(type) {
+ case cgltf_component_type_r_8u:
+ {
+ unsigned char* typedArray = (unsigned char*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ if(adjustOnDownCasting)
+ {
+ typedArray[i] = (unsigned char)(typedAdditionalArray[i] / (UINT_MAX / UCHAR_MAX));
+ }
+ else
{
- GLTFReadValue(acc, a, model.meshes[primitiveIndex].colors + (a*4), 4, sizeof(unsigned char));
+ typedArray[i] = (unsigned char)typedAdditionalArray[i];
}
}
- if (acc->component_type == cgltf_component_type_r_16u)
+ break;
+ }
+ case cgltf_component_type_r_8:
+ {
+ char* typedArray = (char*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
{
- TRACELOG(LOG_WARNING, "MODEL: [%s] converting glTF colors to unsigned char", fileName);
- for (int a = 0; a < acc->count; a++)
+ if(adjustOnDownCasting)
{
- 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);
- }
+ typedArray[i] = (char)(typedAdditionalArray[i] / (UINT_MAX / CHAR_MAX));
+ }
+ else
+ {
+ typedArray[i] = (char)typedAdditionalArray[i];
+ }
+ }
+ break;
+ }
+ case cgltf_component_type_r_16u:
+ {
+ unsigned short* typedArray = (unsigned short*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ if(adjustOnDownCasting)
+ {
+ typedArray[i] = (unsigned short)(typedAdditionalArray[i] / (UINT_MAX / USHRT_MAX));
+ }
+ else
+ {
+ typedArray[i] = (unsigned short)typedAdditionalArray[i];
+ }
+ }
+ break;
+ }
+ case cgltf_component_type_r_16:
+ {
+ short* typedArray = (short*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
+ {
+ if(adjustOnDownCasting)
+ {
+ typedArray[i] = (short)(typedAdditionalArray[i] / (UINT_MAX / SHRT_MAX));
+ }
+ else
+ {
+ typedArray[i] = (short)typedAdditionalArray[i];
}
}
- else
+ break;
+ }
+ case cgltf_component_type_r_32f:
+ {
+ float* typedArray = (float*) array;
+ for (unsigned int i = 0; i < count * typeElements; i++)
{
- TRACELOG(LOG_WARNING, "MODEL: [%s] glTF colors must be uchar or ushort", fileName);
+ typedArray[i] = (float)typedAdditionalArray[i];
}
+ break;
+ }
+ default: {
+ RL_FREE(array);
+ RL_FREE(additionalArray);
+ return NULL;
}
}
+ break;
+ }
+ default: {
+ RL_FREE(array);
+ RL_FREE(additionalArray);
+ return NULL;
+ }
+ }
+
+ RL_FREE(additionalArray);
+ return array;
+ }
+}
- cgltf_accessor *acc = data->meshes[i].primitives[p].indices;
- LoadGLTFModelIndices(&model, acc, primitiveIndex);
+// LoadGLTF loads in model data from given filename, supporting both .gltf and .glb
+static Model LoadGLTF(const char *fileName)
+{
+ /***********************************************************************************
- 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;
- }
+ Function implemented by Wilhem Barbier(@wbrbr), with modifications by Tyler Bezera(@gamerfiend) and Hristo Stamenov(@object71)
- BindGLTFPrimitiveToBones(&model, data, primitiveIndex);
+ Features:
+ - Supports .gltf and .glb files
+ - Supports embedded (base64) or external textures
+ - Loads all raylib supported material textures, values and colors
+ - Supports multiple mesh per model and multiple primitives per model
- primitiveIndex++;
- }
+ Some restrictions (not exhaustive):
+ - Triangle-only meshes
+ - Not supported node hierarchies or transforms
+ - Only supports unsigned short indices (no byte/unsigned int)
+ - Only supports float for texture coordinates (no byte/unsigned short)
+
+ *************************************************************************************/
+
+ Model model = { 0 };
+
+ // glTF file loading
+ unsigned int dataSize = 0;
+ unsigned char *fileData = LoadFileData(fileName, &dataSize);
+
+ if (fileData == NULL) return model;
+ // glTF data loading
+ cgltf_options options = { 0 };
+ cgltf_data *data = NULL;
+ cgltf_result result = cgltf_parse(&options, fileData, dataSize, &data);
+
+ if (result == cgltf_result_success)
+ {
+ TRACELOG(LOG_INFO, "MODEL: [%s] glTF meshes (%s) count: %i", fileName, (data->file_type == 2)? "glb" : "gltf", data->meshes_count);
+ TRACELOG(LOG_INFO, "MODEL: [%s] glTF materials (%s) count: %i", fileName, (data->file_type == 2)? "glb" : "gltf", data->materials_count);
+
+ // Read data buffers
+ 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(data->scenes_count > 1) TRACELOG(LOG_INFO, "MODEL: [%s] Has multiple scenes but only the first one will be loaded", fileName);
+
+ int primitivesCount = 0;
+ for (unsigned int i = 0; i < data->scene->nodes_count; i++)
+ {
+ GetGLTFPrimitiveCount(data->scene->nodes[i], &primitivesCount);
+ }
+
+ // Process glTF data and map to model
+ model.meshCount = primitivesCount;
+ model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh));
+ model.materialCount = (int)data->materials_count + 1;
+ model.materials = RL_MALLOC(model.materialCount*sizeof(Material));
+ model.meshMaterial = RL_MALLOC(model.meshCount*sizeof(int));
+ model.boneCount = (int)data->nodes_count;
+ model.bones = RL_CALLOC(model.boneCount, sizeof(BoneInfo));
+ model.bindPose = RL_CALLOC(model.boneCount, sizeof(Transform));
+
+ InitGLTFBones(&model, data);
+ LoadGLTFMaterial(&model, fileName, data);
+
+ int primitiveIndex = 0;
+ for(unsigned int i = 0; i < data->scene->nodes_count; i++)
+ {
+ Matrix staticTransform = MatrixIdentity();
+ LoadGLTFNode(data, data->scene->nodes[i], &model, staticTransform, &primitiveIndex, fileName);
}
cgltf_free(data);
@@ -4540,79 +4890,20 @@ static void LoadGLTFMaterial(Model *model, const char *fileName, const cgltf_dat
model->materials[model->materialCount - 1] = LoadMaterialDefault();
}
-static void LoadGLTFBoneAttribute(Model *model, cgltf_accessor *jointsAccessor, const cgltf_data *data, int primitiveIndex)
-{
- if (jointsAccessor->component_type == cgltf_component_type_r_16u)
- {
- model->meshes[primitiveIndex].boneIds = RL_MALLOC(jointsAccessor->count*4*sizeof(int));
- short* bones = RL_MALLOC(jointsAccessor->count*4*sizeof(short));
-
- for (unsigned int a = 0; a < jointsAccessor->count; a++)
- {
- GLTFReadValue(jointsAccessor, a, bones + (a*4), 4, sizeof(short));
- }
-
- for (unsigned int a = 0; a < jointsAccessor->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 (jointsAccessor->component_type == cgltf_component_type_r_8u)
- {
- model->meshes[primitiveIndex].boneIds = RL_MALLOC(jointsAccessor->count*4*sizeof(int));
- unsigned char *bones = RL_MALLOC(jointsAccessor->count*4*sizeof(unsigned char));
-
- for (unsigned int a = 0; a < jointsAccessor->count; a++)
- {
- GLTFReadValue(jointsAccessor, a, bones + (a*4), 4, sizeof(unsigned char));
- }
-
- for (unsigned int a = 0; a < jointsAccessor->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
- {
- // TODO: Support other size of bone index?
- TRACELOG(LOG_WARNING, "MODEL: glTF bones in unexpected format");
- }
-}
-
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].boneWeights = RL_CALLOC(4*model->meshes[primitiveIndex].vertexCount, sizeof(float));
+ 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));
- for (int b = 0; b < 4*model->meshes[primitiveIndex].vertexCount; b++)
+ for (int b = 0; b < 4 * model->meshes[primitiveIndex].vertexCount; b++)
{
- if (b%4 == 0)
+ if (b % 4 == 0)
{
model->meshes[primitiveIndex].boneIds[b] = nodeId;
model->meshes[primitiveIndex].boneWeights[b] = 1.0f;
@@ -4622,102 +4913,12 @@ static void BindGLTFPrimitiveToBones(Model* model, const cgltf_data* data, int p
model->meshes[primitiveIndex].boneIds[b] = 0;
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;
}
}
}
}
}
-static void LoadGLTFModelIndices(Model* model, cgltf_accessor* indexAccessor, int primitiveIndex)
-{
- if (indexAccessor)
- {
- if (indexAccessor->component_type == cgltf_component_type_r_16u || indexAccessor->component_type == cgltf_component_type_r_16)
- {
- model->meshes[primitiveIndex].triangleCount = (int)indexAccessor->count/3;
- model->meshes[primitiveIndex].indices = RL_MALLOC(model->meshes[primitiveIndex].triangleCount*3*sizeof(unsigned short));
-
- unsigned short readValue = 0;
- for (unsigned int a = 0; a < indexAccessor->count; a++)
- {
- GLTFReadValue(indexAccessor, a, &readValue, 1, sizeof(short));
- model->meshes[primitiveIndex].indices[a] = readValue;
- }
- }
- else if (indexAccessor->component_type == cgltf_component_type_r_8u || indexAccessor->component_type == cgltf_component_type_r_8)
- {
- model->meshes[primitiveIndex].triangleCount = (int)indexAccessor->count/3;
- model->meshes[primitiveIndex].indices = RL_MALLOC(model->meshes[primitiveIndex].triangleCount*3*sizeof(unsigned short));
-
- unsigned char readValue = 0;
- for (unsigned int a = 0; a < indexAccessor->count; a++)
- {
- GLTFReadValue(indexAccessor, a, &readValue, 1, sizeof(char));
- model->meshes[primitiveIndex].indices[a] = (unsigned short)readValue;
- }
- }
- else if (indexAccessor->component_type == cgltf_component_type_r_32u)
- {
- model->meshes[primitiveIndex].triangleCount = (int)indexAccessor->count/3;
- model->meshes[primitiveIndex].indices = RL_MALLOC(model->meshes[primitiveIndex].triangleCount*3*sizeof(unsigned short));
-
- unsigned int readValue;
- for (unsigned int a = 0; a < indexAccessor->count; a++)
- {
- GLTFReadValue(indexAccessor, a, &readValue, 1, sizeof(unsigned int));
- model->meshes[primitiveIndex].indices[a] = (unsigned short)readValue;
- }
- }
- }
- else
- {
- // Unindexed mesh
- model->meshes[primitiveIndex].triangleCount = model->meshes[primitiveIndex].vertexCount/3;
- }
-}
-
// LoadGLTF loads in animation data from given filename
static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCount)
{
@@ -4759,9 +4960,9 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCo
for (unsigned int a = 0; a < data->animations_count; a++)
{
// 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
- // - node - bone
+ // - node - animatable node
// - transformation type (path) - translation, rotation, scale
// - sampler - animation samples
// - input - points in time this transformation happens
@@ -4773,7 +4974,7 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCo
ModelAnimation *output = animations + a;
// 30 frames sampled per second
- const float timeStep = (1.0f/30.0f);
+ const float timeStep = (1.0f/60.0f);
float animationDuration = 0.0f;
// Getting the max animation time to consider for animation duration
@@ -4783,7 +4984,7 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCo
int frameCounts = (int)channel->sampler->input->count;
float lastFrameTime = 0.0f;
- if (GLTFReadValue(channel->sampler->input, frameCounts - 1, &lastFrameTime, 1, sizeof(float)))
+ if (GLTFReadValue(channel->sampler->input, frameCounts - 1, &lastFrameTime))
{
animationDuration = fmaxf(lastFrameTime, animationDuration);
}
@@ -4808,12 +5009,18 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCo
{
output->framePoses[frame] = RL_MALLOC(output->boneCount*sizeof(Transform));
- for (int i = 0; i < output->boneCount; i++)
+ for (unsigned int i = 0; i < output->boneCount; i++)
{
- output->framePoses[frame][i].translation = Vector3Zero();
- output->framePoses[frame][i].rotation = QuaternionIdentity();
+ if (data->nodes[i].has_translation) memcpy(&output->framePoses[frame][i].translation, data->nodes[i].translation, 3 * sizeof(float));
+ 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].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();
}
}
@@ -4839,7 +5046,7 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCo
for (unsigned int j = 0; j < sampler->input->count; j++)
{
float inputFrameTime;
- if (GLTFReadValue(sampler->input, j, &inputFrameTime, 1, sizeof(float)))
+ if (GLTFReadValue(sampler->input, j, &inputFrameTime))
{
if (frameTime < inputFrameTime)
{
@@ -4848,7 +5055,7 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCo
outputMax = j;
float previousInputTime = 0.0f;
- if (GLTFReadValue(sampler->input, outputMin, &previousInputTime, 1, sizeof(float)))
+ if (GLTFReadValue(sampler->input, outputMin, &previousInputTime))
{
if ((inputFrameTime - previousInputTime) != 0)
{
@@ -4864,38 +5071,72 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCo
// If the current transformation has no information for the current frame time point
if (shouldSkipFurtherTransformation) continue;
-
+
if (channel->target_path == cgltf_animation_path_type_translation)
{
Vector3 translationStart;
Vector3 translationEnd;
- bool success = GLTFReadValue(sampler->output, outputMin, &translationStart, 3, sizeof(float));
- success = GLTFReadValue(sampler->output, outputMax, &translationEnd, 3, sizeof(float)) || success;
+ float values[3];
+
+ bool success = GLTFReadValue(sampler->output, outputMin, values);
+
+ translationStart.x = values[0];
+ translationStart.y = values[1];
+ translationStart.z = values[2];
+
+ success = GLTFReadValue(sampler->output, outputMax, values) || success;
+
+ translationEnd.x = values[0];
+ translationEnd.y = values[1];
+ translationEnd.z = values[2];
if (success) output->framePoses[frame][boneId].translation = Vector3Lerp(translationStart, translationEnd, lerpPercent);
}
if (channel->target_path == cgltf_animation_path_type_rotation)
{
- Quaternion rotationStart;
- Quaternion rotationEnd;
-
- bool success = GLTFReadValue(sampler->output, outputMin, &rotationStart, 4, sizeof(float));
- success = GLTFReadValue(sampler->output, outputMax, &rotationEnd, 4, sizeof(float)) || success;
+ Vector4 rotationStart;
+ Vector4 rotationEnd;
+
+ float values[4];
+
+ bool success = GLTFReadValue(sampler->output, outputMin, &values);
+
+ rotationStart.x = values[0];
+ rotationStart.y = values[1];
+ rotationStart.z = values[2];
+ rotationStart.w = values[3];
+
+ success = GLTFReadValue(sampler->output, outputMax, &values) || success;
+
+ rotationEnd.x = values[0];
+ rotationEnd.y = values[1];
+ rotationEnd.z = values[2];
+ rotationEnd.w = values[3];
if (success)
{
- output->framePoses[frame][boneId].rotation = QuaternionLerp(rotationStart, rotationEnd, lerpPercent);
- output->framePoses[frame][boneId].rotation = QuaternionNormalize(output->framePoses[frame][boneId].rotation);
+ output->framePoses[frame][boneId].rotation = QuaternionNlerp(rotationStart, rotationEnd, lerpPercent);
}
}
if (channel->target_path == cgltf_animation_path_type_scale)
{
Vector3 scaleStart;
Vector3 scaleEnd;
-
- bool success = GLTFReadValue(sampler->output, outputMin, &scaleStart, 3, sizeof(float));
- success = GLTFReadValue(sampler->output, outputMax, &scaleEnd, 3, sizeof(float)) || success;
+
+ float values[3];
+
+ bool success = GLTFReadValue(sampler->output, outputMin, &values);
+
+ scaleStart.x = values[0];
+ scaleStart.y = values[1];
+ scaleStart.z = values[2];
+
+ success = GLTFReadValue(sampler->output, outputMax, &values) || success;
+
+ scaleEnd.x = values[0];
+ scaleEnd.y = values[1];
+ scaleEnd.z = values[2];
if (success) output->framePoses[frame][boneId].scale = Vector3Lerp(scaleStart, scaleEnd, lerpPercent);
}
@@ -4946,4 +5187,167 @@ static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCo
return animations;
}
+void LoadGLTFMesh(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)].animVertices = RL_MALLOC(bufferSize);
+
+ outModel->meshes[(*primitiveIndex)].vertices = GLTFReadValuesAs(acc, cgltf_component_type_r_32f, false);
+
+ // Transform using the nodes matrix attributes
+ 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)].animNormals = RL_MALLOC(bufferSize);
+
+ outModel->meshes[(*primitiveIndex)].normals = GLTFReadValuesAs(acc, cgltf_component_type_r_32f, false);
+
+ // Transform using the nodes matrix attributes
+ 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;
+ outModel->meshes[(*primitiveIndex)].texcoords = GLTFReadValuesAs(acc, cgltf_component_type_r_32f, false);
+ }
+ else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_joints)
+ {
+ cgltf_accessor *acc = mesh->primitives[p].attributes[j].data;
+ unsigned int boneCount = acc->count;
+ unsigned int totalBoneWeights = boneCount * 4;
+
+ outModel->meshes[(*primitiveIndex)].boneIds = RL_MALLOC(totalBoneWeights * sizeof(int));
+ short* bones = GLTFReadValuesAs(acc, cgltf_component_type_r_16, false);
+ for (unsigned int a = 0; a < totalBoneWeights; a++)
+ {
+ outModel->meshes[(*primitiveIndex)].boneIds[a] = 0;
+ 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;
+ }
+ }
+ }
+ RL_FREE(bones);
+ }
+ 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 = GLTFReadValuesAs(acc, cgltf_component_type_r_32f, false);
+ }
+ 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 = GLTFReadValuesAs(acc, cgltf_component_type_r_8u, true);
+ }
+ }
+
+ cgltf_accessor *acc = mesh->primitives[p].indices;
+ if (acc)
+ {
+ outModel->meshes[(*primitiveIndex)].triangleCount = acc->count/3;
+ outModel->meshes[(*primitiveIndex)].indices = GLTFReadValuesAs(acc, cgltf_component_type_r_16u, false);
+ }
+ else
+ {
+ // Unindexed mesh
+ outModel->meshes[(*primitiveIndex)].triangleCount = outModel->meshes[(*primitiveIndex)].vertexCount/3;
+ }
+
+ 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;
+ }
+}
+
+void LoadGLTFNode(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)
+ {
+ LoadGLTFMesh(data, node->mesh, outModel, currentTransform, primitiveIndex, fileName);
+ }
+
+ for(unsigned int i = 0; i < node->children_count; i++)
+ {
+ LoadGLTFNode(data, node->children[i], outModel, currentTransform, primitiveIndex, fileName);
+ }
+}
+
+static void GetGLTFPrimitiveCount(cgltf_node* node, int* outCount)
+{
+ if(node->mesh != NULL)
+ {
+ *outCount += node->mesh->primitives_count;
+ }
+
+ for(unsigned int i = 0; i < node->children_count; i++)
+ {
+ GetGLTFPrimitiveCount(node->children[i], outCount);
+ }
+}
+
#endif