2121
2222#include "raylib.h"
2323
24- #define clamp (x ,a ,b ) ((x < a)? a : (x > b)? b : x)
24+ #define RAYGUI_IMPLEMENTATION
25+ #include "raygui.h" // Required for: UI controls
2526
2627#if defined(PLATFORM_DESKTOP )
2728 #define GLSL_VERSION 330
@@ -43,31 +44,58 @@ int main(void)
4344
4445 // Define the camera to look into our 3d world
4546 Camera camera = { 0 };
46- camera .position = (Vector3 ){ 8 .0f , 8 .0f , 8 .0f }; // Camera position
47+ camera .position = (Vector3 ){ 6 .0f , 6 .0f , 6 .0f }; // Camera position
4748 camera .target = (Vector3 ){ 0.0f , 2.0f , 0.0f }; // Camera looking at point
4849 camera .up = (Vector3 ){ 0.0f , 1.0f , 0.0f }; // Camera up vector (rotation towards target)
4950 camera .fovy = 45.0f ; // Camera field-of-view Y
5051 camera .projection = CAMERA_PERSPECTIVE ; // Camera projection type
5152
5253 // Load model
53- Model characterModel = LoadModel ("resources/models/gltf/robot.glb" ); // Load character model
54-
54+ Model model = LoadModel ("resources/models/gltf/robot.glb" ); // Load character model
55+ Vector3 position = { 0.0f , 0.0f , 0.0f }; // Set model world position
56+
5557 // Load skinning shader
58+ // WARNING: It requires SUPPORT_GPU_SKINNING enabled on raylib (disabled by default)
5659 Shader skinningShader = LoadShader (TextFormat ("resources/shaders/glsl%i/skinning.vs" , GLSL_VERSION ),
5760 TextFormat ("resources/shaders/glsl%i/skinning.fs" , GLSL_VERSION ));
5861
5962 // Assign skinning shader to all materials shaders
60- for (int i = 0 ; i < characterModel .materialCount ; i ++ ) characterModel .materials [i ].shader = skinningShader ;
63+ // for (int i = 0; i < model .materialCount; i++) model .materials[i].shader = skinningShader;
6164
6265 // Load model animations
63- int animsCount = 0 ;
64- ModelAnimation * modelAnimations = LoadModelAnimations ("resources/models/gltf/robot.glb" , & animsCount );
66+ int animCount = 0 ;
67+ ModelAnimation * anims = LoadModelAnimations ("resources/models/gltf/robot.glb" , & animCount );
68+
69+ // Animation playing variables
70+ // NOTE: Two animations are played with a smooth transition between them
71+ int currentAnimPlaying = 0 ; // Current animation playing (0 o 1)
72+ int nextAnimToPlay = 1 ; // Next animation to play (to transition)
73+ bool animTransition = false; // Flag to register anim transition state
74+
75+ int animIndex0 = 10 ; // Current animation playing (walking)
76+ float animCurrentFrame0 = 0.0f ; // Current animation frame (supporting interpolated frames)
77+ float animFrameSpeed0 = 0.5f ; // Current animation play speed
78+ int animIndex1 = 6 ; // Next animation to play (running)
79+ float animCurrentFrame1 = 0.0f ; // Next animation frame (supporting interpolated frames)
80+ float animFrameSpeed1 = 0.5f ; // Next animation play speed
81+
82+ float animBlendFactor = 0.0f ; // Blend factor from anim0[frame0] --> anim1[frame1], [0.0f..1.0f]
83+ // NOTE: 0.0f results in full anim0[] and 1.0f in full anim1[]
84+
85+ float animBlendTime = 2.0f ; // Time to blend from one playing animation to another (in seconds)
86+ float animBlendTimeCounter = 0.0f ; // Time counter (delta time)
6587
66- // Define animation variables
67- unsigned int animIndex0 = 0 ;
68- unsigned int animIndex1 = 0 ;
69- float animCurrentFrame = 0 ;
70- float blendFactor = 0.5f ;
88+ bool animPause = false; // Pause animation
89+
90+ // UI required variables
91+ char * animNames [64 ] = { 0 }; // Pointers to animation names for dropdown box
92+ for (int i = 0 ; i < animCount ; i ++ ) animNames [i ] = anims [i ].name ;
93+
94+ bool dropdownEditMode0 = false;
95+ bool dropdownEditMode1 = false;
96+ float animFrameProgress0 = 0.0f ;
97+ float animFrameProgress1 = 0.0f ;
98+ float animBlendProgress = 0.0f ;
7199
72100 SetTargetFPS (60 ); // Set our game to run at 60 frames-per-second
73101 //--------------------------------------------------------------------------------------
@@ -79,23 +107,100 @@ int main(void)
79107 //----------------------------------------------------------------------------------
80108 UpdateCamera (& camera , CAMERA_ORBITAL );
81109
82- // Select current animation
83- if (IsKeyPressed (KEY_T )) animIndex0 = (animIndex0 + 1 )%animsCount ;
84- else if (IsKeyPressed (KEY_G )) animIndex0 = (animIndex0 + animsCount - 1 )%animsCount ;
85- if (IsKeyPressed (KEY_Y )) animIndex1 = (animIndex1 + 1 )%animsCount ;
86- else if (IsKeyPressed (KEY_H )) animIndex1 = (animIndex1 + animsCount - 1 )%animsCount ;
87-
88- // Select blend factor
89- if (IsKeyPressed (KEY_U )) blendFactor = clamp (blendFactor - 0.1 , 0.0f , 1.0f );
90- else if (IsKeyPressed (KEY_J )) blendFactor = clamp (blendFactor + 0.1 , 0.0f , 1.0f );
110+ if (IsKeyPressed (KEY_P )) animPause = !animPause ;
111+
112+ if (!animPause )
113+ {
114+ // Start transition from anim0[] to anim1[]
115+ if (IsKeyPressed (KEY_SPACE ) && !animTransition )
116+ {
117+ if (currentAnimPlaying == 0 )
118+ {
119+ // Transition anim0 --> anim1
120+ nextAnimToPlay = 1 ;
121+ animCurrentFrame1 = 0.0f ;
122+ }
123+ else
124+ {
125+ // Transition anim1 --> anim0
126+ nextAnimToPlay = 0 ;
127+ animCurrentFrame0 = 0.0f ;
128+ }
129+
130+ // Set animation transition
131+ animTransition = true;
132+ animBlendTimeCounter = 0.0f ;
133+ animBlendFactor = 0.0f ;
134+ }
91135
92- // Update animation
93- animCurrentFrame += 0.2f ;
136+ if (animTransition )
137+ {
138+ // Playing anim0 and anim1 at the same time
139+ animCurrentFrame0 += animFrameSpeed0 ;
140+ if (animCurrentFrame0 >= anims [animIndex0 ].keyframeCount ) animCurrentFrame0 = 0.0f ;
141+ animCurrentFrame1 += animFrameSpeed1 ;
142+ if (animCurrentFrame1 >= anims [animIndex1 ].keyframeCount ) animCurrentFrame1 = 0.0f ;
94143
95- // Update bones
96- // Note: Same animation frame index is used below. By default it loops both animations
97- UpdateModelAnimationEx (characterModel , modelAnimations [animIndex0 ], animCurrentFrame ,
98- modelAnimations [animIndex1 ], animCurrentFrame , blendFactor );
144+ // Increment blend factor over time to transition from anim0 --> anim1 over time
145+ // NOTE: Time blending could be other than linear, using some easing
146+ animBlendFactor = animBlendTimeCounter /animBlendTime ;
147+ animBlendTimeCounter += GetFrameTime ();
148+ animBlendProgress = animBlendFactor ;
149+
150+ // Update model with animations blending
151+ if (nextAnimToPlay == 1 )
152+ {
153+ // Blend anim0 --> anim1
154+ UpdateModelAnimationEx (model , anims [animIndex0 ], animCurrentFrame0 ,
155+ anims [animIndex1 ], animCurrentFrame1 , animBlendFactor );
156+ }
157+ else
158+ {
159+ // Blend anim1 --> anim0
160+ UpdateModelAnimationEx (model , anims [animIndex1 ], animCurrentFrame1 ,
161+ anims [animIndex0 ], animCurrentFrame0 , animBlendFactor );
162+ }
163+
164+ // Check if transition completed
165+ if (animBlendFactor > 1.0f )
166+ {
167+ // Reset frame states
168+ if (currentAnimPlaying == 0 ) animCurrentFrame0 = 0.0f ;
169+ else if (currentAnimPlaying == 1 ) animCurrentFrame1 = 0.0f ;
170+ currentAnimPlaying = nextAnimToPlay ; // Update current animation playing
171+
172+ animBlendFactor = 0.0f ; // Reset blend factor
173+ animTransition = false; // Exit transition mode
174+ animBlendTimeCounter = 0.0f ;
175+ }
176+ }
177+ else
178+ {
179+ // Play only one anim, the current one
180+ if (currentAnimPlaying == 0 )
181+ {
182+ // Playing anim0 at defined speed
183+ animCurrentFrame0 += animFrameSpeed0 ;
184+ if (animCurrentFrame0 >= anims [animIndex0 ].keyframeCount ) animCurrentFrame0 = 0.0f ;
185+ UpdateModelAnimation (model , anims [animIndex0 ], animCurrentFrame0 );
186+ //UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0,
187+ // anims[animIndex1], animCurrentFrame1, 0.0f);
188+ }
189+ else if (currentAnimPlaying == 1 )
190+ {
191+ // Playing anim1 at defined speed
192+ animCurrentFrame1 += animFrameSpeed1 ;
193+ if (animCurrentFrame1 >= anims [animIndex1 ].keyframeCount ) animCurrentFrame1 = 0.0f ;
194+ UpdateModelAnimation (model , anims [animIndex1 ], animCurrentFrame1 );
195+ //UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0,
196+ // anims[animIndex1], animCurrentFrame1, 1.0f);
197+ }
198+ }
199+ }
200+
201+ // Update progress bars values with current frame for each animation
202+ float animFrameProgress0 = animCurrentFrame0 ;
203+ float animFrameProgress1 = animCurrentFrame1 ;
99204 //----------------------------------------------------------------------------------
100205
101206 // Draw
@@ -106,26 +211,56 @@ int main(void)
106211
107212 BeginMode3D (camera );
108213
109- DrawModel (characterModel , (Vector3 ){0.0f , 0.0f , 0.0f }, 1.0f , WHITE );
214+ DrawModel (model , position , 1.0f , WHITE ); // Draw animated model
215+
110216 DrawGrid (10 , 1.0f );
111217
112218 EndMode3D ();
113219
114- DrawText ("Use the U/J to adjust blend factor" , 10 , 10 , 20 , GRAY );
115- DrawText ("Use the T/G to switch first animation" , 10 , 30 , 20 , GRAY );
116- DrawText ("Use the Y/H to switch second animation" , 10 , 50 , 20 , GRAY );
117- DrawText (TextFormat ("Animations: %s, %s" , modelAnimations [animIndex0 ].name , modelAnimations [animIndex1 ].name ), 10 , 70 , 20 , BLACK );
118- DrawText (TextFormat ("Blend Factor: %f" , blendFactor ), 10 , 86 , 20 , BLACK );
220+ if (animTransition ) DrawText ("ANIM TRANSITION BLENDING!" , 170 , 50 , 30 , BLUE );
221+
222+ // Draw UI elements
223+ //---------------------------------------------------------------------------------------------
224+ // Draw animation selectors for blending transition
225+ // NOTE: Transition does not start until requested
226+ GuiSetStyle (DROPDOWNBOX , DROPDOWN_ITEMS_SPACING , 1 );
227+ if (GuiDropdownBox ((Rectangle ){ 10 , 10 , 160 , 24 }, TextJoin (animNames , animCount , ";" ),
228+ & animIndex0 , dropdownEditMode0 )) dropdownEditMode0 = !dropdownEditMode0 ;
229+
230+ // Blending process progress bar
231+ if (nextAnimToPlay == 1 ) GuiSetStyle (PROGRESSBAR , PROGRESS_SIDE , 0 ); // Left-->Right
232+ else GuiSetStyle (PROGRESSBAR , PROGRESS_SIDE , 1 ); // Right-->Left
233+ GuiProgressBar ((Rectangle ){ 180 , 14 , 440 , 16 }, NULL , NULL , & animBlendProgress , 0.0f , 1.0f );
234+ GuiSetStyle (PROGRESSBAR , PROGRESS_SIDE , 0 ); // Reset to Left-->Right
235+
236+ if (GuiDropdownBox ((Rectangle ){ GetScreenWidth () - 170 , 10 , 160 , 24 }, TextJoin (animNames , animCount , ";" ),
237+ & animIndex1 , dropdownEditMode1 )) dropdownEditMode1 = !dropdownEditMode1 ;
238+
239+ // Draw playing timeline with keyframes for anim0[]
240+ GuiProgressBar ((Rectangle ){ 60 , GetScreenHeight () - 60 , GetScreenWidth () - 180 , 20 }, "ANIM 0" ,
241+ TextFormat ("FRAME: %.2f / %i" , animFrameProgress0 , anims [animIndex0 ].keyframeCount ),
242+ & animFrameProgress0 , 0.0f , (float )anims [animIndex0 ].keyframeCount );
243+ for (int i = 0 ; i < anims [animIndex0 ].keyframeCount ; i ++ )
244+ DrawRectangle (60 + ((float )(GetScreenWidth () - 180 )/(float )anims [animIndex0 ].keyframeCount )* (float )i ,
245+ GetScreenHeight () - 60 , 1 , 20 , BLUE );
246+
247+ // Draw playing timeline with keyframes for anim1[]
248+ GuiProgressBar ((Rectangle ){ 60 , GetScreenHeight () - 30 , GetScreenWidth () - 180 , 20 }, "ANIM 1" ,
249+ TextFormat ("FRAME: %.2f / %i" , animFrameProgress1 , anims [animIndex1 ].keyframeCount ),
250+ & animFrameProgress1 , 0.0f , (float )anims [animIndex1 ].keyframeCount );
251+ for (int i = 0 ; i < anims [animIndex1 ].keyframeCount ; i ++ )
252+ DrawRectangle (60 + ((float )(GetScreenWidth () - 180 )/(float )anims [animIndex1 ].keyframeCount )* (float )i ,
253+ GetScreenHeight () - 30 , 1 , 20 , BLUE );
254+ //---------------------------------------------------------------------------------------------
119255
120256 EndDrawing ();
121257 //----------------------------------------------------------------------------------
122258 }
123259
124260 // De-Initialization
125261 //--------------------------------------------------------------------------------------
126- UnloadModelAnimations (modelAnimations , animsCount ); // Unload model animation
127- UnloadModel (characterModel ); // Unload model and meshes/material
128-
262+ UnloadModelAnimations (anims , animCount ); // Unload model animation
263+ UnloadModel (model ); // Unload model and meshes/material
129264 UnloadShader (skinningShader ); // Unload GPU skinning shader
130265
131266 CloseWindow (); // Close window and OpenGL context
0 commit comments