diff --git a/examples/Makefile b/examples/Makefile
index 34cf36f0..e4698917 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -589,6 +589,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 dd5dc688..8e20f0b0 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 d25ad10c..b564c5ee 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) | | ⭐️⭐️☆☆ | 2.5 | 3.5 | [culacant](https://github.com/culacant) |
-| 86 | [models_billboard](models/models_billboard.c) | | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) |
-| 87 | [models_box_collisions](models/models_box_collisions.c) | | ⭐️☆☆☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) |
-| 88 | [models_cubicmap](models/models_cubicmap.c) | | ⭐️⭐️☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) |
-| 89 | [models_first_person_maze](models/models_first_person_maze.c) | | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) |
-| 90 | [models_geometric_shapes](models/models_geometric_shapes.c) | | ⭐️☆☆☆ | 1.0 | 3.5 | [Ray](https://github.com/raysan5) |
-| 91 | [models_mesh_generation](models/models_mesh_generation.c) | | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) |
-| 92 | [models_mesh_picking](models/models_mesh_picking.c) | | ⭐️⭐️⭐️☆ | 1.7 | **4.0** | [Joel Davis](https://github.com/joeld42) |
-| 93 | [models_loading](models/models_loading.c) | | ⭐️☆☆☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) |
-| 94 | [models_loading_gltf](models/models_loading_gltf.c) | | ⭐️☆☆☆ | 3.7 | **4.2** | [Ray](https://github.com/raysan5) |
-| 95 | [models_loading_vox](models/models_loading_vox.c) | | ⭐️☆☆☆ | **4.0** | **4.0** | [Johann Nadalutti](https://github.com/procfxgen) |
-| 96 | [models_loading_m3d](models/models_loading_m3d.c) | | ⭐️☆☆☆ | **4.2** | **4.2** | [bzt](https://bztsrc.gitlab.io/model3d) |
-| 97 | [models_orthographic_projection](models/models_orthographic_projection.c) | | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) |
-| 98 | [models_point_rendering](models/models_point_rendering.c) | | ⭐️⭐️☆☆ | 5.0 | 5.0 | [Reese Gallagher](https://github.com/satchelfrost) |
-| 99 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | | ⭐️⭐️⭐️⭐️ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) |
-| 100 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Berni](https://github.com/Berni8k) |
-| 101 | [models_waving_cubes](models/models_waving_cubes.c) | | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [codecat](https://github.com/codecat) |
-| 102 | [models_heightmap](models/models_heightmap.c) | | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) |
-| 103 | [models_skybox](models/models_skybox.c) | | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) |
+| 86 | [models_animation_blending](models/models_animation_blending.c) | | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Kirandeep-Singh-Khehra](https://github.com/Kirandeep-Singh-Khehra) |
+| 87 | [models_billboard](models/models_billboard.c) | | ⭐️⭐️⭐️☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) |
+| 88 | [models_box_collisions](models/models_box_collisions.c) | | ⭐️☆☆☆ | 1.3 | 3.5 | [Ray](https://github.com/raysan5) |
+| 89 | [models_cubicmap](models/models_cubicmap.c) | | ⭐️⭐️☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) |
+| 90 | [models_first_person_maze](models/models_first_person_maze.c) | | ⭐️⭐️☆☆ | 2.5 | 3.5 | [Ray](https://github.com/raysan5) |
+| 91 | [models_geometric_shapes](models/models_geometric_shapes.c) | | ⭐️☆☆☆ | 1.0 | 3.5 | [Ray](https://github.com/raysan5) |
+| 92 | [models_mesh_generation](models/models_mesh_generation.c) | | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Ray](https://github.com/raysan5) |
+| 93 | [models_mesh_picking](models/models_mesh_picking.c) | | ⭐️⭐️⭐️☆ | 1.7 | **4.0** | [Joel Davis](https://github.com/joeld42) |
+| 94 | [models_loading](models/models_loading.c) | | ⭐️☆☆☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) |
+| 95 | [models_loading_gltf](models/models_loading_gltf.c) | | ⭐️☆☆☆ | 3.7 | **4.2** | [Ray](https://github.com/raysan5) |
+| 96 | [models_loading_vox](models/models_loading_vox.c) | | ⭐️☆☆☆ | **4.0** | **4.0** | [Johann Nadalutti](https://github.com/procfxgen) |
+| 97 | [models_loading_m3d](models/models_loading_m3d.c) | | ⭐️☆☆☆ | **4.2** | **4.2** | [bzt](https://bztsrc.gitlab.io/model3d) |
+| 98 | [models_orthographic_projection](models/models_orthographic_projection.c) | | ⭐️☆☆☆ | 2.0 | 3.7 | [Max Danielsson](https://github.com/autious) |
+| 99 | [models_point_rendering](models/models_point_rendering.c) | | ⭐️⭐️☆☆ | 5.0 | 5.0 | [Reese Gallagher](https://github.com/satchelfrost) |
+| 100 | [models_rlgl_solar_system](models/models_rlgl_solar_system.c) | | ⭐️⭐️⭐️⭐️ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) |
+| 101 | [models_yaw_pitch_roll](models/models_yaw_pitch_roll.c) | | ⭐️⭐️☆☆ | 1.8 | **4.0** | [Berni](https://github.com/Berni8k) |
+| 102 | [models_waving_cubes](models/models_waving_cubes.c) | | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [codecat](https://github.com/codecat) |
+| 103 | [models_heightmap](models/models_heightmap.c) | | ⭐️☆☆☆ | 1.8 | 3.5 | [Ray](https://github.com/raysan5) |
+| 104 | [models_skybox](models/models_skybox.c) | | ⭐️⭐️☆☆ | 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) | | ⭐️⭐️⭐️⭐️ | 3.0 | **4.2** | [Chris Camacho](https://github.com/codifies) |
-| 105 | [shaders_model_shader](shaders/shaders_model_shader.c) | | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) |
-| 106 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) |
-| 107 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) |
-| 108 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | | ⭐️⭐️⭐️☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) |
-| 109 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) |
-| 110 | [shaders_raymarching](shaders/shaders_raymarching.c) | | ⭐️⭐️⭐️⭐️ | 2.0 | **4.2** | [Ray](https://github.com/raysan5) |
-| 111 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/) |
-| 112 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | | ⭐️⭐️⭐️☆ | **4.0** | **4.0** | [Samuel Skiff](https://github.com/GoldenThumbs) |
-| 113 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) |
-| 114 | [shaders_julia_set](shaders/shaders_julia_set.c) | | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [eggmund](https://github.com/eggmund) |
-| 115 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [ProfJski](https://github.com/ProfJski) |
-| 116 | [shaders_fog](shaders/shaders_fog.c) | | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
-| 117 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
-| 118 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) |
-| 119 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | | ⭐️⭐️⭐️⭐️ | 3.7 | **4.2** | [seanpringle](https://github.com/seanpringle) |
-| 120 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) |
-| 121 | [shaders_spotlight](shaders/shaders_spotlight.c) | | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
-| 122 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) |
+| 105 | [shaders_basic_lighting](shaders/shaders_basic_lighting.c) | | ⭐️⭐️⭐️⭐️ | 3.0 | **4.2** | [Chris Camacho](https://github.com/codifies) |
+| 106 | [shaders_model_shader](shaders/shaders_model_shader.c) | | ⭐️⭐️☆☆ | 1.3 | 3.7 | [Ray](https://github.com/raysan5) |
+| 107 | [shaders_shapes_textures](shaders/shaders_shapes_textures.c) | | ⭐️⭐️☆☆ | 1.7 | 3.7 | [Ray](https://github.com/raysan5) |
+| 108 | [shaders_custom_uniform](shaders/shaders_custom_uniform.c) | | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) |
+| 109 | [shaders_postprocessing](shaders/shaders_postprocessing.c) | | ⭐️⭐️⭐️☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) |
+| 110 | [shaders_palette_switch](shaders/shaders_palette_switch.c) | | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Marco Lizza](https://github.com/MarcoLizza) |
+| 111 | [shaders_raymarching](shaders/shaders_raymarching.c) | | ⭐️⭐️⭐️⭐️ | 2.0 | **4.2** | [Ray](https://github.com/raysan5) |
+| 112 | [shaders_texture_drawing](shaders/shaders_texture_drawing.c) | | ⭐️⭐️☆☆ | 2.0 | 3.7 | [Michał Ciesielski](https://github.com/) |
+| 113 | [shaders_texture_outline](shaders/shaders_texture_outline.c) | | ⭐️⭐️⭐️☆ | **4.0** | **4.0** | [Samuel Skiff](https://github.com/GoldenThumbs) |
+| 114 | [shaders_texture_waves](shaders/shaders_texture_waves.c) | | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Anata](https://github.com/anatagawa) |
+| 115 | [shaders_julia_set](shaders/shaders_julia_set.c) | | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [eggmund](https://github.com/eggmund) |
+| 116 | [shaders_eratosthenes](shaders/shaders_eratosthenes.c) | | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [ProfJski](https://github.com/ProfJski) |
+| 117 | [shaders_fog](shaders/shaders_fog.c) | | ⭐️⭐️⭐️☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
+| 118 | [shaders_simple_mask](shaders/shaders_simple_mask.c) | | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
+| 119 | [shaders_hot_reloading](shaders/shaders_hot_reloading.c) | | ⭐️⭐️⭐️☆ | 3.0 | 3.5 | [Ray](https://github.com/raysan5) |
+| 120 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | | ⭐️⭐️⭐️⭐️ | 3.7 | **4.2** | [seanpringle](https://github.com/seanpringle) |
+| 121 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) |
+| 122 | [shaders_spotlight](shaders/shaders_spotlight.c) | | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) |
+| 123 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | | ⭐️⭐️⭐️⭐️ | 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) | | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) |
-| 124 | [audio_music_stream](audio/audio_music_stream.c) | | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) |
-| 125 | [audio_raw_stream](audio/audio_raw_stream.c) | | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) |
-| 126 | [audio_sound_loading](audio/audio_sound_loading.c) | | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) |
+| 124 | [audio_module_playing](audio/audio_module_playing.c) | | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) |
+| 125 | [audio_music_stream](audio/audio_music_stream.c) | | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) |
+| 126 | [audio_raw_stream](audio/audio_raw_stream.c) | | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) |
+| 127 | [audio_sound_loading](audio/audio_sound_loading.c) | | ⭐️☆☆☆ | 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) | | ⭐️⭐️⭐️⭐️ | 1.6 | **4.0** | [Ray](https://github.com/raysan5) |
-| 128 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Teddy Astie](https://github.com/tsnake41) |
-| 129 | [easings_testbed](others/easings_testbed.c) | | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) |
-| 130 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Stephan Soller](https://github.com/arkanis) |
-| 131 | [embedded_files_loading](others/embedded_files_loading.c) | | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Kristian Holmgren](https://github.com/defutura) |
+| 128 | [rlgl_standalone](others/rlgl_standalone.c) | | ⭐️⭐️⭐️⭐️ | 1.6 | **4.0** | [Ray](https://github.com/raysan5) |
+| 129 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Teddy Astie](https://github.com/tsnake41) |
+| 130 | [easings_testbed](others/easings_testbed.c) | | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) |
+| 131 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Stephan Soller](https://github.com/arkanis) |
+| 132 | [embedded_files_loading](others/embedded_files_loading.c) | | ⭐️⭐️☆☆ | 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.c b/examples/models/models_animation_blending.c
new file mode 100644
index 00000000..225c1bfb
--- /dev/null
+++ b/examples/models/models_animation_blending.c
@@ -0,0 +1,152 @@
+/*******************************************************************************************
+*
+* 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
+* Note: This example uses CPU for updating meshes.
+* For GPU skinning see comments with 'INFO:'.
+*
+********************************************************************************************/
+
+#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
+
+/* 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));
+
+ 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
+ UpdateModelAnimationBonesLerp(characterModel, modelAnimations[animIndex0], animCurrentFrame, modelAnimations[animIndex1], animCurrentFrame, blendFactor);
+
+// INFO: Comment the following line to use GPU skinning
+ UpdateModelVertsToCurrentBones(characterModel);
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ 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);
+
+ EndMode3D();
+
+ 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);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation
+ UnloadModel(characterModel); // Unload model and meshes/material
+
+// INFO: Uncomment the following line to use GPU skinning
+ // UnloadShader(skinningShader); // Unload GPU skinning shader
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
diff --git a/examples/models/models_animation_blending.png b/examples/models/models_animation_blending.png
new file mode 100644
index 00000000..0d70c1a8
Binary files /dev/null and b/examples/models/models_animation_blending.png differ
diff --git a/projects/VS2022/examples/models_animation_blending.vcxproj b/projects/VS2022/examples/models_animation_blending.vcxproj
new file mode 100644
index 00000000..bf8a8b27
--- /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 feb0f2c3..a20a56a2 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
diff --git a/src/raylib.h b/src/raylib.h
index 7e1a1f83..9ec01b1f 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -1609,6 +1609,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 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 a159d543..b876ba36 100644
--- a/src/rmodels.c
+++ b/src/rmodels.c
@@ -2308,12 +2308,69 @@ void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame)
}
}
-// at least 2x speed up vs the old method
-// Update model animated vertex data (positions and normals) for a given frame
+// 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)
+{
+ if ((animA.frameCount > 0) && (animA.bones != NULL) && (animA.framePoses != NULL) &&
+ (animB.frameCount > 0) && (animB.bones != NULL) && (animB.framePoses != NULL) &&
+ (value >= 0.0f) && (value <= 1.0f))
+ {
+ 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, value);
+ Quaternion outRotation = QuaternionSlerp(outARotation, outBRotation, value);
+ Vector3 outScale = Vector3Lerp(outAScale, outBScale, value);
+
+ 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;
+ }
+ }
+ }
+ }
+}
+
+// Update model vertex data (positions and normals) from mesh bone data
// NOTE: Updated data is uploaded to GPU
-void UpdateModelAnimation(Model model, ModelAnimation anim, int frame)
+void UpdateModelVertsToCurrentBones(Model model)
{
- UpdateModelAnimationBones(model,anim,frame);
for (int m = 0; m < model.meshCount; m++)
{
Mesh mesh = model.meshes[m];
@@ -2371,6 +2428,15 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame)
}
}
+// 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
+void UpdateModelAnimation(Model model, ModelAnimation anim, int frame)
+{
+ UpdateModelAnimationBones(model,anim,frame);
+ UpdateModelVertsToCurrentBones(model);
+}
+
// Unload animation array data
void UnloadModelAnimations(ModelAnimation *animations, int animCount)
{