From b52d99f56ab531dc2d2800b1c0ab40e97df01a5b Mon Sep 17 00:00:00 2001 From: Roy Qu Date: Fri, 21 Jan 2022 18:18:37 +0800 Subject: [PATCH] - add primitive glTF scene support - fix: glTF material color not correctly loaded --- src/external/cgltf.h | 9 ++- src/raylib.h | 28 +++++-- src/rmodels.c | 175 ++++++++++++++++++++++++++++++++++++------- 3 files changed, 180 insertions(+), 32 deletions(-) diff --git a/src/external/cgltf.h b/src/external/cgltf.h index b3ee7cb4a..b14f46817 100644 --- a/src/external/cgltf.h +++ b/src/external/cgltf.h @@ -614,6 +614,7 @@ struct cgltf_node { cgltf_node** children; cgltf_size children_count; cgltf_skin* skin; + int mesh_id; cgltf_mesh* mesh; cgltf_camera* camera; cgltf_light* light; @@ -635,6 +636,7 @@ struct cgltf_node { typedef struct cgltf_scene { char* name; cgltf_node** nodes; + int* node_ids; cgltf_size nodes_count; cgltf_extras extras; cgltf_size extensions_count; @@ -1930,6 +1932,7 @@ void cgltf_free(cgltf_data* data) { data->memory.free(data->memory.user_data, data->scenes[i].name); data->memory.free(data->memory.user_data, data->scenes[i].nodes); + data->memory.free(data->memory.user_data, data->scenes[i].node_ids); cgltf_free_extensions(data, data->scenes[i].extensions, data->scenes[i].extensions_count); } @@ -5066,7 +5069,8 @@ static int cgltf_parse_json_node(cgltf_options* options, jsmntok_t const* tokens { ++i; CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); - out_node->mesh = CGLTF_PTRINDEX(cgltf_mesh, cgltf_json_to_int(tokens + i, json_chunk)); + out_node->mesh_id = cgltf_json_to_int(tokens + i, json_chunk); + out_node->mesh = CGLTF_PTRINDEX(cgltf_mesh, out_node->mesh_id); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "skin") == 0) @@ -5239,9 +5243,12 @@ static int cgltf_parse_json_scene(cgltf_options* options, jsmntok_t const* token { return i; } + int size = out_scene->nodes_count * sizeof(int); + out_scene->node_ids = (int *)options->memory.alloc(options->memory.user_data, size); for (cgltf_size k = 0; k < out_scene->nodes_count; ++k) { + out_scene->node_ids[k] = cgltf_json_to_int(tokens + i, json_chunk); out_scene->nodes[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); ++i; } diff --git a/src/raylib.h b/src/raylib.h index bd80f371a..3b10a8f53 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -366,11 +366,23 @@ typedef struct Transform { Vector3 scale; // Scale } Transform; -//Scene +// Mode Node +typedef struct ModelNode { + /* Don't support node hierarchy for now + struct ModelNode *parent; // Parent nodes; + struct ModelNode *children; // Children nodes array; + */ + // every primitive in the glTF as a separate raylib mesh, so we need to covert mesh id in glTF to interval + int meshStart; // Start position of the interval in the model mesh array; + int meshEnd; // End position of the interval in the model mesh array; + Transform transform; // Transform for node meshes; + Matrix transformMatrix; // Transform matrix for node meshes; +} ModelNode; + +// Scene typedef struct Scene { - int meshCount; // Number of meshes - int *meshes; // Scene meshes array - Transform *meshTransforms; // transform matrices for meshes + int nodeCount; // Number of nodes + int *nodes; // Scene nodes array } Scene; // Bone, skeletal animation bone @@ -389,7 +401,9 @@ typedef struct Model { Material *materials; // Materials array int *meshMaterial; // Mesh material number - // Scene data + // Scene and node data + int nodeCount; // Number of nodes; + ModelNode *nodes; // Nodes array; int sceneCount; // Number of scenes Scene *scenes; // Scenes array int scene; // Scene should be displayed @@ -1426,6 +1440,10 @@ RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, floa RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set) RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) +RLAPI void DrawModelScene(Model model, int scene_id, Vector3 position, float scale, Color tint); // Draw a model's scene (with texture if set) +RLAPI void DrawModelSceneEx(Model model, int scene_id, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model's scene with extended parameters +RLAPI void DrawModelSceneWires(Model model, int scene_id, Vector3 position, float scale, Color tint); // Draw a model's scene wires (with texture if set) +RLAPI void DrawModelSceneWiresEx(Model model, int scene_id, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model's scene wires (with texture if set) with extended parameters RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float size, Color tint); // Draw a billboard texture RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source RLAPI void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint); // Draw a billboard texture defined by source and rotation diff --git a/src/rmodels.c b/src/rmodels.c index b72349824..71acc9ea4 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1005,6 +1005,11 @@ void UnloadModel(Model model) RL_FREE(model.materials); RL_FREE(model.meshMaterial); + // Unload scenes and nodes + RL_FREE(model.nodes); + for (int i = 0; i < model.sceneCount; i++) RL_FREE(model.scenes[i].nodes); + RL_FREE(model.scenes); + // Unload animation data RL_FREE(model.bones); RL_FREE(model.bindPose); @@ -3278,20 +3283,24 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota // Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform) model.transform = MatrixMultiply(model.transform, matTransform); + if (model.scene >= 0 + && model.scene < model.sceneCount + && model.scenes[model.scene].nodeCount>0) { // we have a scene to draw + DrawModelSceneEx(model, model.scene, position, rotationAxis, rotationAngle, scale, tint); + } else { + for (int i = 0; i < model.meshCount; i++) { + Color color = model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color; - for (int i = 0; i < model.meshCount; i++) - { - Color color = model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color; + Color colorTint = WHITE; + colorTint.r = (unsigned char) ((((float) color.r / 255.0) * ((float) tint.r / 255.0)) * 255.0f); + colorTint.g = (unsigned char) ((((float) color.g / 255.0) * ((float) tint.g / 255.0)) * 255.0f); + colorTint.b = (unsigned char) ((((float) color.b / 255.0) * ((float) tint.b / 255.0)) * 255.0f); + colorTint.a = (unsigned char) ((((float) color.a / 255.0) * ((float) tint.a / 255.0)) * 255.0f); - Color colorTint = WHITE; - colorTint.r = (unsigned char)((((float)color.r/255.0)*((float)tint.r/255.0))*255.0f); - colorTint.g = (unsigned char)((((float)color.g/255.0)*((float)tint.g/255.0))*255.0f); - colorTint.b = (unsigned char)((((float)color.b/255.0)*((float)tint.b/255.0))*255.0f); - colorTint.a = (unsigned char)((((float)color.a/255.0)*((float)tint.a/255.0))*255.0f); - - model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = colorTint; - DrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform); - model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = color; + model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = colorTint; + DrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform); + model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = color; + } } } @@ -3315,6 +3324,69 @@ void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rlDisableWireMode(); } +// Draw a model (with texture if set) +void DrawModelScene(Model model, int scene_id, Vector3 position, float scale, Color tint) +{ + Vector3 vScale = { scale, scale, scale }; + Vector3 rotationAxis = { 0.0f, 1.0f, 0.0f }; + + DrawModelSceneEx(model, scene_id, position, rotationAxis, 0.0f, vScale, tint); +} + +// Draw a model with extended parameters +void DrawModelSceneEx(Model model, int scene_id, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint) +{ + // Calculate transformation matrix from function parameters + // Get transform matrix (rotation -> scale -> translation) + Matrix matScale = MatrixScale(scale.x, scale.y, scale.z); + Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD); + Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z); + + Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); + + // Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform) + model.transform = MatrixMultiply(model.transform, matTransform); + if (scene_id >= 0 + && scene_id < model.sceneCount) { + for (int i = 0; i < model.scenes[scene_id].nodeCount; i++) { + int node_id = model.scenes[scene_id].nodes[i]; + Matrix transform = MatrixMultiply(model.transform, model.nodes[node_id].transformMatrix); + for (int j = model.nodes[node_id].meshStart; j < model.nodes[node_id].meshEnd; j++) { + Color color = model.materials[model.meshMaterial[j]].maps[MATERIAL_MAP_DIFFUSE].color; + Color colorTint = WHITE; + colorTint.r = (unsigned char) ((((float) color.r / 255.0) * ((float) tint.r / 255.0)) * 255.0f); + colorTint.g = (unsigned char) ((((float) color.g / 255.0) * ((float) tint.g / 255.0)) * 255.0f); + colorTint.b = (unsigned char) ((((float) color.b / 255.0) * ((float) tint.b / 255.0)) * 255.0f); + colorTint.a = (unsigned char) ((((float) color.a / 255.0) * ((float) tint.a / 255.0)) * 255.0f); + + model.materials[model.meshMaterial[j]].maps[MATERIAL_MAP_DIFFUSE].color = colorTint; + DrawMesh(model.meshes[j], model.materials[model.meshMaterial[i]], transform); + model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = color; + } + } + } +} + +// Draw a model wires (with texture if set) +void DrawModelSceneWires(Model model, int scene_id, Vector3 position, float scale, Color tint) +{ + rlEnableWireMode(); + + DrawModelScene(model, scene_id, position, scale, tint); + + rlDisableWireMode(); +} + +// Draw a model wires (with texture if set) with extended parameters +void DrawModelSceneWiresEx(Model model, int scene_id, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint) +{ + rlEnableWireMode(); + + DrawModelSceneEx(model, scene_id, position, rotationAxis, rotationAngle, scale, tint); + + rlDisableWireMode(); +} + // Draw a billboard void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float size, Color tint) { @@ -4626,6 +4698,8 @@ static Model LoadGLTF(const char *fileName) } Model model = { 0 }; + int *mesh_id_starts = NULL; + int *mesh_id_ends = NULL; // glTF file loading unsigned int dataSize = 0; @@ -4649,6 +4723,7 @@ static Model LoadGLTF(const char *fileName) TRACELOG(LOG_DEBUG, " > Buffers count: %i", data->buffers_count); TRACELOG(LOG_DEBUG, " > Images count: %i", data->images_count); TRACELOG(LOG_DEBUG, " > Textures count: %i", data->textures_count); + TRACELOG(LOG_DEBUG, " > Nodes count: %1", data->nodes_count); TRACELOG(LOG_DEBUG, " > Scenes count: %1", data->scenes_count); // Force reading data buffers (fills buffer_view->buffer->data) @@ -4662,6 +4737,8 @@ static Model LoadGLTF(const char *fileName) // Load our model data: meshes and materials model.meshCount = primitivesCount; + mesh_id_starts = RL_CALLOC(data->meshes_count, sizeof(int)); + mesh_id_ends = RL_CALLOC(data->meshes_count, sizeof(int)); model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh)); for (int i = 0; i < model.meshCount; i++) model.meshes[i].vboId = (unsigned int*)RL_CALLOC(MAX_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); @@ -4694,12 +4771,12 @@ static Model LoadGLTF(const char *fileName) UnloadImage(imAlbedo); } - // Load base color factor (tint) - model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255); - model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255); - model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255); - model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255); } + // Load base color factor (tint) + model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255); + model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255); + model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255); + model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255); // Load metallic/roughness texture if (data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture) @@ -4768,7 +4845,7 @@ static Model LoadGLTF(const char *fileName) for (unsigned int i = 0, meshIndex = 0; i < data->meshes_count; i++) { // NOTE: meshIndex accumulates primitives - + mesh_id_starts[i]=meshIndex; for (unsigned int p = 0; p < data->meshes[i].primitives_count; p++) { // NOTE: We only support primitives defined by triangles @@ -4942,21 +5019,65 @@ static Model LoadGLTF(const char *fileName) meshIndex++; // Move to next mesh } + mesh_id_ends[i] = meshIndex; + } + + // Load node data + model.nodeCount = data->nodes_count; + model.nodes = RL_CALLOC(data->nodes_count, sizeof(ModelNode)); + memset(model.nodes,0,data->nodes_count * sizeof(ModelNode)); + for (unsigned int i = 0; i < data->nodes_count; i++) { + model.nodes[i].meshStart = mesh_id_starts[data->nodes[i].mesh_id]; + model.nodes[i].meshEnd = mesh_id_ends[data->nodes[i].mesh_id]; + Matrix matScale; + Matrix matRotation; + Matrix matTranslation; + + if (data->nodes[i].has_translation) { + model.nodes[i].transform.translation.x = data->nodes[i].translation[0]; + model.nodes[i].transform.translation.y = data->nodes[i].translation[1]; + model.nodes[i].transform.translation.z = data->nodes[i].translation[2]; + matTranslation = MatrixTranslate(model.nodes[i].transform.translation.x, + model.nodes[i].transform.translation.y, + model.nodes[i].transform.translation.z); + } else + matTranslation = MatrixIdentity(); + if (data->nodes[i].has_rotation) { + model.nodes[i].transform.rotation.x = data->nodes[i].rotation[0]; + model.nodes[i].transform.rotation.y = data->nodes[i].rotation[1]; + model.nodes[i].transform.rotation.z = data->nodes[i].rotation[2]; + model.nodes[i].transform.rotation.w = data->nodes[i].rotation[3]; + matRotation = QuaternionToMatrix(model.nodes[i].transform.rotation); + } else + matRotation = MatrixIdentity(); + + if (data->nodes[i].has_scale) { + model.nodes[i].transform.scale.x = data->nodes[i].scale[0]; + model.nodes[i].transform.scale.y = data->nodes[i].scale[1]; + model.nodes[i].transform.scale.z = data->nodes[i].scale[2]; + matScale = MatrixScale(model.nodes[i].transform.scale.x, + model.nodes[i].transform.scale.y, + model.nodes[i].transform.scale.z); + } else { + model.nodes[i].transform.scale.x = 1; + model.nodes[i].transform.scale.y = 1; + model.nodes[i].transform.scale.z = 1; + matScale = MatrixIdentity(); + } + model.nodes[i].transformMatrix = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); } // Load scene data if (data->scenes_count > 0) { model.scene = data->scene_id; - model.scenes = (Scene *)malloc(sizeof(Scene) * data->scenes_count); + model.sceneCount = data->scenes_count; + model.scenes = RL_CALLOC(data->scenes_count, sizeof(Scene)); memset(model.scenes, 0, sizeof(Scene) * data->scenes_count); for (unsigned int i = 0; i < data->scenes_count; i++) { - // TODO: for each nodes, find - if (data->scenes[i].nodes_count>0) { - for (unsigned int j = 0; j < data->scenes[i].nodes_count; j++) { - scenes - - } - } + model.scenes[i].nodeCount = data->scenes[i].nodes_count; + model.scenes[i].nodes = RL_CALLOC(data->scenes[i].nodes_count, sizeof(int)); + for (unsigned int j = 0; j < data->scenes[i].nodes_count; j++) + model.scenes[i].nodes[j] = data->scenes[i].node_ids[j]; } } @@ -5010,6 +5131,8 @@ static Model LoadGLTF(const char *fileName) } } */ + RL_FREE(mesh_id_starts); + RL_FREE(mesh_id_ends); // Free all cgltf loaded data cgltf_free(data); }