diff --git a/src/rmodels.c b/src/rmodels.c index 2042195f4..bb07412c3 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -132,16 +132,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned int #endif #if defined(SUPPORT_FILEFORMAT_GLTF) static Model LoadGLTF(const char *fileName); // Load GLTF mesh data -static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, unsigned int *animCount); // Load GLTF animation data -static void LoadGLTFMaterial(Model *model, const char *fileName, const cgltf_data *data); -static void LoadGLTFMesh(cgltf_data *data, cgltf_node *node, Model *outModel, Matrix currentTransform, int *primitiveIndex, const char *fileName); -static void LoadGLTFNode(cgltf_data *data, cgltf_node *node, Model *outModel, Matrix currentTransform, int *primitiveIndex, const char *fileName); -static void InitGLTFBones(Model *model, const cgltf_data *data); -static void BindGLTFPrimitiveToBones(Model *model, cgltf_node *node, const cgltf_data *data, int primitiveIndex); -static void GetGLTFPrimitiveCount(cgltf_node *node, int *outCount); -static bool ReadGLTFValue(cgltf_accessor *acc, unsigned int index, void *variable); -static void *ReadGLTFValuesAs(cgltf_accessor *acc, cgltf_component_type type, bool adjustOnDownCasting); -static Matrix GetNodeTransformationMatrix(cgltf_node *node, Matrix current); +//static ModelAnimation *LoadModelAnimationGLTF(const char *fileName, unsigned int *animCount); // Load GLTF animation data #endif #if defined(SUPPORT_FILEFORMAT_VOX) static Model LoadVOX(const char *filename); // Load VOX mesh data @@ -1812,7 +1803,7 @@ ModelAnimation *LoadModelAnimations(const char *fileName, unsigned int *animCoun if (IsFileExtension(fileName, ".iqm")) animations = LoadModelAnimationsIQM(fileName, animCount); #endif #if defined(SUPPORT_FILEFORMAT_GLTF) - if (IsFileExtension(fileName, ".gltf;.glb")) animations = LoadGLTFModelAnimations(fileName, animCount); + //if (IsFileExtension(fileName, ".gltf;.glb")) animations = LoadModelAnimationGLTF(fileName, animCount); #endif return animations; @@ -4503,573 +4494,6 @@ static ModelAnimation* LoadModelAnimationsIQM(const char *fileName, unsigned int #endif #if defined(SUPPORT_FILEFORMAT_GLTF) -// Encode data to Base64 string -char *EncodeBase64(const unsigned char *data, int inputLength, int *outputLength) -{ - static const unsigned char base64encodeTable[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' - }; - - static const int modTable[] = { 0, 2, 1 }; - - *outputLength = 4*((inputLength + 2)/3); - - char *encodedData = RL_MALLOC(*outputLength); - - if (encodedData == NULL) return NULL; - - for (int i = 0, j = 0; i < inputLength;) - { - unsigned int octetA = (i < inputLength)? (unsigned char)data[i++] : 0; - unsigned int octetB = (i < inputLength)? (unsigned char)data[i++] : 0; - unsigned int octetC = (i < inputLength)? (unsigned char)data[i++] : 0; - - unsigned int triple = (octetA << 0x10) + (octetB << 0x08) + octetC; - - encodedData[j++] = base64encodeTable[(triple >> 3*6) & 0x3F]; - encodedData[j++] = base64encodeTable[(triple >> 2*6) & 0x3F]; - encodedData[j++] = base64encodeTable[(triple >> 1*6) & 0x3F]; - encodedData[j++] = base64encodeTable[(triple >> 0*6) & 0x3F]; - } - - for (int i = 0; i < modTable[inputLength%3]; i++) encodedData[*outputLength - 1 - i] = '='; - - return encodedData; -} - -// Decode Base64 string data -static unsigned char *DecodeBase64(char *data, int *outputLength) -{ - static const unsigned char base64decodeTable[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 - }; - - // Get output size of Base64 input data - int outLength = 0; - for (int i = 0; data[4*i] != 0; i++) - { - if (data[4*i + 3] == '=') - { - if (data[4*i + 2] == '=') outLength += 1; - else outLength += 2; - } - else outLength += 3; - } - - // Allocate memory to store decoded Base64 data - unsigned char *decodedData = (unsigned char *)RL_MALLOC(outLength); - - for (int i = 0; i < outLength/3; i++) - { - unsigned char a = base64decodeTable[(int)data[4*i]]; - unsigned char b = base64decodeTable[(int)data[4*i + 1]]; - unsigned char c = base64decodeTable[(int)data[4*i + 2]]; - unsigned char d = base64decodeTable[(int)data[4*i + 3]]; - - decodedData[3*i] = (a << 2) | (b >> 4); - decodedData[3*i + 1] = (b << 4) | (c >> 2); - decodedData[3*i + 2] = (c << 6) | d; - } - - if (outLength%3 == 1) - { - int n = outLength/3; - unsigned char a = base64decodeTable[(int)data[4*n]]; - unsigned char b = base64decodeTable[(int)data[4*n + 1]]; - decodedData[outLength - 1] = (a << 2) | (b >> 4); - } - else if (outLength%3 == 2) - { - int n = outLength/3; - unsigned char a = base64decodeTable[(int)data[4*n]]; - unsigned char b = base64decodeTable[(int)data[4*n + 1]]; - unsigned char c = base64decodeTable[(int)data[4*n + 2]]; - decodedData[outLength - 2] = (a << 2) | (b >> 4); - decodedData[outLength - 1] = (b << 4) | (c >> 2); - } - - *outputLength = outLength; - return decodedData; -} - -// Load texture from cgltf_image -static Image LoadImageFromCgltfImage(cgltf_image *image, const char *texPath, Color tint) -{ - Image rimage = { 0 }; - - if (image->uri) - { - if ((strlen(image->uri) > 5) && - (image->uri[0] == 'd') && - (image->uri[1] == 'a') && - (image->uri[2] == 't') && - (image->uri[3] == 'a') && - (image->uri[4] == ':')) - { - // Data URI - // Format: data:<mediatype>;base64,<data> - - // Find the comma - int i = 0; - while ((image->uri[i] != ',') && (image->uri[i] != 0)) i++; - - if (image->uri[i] == 0) TRACELOG(LOG_WARNING, "IMAGE: glTF data URI is not a valid image"); - else - { - //cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data) - - int size = 0; - unsigned char *data = DecodeBase64(image->uri + i + 1, &size); // TODO: Use cgltf_load_buffer_base64() - - rimage = LoadImageFromMemory(".png", data, size); - RL_FREE(data); - - // TODO: Tint shouldn't be applied here! - ImageColorTint(&rimage, tint); - } - } - else - { - rimage = LoadImage(TextFormat("%s/%s", texPath, image->uri)); - - // TODO: Tint shouldn't be applied here! - ImageColorTint(&rimage, tint); - } - } - else if (image->buffer_view) - { - unsigned char *data = RL_MALLOC(image->buffer_view->size); - int n = (int)image->buffer_view->offset; - int stride = (int)image->buffer_view->stride ? (int)image->buffer_view->stride : 1; - - for (unsigned int i = 0; i < image->buffer_view->size; i++) - { - data[i] = ((unsigned char *)image->buffer_view->buffer->data)[n]; - n += stride; - } - - rimage = LoadImageFromMemory(".png", data, (int)image->buffer_view->size); - RL_FREE(data); - - // TODO: Tint shouldn't be applied here! - ImageColorTint(&rimage, tint); - } - else rimage = GenImageColor(1, 1, tint); - - return rimage; -} - -// -static bool ReadGLTFValue(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; - default: 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; - default: break; - } - - unsigned int singleElementSize = typeSize*typeElements; - - if (acc->count == 2) - { - if (index > 1) return false; - - memcpy(variable, index == 0 ? acc->min : acc->max, singleElementSize); - return true; - } - - memset(variable, 0, singleElementSize); - - if (acc->buffer_view == NULL || acc->buffer_view->buffer == NULL || acc->buffer_view->buffer->data == NULL) return false; - - 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; -} - -static void *ReadGLTFValuesAs(cgltf_accessor *acc, cgltf_component_type type, bool adjustOnDownCasting) -{ - 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; - default: 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; - default: break; - } - - if (acc->component_type == type) - { - void *array = RL_MALLOC(count*typeElements*typeSize); - - for (unsigned int i = 0; i < count; i++) ReadGLTFValue(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; - default: break; - } - - void *array = RL_MALLOC(count*typeElements*typeSize); - void *additionalArray = RL_MALLOC(count*typeElements*accTypeSize); - - for (unsigned int i = 0; i < count; i++) - { - ReadGLTFValue(acc, i, (char *)additionalArray + i*typeElements*accTypeSize); - } - - switch (acc->component_type) - { - case cgltf_component_type_r_8u: - { - unsigned char *typedAdditionalArray = (unsigned char *)additionalArray; - switch (type) - { - case cgltf_component_type_r_8: - { - char *typedArray = (char *)array; - for (unsigned int i = 0; i < count*typeElements; i++) - { - if (adjustOnDownCasting) typedArray[i] = (char)(typedAdditionalArray[i]/(UCHAR_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++) 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; - } - } 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++) 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; - } - } 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 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++) - { - if (adjustOnDownCasting) typedArray[i] = (char)(typedAdditionalArray[i]/(USHRT_MAX/CHAR_MAX)); - else typedArray[i] = (char)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]/(USHRT_MAX/SHRT_MAX)); - else 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; - } - } break; - case cgltf_component_type_r_16: - { - short *typedAdditionalArray = (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]/(SHRT_MAX/UCHAR_MAX)); - else 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++) - { - if (adjustOnDownCasting) typedArray[i] = (char)(typedAdditionalArray[i]/(SHRT_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++) typedArray[i] = (unsigned 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; - } - } 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++) 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; - } - } 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 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++) - { - if (adjustOnDownCasting) 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]; - } - } 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; - default: - { - RL_FREE(array); - RL_FREE(additionalArray); - return NULL; - } break; - } - } break; - default: - { - RL_FREE(array); - RL_FREE(additionalArray); - return NULL; - } break; - } - - RL_FREE(additionalArray); - return array; - } -} - // LoadGLTF loads in model data from given filename, supporting both .gltf and .glb static Model LoadGLTF(const char *fileName) { @@ -5118,7 +4542,7 @@ static Model LoadGLTF(const char *fileName) int primitiveCount = 0; for (unsigned int i = 0; i < gltfData->scene->nodes_count; i++) { - GetGLTFPrimitiveCount(gltfData->scene->nodes[i], &primitiveCount); // TODO: Recursive function, really needed? + //GetGLTFPrimitiveCount(gltfData->scene->nodes[i], &primitiveCount); // TODO: Recursive function, really needed? } // Process glTF data and map to model @@ -5131,14 +4555,14 @@ static Model LoadGLTF(const char *fileName) model.bones = RL_CALLOC(model.boneCount, sizeof(BoneInfo)); model.bindPose = RL_CALLOC(model.boneCount, sizeof(Transform)); - InitGLTFBones(&model, gltfData); - LoadGLTFMaterial(&model, fileName, gltfData); + //InitGLTFBones(&model, gltfData); + //LoadGLTFMaterial(&model, fileName, gltfData); int primitiveIndex = 0; for (unsigned int i = 0; i < gltfData->scene->nodes_count; i++) { Matrix staticTransform = MatrixIdentity(); - LoadGLTFNode(gltfData, gltfData->scene->nodes[i], &model, staticTransform, &primitiveIndex, fileName); // TODO: Recursive function, really needed? + //LoadGLTFNode(gltfData, gltfData->scene->nodes[i], &model, staticTransform, &primitiveIndex, fileName); // TODO: Recursive function, really needed? } cgltf_free(gltfData); @@ -5149,620 +4573,6 @@ static Model LoadGLTF(const char *fileName) return model; } - -static void InitGLTFBones(Model *model, const cgltf_data *data) -{ - for (unsigned int j = 0; j < data->nodes_count; j++) - { - strcpy(model->bones[j].name, data->nodes[j].name == 0 ? "ANIMJOINT" : data->nodes[j].name); - model->bones[j].parent = (data->nodes[j].parent != NULL) ? (int)(data->nodes[j].parent - data->nodes) : -1; - } - - for (unsigned int i = 0; i < data->nodes_count; i++) - { - if (data->nodes[i].has_translation) memcpy(&model->bindPose[i].translation, data->nodes[i].translation, 3*sizeof(float)); - else model->bindPose[i].translation = Vector3Zero(); - - if (data->nodes[i].has_rotation) memcpy(&model->bindPose[i].rotation, data->nodes[i].rotation, 4*sizeof(float)); - else model->bindPose[i].rotation = QuaternionIdentity(); - - model->bindPose[i].rotation = QuaternionNormalize(model->bindPose[i].rotation); - - if (data->nodes[i].has_scale) memcpy(&model->bindPose[i].scale, data->nodes[i].scale, 3*sizeof(float)); - else model->bindPose[i].scale = Vector3One(); - } - - { - bool *completedBones = RL_CALLOC(model->boneCount, sizeof(bool)); - int numberCompletedBones = 0; - - while (numberCompletedBones < model->boneCount) - { - for (int i = 0; i < model->boneCount; i++) - { - if (completedBones[i]) continue; - - if (model->bones[i].parent < 0) - { - completedBones[i] = true; - numberCompletedBones++; - continue; - } - - if (!completedBones[model->bones[i].parent]) continue; - - Transform* currentTransform = &model->bindPose[i]; - BoneInfo* currentBone = &model->bones[i]; - int root = currentBone->parent; - if (root >= model->boneCount) root = 0; - Transform* parentTransform = &model->bindPose[root]; - - currentTransform->rotation = QuaternionMultiply(parentTransform->rotation, currentTransform->rotation); - currentTransform->translation = Vector3RotateByQuaternion(currentTransform->translation, parentTransform->rotation); - currentTransform->translation = Vector3Add(currentTransform->translation, parentTransform->translation); - currentTransform->scale = Vector3Multiply(currentTransform->scale, parentTransform->scale); - completedBones[i] = true; - numberCompletedBones++; - } - } - - RL_FREE(completedBones); - } -} - -static void LoadGLTFMaterial(Model *model, const char *fileName, const cgltf_data *data) -{ - for (int i = 0; i < model->materialCount - 1; i++) - { - model->materials[i] = LoadMaterialDefault(); - Color tint = (Color){ 255, 255, 255, 255 }; - const char *texPath = GetDirectoryPath(fileName); - - // Ensure material follows raylib support for PBR (metallic/roughness flow) - if (data->materials[i].has_pbr_metallic_roughness) - { - tint.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255); - tint.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255); - tint.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255); - tint.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255); - - model->materials[i].maps[MATERIAL_MAP_ALBEDO].color = tint; - - if (data->materials[i].pbr_metallic_roughness.base_color_texture.texture) - { - Image albedo = LoadImageFromCgltfImage(data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image, texPath, tint); - model->materials[i].maps[MATERIAL_MAP_ALBEDO].texture = LoadTextureFromImage(albedo); - UnloadImage(albedo); - } - - tint = WHITE; // Set tint to white after it's been used by Albedo - - if (data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture) - { - Image metallicRoughness = LoadImageFromCgltfImage(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture->image, texPath, tint); - model->materials[i].maps[MATERIAL_MAP_ROUGHNESS].texture = LoadTextureFromImage(metallicRoughness); - - float roughness = data->materials[i].pbr_metallic_roughness.roughness_factor; - model->materials[i].maps[MATERIAL_MAP_ROUGHNESS].value = roughness; - - float metallic = data->materials[i].pbr_metallic_roughness.metallic_factor; - model->materials[i].maps[MATERIAL_MAP_METALNESS].value = metallic; - - UnloadImage(metallicRoughness); - } - - if (data->materials[i].normal_texture.texture) - { - Image normalImage = LoadImageFromCgltfImage(data->materials[i].normal_texture.texture->image, texPath, tint); - model->materials[i].maps[MATERIAL_MAP_NORMAL].texture = LoadTextureFromImage(normalImage); - UnloadImage(normalImage); - } - - if (data->materials[i].occlusion_texture.texture) - { - Image occulsionImage = LoadImageFromCgltfImage(data->materials[i].occlusion_texture.texture->image, texPath, tint); - model->materials[i].maps[MATERIAL_MAP_OCCLUSION].texture = LoadTextureFromImage(occulsionImage); - UnloadImage(occulsionImage); - } - - if (data->materials[i].emissive_texture.texture) - { - Image emissiveImage = LoadImageFromCgltfImage(data->materials[i].emissive_texture.texture->image, texPath, tint); - model->materials[i].maps[MATERIAL_MAP_EMISSION].texture = LoadTextureFromImage(emissiveImage); - tint.r = (unsigned char)(data->materials[i].emissive_factor[0]*255); - tint.g = (unsigned char)(data->materials[i].emissive_factor[1]*255); - tint.b = (unsigned char)(data->materials[i].emissive_factor[2]*255); - model->materials[i].maps[MATERIAL_MAP_EMISSION].color = tint; - UnloadImage(emissiveImage); - } - } - } - - model->materials[model->materialCount - 1] = LoadMaterialDefault(); -} - -static void BindGLTFPrimitiveToBones(Model *model, cgltf_node *node, const cgltf_data *data, int primitiveIndex) -{ - int nodeId = node - data->nodes; - - if (model->meshes[primitiveIndex].boneIds == NULL) - { - model->meshes[primitiveIndex].boneIds = RL_CALLOC(model->meshes[primitiveIndex].vertexCount*4, sizeof(int)); - model->meshes[primitiveIndex].boneWeights = RL_CALLOC(model->meshes[primitiveIndex].vertexCount*4, sizeof(float)); - - for (int b = 0; b < model->meshes[primitiveIndex].vertexCount*4; b++) - { - if (b%4 == 0) - { - model->meshes[primitiveIndex].boneIds[b] = nodeId; - model->meshes[primitiveIndex].boneWeights[b] = 1.0f; - } - else - { - model->meshes[primitiveIndex].boneIds[b] = 0; - model->meshes[primitiveIndex].boneWeights[b] = 0.0f; - } - } - } -} - -// LoadGLTF loads in animation data from given filename -static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, unsigned int *animCount) -{ - /*********************************************************************************** - - Function implemented by Hristo Stamenov (@object71) - - Features: - - Supports .gltf and .glb files - - Some restrictions (not exhaustive): - - ... - - *************************************************************************************/ - - // glTF file loading - unsigned int dataSize = 0; - unsigned char *fileData = LoadFileData(fileName, &dataSize); - - ModelAnimation *animations = NULL; - - if (fileData == NULL) return animations; - - // 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 animations (%s) count: %i", fileName, (data->file_type == 2)? "glb" : - "gltf", data->animations_count); - - result = cgltf_load_buffers(&options, data, fileName); - if (result != cgltf_result_success) TRACELOG(LOG_WARNING, "MODEL: [%s] unable to load glTF animations data", fileName); - animations = RL_MALLOC(data->animations_count*sizeof(ModelAnimation)); - *animCount = (unsigned int)data->animations_count; - - for (unsigned int a = 0; a < data->animations_count; a++) - { - // gltf animation consists of the following structures: - // - nodes - bones are part of the node system (the whole node system is animatable) - // - channels - single transformation type on a single bone - // - node - animatable node - // - transformation type (path) - translation, rotation, scale - // - sampler - animation samples - // - input - points in time this transformation happens - // - output - the transformation amount at the given input points in time - // - interpolation - the type of interpolation to use between the frames - - cgltf_animation *animation = data->animations + a; - - ModelAnimation *output = animations + a; - - // 30 frames sampled per second - const float timeStep = (1.0f/60.0f); - float animationDuration = 0.0f; - - // Getting the max animation time to consider for animation duration - for (unsigned int i = 0; i < animation->channels_count; i++) - { - cgltf_animation_channel *channel = animation->channels + i; - int frameCounts = (int)channel->sampler->input->count; - float lastFrameTime = 0.0f; - - if (ReadGLTFValue(channel->sampler->input, frameCounts - 1, &lastFrameTime)) - { - animationDuration = fmaxf(lastFrameTime, animationDuration); - } - } - - output->frameCount = (int)(animationDuration/timeStep); - output->boneCount = (int)data->nodes_count; - output->bones = RL_MALLOC(output->boneCount*sizeof(BoneInfo)); - output->framePoses = RL_MALLOC(output->frameCount*sizeof(Transform *)); - // output->framerate = // TODO: Use framerate instead of const timestep - - // Name and parent bones - for (int j = 0; j < output->boneCount; j++) - { - strcpy(output->bones[j].name, data->nodes[j].name == 0 ? "ANIMJOINT" : data->nodes[j].name); - output->bones[j].parent = (data->nodes[j].parent != NULL) ? (int)(data->nodes[j].parent - data->nodes) : -1; - } - - // Allocate data for frames - // Initiate with zero bone translations - for (int frame = 0; frame < output->frameCount; frame++) - { - output->framePoses[frame] = RL_MALLOC(output->boneCount*sizeof(Transform)); - - for (int i = 0; i < output->boneCount; i++) - { - 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); - - 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(); - } - } - - // for each single transformation type on single bone - for (unsigned int channelId = 0; channelId < animation->channels_count; channelId++) - { - cgltf_animation_channel *channel = animation->channels + channelId; - cgltf_animation_sampler *sampler = channel->sampler; - - int boneId = (int)(channel->target_node - data->nodes); - - for (int frame = 0; frame < output->frameCount; frame++) - { - bool shouldSkipFurtherTransformation = true; - int outputMin = 0; - int outputMax = 0; - float frameTime = frame*timeStep; - float lerpPercent = 0.0f; - - // For this transformation: - // getting between which input values the current frame time position - // and also what is the percent to use in the linear interpolation later - for (unsigned int j = 0; j < sampler->input->count; j++) - { - float inputFrameTime; - if (ReadGLTFValue(sampler->input, j, &inputFrameTime)) - { - if (frameTime < inputFrameTime) - { - shouldSkipFurtherTransformation = false; - outputMin = (j == 0) ? 0 : j - 1; - outputMax = j; - - float previousInputTime = 0.0f; - if (ReadGLTFValue(sampler->input, outputMin, &previousInputTime)) - { - if ((inputFrameTime - previousInputTime) != 0) - { - lerpPercent = (frameTime - previousInputTime)/(inputFrameTime - previousInputTime); - } - } - - break; - } - } - else break; - } - - // 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; - - float values[3]; - - bool success = ReadGLTFValue(sampler->output, outputMin, values); - - translationStart.x = values[0]; - translationStart.y = values[1]; - translationStart.z = values[2]; - - success = ReadGLTFValue(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) - { - Vector4 rotationStart; - Vector4 rotationEnd; - - float values[4]; - - bool success = ReadGLTFValue(sampler->output, outputMin, &values); - - rotationStart.x = values[0]; - rotationStart.y = values[1]; - rotationStart.z = values[2]; - rotationStart.w = values[3]; - - success = ReadGLTFValue(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 = QuaternionNlerp(rotationStart, rotationEnd, lerpPercent); - } - } - if (channel->target_path == cgltf_animation_path_type_scale) - { - Vector3 scaleStart; - Vector3 scaleEnd; - - float values[3]; - - bool success = ReadGLTFValue(sampler->output, outputMin, &values); - - scaleStart.x = values[0]; - scaleStart.y = values[1]; - scaleStart.z = values[2]; - - success = ReadGLTFValue(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); - } - } - } - } - - // Build frameposes - for (int frame = 0; frame < output->frameCount; frame++) - { - bool *completedBones = RL_CALLOC(output->boneCount, sizeof(bool)); - int numberCompletedBones = 0; - - while (numberCompletedBones < output->boneCount) - { - for (int i = 0; i < output->boneCount; i++) - { - if (completedBones[i]) - { - continue; - } - - if (output->bones[i].parent < 0) - { - completedBones[i] = true; - numberCompletedBones++; - continue; - } - - if (!completedBones[output->bones[i].parent]) - { - continue; - } - - output->framePoses[frame][i].rotation = QuaternionMultiply(output->framePoses[frame][output->bones[i].parent].rotation, output->framePoses[frame][i].rotation); - output->framePoses[frame][i].translation = Vector3RotateByQuaternion(output->framePoses[frame][i].translation, output->framePoses[frame][output->bones[i].parent].rotation); - output->framePoses[frame][i].translation = Vector3Add(output->framePoses[frame][i].translation, output->framePoses[frame][output->bones[i].parent].translation); - output->framePoses[frame][i].scale = Vector3Multiply(output->framePoses[frame][i].scale, output->framePoses[frame][output->bones[i].parent].scale); - completedBones[i] = true; - numberCompletedBones++; - } - } - RL_FREE(completedBones); - } - } - - cgltf_free(data); - } - else TRACELOG(LOG_WARNING, ": [%s] Failed to load glTF data", fileName); - - RL_FREE(fileData); - - return animations; -} - -void LoadGLTFMesh(cgltf_data *data, cgltf_node *node, Model *outModel, Matrix currentTransform, int *primitiveIndex, const char *fileName) -{ - cgltf_mesh *mesh = node->mesh; - - for (unsigned int p = 0; p < mesh->primitives_count; p++) - { - for (unsigned int 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 = ReadGLTFValuesAs(acc, cgltf_component_type_r_32f, false); - - // Transform using the nodes matrix attributes - for (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 = ReadGLTFValuesAs(acc, cgltf_component_type_r_32f, false); - - // Transform using the nodes matrix attributes - for (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 = ReadGLTFValuesAs(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 = ReadGLTFValuesAs(acc, cgltf_component_type_r_16, false); - - // Find skin joint - for (unsigned int a = 0; a < totalBoneWeights; a++) - { - outModel->meshes[(*primitiveIndex)].boneIds[a] = 0; - if (bones[a] < 0) continue; - cgltf_node* skinJoint = node->skin->joints[bones[a]]; - unsigned int skinJointId = skinJoint - data->nodes; - outModel->meshes[(*primitiveIndex)].boneIds[a] = skinJointId; - } - - 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 = ReadGLTFValuesAs(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 = ReadGLTFValuesAs(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 = ReadGLTFValuesAs(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, node, data, *primitiveIndex); - - (*primitiveIndex) = (*primitiveIndex) + 1; - } -} - -static Matrix GetNodeTransformationMatrix(cgltf_node *node, Matrix current) -{ - if (node->has_matrix) - { - 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] }; - current= MatrixMultiply(nodeTransform, current); - } - if (node->has_translation) - { - Matrix tl = MatrixTranslate(node->translation[0],node->translation[1],node->translation[2]); - current = MatrixMultiply(tl, current); - } - if (node->has_rotation) - { - Matrix rot = QuaternionToMatrix((Quaternion){node->rotation[0],node->rotation[1],node->rotation[2],node->rotation[3]}); - current = MatrixMultiply(rot, current); - } - if (node->has_scale) - { - Matrix scale = MatrixScale(node->scale[0],node->scale[1],node->scale[2]); - current = MatrixMultiply(scale, current); - } - return current; -} - -void LoadGLTFNode(cgltf_data *data, cgltf_node *node, Model *outModel, Matrix currentTransform, int *primitiveIndex, const char *fileName) -{ - // Apply the transforms if they exist (Will still be applied even if no mesh is present to support emptys and bone structures) - Matrix localTransform = GetNodeTransformationMatrix(node, MatrixIdentity()); - currentTransform = MatrixMultiply(localTransform, currentTransform); - // Load mesh if it exists - if (node->mesh != NULL) - { - // Check if skinning is enabled and load Mesh accordingly - Matrix vertexTransform = currentTransform; - if ((node->skin != NULL) && (node->parent != NULL)) - { - vertexTransform = localTransform; - TRACELOG(LOG_WARNING,"MODEL: GLTF Node %s is skinned but not root node! Parent transformations will be ignored (NODE_SKINNED_MESH_NON_ROOT)",node->name); - } - LoadGLTFMesh(data, node, outModel, vertexTransform, primitiveIndex, fileName); - } - for (unsigned int i = 0; i < node->children_count; i++) LoadGLTFNode(data, node->children[i], outModel, currentTransform, primitiveIndex, fileName); -} - -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 #if defined(SUPPORT_FILEFORMAT_VOX)