From 8808a3958a705120bea516cf8d50155e5b9d8725 Mon Sep 17 00:00:00 2001 From: Kirandeep-Singh-Khehra Date: Wed, 4 Dec 2024 23:02:28 +0530 Subject: [PATCH 1/6] [rmodels] Added implementation of `UpdateModelAnimationBonesWithBlending()` function Signed-off-by: Kirandeep-Singh-Khehra --- src/raylib.h | 1 + src/rmodels.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index 641bd10e0b4f..bb25ea94206a 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1605,6 +1605,7 @@ RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount); // Load model animations from file RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose (CPU) RLAPI void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame); // Update model animation mesh bone matrices (GPU skinning) +RLAPI void UpdateModelAnimationBonesWithBlending(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float blendFactor); // Update model animation mesh bone matrices with blending between two poses(GPU skinning) RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match diff --git a/src/rmodels.c b/src/rmodels.c index 24f4a4fc9fc6..b68dc0cf495b 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2308,6 +2308,65 @@ void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame) } } +// Update model animated bones transform matrices by blendin between two different given frames of different ModelAnimation(could be same too) +// NOTE: Updated data is not uploaded to GPU but kept at model.meshes[i].boneMatrices[boneId], +// to be uploaded to shader at drawing, in case GPU skinning is enabled +void UpdateModelAnimationBonesWithBlending(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float blendFactor) +{ + if ((animA.frameCount > 0) && (animA.bones != NULL) && (animA.framePoses != NULL) && + (animB.frameCount > 0) && (animB.bones != NULL) && (animB.framePoses != NULL) && + (blendFactor >= 0.0f) && (blendFactor <= 1.0)) + { + frameA = frameA % animA.frameCount; + frameB = frameB % animB.frameCount; + + for (int i = 0; i < model.meshCount; i++) + { + if (model.meshes[i].boneMatrices) + { + assert(model.meshes[i].boneCount == animA.boneCount); + assert(model.meshes[i].boneCount == animB.boneCount); + + for (int boneId = 0; boneId < model.meshes[i].boneCount; boneId++) + { + Vector3 inTranslation = model.bindPose[boneId].translation; + Quaternion inRotation = model.bindPose[boneId].rotation; + Vector3 inScale = model.bindPose[boneId].scale; + + Vector3 outATranslation = animA.framePoses[frameA][boneId].translation; + Quaternion outARotation = animA.framePoses[frameA][boneId].rotation; + Vector3 outAScale = animA.framePoses[frameA][boneId].scale; + + Vector3 outBTranslation = animB.framePoses[frameB][boneId].translation; + Quaternion outBRotation = animB.framePoses[frameB][boneId].rotation; + Vector3 outBScale = animB.framePoses[frameB][boneId].scale; + + Vector3 outTranslation = Vector3Lerp(outATranslation, outBTranslation, blendFactor); + Quaternion outRotation = QuaternionSlerp(outARotation, outBRotation, blendFactor); + Vector3 outScale = Vector3Lerp(outAScale, outBScale, blendFactor); + + Vector3 invTranslation = Vector3RotateByQuaternion(Vector3Negate(inTranslation), QuaternionInvert(inRotation)); + Quaternion invRotation = QuaternionInvert(inRotation); + Vector3 invScale = Vector3Divide((Vector3){ 1.0f, 1.0f, 1.0f }, inScale); + + Vector3 boneTranslation = Vector3Add( + Vector3RotateByQuaternion(Vector3Multiply(outScale, invTranslation), + outRotation), outTranslation); + Quaternion boneRotation = QuaternionMultiply(outRotation, invRotation); + Vector3 boneScale = Vector3Multiply(outScale, invScale); + + Matrix boneMatrix = MatrixMultiply(MatrixMultiply( + QuaternionToMatrix(boneRotation), + MatrixTranslate(boneTranslation.x, boneTranslation.y, boneTranslation.z)), + MatrixScale(boneScale.x, boneScale.y, boneScale.z)); + + model.meshes[i].boneMatrices[boneId] = boneMatrix; + } + } + } + } +} + // at least 2x speed up vs the old method // Update model animated vertex data (positions and normals) for a given frame // NOTE: Updated data is uploaded to GPU From ec02d80868083e593d339093ddc128cc5aa1cab5 Mon Sep 17 00:00:00 2001 From: Kirandeep-Singh-Khehra Date: Sat, 7 Dec 2024 22:54:34 +0530 Subject: [PATCH 2/6] [rmodels] Added example for animation blending and fixed wrap issue for blend factor Signed-off-by: Kirandeep-Singh-Khehra --- examples/models/models_animation_blending.c | 138 ++++++++++++++++++++ src/rmodels.c | 2 +- 2 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 examples/models/models_animation_blending.c diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c new file mode 100644 index 000000000000..8c5394ac0ade --- /dev/null +++ b/examples/models/models_animation_blending.c @@ -0,0 +1,138 @@ +/******************************************************************************************* +* +* raylib [core] example - Model animation blending +* +* Example originally created with raylib 5.5 +* +* Example contributed by Kirandeep (@Kirandeep-Singh-Khehra) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2024 Kirandeep (@Kirandeep-Singh-Khehra) +* +* Note: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS +* +********************************************************************************************/ + +#include "raylib.h" + +#define clamp(x,a,b) ((x < a)? a : (x > b)? b : x) + +#if defined(PLATFORM_DESKTOP) + #define GLSL_VERSION 330 +#else // PLATFORM_ANDROID, PLATFORM_WEB + #define GLSL_VERSION 100 +#endif + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - Model Animation Blending"); + + // Define the camera to look into our 3d world + Camera camera = { 0 }; + camera.position = (Vector3){ 8.0f, 8.0f, 8.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) + camera.fovy = 45.0f; // Camera field-of-view Y + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type + + // Load gltf model + Model characterModel = LoadModel("resources/models/gltf/robot.glb"); // Load character model + + // Load skinning shader + Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION), + TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION)); + + for (int i = 0; i < characterModel.materialCount; i++) + { + characterModel.materials[i].shader = skinningShader; + } + + // Load gltf model animations + int animsCount = 0; + unsigned int animIndex0 = 0; + unsigned int animIndex1 = 0; + unsigned int animCurrentFrame = 0; + ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount); + + float blendFactor = 0.5f; + + DisableCursor(); // Limit cursor to relative movement inside the window + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera, CAMERA_THIRD_PERSON); + + // Select current animation + if (IsKeyPressed(KEY_T)) animIndex0 = (animIndex0 + 1)%animsCount; + else if (IsKeyPressed(KEY_G)) animIndex0 = (animIndex0 + animsCount - 1)%animsCount; + if (IsKeyPressed(KEY_Y)) animIndex1 = (animIndex1 + 1)%animsCount; + else if (IsKeyPressed(KEY_H)) animIndex1 = (animIndex1 + animsCount - 1)%animsCount; + + // Select blend factor + if (IsKeyPressed(KEY_U)) blendFactor = clamp(blendFactor - 0.1, 0.0f, 1.0f); + else if (IsKeyPressed(KEY_J)) blendFactor = clamp(blendFactor + 0.1, 0.0f, 1.0f); + + // Update animation + animCurrentFrame++; + + // Update bones + // Note: Same animation frame index is used below. By default it loops both animations + UpdateModelAnimationBonesWithBlending(characterModel, modelAnimations[animIndex0], animCurrentFrame, modelAnimations[animIndex1], animCurrentFrame, blendFactor); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + // Draw character mesh, pose calculation is done in shader (GPU skinning) + for (int i = 0; i < characterModel.meshCount; i++) + { + DrawMesh(characterModel.meshes[i], characterModel.materials[characterModel.meshMaterial[i]], characterModel.transform); + } + + + DrawGrid(10, 1.0f); + + EndMode3D(); + + DrawText("Use the U/J Arrow to adjust blend factor", 10, 10, 20, GRAY); + DrawText("Use the T/G to switch animation", 10, 30, 20, GRAY); + DrawText("Use the Y/H to switch animation", 10, 50, 20, GRAY); + DrawText(TextFormat("Animations: %s, %s", modelAnimations[animIndex0].name, modelAnimations[animIndex1].name), 10, 70, 20, BLACK); + DrawText(TextFormat("Blend Factor: %f", blendFactor), 10, 86, 20, BLACK); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation + UnloadModel(characterModel); // Unload model and meshes/material + UnloadShader(skinningShader); // Unload GPU skinning shader + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/src/rmodels.c b/src/rmodels.c index b68dc0cf495b..a215bcc8c7c8 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2315,7 +2315,7 @@ void UpdateModelAnimationBonesWithBlending(Model model, ModelAnimation animA, in { if ((animA.frameCount > 0) && (animA.bones != NULL) && (animA.framePoses != NULL) && (animB.frameCount > 0) && (animB.bones != NULL) && (animB.framePoses != NULL) && - (blendFactor >= 0.0f) && (blendFactor <= 1.0)) + (blendFactor >= 0.0f) && (blendFactor <= 1.0f)) { frameA = frameA % animA.frameCount; frameB = frameB % animB.frameCount; From bf85ba59f7bef3018c7efcd55021610d5b077c3b Mon Sep 17 00:00:00 2001 From: Kirandeep-Singh-Khehra Date: Sun, 8 Dec 2024 11:43:30 +0530 Subject: [PATCH 3/6] [rmodels] Updated build information for animation blending example Signed-off-by: Kirandeep-Singh-Khehra --- examples/Makefile | 1 + examples/Makefile.Web | 1 + examples/README.md | 93 ++--- examples/models/models_animation_blending.png | Bin 0 -> 24735 bytes .../models_animation_blending.vcxproj | 387 ++++++++++++++++++ projects/VS2022/raylib.sln | 2 + 6 files changed, 438 insertions(+), 46 deletions(-) create mode 100644 examples/models/models_animation_blending.png create mode 100644 projects/VS2022/examples/models_animation_blending.vcxproj diff --git a/examples/Makefile b/examples/Makefile index a65a4961e683..9428c295f0f0 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -584,6 +584,7 @@ TEXT = \ MODELS = \ models/models_animation \ + models/models_animation_blending \ models/models_billboard \ models/models_bone_socket \ models/models_box_collisions \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index dd5dc68814d4..8e20f0b05170 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -472,6 +472,7 @@ TEXT = \ MODELS = \ models/models_animation \ + models/models_animation_blending \ models/models_gpu_skinning \ models/models_billboard \ models/models_bone_socket \ diff --git a/examples/README.md b/examples/README.md index d25ad10cc867..f6ad6ff27694 100644 --- a/examples/README.md +++ b/examples/README.md @@ -137,24 +137,25 @@ Examples using raylib models functionality, including models loading/generation | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| | 85 | [models_animation](models/models_animation.c) | models_animation | ⭐️⭐️☆☆ | 2.5 | 3.5 | [culacant](https://github.com/culacant) | -| 86 | [models_billboard](models/models_billboard.c) | models_billboard | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | -| 87 | [models_box_collisions](models/models_box_collisions.c) | models_box_collisions | ⭐️☆☆☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | -| 88 | [models_cubicmap](models/models_cubicmap.c) | models_cubicmap | ⭐️⭐️☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | -| 89 | [models_first_person_maze](models/models_first_person_maze.c) | models_first_person_maze | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 90 | [models_geometric_shapes](models/models_geometric_shapes.c) | models_geometric_shapes | ⭐️☆☆☆ | 1.0 | 3.5 | [Ray](https://github.com/raysan5) | -| 91 | [models_mesh_generation](models/models_mesh_generation.c) | models_mesh_generation | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) | -| 92 | [models_mesh_picking](models/models_mesh_picking.c) | models_mesh_picking | ⭐️⭐️⭐️☆ | 1.7 | **4.0** | [Joel Davis](https://github.com/joeld42) | -| 93 | [models_loading](models/models_loading.c) | models_loading | ⭐️☆☆☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | -| 94 | [models_loading_gltf](models/models_loading_gltf.c) | models_loading_gltf | ⭐️☆☆☆ | 3.7 | **4.2** | [Ray](https://github.com/raysan5) | -| 95 | [models_loading_vox](models/models_loading_vox.c) | models_loading_vox | ⭐️☆☆☆ | **4.0** | **4.0** | [Johann Nadalutti](https://github.com/procfxgen) | -| 96 | [models_loading_m3d](models/models_loading_m3d.c) | models_loading_m3d | ⭐️☆☆☆ | **4.2** | **4.2** | [bzt](https://bztsrc.gitlab.io/model3d) | -| 97 | [models_orthographic_projection](models/models_orthographic_projection.c) | models_orthographic_projection | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) | -| 98 | [models_point_rendering](models/models_point_rendering.c) | models_point_rendering | ⭐️⭐️☆☆ | 5.0 | 5.0 | [Reese Gallagher](https://github.com/satchelfrost) | -| 99 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | models_rlgl_solar_system | ⭐️⭐️⭐️⭐️ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | -| 100 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | models_yaw_pitch_roll | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Berni](https://github.com/Berni8k) | -| 101 | [models_waving_cubes](models/models_waving_cubes.c) | models_waving_cubes | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [codecat](https://github.com/codecat) | -| 102 | [models_heightmap](models/models_heightmap.c) | models_heightmap | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | -| 103 | [models_skybox](models/models_skybox.c) | models_skybox | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) | +| 86 | [models_animation_blending](models/models_animation_blending.c) | models_animation | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Kirandeep-Singh-Khehra](https://github.com/Kirandeep-Singh-Khehra) | +| 87 | [models_billboard](models/models_billboard.c) | models_billboard | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | +| 88 | [models_box_collisions](models/models_box_collisions.c) | models_box_collisions | ⭐️☆☆☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | +| 89 | [models_cubicmap](models/models_cubicmap.c) | models_cubicmap | ⭐️⭐️☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | +| 90 | [models_first_person_maze](models/models_first_person_maze.c) | models_first_person_maze | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 91 | [models_geometric_shapes](models/models_geometric_shapes.c) | models_geometric_shapes | ⭐️☆☆☆ | 1.0 | 3.5 | [Ray](https://github.com/raysan5) | +| 92 | [models_mesh_generation](models/models_mesh_generation.c) | models_mesh_generation | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) | +| 93 | [models_mesh_picking](models/models_mesh_picking.c) | models_mesh_picking | ⭐️⭐️⭐️☆ | 1.7 | **4.0** | [Joel Davis](https://github.com/joeld42) | +| 94 | [models_loading](models/models_loading.c) | models_loading | ⭐️☆☆☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | +| 95 | [models_loading_gltf](models/models_loading_gltf.c) | models_loading_gltf | ⭐️☆☆☆ | 3.7 | **4.2** | [Ray](https://github.com/raysan5) | +| 96 | [models_loading_vox](models/models_loading_vox.c) | models_loading_vox | ⭐️☆☆☆ | **4.0** | **4.0** | [Johann Nadalutti](https://github.com/procfxgen) | +| 97 | [models_loading_m3d](models/models_loading_m3d.c) | models_loading_m3d | ⭐️☆☆☆ | **4.2** | **4.2** | [bzt](https://bztsrc.gitlab.io/model3d) | +| 98 | [models_orthographic_projection](models/models_orthographic_projection.c) | models_orthographic_projection | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) | +| 99 | [models_point_rendering](models/models_point_rendering.c) | models_point_rendering | ⭐️⭐️☆☆ | 5.0 | 5.0 | [Reese Gallagher](https://github.com/satchelfrost) | +| 100 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | models_rlgl_solar_system | ⭐️⭐️⭐️⭐️ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | +| 101 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | models_yaw_pitch_roll | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Berni](https://github.com/Berni8k) | +| 102 | [models_waving_cubes](models/models_waving_cubes.c) | models_waving_cubes | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [codecat](https://github.com/codecat) | +| 103 | [models_heightmap](models/models_heightmap.c) | models_heightmap | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | +| 104 | [models_skybox](models/models_skybox.c) | models_skybox | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) | ### category: shaders @@ -162,25 +163,25 @@ Examples using raylib shaders functionality, including shaders loading, paramete | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 104 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | shaders_basic_lighting | ⭐️⭐️⭐️⭐️ | 3.0 | **4.2** | [Chris Camacho](https://github.com/codifies) | -| 105 | [shaders_model_shader](shaders/shaders_model_shader.c) | shaders_model_shader | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) | -| 106 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | shaders_shapes_textures | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | -| 107 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | shaders_custom_uniform | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | -| 108 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | shaders_postprocessing | ⭐️⭐️⭐️☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | -| 109 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | shaders_palette_switch | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) | -| 110 | [shaders_raymarching](shaders/shaders_raymarching.c) | shaders_raymarching | ⭐️⭐️⭐️⭐️ | 2.0 | **4.2** | [Ray](https://github.com/raysan5) | -| 111 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | shaders_texture_drawing | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/) | -| 112 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | shaders_texture_outline | ⭐️⭐️⭐️☆ | **4.0** | **4.0** | [Samuel Skiff](https://github.com/GoldenThumbs) | -| 113 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | shaders_texture_waves | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) | -| 114 | [shaders_julia_set](shaders/shaders_julia_set.c) | shaders_julia_set | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [eggmund](https://github.com/eggmund) | -| 115 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | shaders_eratosthenes | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [ProfJski](https://github.com/ProfJski) | -| 116 | [shaders_fog](shaders/shaders_fog.c) | shaders_fog | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | -| 117 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | shaders_simple_mask | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | -| 118 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | shaders_hot_reloading | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) | -| 119 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | shaders_mesh_instancing | ⭐️⭐️⭐️⭐️ | 3.7 | **4.2** | [seanpringle](https://github.com/seanpringle) | -| 120 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | shaders_multi_sample2d | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 121 | [shaders_spotlight](shaders/shaders_spotlight.c) | shaders_spotlight | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | -| 122 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | shaders_deferred_render | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) | +| 105 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | shaders_basic_lighting | ⭐️⭐️⭐️⭐️ | 3.0 | **4.2** | [Chris Camacho](https://github.com/codifies) | +| 106 | [shaders_model_shader](shaders/shaders_model_shader.c) | shaders_model_shader | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) | +| 107 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | shaders_shapes_textures | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) | +| 108 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | shaders_custom_uniform | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | +| 109 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | shaders_postprocessing | ⭐️⭐️⭐️☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | +| 110 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | shaders_palette_switch | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) | +| 111 | [shaders_raymarching](shaders/shaders_raymarching.c) | shaders_raymarching | ⭐️⭐️⭐️⭐️ | 2.0 | **4.2** | [Ray](https://github.com/raysan5) | +| 112 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | shaders_texture_drawing | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/) | +| 113 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | shaders_texture_outline | ⭐️⭐️⭐️☆ | **4.0** | **4.0** | [Samuel Skiff](https://github.com/GoldenThumbs) | +| 114 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | shaders_texture_waves | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) | +| 115 | [shaders_julia_set](shaders/shaders_julia_set.c) | shaders_julia_set | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [eggmund](https://github.com/eggmund) | +| 116 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | shaders_eratosthenes | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [ProfJski](https://github.com/ProfJski) | +| 117 | [shaders_fog](shaders/shaders_fog.c) | shaders_fog | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | +| 118 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | shaders_simple_mask | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | +| 119 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | shaders_hot_reloading | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) | +| 120 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | shaders_mesh_instancing | ⭐️⭐️⭐️⭐️ | 3.7 | **4.2** | [seanpringle](https://github.com/seanpringle) | +| 121 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | shaders_multi_sample2d | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 122 | [shaders_spotlight](shaders/shaders_spotlight.c) | shaders_spotlight | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | +| 123 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | shaders_deferred_render | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) | ### category: audio @@ -188,10 +189,10 @@ Examples using raylib audio functionality, including sound/music loading and pla | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 123 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 124 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | -| 125 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) | -| 126 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | +| 124 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 125 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | +| 126 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) | +| 127 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | ### category: others @@ -199,11 +200,11 @@ Examples showing raylib misc functionality that does not fit in other categories | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 127 | [rlgl_standalone](others/rlgl_standalone.c) | rlgl_standalone | ⭐️⭐️⭐️⭐️ | 1.6 | **4.0** | [Ray](https://github.com/raysan5) | -| 128 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | rlgl_compute_shader | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Teddy Astie](https://github.com/tsnake41) | -| 129 | [easings_testbed](others/easings_testbed.c) | easings_testbed | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) | -| 130 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | raylib_opengl_interop | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Stephan Soller](https://github.com/arkanis) | -| 131 | [embedded_files_loading](others/embedded_files_loading.c) | embedded_files_loading | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Kristian Holmgren](https://github.com/defutura) | +| 128 | [rlgl_standalone](others/rlgl_standalone.c) | rlgl_standalone | ⭐️⭐️⭐️⭐️ | 1.6 | **4.0** | [Ray](https://github.com/raysan5) | +| 129 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | rlgl_compute_shader | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Teddy Astie](https://github.com/tsnake41) | +| 130 | [easings_testbed](others/easings_testbed.c) | easings_testbed | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) | +| 131 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | raylib_opengl_interop | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Stephan Soller](https://github.com/arkanis) | +| 132 | [embedded_files_loading](others/embedded_files_loading.c) | embedded_files_loading | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Kristian Holmgren](https://github.com/defutura) | As always contributions are welcome, feel free to send new examples! Here is an [examples template](examples_template.c) to start with! diff --git a/examples/models/models_animation_blending.png b/examples/models/models_animation_blending.png new file mode 100644 index 0000000000000000000000000000000000000000..522febb412f26ff1c74ebbe858407be1f1be14bf GIT binary patch literal 24735 zcmeFZc|6qX`#(OG#?XwlWQM^cp;8lLX)Fzsu|*Rtk~Ad7dL&CpH5v_R#+C>v8f&Fg z!$B#srpO>6oov-8Y15+hch9`cIi2%)zt89Q`2F+!{qg#BBr|{L7+U740w>&EIjI&8B_cyS{z-@bWNgBOW8k z#r>BbkqC(rK2;MH&sS9Yk3S@0k2cU{{0J2sRs$Wu{trL!>}V22rSU)iPw+zJ{CGY( zjla(7zllNmT~X~7gT1u+zsgVt$#CVr${-Y>_rJ&oM0f*X&EMkb z<;C7Q|H=!&!c20@V*@W#IdnZ%>-QMu4 zgGoST>p0bAXOr$uwrL85Oe08(emH=o>3VPt{OA7u>;KIC#t6eT7-9N#bfm*M1Z4}Z zU64M%W0*ffG$+%F?)jmzXYA=E5d>>{=iEQ{U&52sAV9X$zhM5OP5@;o*pBFqgC}%5 zsNXBCR5EjWprY<3cs7O^)rs~?+cI4geNW+}Nk7k3Zz6YtetsI!ZM{C$&0+_CnHRs; zAeN|;IUQcEm-&O=ls6FP((dIHAWdJ`Rg`tSC`+p^x4ek=tt9$pL9{g{^u%8(M>UhC z1du51eg93>V2^DXVFySKSc9c8EXl3yO2KQ(Ke|22?@ONbUcAAOyV*%6T3Y$>p`cRNM$l_FzH^BbRa#h;mF99>(hQ_9_cwho8i^iJvC!_4Ux zHNlK=Uz57k6M2)L>@2OnMkMP%D}C{AD}83HJ>yBXqz1D3OkVOL^lFW@n7dp32={wU zrQt8Vbx&PRyvrV>87L&VxHQ+rRF#NrUH_`;(@e+jg10Ug>nFZsdroEz zzrjV_mo7G5(&xIUZ`pGPPK<9;Xyk?tdN6%X5LN(!-Nj&+O#P{Q|L{Z-bw^?24=?Zh znacV!w;NPt$$F`{*Z1gC>8g_4wTzr4OXm|Nf>Zs_%B{AJf?LPkZQCvtI?(Sp%*1=fA9$HnZTOO~ybt z=d}BQbvbGK6DV))tv_U8^6W1Dp2;K4QQ)`#>8;;#cU^&kZt|CfOoKmnUmPBl^V z9XD?XaVX4lrngFD*YcwEDtym)8XIIzuZf>kc&riBW>GttGLWP1HR`CJpZ;=#?H7l~ zC&=K}ye@vz(cyjC{Ri<;%`A)JAB!LC;6@GpeNLD^q#?=wrXi0%GSY||&mip32yM9Lv2@K*;Cbw;ahmR#5%5 z-9?Khm&mUR{mVXtzcJ!{gj%rhFCX-e-+&efOo5GHqJ1BxC`5XqFHRTr-;-Gp=eWaS zt;aij`D9p4wiVNAC+cL}T2K3b#IH0hNv^Rrw>;qQvx;3im_H0hdXH9&y*ybGGlYvW zTwC$j{#8zDVeXR@R*V8gdSeQMMR!(mz^|>Y#Gr%#cCqmQmOC{E187 z^-P9o(*=EJi%Pb;^~&NtjT)u)%y6vVGlG{M8JgBHJJ`KUgYuI1_t_-YLC%8p_kaE? zrU=8yV}Fvvj=QcEIWW(}B~`4yARuga5)@$G#|e{B4Bbt8R+ z(9G^%HSceh6FFa^_43YJKeSM<|J$Al;h;UV@7Mm@Ghp9EX~h3o2oK|bVubJAdtyR= zWYW`bzofrH?H9+YOpmXES1UbB8KcT)dC7}>n-aYzBP+9yaVKRDBRM6de9wcw+LR&= zPn0ss#ry?2{_&eYRdUIb1Xp@g)&|`0( z9kcK4rN6TN^+=FNQAGI5)%@c(A)2=h&U4uDm3_11tk?I1Z%3__kA*3$+<(fv;;~&| z4S$*U9iZF}TD7Bc@AV2lbGN2oE>0fl`-b;un!h!Udp(9=HJWM@dvX^yYW7z}qd_@R z zO@q$E&8$cC*mGfW>XVa!Ji}j9%MwCZyZ_A#!yg-XWPUk4tTfP=`z-0Ph0Ud}vah1h zoyUugHqcxS&qO>Fj-7%Qj{Ob&mp42E9nsY}l=qK41b>1fTG3>3akA=J{+3bu7el`d zI2hC(u%Z26OqOQt*D+b8P(5QGSf_2KY2T|7712PC#f1KX0+6MU+1&pZvx)by0Ki}< z(CdX`{;`5B_tIY+3Q7%E!PJ&)`8+1KDyU8C%Cpg-CdS=-DH9`?7k_9+JaAvWgHxjC zAh$SX!=m!p_`Zmwey}jtU){XK1T?e4zsxK_8EK#R$$vAK)k{zj4V2sER$Nm|-T!zC z2Aw+ZmxK9L9l1CVhO}c95VZ%C;V+Yxl}}>?R_GeHW-9i|Qd3KV$GhTheqvC&Fe_i& zF|?rsSW|+}{@n#gcqWSS49q{`-(MLONF|Zi^ST4E5nP?-K9o;TXSm|aWp1z3U%art z+w=F)gpND}I^3oIcDPc}3W)LpFnaNsew<72;oHNhVG3W`t?F+dh-A@uGyibBB3T-s z_&NDYiu9}1LtyM35>ab5JNAxG+rDdq{)FR)wS{O2uV?AR6Qql=K}XGE%~5^#WU6|{ z>4X39w4x0rJ%Ucp=YO1oc>jUB&CNezH=vfyuU?AIAYP(o0tX;8X!W9*X_+IS$f>#Q$eX##CMxl`$!vJGdsWW zuSWiTFM$cdw?iT0&VO2x@ZW@?S|@zs%TM~g|LP;yESMF9=JNl6M*afo!c6UdLv#N> z0F?h%1Yx@Ke|O>k#V!CRk;Sk4m9&-c!i8JzI9=9o<@9U>LGf4D$S6~!35npJ{K{Y4 zUJi~FBE#(KM!!m9KP&sw1i?6>6Hphs4@q0b7A(q_8O9?U<#zt%1ZdE6^Tp4xZD|D- z7%n!0KSlU6fROY>mKW$ZXZ$Q00(mk8e#ORu2u4GRrJP6*LBlGsm21n#kM{GmmzLRE z=y11*^BGnQLdf#Ef1U#^$lHo)$FK@BbhTGWZ2wO25@BW$`w2{Td;$a%6dgN6>CMIiX<1L!F^&{ClV3Q zhrgp6{ThZP9H4EuJ+}?r!Uby0IQnu&EptzbkB1H&PqBExbObUFY*zw%=`*oJ-=Iw| zo?9)cNl`6Sv3Ko$4r`X4C6=fWE0Sn|SRw-0DApT#&o5eulDH=-1(f_k_V|FfoPX=D z@&OaDz`&iC@0~jpx}qc(H4?vJ3wIAbw@NH@ILPfm)nfOY(6AK6w9HID*eD-{sl&lu zBoIupQYfQy>jtqx8wiuvZGS>ccxfMnt;VK2zkhZdb&a3?sqth{v~x?LdD*3#2MC>F zMFl%SLsYU^Kg8qAC~8M#N#(`b=4bFXnTF#=zJw@A#p?MHtp7TB_)19r-$e2^vtTCK zTMKhrnk&0rKcRR0x)}iMG$@Pq%ufu;35fUtms8uPC>FY0$G_1NYHC6{vINS$m9Uah zwRhr*VjL=?dPv98RY#B9ke<(5ayR1HUH<8{e;O#B1gX-DSUiR;O}{H+iT@bJ_V#et)}0blv)pVuT=C`#QxxErbQaofZ zf&YsFdqFNh%$JWKB!Z%>;k~U9K~Wgep~bsXWpzKg9jM;*FGqxuSb@W9OiL8DAm`Pp zKl1tFu%}mGbb){I&vQ1GR=aiR`;fDB%e_5K9yexj9|2#~?`4*SxoxrvD^GxeE3l z!(G7=d@XP_w?qE+mhca_*m}AXU{+Z>_I0M`H{FA~;f@1#h#+si}Yu1S(fZ>QKN414} zEd0+Cpb61Vg<0;9STY8gvE?h472DiXw=3FmM;zgm_{2eY0ecEC&xyxkn*hbfDdSZ$ zf>WGmTgo+Y*{2(D{lcRV&&$BUhGdL-Bz+^F?IukAG8|w>g@y7zhhsG)p(O6+tg_#A z_)h^DUC>!Xh()aO!tlZ^eE{pi7(Jvz)= zw|>&kt2KZO`Xt)r&ojC~7CF+}&~|zu3Sz8D5{zMV;luUqv_Lv@cy%m#jBfK+>>yo+ ze0G&c_mos5E4Rr{bir5N@2yiYB6e(e;`jUZWnqNUU$#w%4ne7}1$skCa=_mTv0o$F zEUBnyg1!uW4s6(4HJ3lhmCv>esLr7yPOw1`O6#2^DJTR*+rnQ?wb49Sb`_PllJQXh z5X^J!Q^&D}KgFQ{*n*H>gc5L86oSfqFCnKLwBinCN3`9eB1%Vy%*FSsbH9rf2(VcI zObUa4ok&0*v}8ewOchb95+)gCOENdsVNKxGVGqjhROGKrcZ)4M7*J%A?bq=N}n zBAZ2CS1gEbld@vuVw_js4*9~?)%PXuuV((?d_{k9A2nm&OW>mSiuDdeeON++K=~ry z4^o1Qwv7IK1+Vy{tCveY-icozwWtqQuvIVNbXS`&s1=&b1%4s2&ZconMj(MzB&8nA zHmEYl_36kLYTdD_iJRWrXM1u~w}^Gn5=!!QMTdf%Rn&fZNFg>>wO@7*HXmgp8y!K^ z>`VEb|8y&No;Z>r{}$QO7du&8ycSUu?lls=CvJE!N&E!P}` z+HDKD2jDIM;xQ{sEol&mh*OtrUQv6y-#}vjsh$+sYK~w+dV>1$6tzbdSMuKGWpS|Z zWywEZwuTH6GJ6LWtoW;yevV=Mpi6}-B32st$zmBi=;s%^0sa0 znZa)M6ZCNi#LSbdPS$lV8L#(*4ASV_MJZ1X!i&_Cm2moafEGNHzSno5Zdk)YdUG_1_QeJ@ z#n(Bok*x9v^}6pxlA_AmzRFfkx zy*O5Bi3YXfOic+^=#!@KsZ7P*S?vXXOhn@0%O0hM9nvxk7cb`5f;}Mq=nrPKTcmiJ z@g<+=UujdBw3nMkhThw9JT(s>3FQR~_R@13}gK7^0aVEJaXo-`Gmia>NKqiP~6jud4*q+Ufe zI^BMeIvKFPt=e){>hk529M8il)&^;aah+?Yr}?S1E8^R~~mNoRj+ zxOwzN`wQu!RrEj!9n^}{E+^rJq1IsT^~uG?5{Cq zk5g2xitQxoLth9%p(LlS@tJCf>?uttHSPmZmj>)0q!ADhaFCY>B6ybZ4dQcbF2m&v z^7xLENh6O=yEu&>sCi7b?YO8|xNd!*jCA(wMC5wlGfx#BTeY47{KfKeARxf~LT~s$ zq}S^bVHN7%iXxrh=auYVs$8kGgI~!{8{Mb0*o26iV9nQ!;fy%l1^m=h3u?`%5%3lj zH^80=AWjA9iBen$f_6BD&0#kJqU(~`6?gETBzZ*jdXs^kcj2#)oM zHeKR733Gf91Oi2VftHlmKn*OT1vm4rx*MG$;+H56*ei3?U2K4j2^ktdmx`JRV3wfV@|>BARfyxSf8n4f*tf;kb<1M^ zW2&_WOeteV7^jq$Vv1>E6vd%lJHvZY?Q!a7qV9nIJ9tMvvlvlA*s#&4X0;~@BtpV| zQ{ZZHMUj@qFrmPmrTi)hmwZO!r!+%QX10z=L+VY5d!I|~msO9gY5Zo@{nZ*i-m}6j z`AVbFk+sT1evy0_d&YGHoJj;^G{RgcA+doLERBr*)PO;J&hp_KIdY0}6lT5?3M%zV zr5uib)-F*o>U-BOKv;iYCgn?9Dx_+k-3bR7D-h=kAQ_c3gp}&H^tBl4nNE%7kP#V^ z4u{8)3dS)@v^K5}Tc46L(rc!mo%qQC6QUNH#J&L?-4}3lg*ceHBSmwgX1mhw{8sDP zd|daLgu+-ExYaeSD_EtV(|)GIy~f3NYbfduxtm{ubDt8u{EPu!_QAnkhAUnhKH?47 z?=G3y{M6BmxMP7c?!m9;RlE*G-#NO>;BF1v-JBDhM9CAnzbDYlHQ;3%9H}hB?l()? z_TM&iy2{?{>L%IEzk*Wo-Ee#HL#=yv^3at>*RFg|=~#h?(DSS8`ocE#-{@{|$yN5! zj8)wyAD$;z`6$5uLXHS!spGJWu-J@6BGB%fq(G8~hM@I;eV8}3(f z=w+!|V`W|)ETU$iEVGK7aA8Cxu5KjU}>g-@+0G-(-GrBHO zlR}OPoWFW-eqXwFwaSr~^76jZUWNW!KD|NO}OJWqLA{%al_21g^m5PwwhI?TW{)=2rw(L z=lFve{PTc4_$6E+K~NR2WMY{Y;CC`-& zsj>F=_qGRWKI`Kv9eA#0H2x z5>f?tx%fujjMN&BJtoAY&;q`1-#+t{So!Bqx3(XW>O7Qr^5klb4X=xxrgfJNE9QN@ zZg6#PqyfKSVa?~;ng^?cB1?jJfgoMw&C#GBruzVQ5XDnjaHD9`v*t)A{ZI|$L)}H> zx6&&-KI%lPO7_nd#8kIDbS$LAo{J8Mi`=FZXpnGfaGTrRtNacTa)!+nPuc*)VJfOk zI~$hOMh4fI-D5@QNpIbfVDdv@`66S6zcqZ>-6L7o`MuNe-nZ>SZ^;+muzz86N$8|X zn(2r%&~FPAZ2^e-WJ_t*f#*`&XVNS@EjM9EHJG~g16JX$>|jS9QeP&Foep8e#>WE5#{QgiB5V6r@CbHhh>5$A5aZ6(|Xrl+=yDPllC1yq+jB~R<_WWyn2uz zPsszrG&D4P{_yhQPp$-%L-1^Al_?Vn(J}n)P}jvQ%_sC2fOA)S#< z(D15sim0pzEa}_POla+XfMhw#+Bj!;#VbkYeQ2m|md#VEx6mCJX8h)ErQaEU`#>e3 zgVI5QRedyJ%b!l$a)-V07A7={odCIo_rNVo;9wWnY3fot=%o^!LwT@+=j8 zUd5Ka1@>rOq9C6!K6#veM(0hGC2f(s=7!f;!@MM9d}FSe<1v!0a}CXZ;{_>mjoTpv zCK!i07aIZavQd_txuocrBODU>BgA&9buU#fVaU)D1Pe$h z41cu7#9!Zj{*EzQ#(=AAJCC1J&ff%(2^j+3LJk^kgFt4%tz%22CoVPa`?gBI1O6Vd zv^{j@^gP^$zU2{^-~po208v-_cWPa^#-sJ}U#2=q8VA@KVU;O}h_|a1IU|i{6w0tm zxz06|3&Qq)O~@160ox2H$5Zz1J}C7GIjw_Qvibr_DvaVnYDd||M%uR5Hc35lTM=

