summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorHristo Stamenov <[email protected]>2021-06-26 14:22:00 +0300
committerGitHub <[email protected]>2021-06-26 13:22:00 +0200
commit71700254b41c84184db42dcfb3162943cf7aab27 (patch)
tree37d164d421636a9ed2f540d937708e91bf8a3ced
parentaa86e9d39989edea646504b01323559de526e475 (diff)
downloadraylib-71700254b41c84184db42dcfb3162943cf7aab27.tar.gz
raylib-71700254b41c84184db42dcfb3162943cf7aab27.zip
Major revamp gltf (#1849)
* Added my own model and license to Raylib exclusively created by me and provided for use in the examples (and other projects if anyone decides to) * Use animation vertices on initial load if possible. * Added girl model to model example * 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. * Refactored the new names and structure of extracted functions. * Safer and easier read value. * Made reading easier for accessor->bufferView->buffer in GLTF. Now there is no need to check for supported types or anything. * Correct inclusion of limits.h in the cases of MSVC based compilers vs the world * Removed weird example file
-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