=CQHvbWy@@B*-Fkwt;ziP0y|7FVZCqSe-kVrp)NH84!be0QS zQv&Ho`aG7o1-08L46n8Rnr!6?^HZt)x}Ta?dPX}@hTbg?TJWIYbAxIabGPGT`cZ&8 zb#x*43P^c@hGbJcAK#M#TWUHhg8x>Uf-o#nr=k6t)phGo*_j)B}C8b2{KBF2qB1|_=PB$*^QR3c#^s5tygCCg~P^u{1 zjJu!KFm@&vOZUPm#*H4i+sX5~X*XH!-FMH7m!c9>6IFCFW?)QuLCVC$Nq)*xdaz#+ z6#ptxS+J&dpkvRrp{e72jg9dV(a1NX)75tRyQ$Z%kjf-bFBuIV7gH}Zx&)15>aW;^ zB_`zND|lG)&#M41udRLST>##?x#z*t9Bs?_BK9IuI1f`j~QL__@WMb00i+NJ47E8g3%&muxk zCmtOovF?*5xcp*U<~58uC!7DQg^n134CMu2E---4L}>4hPd5ApZ>&@8s(ol6h2S zaY?P1qH{b`UTVGJ%{SkrPBXF$<1p(sw6+TXM?n;wvs^a)J{%)lt>f-0mJ_20$ zv_-mB{eIvMQX^4~yNA8D$Q~4@H$N3Rc$f(k>dpW~KBA~y%{QWAm1lBt8r24+9=}mv zpZ7?%*4&u18acB3lD$O^_W@uYkY}I} z)CvmH#?t!b_XK<#TJGd4T9i%%@I$SQ5z=@%?~H1GTHzl_r98gH5Wd*k1W$A~@`Sn< z6rqwuBw!(yuwWOjkUKY81%Y^Dq1ETuUm4!4`cROs9J?da;kYZp@Y9;Ky`$RYS3bpD zS|wGd1%L1GEqq1f+mma!01gL<%5X3%$g+J2(hdlTSd%G($uxGfb=jlYhTWJd)89{w zdrlmjU(jk+YhCgI9_+cYe(^+rFQ#7Gggd*NcAg;wK@UK!RtM@dSpyPW;LhVi$`l{r z(4p%$>a97Uk}?qzR@z*JH!}@Q-4%YYI*HDO_m;cPtc(kPsxvW|(Rg*I&l1&pLZMy= zE!!0$IY3SYippwJsIP@rhy99T(#onr=KnEEUoo%L+l{Dwy?Cr47Svkr!TyA2T0-(SqTq2vjr48gKgDzsq64V zFdD6(b%iXTRu{w+xXv@kS4=yzl=l4Smi)AMk6613&J36PhIz|Ljl<;!fmwLC>!B4itmvHNrwSE8(GOPl;nNpW8JN+36IMI@Dc2x+IS@N;lH z(LAkJSiLsU_9lA89_RHyL_N{nJR5MXfNfR*{5PoYr+3p>ozo3BHeA?t2I%MRZODUz z{i&}n2WAA6Kaj=we`Id?l#_N9d3q?%%UW)4;lS-o zPhS9CL;-HQ+m=?e>C4P1a44jWXXV3aYe`REX`ek0#W|la zu@XWGL|N$H?U?xWzeNn7{DY9x1C{ zU0vtjDfUg}XAg(xBwe_tbx)XJfX)Tv2T1Wp`HC8Kx}@X_Mx@TpBaEa7|2h!6U1*Qg8nGk>-=u$I$yr z1Abs$HIHwQ;2P_4ITt`@43zUOFd74sNjQDiZ&NK{Ry0b#`#|L!@LYN82=HS%?F8lFsp`znt>V9WwA$pIoyiCPB^tD+-dSdooVjOd1aD}L4HrRUjM_&WEI96npz#kSv}3+Ph-|7VHX~iLgx5aO?tD2Z zM>oD4*Ozmolf@Wj2UDcCp;lwl3x{WmHjl2K?5W`6ix1snvnJ`gLW&@O*{SeSk+ib! ztxJ>wD<_@LGV*sT0=<2hl7VYxh5_fobU>W0fT!)rJ7!1QkwUSC-2F8S_>0OS>1;CexDIR&Zc37>_MedpUJL0FL2 znPD2fZ5TtTR&zNzeQKC8mq*-dT1~&7!}%-Hc#)eIkR)z`^s#>syEeQU}X6|Irt@s={JP zQO&~shNV#o^GrU)Qi)$}?tj-RkeAVq^8pQ&&dN5kVfZLu{CAQv!Xqd9>AQp-RdYc4 zTN<$BO?#Njw)lOz?H@{$SUb>fn>x`YQ+*GwXuL!)O?pio3GP!U>5?&=zbNF#x(y(+ za5N%1+j^})t*gZg;b;M{x;GHL@j^2KKpsrHFzvNVzHDRB4QM^UHBFHm6B9f~j|1DG zmU^LBwh053x=)`)dwvOy{K&k~ty|)5GyspHp%!bqONiIaXIHGz|^GQY7@^LN{b{1I(UsYBEb@O5q76GLrVSC1_Tk#u0BCwbfO<2F7WI(e$&1J|JtMv zle9D63qJHL_ids_?*00v%v|=VUD_&p83aY$5%_IkmnP^6xM+ixtlT)3UOAPVfoRwo zuPk?+ztQYVjCJLp`6-MIITYkS%Wforbh9&t`FoG9f0p#b7a=lf0`(Pr$UK%|B60Wy z%LR>N{u9YcPu$M(@tPW-VnSR3pv>KBH8uF%DVZwo^iJEv(k`hdIYkgc zsjEV@+jL{OiD#~9HG8jjD0{y;qZe+K9m;uTGn9S5^2VAWC3Gm81G?CR9}svi1<16M zk49d_6_~lbHK7HL9_RCH9#dOP$Ps|Ofp_1Oj$J4zz|IfBW{eKZ7WjWoS@{U#ebO`} zaEk6Ow8*)V2wg*x9Mv4K#4LHTwmAT}uCI#`s%Hy8DF&`sPuZ3D$Y{COYWtT2m*dBk z6Md~UDsG5Mj4S{Yg*WfF)Bz;yCdDV0>lh8`Q(TtvUH}!16=Y-6v%OI#t(I^nnKd)3 z-2xnitPNAx(lR$O-WFCHK?-}*ePF+W!A9duqGS7yb1`=7Iq)fJs3p=aMrG)ejlQ)_ zU}$6~agXjDnxhsoaPL?8hd(c>7=xk?V;E^AD7F^j-A@QotuA;4;OVS!zyfr7u)$fyI#eDNVrddzdbvibj_=BnOj*oc*!`8VQM0-ae!vHu0W*td7p;)k) zDN&nFKCbi-ynoPGy82DY2M3}_ocWOUQjKKZR2p0L5pTXkT;KuxZ(&-UC%CmBW{G); z6%WV^?}ZXo0YdSmp3K5(X_aPPi9y7|=SP_XZpyW9iMgv}VsuUFY7g?1M2sd092|(5 zy{HGaFw+Pts`?^>94tC?0-4e!Q;q^mYZZ{=LyzogP>$I6s&K0f2k1@cCg5}F2yp^r zds0}cn&%r}53lQ8=Qxtu=c-bXJvy3vg5Qb{o$u_mKU)78pU*quu$&7*m8lWfFM@O> z1R-`%fyNbH#g_4FuU45kLXOc54@x$+4(JqGxHeX_QWw1>xMX!o< zhH*|&Eg9uQ<-!LaS1N6xlDZE#Dy)fL3&=Ta5Kueh1H4v8zQT-$=Cb@w{-8pSvxyT> zyQVlEPUzNGi8QY~D}I>rh8kGO{i;xZ730Lc2vUb0x8)|73NAE2X86B;S)F}C?u9XQ{l@ilFx!& zU*eb8akYXUlypOp(V5ZE(KgzRL%RqsNw`78VgE7t_;l;K#LKJ6ck6Yy-19SciU!8A zyODS3Z&w4R8X&P7?o$FVhB9!zLK9uth;gZqCd2v>>T>Elz7=`Z@4Z3gZ2%O|jP$F+ zOE+t{KS~6^W+G8vcaLT6R+$anEzdI=O`VO1*R6&S>g7;3*~how_=}yqBnw-n+UWxd zm*AaLH7hOGQg76{sno7CA`gA$j+!;b7ZY_yyTs@s`E|>Qe1@ zEh~?{1tPzRu}<29E8JI7y~;k@o4@w4cH8RqkA-#Xqsp>A%q)8ySQQe?8z&CI)B*D* zY=Zl-(Uw-WD2d$o{ju=hZ;9|48(!brB50LE!% zV_)M{%inlGr9!nFyseqS#g_3kz?V~?i!wU;^K{HM(WVOfJZ3z1cAM>y zf1dtsG%bt1=XsmpU4HsQX;{me*h8dgHpA9WU*R*`G#HAy6a*f0!hiTl_d{J)EQs25 zTvI0i^zt>PFZFqg&s5h3TJf$|*<}16VS~aYOU`{0rq!*~l4AarN5H;-Qs+zffU8QN z{f6g#-o&#Od6U&a&BvnU0|T|)n*_)^?IF+DtzYTyMf?<_U!*>izz}WB_P%L0Fa`L| z=Tdpc;Z4f*>26ltzLN`UeeA>RBMCbdUiA@&0Kvf)b#zfhj8v1hOJm7hnb^lGivcXB zbabz0GozyYA}1HV0yo#~Bk51K1wB0m?o-friLO>LdW1TJd`lpBsBbGW1#Ku=Og)?% zW)ZV+Be8BK`N!ljDmRi>rzvE$foFUyQUw630L_Rf%_;0@UtHp2)P2n?b18GQXQa4# zE!;QSuco<$<{4|}`h(G8wZfW2xVnaHa6dJg?&vHbjIOOMbaD)Emf#Zv(Ighl^7gWK zg+p}vuu~034(o4S1~zoem`^)}MaykFR0dG*)0g~>@8VtdOKo%Iw0@_b5H+wvOdffK z3-WpE%6EuK>ZBJZ+PeKdi3eW9VzHL2GC9bVjV zW))D!*@CcVg^@zR+ro3cYnw!|yHYsAaM?n6S$#j&3-(XCvFP>^?0|@?Tj$p55H=;> z3es^QfRHaF0O+aaH4j}iF16;z# zEi@>H+YQ5I9<>YT+=L%F@ST`~0A?ibJR0Q1JFXh%z9R*~YNxPaz!=m(zku+wN!TUb;Ci7xdKj@Rz;NfV%sh8APF)O`w8|iLiS|jF@PL+;{P#04`Jrg45 zwUf_|R7H%wW{8SbfTJQwg!p6K)Zgm zBH5U0U}`Hnq*cwhIt9!N^x^;vH>xcX4AEU)Z6DA<$@8cos#g*vRc(cb!vi#Svxt3w zx!ny{PjB69uF%3xG7L*wo>RD~5&3(hDb;7K;x!%a$$^FhSfM4Ez9|lWQlZJ6ky<^BZku9EuB;i39L&1&p9BoBoScWQP6P_%IMKsndr3NG*^?N zJ`0u{C$u)fDNl;}QS#;Y@zZ04c#Jjio#U-YN>HrY<4>z+?!w}%!FwsDx6;6(W7SX< zz<-M>YK7#frmQ!}><@)a74go;4u^AC3Y)xATRR(6pJ#TZAd5TbJg&2OH9V-nmk+%MS007O9MB$!$sip{-nErGA-;j|3;PZs2OFY&epQSqwnVXyA*{1R` zJ99{v&C!n7KEA)G76H)jh_H7FffA-M;S-V8-3OgsceT?!)rSL72PYSPK?q2lr_$Af zxcT}Wz3MjymC`23hAwQ=swD8<4dYxl0Q-ga@f0zo(jMepWm}hWQC{y3fcA zN=j7`0tid-pr0f&v=1avSr547aUdGcrJf0_D};fMGHK%vMtkCIKYk@`>E_Y*rI?z- zV>UY9YkgnX=ZM8N7H^1E&j+f%PUMD>&qZd_eapzk`_SrB0Tk9Q@~+C+V?C}f0GtxU zfHmMxLAgWrAC!PjiaHmHt^lN3XiF*Pk$wFAKuY*C!6v8<#0@sPgRbA1f~-2xzmmNT&O)zD3(I1#dLAJ6dSbn?5}M(+0wjM2+tz)m$~d0*2?aGMfL zA*K~EDU&G+*7C1jBMy<~E?r6Zie63Vku}8R6*$dbAf1_#CSO9+DCZn6>=(MH?o|(| zJtG?V-_?F_j`6!*(3zl94_NIVqOf`{Fa^WuOt+RZ-*oQG>3x6?2PX8Y0km|(pVN0q zfezc14>2y#Rq7cL$&7ahm>`(iTWIN&GSHzRJ$jd9?)EHqJ802^InCXz6=>jB(9)oYf{szwSvPKlbvu=L-{I{7UzQO z=N+gW0$Ts6DA%KkTIZY0vFXOfMhlp`YEm-!O*>~?b%3ugJlO;%C!Wm3nG(;texETL z?m-yt70|z^ht`1N(;=I%Sh0UB?!%!g&On%Jj$}5YAdt_5E86vDiH9#H zr^)0d>t)Y!&z6Uz`zh?`BZ(9{8E3u9&ysw6HaFXhyBWGOD!dd2npEUk)g_tb!sxCx zd~D%PoxSF}uchC3&BVMlCt4VJPAY<{mv_HVdOJX{n``h3gF7vkie;|{5t6mVc3$x@ z{>FQS3j?H1w$UHA3$;q~q{hvZ0c+?gpvq~rm*&o`$TR@1P%1O1K|s-$-0IqCE5IrWKel`S;d_M zH-F)CO=S9=wy^C1()Df)yJv-7NO!%{I6KQYFq8F6Fx58VoJ!tr%IWFW$REU6N!v%n zqC=knDQVC>`MM%!M1$O6YK!bO#jPSc&()W45AZXOv1U1YH<=At<>moDYbA{+UgRs& zK_NpxRum`MC7OV&Dr7wcv(lG3c(y*Gok61wt>FO(Mh|j@%VLFChL^=qjfP8=07pud zzazN#K-d&7(I@@{0tqm(@$wSB{uEZk3QqLJGOYtMbdQv-LA#8tOYAFlfIRumr2 zEuw5Q7B(IPfYLYU8W-|pNq~23+MjuV*GS4+!uqW*1H&EWezj%=j3!;UC@~ui@E*^$ zSyhs^5|E9;`uWs;yhBLlW{&KLzNm8useXEaGj#p zp-~bYfK&u2Z!$azfqY7N8vZ&C>Hl+MZWtwCEycgld_X;mD}`aM+R?M?XFJ^(oCj+c zG{XmQ;inMH0N~UaKf9nh^fb5|L|L&XaIa9Nh$VW`h$sipxhZsAq5uYN8R3_ZY4{kS zhB3_?IWxs7+`tpxcx&pWSk<$!H6<<{bv94ht%qiWZe3I8*7=tB3DAzv1UtAOh>%;Z zBH2`)&Im5*X>9Z%)B<+)4@I>kkeTLO;ID(O{hk&%*W5FSn-Mhi&~pLctkdz4Cwk+h zWYju3IvO-dniU!=^9KRXGgJ)No^*b)`11^NJ;urCEF7r4@F?p0n)D1%&vXxQ?fL;6 zj0ce1MMdNcz>hv?9{(gy^b=t|1_2cU&P+$0QYpP>61q#o1+kEH4vTV0z9rKLa{eZLl(3SPuqKXo11%pxAP|ox8G>&Q4H}9t?a7gY|%~t z9Gq54Nd_o>Lbh}sG^|(xZppsNe);+ryN|~^k-=M|?!3`FtWCDMVYhfDG4&04O430i z2X2kV@}KG5;l)pYg3u)f58fSQ<$n4y?}A1Bk^>go6oBfoy0T8}QOJk)f;&uZptud65c-Wfa+yWN0B-UZqd&;hma^~(?<9QmKBGU&PL48We zL*Q*|p{Kutr@_4+Y^Ua_2~#R4I`{lu)6*0FTOY^NGw{TD3cyzvgj@_tA{G**9;^Ze zzCZ>xWMw@@^Nv<#R@%67Giw%ku;>7xNdZ(U1aWKXV*Ci%qDb5=;vKkw;FFfZwz+iy zevwP|e_r~1l-O&U>o2pk6ZmxYmJ=e4?t`LDF8C0HjjH6pvI9Y>zNwk4;c&0mqlUF( zfR)&Nm}{p28f(C^309*01d)F3e!afv#DSxx?se=0UUFY?bj0}HGQ(bs!DO`lSCus` z@e$~o@h8#=c_zfYW<$5ZqQECNg!KqOn&G9vDJ?ia$Z-?i&wRixG4L!gNE;+qoNzwA zU^TF;&Hyfs0rk8XfbV3nJP_0#G#Ugl?>qUPZ`aga^yjG+*&#+=PNcA)PeZgr%aI3u z1g9Zc6e&DBu4DcHpr4JyxZHN~+E>0WCvN*Yl-0L|&Rx1i&Q*GZ-~L((iUWM}A%^;) zJEy((5C@1lO|=b}z}W>^)eeIo?D{|ol?o};1bBl56r69aj4{)B+IJz|>8cbTct%Tw zpNnH7Wk={H!hXCtBKZ(l0k|cRsWg=iimFJ?JHE>l*G{h%N7DbEQc}U^fsV(fxvSMe z98U#+t`zMu%|l3da%ibVKvi=zrwl}1_8vH|QMAZWW`g@%I)E&XqI56+TBu9_>GS`y zX6bK{n%DsgitQ^-$~ zU)8L+cqQoM4KGM}4?ZCQxTV8Qx_fbF<1|GFuU}HJb*8h#Jb`nnz;YC{Da#qUJ0DW$ zXLInh0$WvYj$iQIv$*%@#K;dkM?PL$FhLGDagAE>8yRnAQM`^W(H^_1aOr-u*?8%<}~Cza&c++;?60+^%gUL)j>Nq&wCSIFW^Sw z2^Wj9O;`#b>kdl6B@E9jfdf|^bhNdldzS)tSukIekdL)!FnHA#j*XfHW_F zTR4CL#Fh#ypqCmi<~9ALe*EIuql;T1+ZZx9Efc2duk*mM$|~u% hDSgYHgToBiC}7BA2$U-G$}4360#8>zmvv4FO#pAURKoxO literal 0 HcmV?d00001 diff --git a/projects/VS2022/examples/models_animation_blending.vcxproj b/projects/VS2022/examples/models_animation_blending.vcxproj new file mode 100644 index 000000000000..bf8a8b27acc1 --- /dev/null +++ b/projects/VS2022/examples/models_animation_blending.vcxproj @@ -0,0 +1,387 @@ + + + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + Win32 + + + Release + x64 + + + + {AFDDE100-2D36-4749-817D-12E54C56312F} + Win32Proj + models_animation_blending + 10.0 + models_animation_blending + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\models + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 5142c14a14f9..2b8a5f1bd139 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -309,6 +309,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_bone_socket", "examp EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_vertex_displacement", "examples\shaders_vertex_displacement.vcxproj", "{CCA63A76-D9FC-4130-9F67-4D97F9770D53}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_blending", "examples\models_animation_blending.vcxproj", "{AFDDE100-2D36-4749-817D-12E54C56312F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|x64 = Debug.DLL|x64 From 438318e7ee03f41691fdabda3e16177805ed5e6d Mon Sep 17 00:00:00 2001 From: Kirandeep-Singh-Khehra Date: Sun, 8 Dec 2024 11:52:23 +0530 Subject: [PATCH 4/6] [rmodels] Fixed typos in anmation blending example Signed-off-by: Kirandeep-Singh-Khehra --- examples/README.md | 2 +- examples/models/models_animation_blending.c | 6 +++--- examples/models/models_animation_blending.png | Bin 24735 -> 26143 bytes 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/README.md b/examples/README.md index f6ad6ff27694..b564c5ee2c0c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -137,7 +137,7 @@ Examples using raylib models functionality, including models loading/generation | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| | 85 | [models_animation](models/models_animation.c) | models_animation | ⭐️⭐️☆☆ | 2.5 | 3.5 | [culacant](https://github.com/culacant) | -| 86 | [models_animation_blending](models/models_animation_blending.c) | models_animation | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Kirandeep-Singh-Khehra](https://github.com/Kirandeep-Singh-Khehra) | +| 86 | [models_animation_blending](models/models_animation_blending.c) | models_animation_blending | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Kirandeep-Singh-Khehra](https://github.com/Kirandeep-Singh-Khehra) | | 87 | [models_billboard](models/models_billboard.c) | models_billboard | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | | 88 | [models_box_collisions](models/models_box_collisions.c) | models_box_collisions | ⭐️☆☆☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) | | 89 | [models_cubicmap](models/models_cubicmap.c) | models_cubicmap | ⭐️⭐️☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) | diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c index 8c5394ac0ade..43a772756a99 100644 --- a/examples/models/models_animation_blending.c +++ b/examples/models/models_animation_blending.c @@ -115,9 +115,9 @@ int main(void) EndMode3D(); - DrawText("Use the U/J Arrow to adjust blend factor", 10, 10, 20, GRAY); - DrawText("Use the T/G to switch animation", 10, 30, 20, GRAY); - DrawText("Use the Y/H to switch animation", 10, 50, 20, GRAY); + DrawText("Use the U/J to adjust blend factor", 10, 10, 20, GRAY); + DrawText("Use the T/G to switch first animation", 10, 30, 20, GRAY); + DrawText("Use the Y/H to switch second animation", 10, 50, 20, GRAY); DrawText(TextFormat("Animations: %s, %s", modelAnimations[animIndex0].name, modelAnimations[animIndex1].name), 10, 70, 20, BLACK); DrawText(TextFormat("Blend Factor: %f", blendFactor), 10, 86, 20, BLACK); diff --git a/examples/models/models_animation_blending.png b/examples/models/models_animation_blending.png index 522febb412f26ff1c74ebbe858407be1f1be14bf..0d70c1a88436a01b3689693035840c34133cd936 100644 GIT binary patch literal 26143 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 z zO@q$E&8$cC*mGfW>XVa!Ji}j9%MwCZyZ_A#!yg-XWPUk4tTfP=`z-0Ph0Ud}vah1h zoyUugHqcxS&qO>Fj-7%Qj{Ob&mp42E9nsY}l=qK41b>1fTG3>3akA=J{+3bu7el`d zI2hC(u%Z26OqOQt*D+b8P(5QGSf_2KY2T|7712PC#f1KX0+6MU+1&pZvx)by0Ki}< z(CdX`{;`5B_tIY+3Q7%E!PJ&)`8+1KDyU8C%Cpg-CdS=-DH9`?7k_9+JaAvWgHxjC zAh$SX!=m!p_`Zmwey}jtU){XK1T?e4zsxK_8EK#R$$vAK)k{zj4V2sER$Nm|-T!zC z2Aw+ZmxK9L9l1CVhO}c95VZ%C;V+Yxl}}>?R_GeHW-9i|Qd3KV$GhTheqvC&Fe_i& zF|?rsSW|+}{@n#gcqWSS49q{`-(MLONF|Zi^ST4E5nP?-K9o;TXSm|aWp1z3U%art z+w=F)gpND}I^3oIcDPc}3W)LpFnaNsew<72;oHNhVG3W`t?F+dh-A@uGyibBB3T-s z_&NDYiu9}1LtyM35>ab5JNAxG+rDdq{)FR)wS{O2uV?AR6Qql=K}XGE%~5^#WU6|{ z>4X39w4x0rJ%Ucp=YO1oc>jUB&CNezH=vfyuU?AIAYP(o0tX;8X!W9*X_+IS$f>#Q$eX##CMxl`$!vJGdsWW zuSWiTFM$cdw?iT0&VO2x@ZW@?S|@zs%TM~g|LP;yESMF9=JNl6M*afo!c6UdLv#N> z0F?h%1Yx@Ke|O>k#V!CRk;Sk4m9&-c!i8JzI9=9o<@9U>LGf4D$S6~!35npJ{K{Y4 zUJi~FBE#(KM!!m9KP&sw1i?6>6Hphs4@q0b7A(q_8O9?U<#zt%1ZdE6^Tp4xZD|D- z7%n!0KSlU6fROY>mKW$ZXZ$Q00(mk8e#ORu2u4GRrJP6*LBlGsm21n#kM{GmmzLRE z=y11*^BGnQLdf#Ef1U#^$lHo)$FK@BbhTGWZ2wO25@BW$`w2{Td;$a%6dgN6>CMIiX<1L!F^&{ClV3Q zhrgp6{ThZP9H4EuJ+}?r!Uby0IQnu&EptzbkB1H&PqBExbObUFY*zw%=`*oJ-=Iw| zo?9)cNl`6Sv3Ko$4r`X4C6=fWE0Sn|SRw-0DApT#&o5eulDH=-1(f_k_V|FfoPX=D z@&OaDz`&iC@0~jpx}qc(H4?vJ3wIAbw@NH@ILPfm)nfOY(6AK6w9HID*eD-{sl&lu zBoIupQYfQy>jtqx8wiuvZGS>ccxfMnt;VK2zkhZdb&a3?sqth{v~x?LdD*3#2MC>F zMFl%SLsYU^Kg8qAC~8M#N#(`b=4bFXnTF#=zJw@A#p?MHtp7TB_)19r-$e2^vtTCK zTMKhrnk&0rKcRR0x)}iMG$@Pq%ufu;35fUtms8uPC>FY0$G_1NYHC6{vINS$m9Uah zwRhr*VjL=?dPv98RY#B9ke<(5ayR1HUH<8{e;O#B1gX-DSUiR;O}{H+iT@bJ_V#et)}0blv)pVuT=C`#QxxErbQaofZ zf&YsFdqFNh%$JWKB!Z%>;k~U9K~Wgep~bsXWpzKg9jM;*FGqxuSb@W9OiL8DAm`Pp zKl1tFu%}mGbb){I&vQ1GR=aiR`;fDB%e_5K9yexj9|2#~?`4*SxoxrvD^GxeE3l z!(G7=d@XP_w?qE+mhca_*m}AXU{+Z>_I0M`H{FA~;f@1#h#+si}Yu1S(fZ>QKN414} zEd0+Cpb61Vg<0;9STY8gvE?h472DiXw=3FmM;zgm_{2eY0ecEC&xyxkn*hbfDdSZ$ zf>WGmTgo+Y*{2(D{lcRV&&$BUhGdL-Bz+^F?IukAG8|w>g@y7zhhsG)p(O6+tg_#A z_)h^DUC>!Xh()aO!tlZ^eE{pi7(Jvz)= zw|>&kt2KZO`Xt)r&ojC~7CF+}&~|zu3Sz8D5{zMV;luUqv_Lv@cy%m#jBfK+>>yo+ ze0G&c_mos5E4Rr{bir5N@2yiYB6e(e;`jUZWnqNUU$#w%4ne7}1$skCa=_mTv0o$F zEUBnyg1!uW4s6(4HJ3lhmCv>esLr7yPOw1`O6#2^DJTR*+rnQ?wb49Sb`_PllJQXh z5X^J!Q^&D}KgFQ{*n*H>gc5L86oSfqFCnKLwBinCN3`9eB1%Vy%*FSsbH9rf2(VcI zObUa4ok&0*v}8ewOchb95+)gCOENdsVNKxGVGqjhROGKrcZ)4M7*J%A?bq=N}n zBAZ2CS1gEbld@vuVw_js4*9~?)%PXuuV((?d_{k9A2nm&OW>mSiuDdeeON++K=~ry z4^o1Qwv7IK1+Vy{tCveY-icozwWtqQuvIVNbXS`&s1=&b1%4s2&ZconMj(MzB&8nA zHmEYl_36kLYTdD_iJRWrXM1u~w}^Gn5=!!QMTdf%Rn&fZNFg>>wO@7*HXmgp8y!K^ z>`VEb|8y&No;Z>r{}$QO7du&8ycSUu?lls=CvJE!N&E!P}` z+HDKD2jDIM;xQ{sEol&mh*OtrUQv6y-#}vjsh$+sYK~w+dV>1$6tzbdSMuKGWpS|Z zWywEZwuTH6GJ6LWtoW;yevV=Mpi6}-B32st$zmBi=;s%^0sa0 znZa)M6ZCNi#LSbdPS$lV8L#(*4ASV_MJZ1X!i&_Cm2moafEGNHzSno5Zdk)YdUG_1_QeJ@ z#n(Bok*x9v^}6pxlA_AmzRFfkx zy*O5Bi3YXfOic+^=#!@KsZ7P*S?vXXOhn@0%O0hM9nvxk7cb`5f;}Mq=nrPKTcmiJ z@g<+=UujdBw3nMkhThw9JT(s>3FQR~_R@13}gK7^0aVEJaXo-`Gmia>NKqiP~6jud4*q+Ufe zI^BMeIvKFPt=e){>hk529M8il)&^;aah+?Yr}?S1E8^R~~mNoRj+ zxOwzN`wQu!RrEj!9n^}{E+^rJq1IsT^~uG?5{Cq zk5g2xitQxoLth9%p(LlS@tJCf>?uttHSPmZmj>)0q!ADhaFCY>B6ybZ4dQcbF2m&v z^7xLENh6O=yEu&>sCi7b?YO8|xNd!*jCA(wMC5wlGfx#BTeY47{KfKeARxf~LT~s$ zq}S^bVHN7%iXxrh=auYVs$8kGgI~!{8{Mb0*o26iV9nQ!;fy%l1^m=h3u?`%5%3lj zH^80=AWjA9iBen$f_6BD&0#kJqU(~`6?gETBzZ*jdXs^kcj2#)oM zHeKR733Gf91Oi2VftHlmKn*OT1vm4rx*MG$;+H56*ei3?U2K4j2^ktdmx`JRV3wfV@|>BARfyxSf8n4f*tf;kb<1M^ zW2&_WOeteV7^jq$Vv1>E6vd%lJHvZY?Q!a7qV9nIJ9tMvvlvlA*s#&4X0;~@BtpV| zQ{ZZHMUj@qFrmPmrTi)hmwZO!r!+%QX10z=L+VY5d!I|~msO9gY5Zo@{nZ*i-m}6j z`AVbFk+sT1evy0_d&YGHoJj;^G{RgcA+doLERBr*)PO;J&hp_KIdY0}6lT5?3M%zV zr5uib)-F*o>U-BOKv;iYCgn?9Dx_+k-3bR7D-h=kAQ_c3gp}&H^tBl4nNE%7kP#V^ z4u{8)3dS)@v^K5}Tc46L(rc!mo%qQC6QUNH#J&L?-4}3lg*ceHBSmwgX1mhw{8sDP zd|daLgu+-ExYaeSD_EtV(|)GIy~f3NYbfduxtm{ubDt8u{EPu!_QAnkhAUnhKH?47 z?=G3y{M6BmxMP7c?!m9;RlE*G-#NO>;BF1v-JBDhM9CAnzbDYlHQ;3%9H}hB?l()? z_TM&iy2{?{>L%IEzk*Wo-Ee#HL#=yv^3at>*RFg|=~#h?(DSS8`ocE#-{@{|$yN5! zj8)wyAD$;z`6$5uLXHS!spGJWu-J@6BGB%fq(G8~hM@I;eV8}3(f z=w+!|V`W|)ETU$iEVGK7aA8Cxu5KjU}>g-@+0G-(-GrBHO zlR}OPoWFW-eqXwFwaSr~^76jZUWNW!KD|NO}OJWqLA{%al_21g^m5PwwhI?TW{)=2rw(L z=lFve{PTc4_$6E+K~NR2WMY{Y;CC`-& zsj>F=_qGRWKI`Kv9eA#0H2x z5>f?tx%fujjMN&BJtoAY&;q`1-#+t{So!Bqx3(XW>O7Qr^5klb4X=xxrgfJNE9QN@ zZg6#PqyfKSVa?~;ng^?cB1?jJfgoMw&C#GBruzVQ5XDnjaHD9`v*t)A{ZI|$L)}H> zx6&&-KI%lPO7_nd#8kIDbS$LAo{J8Mi`=FZXpnGfaGTrRtNacTa)!+nPuc*)VJfOk zI~$hOMh4fI-D5@QNpIbfVDdv@`66S6zcqZ>-6L7o`MuNe-nZ>SZ^;+muzz86N$8|X zn(2r%&~FPAZ2^e-WJ_t*f#*`&XVNS@EjM9EHJG~g16JX$>|jS9QeP&Foep8e#>WE5#{QgiB5V6r@CbHhh>5$A5aZ6(|Xrl+=yDPllC1yq+jB~R<_WWyn2uz zPsszrG&D4P{_yhQPp$-%L-1^Al_?Vn(J}n)P}jvQ%_sC2fOA)S#< z(D15sim0pzEa}_POla+XfMhw#+Bj!;#VbkYeQ2m|md#VEx6mCJX8h)ErQaEU`#>e3 zgVI5QRedyJ%b!l$a)-V07A7={odCIo_rNVo;9wWnY3fot=%o^!LwT@+=j8 zUd5Ka1@>rOq9C6!K6#veM(0hGC2f(s=7!f;!@MM9d}FSe<1v!0a}CXZ;{_>mjoTpv zCK!i07aIZavQd_txuocrBODU>BgA&9buU#fVaU)D1Pe$h z41cu7#9!Zj{*EzQ#(=AAJCC1J&ff%(2^j+3LJk^kgFt4%tz%22CoVPa`?gBI1O6Vd zv^{j@^gP^$zU2{^-~po208v-_cWPa^#-sJ}U#2=q8VA@KVU;O}h_|a1IU|i{6w0tm zxz06|3&Qq)O~@160ox2H$5Zz1J}C7GIjw_Qvibr_DvaVnYDd||M%uR5Hc35lTM=

=CQHvbWy@@B*-Fkwt;ziP0y|7FVZCqSe-kVrp)NH84!be0QS zQv&Ho`aG7o1-08L46n8Rnr!6?^HZt)x}Ta?dPX}@hTbg?TJWIYbAxIabGPGT`cZ&8 zb#x*43P^c@hGbJcAK#M#TWUHhg8x>Uf-o#nr=k6t)phGo*_j)B}C8b2{KBF2qB1|_=PB$*^QR3c#^s5tygCCg~P^u{1 zjJu!KFm@&vOZUPm#*H4i+sX5~X*XH!-FMH7m!c9>6IFCFW?)QuLCVC$Nq)*xdaz#+ z6#ptxS+J&dpkvRrp{e72jg9dV(a1NX)75tRyQ$Z%kjf-bFBuIV7gH}Zx&)15>aW;^ zB_`zND|lG)&#M41udRLST>##?x#z*t9Bs?_BK9IuI1f`j~QL__@WMb00i+NJ47E8g3%&muxk zCmtOovF?*5xcp*U<~58uC!7DQg^n134CMu2E---4L}>4hPd5ApZ>&@8s(ol6h2S zaY?P1qH{b`UTVGJ%{SkrPBXF$<1p(sw6+TXM?n;wvs^a)J{%)lt>f-0mJ_20$ zv_-mB{eIvMQX^4~yNA8D$Q~4@H$N3Rc$f(k>dpW~KBA~y%{QWAm1lBt8r24+9=}mv zpZ7?%*4&u18acB3lD$O^_W@uYkY}I} z)CvmH#?t!b_XK<#TJGd4T9i%%@I$SQ5z=@%?~H1GTHzl_r98gH5Wd*k1W$A~@`Sn< z6rqwuBw!(yuwWOjkUKY81%Y^Dq1ETuUm4!4`cROs9J?da;kYZp@Y9;Ky`$RYS3bpD zS|wGd1%L1GEqq1f+mma!01gL<%5X3%$g+J2(hdlTSd%G($uxGfb=jlYhTWJd)89{w zdrlmjU(jk+YhCgI9_+cYe(^+rFQ#7Gggd*NcAg;wK@UK!RtM@dSpyPW;LhVi$`l{r z(4p%$>a97Uk}?qzR@z*JH!}@Q-4%YYI*HDO_m;cPtc(kPsxvW|(Rg*I&l1&pLZMy= zE!!0$IY3SYippwJsIP@rhy99T(#onr=KnEEUoo%L+l{Dwy?Cr47Svkr!TyA2T0-(SqTq2vjr48gKgDzsq64V zFdD6(b%iXTRu{w+xXv@kS4=yzl=l4Smi)AMk6613&J36PhIz|Ljl<;!fmwLC>!B4itmvHNrwSE8(GOPl;nNpW8JN+36IMI@Dc2x+IS@N;lH z(LAkJSiLsU_9lA89_RHyL_N{nJR5MXfNfR*{5PoYr+3p>ozo3BHeA?t2I%MRZODUz z{i&}n2WAA6Kaj=we`Id?l#_N9d3q?%%UW)4;lS-o zPhS9CL;-HQ+m=?e>C4P1a44jWXXV3aYe`REX`ek0#W|la zu@XWGL|N$H?U?xWzeNn7{DY9x1C{ zU0vtjDfUg}XAg(xBwe_tbx)XJfX)Tv2T1Wp`HC8Kx}@X_Mx@TpBaEa7|2h!6U1*Qg8nGk>-=u$I$yr z1Abs$HIHwQ;2P_4ITt`@43zUOFd74sNjQDiZ&NK{Ry0b#`#|L!@LYN82=HS%?F8lFsp`znt>V9WwA$pIoyiCPB^tD+-dSdooVjOd1aD}L4HrRUjM_&WEI96npz#kSv}3+Ph-|7VHX~iLgx5aO?tD2Z zM>oD4*Ozmolf@Wj2UDcCp;lwl3x{WmHjl2K?5W`6ix1snvnJ`gLW&@O*{SeSk+ib! ztxJ>wD<_@LGV*sT0=<2hl7VYxh5_fobU>W0fT!)rJ7!1QkwUSC-2F8S_>0OS>1;CexDIR&Zc37>_MedpUJL0FL2 znPD2fZ5TtTR&zNzeQKC8mq*-dT1~&7!}%-Hc#)eIkR)z`^s#>syEeQU}X6|Irt@s={JP zQO&~shNV#o^GrU)Qi)$}?tj-RkeAVq^8pQ&&dN5kVfZLu{CAQv!Xqd9>AQp-RdYc4 zTN<$BO?#Njw)lOz?H@{$SUb>fn>x`YQ+*GwXuL!)O?pio3GP!U>5?&=zbNF#x(y(+ za5N%1+j^})t*gZg;b;M{x;GHL@j^2KKpsrHFzvNVzHDRB4QM^UHBFHm6B9f~j|1DG zmU^LBwh053x=)`)dwvOy{K&k~ty|)5GyspHp%!bqONiIaXIHGz|^GQY7@^LN{b{1I(UsYBEb@O5q76GLrVSC1_Tk#u0BCwbfO<2F7WI(e$&1J|JtMv zle9D63qJHL_ids_?*00v%v|=VUD_&p83aY$5%_IkmnP^6xM+ixtlT)3UOAPVfoRwo zuPk?+ztQYVjCJLp`6-MIITYkS%Wforbh9&t`FoG9f0p#b7a=lf0`(Pr$UK%|B60Wy z%LR>N{u9YcPu$M(@tPW-VnSR3pv>KBH8uF%DVZwo^iJEv(k`hdIYkgc zsjEV@+jL{OiD#~9HG8jjD0{y;qZe+K9m;uTGn9S5^2VAWC3Gm81G?CR9}svi1<16M zk49d_6_~lbHK7HL9_RCH9#dOP$Ps|Ofp_1Oj$J4zz|IfBW{eKZ7WjWoS@{U#ebO`} zaEk6Ow8*)V2wg*x9Mv4K#4LHTwmAT}uCI#`s%Hy8DF&`sPuZ3D$Y{COYWtT2m*dBk z6Md~UDsG5Mj4S{Yg*WfF)Bz;yCdDV0>lh8`Q(TtvUH}!16=Y-6v%OI#t(I^nnKd)3 z-2xnitPNAx(lR$O-WFCHK?-}*ePF+W!A9duqGS7yb1`=7Iq)fJs3p=aMrG)ejlQ)_ zU}$6~agXjDnxhsoaPL?8hd(c>7=xk?V;E^AD7F^j-A@QotuA;4;OVS!zyfr7u)$fyI#eDNVrddzdbvibj_=BnOj*oc*!`8VQM0-ae!vHu0W*td7p;)k) zDN&nFKCbi-ynoPGy82DY2M3}_ocWOUQjKKZR2p0L5pTXkT;KuxZ(&-UC%CmBW{G); z6%WV^?}ZXo0YdSmp3K5(X_aPPi9y7|=SP_XZpyW9iMgv}VsuUFY7g?1M2sd092|(5 zy{HGaFw+Pts`?^>94tC?0-4e!Q;q^mYZZ{=LyzogP>$I6s&K0f2k1@cCg5}F2yp^r zds0}cn&%r}53lQ8=Qxtu=c-bXJvy3vg5Qb{o$u_mKU)78pU*quu$&7*m8lWfFM@O> z1R-`%fyNbH#g_4FuU45kLXOc54@x$+4(JqGxHeX_QWw1>xMX!o< zhH*|&Eg9uQ<-!LaS1N6xlDZE#Dy)fL3&=Ta5Kueh1H4v8zQT-$=Cb@w{-8pSvxyT> zyQVlEPUzNGi8QY~D}I>rh8kGO{i;xZ730Lc2vUb0x8)|73NAE2X86B;S)F}C?u9XQ{l@ilFx!& zU*eb8akYXUlypOp(V5ZE(KgzRL%RqsNw`78VgE7t_;l;K#LKJ6ck6Yy-19SciU!8A zyODS3Z&w4R8X&P7?o$FVhB9!zLK9uth;gZqCd2v>>T>Elz7=`Z@4Z3gZ2%O|jP$F+ zOE+t{KS~6^W+G8vcaLT6R+$anEzdI=O`VO1*R6&S>g7;3*~how_=}yqBnw-n+UWxd zm*AaLH7hOGQg76{sno7CA`gA$j+!;b7ZY_yyTs@s`E|>Qe1@ zEh~?{1tPzRu}<29E8JI7y~;k@o4@w4cH8RqkA-#Xqsp>A%q)8ySQQe?8z&CI)B*D* zY=Zl-(Uw-WD2d$o{ju=hZ;9|48(!brB50LE!% zV_)M{%inlGr9!nFyseqS#g_3kz?V~?i!wU;^K{HM(WVOfJZ3z1cAM>y zf1dtsG%bt1=XsmpU4HsQX;{me*h8dgHpA9WU*R*`G#HAy6a*f0!hiTl_d{J)EQs25 zTvI0i^zt>PFZFqg&s5h3TJf$|*<}16VS~aYOU`{0rq!*~l4AarN5H;-Qs+zffU8QN z{f6g#-o&#Od6U&a&BvnU0|T|)n*_)^?IF+DtzYTyMf?<_U!*>izz}WB_P%L0Fa`L| z=Tdpc;Z4f*>26ltzLN`UeeA>RBMCbdUiA@&0Kvf)b#zfhj8v1hOJm7hnb^lGivcXB zbabz0GozyYA}1HV0yo#~Bk51K1wB0m?o-friLO>LdW1TJd`lpBsBbGW1#Ku=Og)?% zW)ZV+Be8BK`N!ljDmRi>rzvE$foFUyQUw630L_Rf%_;0@UtHp2)P2n?b18GQXQa4# zE!;QSuco<$<{4|}`h(G8wZfW2xVnaHa6dJg?&vHbjIOOMbaD)Emf#Zv(Ighl^7gWK zg+p}vuu~034(o4S1~zoem`^)}MaykFR0dG*)0g~>@8VtdOKo%Iw0@_b5H+wvOdffK z3-WpE%6EuK>ZBJZ+PeKdi3eW9VzHL2GC9bVjV zW))D!*@CcVg^@zR+ro3cYnw!|yHYsAaM?n6S$#j&3-(XCvFP>^?0|@?Tj$p55H=;> z3es^QfRHaF0O+aaH4j}iF16;z# zEi@>H+YQ5I9<>YT+=L%F@ST`~0A?ibJR0Q1JFXh%z9R*~YNxPaz!=m(zku+wN!TUb;Ci7xdKj@Rz;NfV%sh8APF)O`w8|iLiS|jF@PL+;{P#04`Jrg45 zwUf_|R7H%wW{8SbfTJQwg!p6K)Zgm zBH5U0U}`Hnq*cwhIt9!N^x^;vH>xcX4AEU)Z6DA<$@8cos#g*vRc(cb!vi#Svxt3w zx!ny{PjB69uF%3xG7L*wo>RD~5&3(hDb;7K;x!%a$$^FhSfM4Ez9|lWQlZJ6ky<^BZku9EuB;i39L&1&p9BoBoScWQP6P_%IMKsndr3NG*^?N zJ`0u{C$u)fDNl;}QS#;Y@zZ04c#Jjio#U-YN>HrY<4>z+?!w}%!FwsDx6;6(W7SX< zz<-M>YK7#frmQ!}><@)a74go;4u^AC3Y)xATRR(6pJ#TZAd5TbJg&2OH9V-nmk+%MS007O9MB$!$sip{-nErGA-;j|3;PZs2OFY&epQSqwnVXyA*{1R` zJ99{v&C!n7KEA)G76H)jh_H7FffA-M;S-V8-3OgsceT?!)rSL72PYSPK?q2lr_$Af zxcT}Wz3MjymC`23hAwQ=swD8<4dYxl0Q-ga@f0zo(jMepWm}hWQC{y3fcA zN=j7`0tid-pr0f&v=1avSr547aUdGcrJf0_D};fMGHK%vMtkCIKYk@`>E_Y*rI?z- zV>UY9YkgnX=ZM8N7H^1E&j+f%PUMD>&qZd_eapzk`_SrB0Tk9Q@~+C+V?C}f0GtxU zfHmMxLAgWrAC!PjiaHmHt^lN3XiF*Pk$wFAKuY*C!6v8<#0@sPgRbA1f~-2xzmmNT&O)zD3(I1#dLAJ6dSbn?5}M(+0wjM2+tz)m$~d0*2?aGMfL zA*K~EDU&G+*7C1jBMy<~E?r6Zie63Vku}8R6*$dbAf1_#CSO9+DCZn6>=(MH?o|(| zJtG?V-_?F_j`6!*(3zl94_NIVqOf`{Fa^WuOt+RZ-*oQG>3x6?2PX8Y0km|(pVN0q zfezc14>2y#Rq7cL$&7ahm>`(iTWIN&GSHzRJ$jd9?)EHqJ802^InCXz6=>jB(9)oYf{szwSvPKlbvu=L-{I{7UzQO z=N+gW0$Ts6DA%KkTIZY0vFXOfMhlp`YEm-!O*>~?b%3ugJlO;%C!Wm3nG(;texETL z?m-yt70|z^ht`1N(;=I%Sh0UB?!%!g&On%Jj$}5YAdt_5E86vDiH9#H zr^)0d>t)Y!&z6Uz`zh?`BZ(9{8E3u9&ysw6HaFXhyBWGOD!dd2npEUk)g_tb!sxCx zd~D%PoxSF}uchC3&BVMlCt4VJPAY<{mv_HVdOJX{n``h3gF7vkie;|{5t6mVc3$x@ z{>FQS3j?H1w$UHA3$;q~q{hvZ0c+?gpvq~rm*&o`$TR@1P%1O1K|s-$-0IqCE5IrWKel`S;d_M zH-F)CO=S9=wy^C1()Df)yJv-7NO!%{I6KQYFq8F6Fx58VoJ!tr%IWFW$REU6N!v%n zqC=knDQVC>`MM%!M1$O6YK!bO#jPSc&()W45AZXOv1U1YH<=At<>moDYbA{+UgRs& zK_NpxRum`MC7OV&Dr7wcv(lG3c(y*Gok61wt>FO(Mh|j@%VLFChL^=qjfP8=07pud zzazN#K-d&7(I@@{0tqm(@$wSB{uEZk3QqLJGOYtMbdQv-LA#8tOYAFlfIRumr2 zEuw5Q7B(IPfYLYU8W-|pNq~23+MjuV*GS4+!uqW*1H&EWezj%=j3!;UC@~ui@E*^$ zSyhs^5|E9;`uWs;yhBLlW{&KLzNm8useXEaGj#p zp-~bYfK&u2Z!$azfqY7N8vZ&C>Hl+MZWtwCEycgld_X;mD}`aM+R?M?XFJ^(oCj+c zG{XmQ;inMH0N~UaKf9nh^fb5|L|L&XaIa9Nh$VW`h$sipxhZsAq5uYN8R3_ZY4{kS zhB3_?IWxs7+`tpxcx&pWSk<$!H6<<{bv94ht%qiWZe3I8*7=tB3DAzv1UtAOh>%;Z zBH2`)&Im5*X>9Z%)B<+)4@I>kkeTLO;ID(O{hk&%*W5FSn-Mhi&~pLctkdz4Cwk+h zWYju3IvO-dniU!=^9KRXGgJ)No^*b)`11^NJ;urCEF7r4@F?p0n)D1%&vXxQ?fL;6 zj0ce1MMdNcz>hv?9{(gy^b=t|1_2cU&P+$0QYpP>61q#o1+kEH4vTV0z9rKLa{eZLl(3SPuqKXo11%pxAP|ox8G>&Q4H}9t?a7gY|%~t z9Gq54Nd_o>Lbh}sG^|(xZppsNe);+ryN|~^k-=M|?!3`FtWCDMVYhfDG4&04O430i z2X2kV@}KG5;l)pYg3u)f58fSQ<$n4y?}A1Bk^>go6oBfoy0T8}QOJk)f;&uZptud65c-Wfa+yWN0B-UZqd&;hma^~(?<9QmKBGU&PL48We zL*Q*|p{Kutr@_4+Y^Ua_2~#R4I`{lu)6*0FTOY^NGw{TD3cyzvgj@_tA{G**9;^Ze zzCZ>xWMw@@^Nv<#R@%67Giw%ku;>7xNdZ(U1aWKXV*Ci%qDb5=;vKkw;FFfZwz+iy zevwP|e_r~1l-O&U>o2pk6ZmxYmJ=e4?t`LDF8C0HjjH6pvI9Y>zNwk4;c&0mqlUF( zfR)&Nm}{p28f(C^309*01d)F3e!afv#DSxx?se=0UUFY?bj0}HGQ(bs!DO`lSCus` z@e$~o@h8#=c_zfYW<$5ZqQECNg!KqOn&G9vDJ?ia$Z-?i&wRixG4L!gNE;+qoNzwA zU^TF;&Hyfs0rk8XfbV3nJP_0#G#Ugl?>qUPZ`aga^yjG+*&#+=PNcA)PeZgr%aI3u z1g9Zc6e&DBu4DcHpr4JyxZHN~+E>0WCvN*Yl-0L|&Rx1i&Q*GZ-~L((iUWM}A%^;) zJEy((5C@1lO|=b}z}W>^)eeIo?D{|ol?o};1bBl56r69aj4{)B+IJz|>8cbTct%Tw zpNnH7Wk={H!hXCtBKZ(l0k|cRsWg=iimFJ?JHE>l*G{h%N7DbEQc}U^fsV(fxvSMe z98U#+t`zMu%|l3da%ibVKvi=zrwl}1_8vH|QMAZWW`g@%I)E&XqI56+TBu9_>GS`y zX6bK{n%DsgitQ^-$~ zU)8L+cqQoM4KGM}4?ZCQxTV8Qx_fbF<1|GFuU}HJb*8h#Jb`nnz;YC{Da#qUJ0DW$ zXLInh0$WvYj$iQIv$*%@#K;dkM?PL$FhLGDagAE>8yRnAQM`^W(H^_1aOr-u*?8%<}~Cza&c++;?60+^%gUL)j>Nq&wCSIFW^Sw z2^Wj9O;`#b>kdl6B@E9jfdf|^bhNdldzS)tSukIekdL)!FnHA#j*XfHW_F zTR4CL#Fh#ypqCmi<~9ALe*EIuql;T1+ZZx9Efc2duk*mM$|~u% hDSgYHgToBiC}7BA2$U-G$}4360#8>zmvv4FO#pAURKoxO From 7f7815e3a2cfcd5e5c524ebed9861f1e1c50b006 Mon Sep 17 00:00:00 2001 From: Kirandeep-Singh-Khehra Date: Wed, 11 Dec 2024 22:40:39 +0530 Subject: [PATCH 5/6] [rmodels] Updated blend function signature and added function to update verts from bones Signed-off-by: Kirandeep-Singh-Khehra --- examples/models/models_animation_blending.c | 18 +++++- src/raylib.h | 3 +- src/rmodels.c | 71 +++++++++++++++++++-- 3 files changed, 83 insertions(+), 9 deletions(-) diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c index 43a772756a99..f6985e32c8d1 100644 --- a/examples/models/models_animation_blending.c +++ b/examples/models/models_animation_blending.c @@ -48,6 +48,7 @@ int main(void) // Load gltf model Model characterModel = LoadModel("resources/models/gltf/robot.glb"); // Load character model +/* INFO: Uncomment this to use GPU skinning // Load skinning shader Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION), TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION)); @@ -56,6 +57,7 @@ int main(void) { characterModel.materials[i].shader = skinningShader; } +*/ // Load gltf model animations int animsCount = 0; @@ -93,7 +95,10 @@ int main(void) // Update bones // Note: Same animation frame index is used below. By default it loops both animations - UpdateModelAnimationBonesWithBlending(characterModel, modelAnimations[animIndex0], animCurrentFrame, modelAnimations[animIndex1], animCurrentFrame, blendFactor); + UpdateModelAnimationBonesLerp(characterModel, modelAnimations[animIndex0], animCurrentFrame, modelAnimations[animIndex1], animCurrentFrame, blendFactor); + +// INFO: Comment the following line to use GPU skinning + UpdateModelVertsToCurrentBones(characterModel); //---------------------------------------------------------------------------------- // Draw @@ -103,12 +108,17 @@ int main(void) ClearBackground(RAYWHITE); BeginMode3D(camera); - + +/* INFO: Uncomment this to use GPU skinning // Draw character mesh, pose calculation is done in shader (GPU skinning) for (int i = 0; i < characterModel.meshCount; i++) { DrawMesh(characterModel.meshes[i], characterModel.materials[characterModel.meshMaterial[i]], characterModel.transform); } +*/ + +// INFO: Comment the following line to use GPU skinning + DrawModel(characterModel, (Vector3){0.0f, 0.0f, 0.0f}, 1.0f, WHITE); DrawGrid(10, 1.0f); @@ -129,7 +139,9 @@ int main(void) //-------------------------------------------------------------------------------------- UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation UnloadModel(characterModel); // Unload model and meshes/material - UnloadShader(skinningShader); // Unload GPU skinning shader + +// INFO: Uncomment the following line to use GPU skinning + // UnloadShader(skinningShader); // Unload GPU skinning shader CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/src/raylib.h b/src/raylib.h index bb25ea94206a..56f752c0e054 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1605,7 +1605,8 @@ RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount); // Load model animations from file RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose (CPU) RLAPI void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame); // Update model animation mesh bone matrices (GPU skinning) -RLAPI void UpdateModelAnimationBonesWithBlending(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float blendFactor); // Update model animation mesh bone matrices with blending between two poses(GPU skinning) +RLAPI void UpdateModelAnimationBonesLerp(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float value); // Update model animation mesh bone matrices with interpolation between two poses(GPU skinning) +RLAPI void UpdateModelVertsToCurrentBones(Model model); // Update model vertices according to mesh bone matrices (CPU) RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match diff --git a/src/rmodels.c b/src/rmodels.c index a215bcc8c7c8..c74feb6c81b3 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2311,11 +2311,11 @@ void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame) // Update model animated bones transform matrices by blendin between two different given frames of different ModelAnimation(could be same too) // NOTE: Updated data is not uploaded to GPU but kept at model.meshes[i].boneMatrices[boneId], // to be uploaded to shader at drawing, in case GPU skinning is enabled -void UpdateModelAnimationBonesWithBlending(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float blendFactor) +void UpdateModelAnimationBonesLerp(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float value) { if ((animA.frameCount > 0) && (animA.bones != NULL) && (animA.framePoses != NULL) && (animB.frameCount > 0) && (animB.bones != NULL) && (animB.framePoses != NULL) && - (blendFactor >= 0.0f) && (blendFactor <= 1.0f)) + (value >= 0.0f) && (value <= 1.0f)) { frameA = frameA % animA.frameCount; frameB = frameB % animB.frameCount; @@ -2341,9 +2341,9 @@ void UpdateModelAnimationBonesWithBlending(Model model, ModelAnimation animA, in Quaternion outBRotation = animB.framePoses[frameB][boneId].rotation; Vector3 outBScale = animB.framePoses[frameB][boneId].scale; - Vector3 outTranslation = Vector3Lerp(outATranslation, outBTranslation, blendFactor); - Quaternion outRotation = QuaternionSlerp(outARotation, outBRotation, blendFactor); - Vector3 outScale = Vector3Lerp(outAScale, outBScale, blendFactor); + Vector3 outTranslation = Vector3Lerp(outATranslation, outBTranslation, value); + Quaternion outRotation = QuaternionSlerp(outARotation, outBRotation, value); + Vector3 outScale = Vector3Lerp(outAScale, outBScale, value); Vector3 invTranslation = Vector3RotateByQuaternion(Vector3Negate(inTranslation), QuaternionInvert(inRotation)); Quaternion invRotation = QuaternionInvert(inRotation); @@ -2367,6 +2367,67 @@ void UpdateModelAnimationBonesWithBlending(Model model, ModelAnimation animA, in } } +// Update model vertex data (positions and normals) from mesh bone data +// NOTE: Updated data is uploaded to GPU +void UpdateModelVertsToCurrentBones(Model model) +{ + for (int m = 0; m < model.meshCount; m++) + { + Mesh mesh = model.meshes[m]; + Vector3 animVertex = { 0 }; + Vector3 animNormal = { 0 }; + int boneId = 0; + int boneCounter = 0; + float boneWeight = 0.0; + bool updated = false; // Flag to check when anim vertex information is updated + const int vValues = mesh.vertexCount*3; + for (int vCounter = 0; vCounter < vValues; vCounter += 3) + { + mesh.animVertices[vCounter] = 0; + mesh.animVertices[vCounter + 1] = 0; + mesh.animVertices[vCounter + 2] = 0; + if (mesh.animNormals != NULL) + { + mesh.animNormals[vCounter] = 0; + mesh.animNormals[vCounter + 1] = 0; + mesh.animNormals[vCounter + 2] = 0; + } + // Iterates over 4 bones per vertex + for (int j = 0; j < 4; j++, boneCounter++) + { + boneWeight = mesh.boneWeights[boneCounter]; + boneId = mesh.boneIds[boneCounter]; + + // Early stop when no transformation will be applied + if (boneWeight == 0.0f) continue; + animVertex = (Vector3){ mesh.vertices[vCounter], mesh.vertices[vCounter + 1], mesh.vertices[vCounter + 2] }; + animVertex = Vector3Transform(animVertex,model.meshes[m].boneMatrices[boneId]); + mesh.animVertices[vCounter] += animVertex.x*boneWeight; + mesh.animVertices[vCounter+1] += animVertex.y*boneWeight; + mesh.animVertices[vCounter+2] += animVertex.z*boneWeight; + updated = true; + + // Normals processing + // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) + if (mesh.normals != NULL) + { + animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] }; + animNormal = Vector3Transform(animNormal,model.meshes[m].boneMatrices[boneId]); + mesh.animNormals[vCounter] += animNormal.x*boneWeight; + mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight; + mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight; + } + } + } + + if (updated) + { + rlUpdateVertexBuffer(mesh.vboId[0], mesh.animVertices, mesh.vertexCount*3*sizeof(float), 0); // Update vertex position + rlUpdateVertexBuffer(mesh.vboId[2], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0); // Update vertex normals + } + } +} + // at least 2x speed up vs the old method // Update model animated vertex data (positions and normals) for a given frame // NOTE: Updated data is uploaded to GPU From fa5f5e38b5f42301b78e4a73679f3a65b4490451 Mon Sep 17 00:00:00 2001 From: Kirandeep-Singh-Khehra Date: Wed, 11 Dec 2024 22:53:23 +0530 Subject: [PATCH 6/6] [rmodels] Updated documentation Signed-off-by: Kirandeep-Singh-Khehra --- examples/models/models_animation_blending.c | 2 + src/rmodels.c | 58 +-------------------- 2 files changed, 4 insertions(+), 56 deletions(-) diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c index f6985e32c8d1..225c1bfbb451 100644 --- a/examples/models/models_animation_blending.c +++ b/examples/models/models_animation_blending.c @@ -12,6 +12,8 @@ * Copyright (c) 2024 Kirandeep (@Kirandeep-Singh-Khehra) * * Note: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS +* Note: This example uses CPU for updating meshes. +* For GPU skinning see comments with 'INFO:'. * ********************************************************************************************/ diff --git a/src/rmodels.c b/src/rmodels.c index c74feb6c81b3..26c26281a516 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2308,7 +2308,7 @@ void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame) } } -// Update model animated bones transform matrices by blendin between two different given frames of different ModelAnimation(could be same too) +// Update model animated bones transform matrices by interpolating between two different given frames of different ModelAnimation(could be same too) // NOTE: Updated data is not uploaded to GPU but kept at model.meshes[i].boneMatrices[boneId], // to be uploaded to shader at drawing, in case GPU skinning is enabled void UpdateModelAnimationBonesLerp(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float value) @@ -2434,61 +2434,7 @@ void UpdateModelVertsToCurrentBones(Model model) void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) { UpdateModelAnimationBones(model,anim,frame); - for (int m = 0; m < model.meshCount; m++) - { - Mesh mesh = model.meshes[m]; - Vector3 animVertex = { 0 }; - Vector3 animNormal = { 0 }; - int boneId = 0; - int boneCounter = 0; - float boneWeight = 0.0; - bool updated = false; // Flag to check when anim vertex information is updated - const int vValues = mesh.vertexCount*3; - for (int vCounter = 0; vCounter < vValues; vCounter += 3) - { - mesh.animVertices[vCounter] = 0; - mesh.animVertices[vCounter + 1] = 0; - mesh.animVertices[vCounter + 2] = 0; - if (mesh.animNormals != NULL) - { - mesh.animNormals[vCounter] = 0; - mesh.animNormals[vCounter + 1] = 0; - mesh.animNormals[vCounter + 2] = 0; - } - // Iterates over 4 bones per vertex - for (int j = 0; j < 4; j++, boneCounter++) - { - boneWeight = mesh.boneWeights[boneCounter]; - boneId = mesh.boneIds[boneCounter]; - - // Early stop when no transformation will be applied - if (boneWeight == 0.0f) continue; - animVertex = (Vector3){ mesh.vertices[vCounter], mesh.vertices[vCounter + 1], mesh.vertices[vCounter + 2] }; - animVertex = Vector3Transform(animVertex,model.meshes[m].boneMatrices[boneId]); - mesh.animVertices[vCounter] += animVertex.x*boneWeight; - mesh.animVertices[vCounter+1] += animVertex.y*boneWeight; - mesh.animVertices[vCounter+2] += animVertex.z*boneWeight; - updated = true; - - // Normals processing - // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) - if (mesh.normals != NULL) - { - animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] }; - animNormal = Vector3Transform(animNormal,model.meshes[m].boneMatrices[boneId]); - mesh.animNormals[vCounter] += animNormal.x*boneWeight; - mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight; - mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight; - } - } - } - - if (updated) - { - rlUpdateVertexBuffer(mesh.vboId[0], mesh.animVertices, mesh.vertexCount*3*sizeof(float), 0); // Update vertex position - rlUpdateVertexBuffer(mesh.vboId[2], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0); // Update vertex normals - } - } + UpdateModelVertsToCurrentBones(model); } // Unload animation array data