Renamed some models examples for consistency

This commit is contained in:
Ray
2026-02-23 01:43:53 +01:00
parent 542333b6d3
commit ae6d34a731
15 changed files with 795 additions and 102 deletions

View File

@ -1,6 +1,6 @@
/*******************************************************************************************
*
* raylib [models] example - animation bone blending
* raylib [models] example - animation blend custom
*
* Example complexity rating: [] 4/4
*
@ -53,7 +53,7 @@ int main(void)
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - animation bone blending");
InitWindow(screenWidth, screenHeight, "raylib [models] example - animation blend custom");
// Define the camera to look into our 3d world
Camera camera = { 0 };
@ -80,7 +80,7 @@ int main(void)
TraceLog(LOG_INFO, "Found %d animations:", animsCount);
for (int i = 0; i < animsCount; i++)
{
TraceLog(LOG_INFO, " Animation %d: %s (%d frames)", i, modelAnimations[i].name, modelAnimations[i].frameCount);
TraceLog(LOG_INFO, " Animation %d: %s (%d frames)", i, modelAnimations[i].name, modelAnimations[i].keyframeCount);
}
// Use specific indices: walk/move = 2, attack = 3
@ -118,8 +118,8 @@ int main(void)
ModelAnimation anim1 = modelAnimations[animIndex1];
ModelAnimation anim2 = modelAnimations[animIndex2];
animCurrentFrame1 = (animCurrentFrame1 + 1)%anim1.frameCount;
animCurrentFrame2 = (animCurrentFrame2 + 1)%anim2.frameCount;
animCurrentFrame1 = (animCurrentFrame1 + 1)%anim1.keyframeCount;
animCurrentFrame2 = (animCurrentFrame2 + 1)%anim2.keyframeCount;
// Blend the two animations
characterModel.transform = MatrixTranslate(position.x, position.y, position.z);
@ -203,9 +203,9 @@ static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int f
ModelAnimation *anim2, int frame2, float blendFactor, bool upperBodyBlend)
{
// Validate inputs
if (anim1->boneCount == 0 || anim1->framePoses == NULL ||
anim2->boneCount == 0 || anim2->framePoses == NULL ||
model->boneCount == 0 || model->bindPose == NULL)
if (anim1->boneCount == 0 || anim1->keyframePoses == NULL ||
anim2->boneCount == 0 || anim2->keyframePoses == NULL ||
model->skeleton.boneCount == 0 || model->skeleton.bindPose == NULL)
{
return;
}
@ -214,26 +214,13 @@ static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int f
blendFactor = fminf(1.0f, fmaxf(0.0f, blendFactor));
// Ensure frame indices are valid
if (frame1 >= anim1->frameCount) frame1 = anim1->frameCount - 1;
if (frame2 >= anim2->frameCount) frame2 = anim2->frameCount - 1;
if (frame1 >= anim1->keyframeCount) frame1 = anim1->keyframeCount - 1;
if (frame2 >= anim2->keyframeCount) frame2 = anim2->keyframeCount - 1;
if (frame1 < 0) frame1 = 0;
if (frame2 < 0) frame2 = 0;
// Find first mesh with bones
int firstMeshWithBones = -1;
for (int i = 0; i < model->meshCount; i++)
{
if (model->meshes[i].boneMatrices)
{
firstMeshWithBones = i;
break;
}
}
if (firstMeshWithBones == -1) return;
// Get bone count (use minimum of all to be safe)
int boneCount = model->boneCount;
int boneCount = model->skeleton.boneCount;
if (anim1->boneCount < boneCount) boneCount = anim1->boneCount;
if (anim2->boneCount < boneCount) boneCount = anim2->boneCount;
@ -246,7 +233,7 @@ static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int f
// If upper body blending is enabled, use different blend factors for upper vs lower body
if (upperBodyBlend)
{
const char *boneName = model->bones[boneId].name;
const char *boneName = model->skeleton.bones[boneId].name;
bool isUpperBody = IsUpperBodyBone(boneName);
// Upper body: use anim2 (attack), Lower body: use anim1 (walk)
@ -256,12 +243,12 @@ static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int f
}
// Get transforms from both animations
Transform *bindTransform = &model->bindPose[boneId];
Transform *anim1Transform = &anim1->framePoses[frame1][boneId];
Transform *anim2Transform = &anim2->framePoses[frame2][boneId];
Transform *bindTransform = &model->skeleton.bindPose[boneId];
Transform *anim1Transform = &anim1->keyframePoses[frame1][boneId];
Transform *anim2Transform = &anim2->keyframePoses[frame2][boneId];
// Blend the transforms
Transform blended;
Transform blended = { 0 };
blended.translation = Vector3Lerp(anim1Transform->translation, anim2Transform->translation, boneBlendFactor);
blended.rotation = QuaternionSlerp(anim1Transform->rotation, anim2Transform->rotation, boneBlendFactor);
blended.scale = Vector3Lerp(anim1Transform->scale, anim2Transform->scale, boneBlendFactor);
@ -279,18 +266,7 @@ static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int f
MatrixTranslate(blended.translation.x, blended.translation.y, blended.translation.z));
// Calculate final bone matrix (similar to UpdateModelAnimationBones)
model->meshes[firstMeshWithBones].boneMatrices[boneId] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix);
}
// Copy bone matrices to remaining meshes
for (int i = firstMeshWithBones + 1; i < model->meshCount; i++)
{
if (model->meshes[i].boneMatrices)
{
memcpy(model->meshes[i].boneMatrices,
model->meshes[firstMeshWithBones].boneMatrices,
model->meshes[i].boneCount*sizeof(model->meshes[i].boneMatrices[0]));
}
model->boneMatrices[boneId] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -2,7 +2,7 @@
*
* raylib [models] example - animation blending
*
* Example complexity rating: [★★★☆] 3/4
* Example complexity rating: [☆☆☆☆] 0/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.6-dev
*

View File

@ -8,13 +8,15 @@
*
* Example contributed by Daniel Holden (@orangeduck) and reviewed by Ramon Santamaria (@raysan5)
*
* WARNING: GPU skinning must be enabled in raylib with a compilation flag,
* if not enabled, CPU skinning will be used instead
* NOTE: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS
*
* 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-2025 Daniel Holden (@orangeduck)
*
* Note: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS
*
********************************************************************************************/
#include "raylib.h"
@ -42,29 +44,29 @@ int main(void)
// Define the camera to look into our 3d world
Camera camera = { 0 };
camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position
camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point
camera.target = (Vector3){ 0.0f, 1.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/greenman.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));
characterModel.materials[1].shader = skinningShader;
// Load gltf model animations
int animsCount = 0;
unsigned int animIndex = 0;
unsigned int animCurrentFrame = 0;
ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", &animsCount);
Model model = LoadModel("resources/models/gltf/greenman.glb"); // Load character model
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
DisableCursor(); // Limit cursor to relative movement inside the window
// Load skinning shader
// WARNING: GPU skinning must be enabled in raylib with a compilation flag,
// if not enabled, CPU skinning will be used instead
Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
model.materials[1].shader = skinningShader;
// Load gltf model animations
int animCount = 0;
ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/greenman.glb", &animCount);
// Animation playing variables
unsigned int animIndex = 0; // Current animation playing
unsigned int animCurrentFrame = 0; // Current animation frame
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@ -74,17 +76,15 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(&camera, CAMERA_THIRD_PERSON);
UpdateCamera(&camera, CAMERA_ORBITAL);
// Select current animation
if (IsKeyPressed(KEY_T)) animIndex = (animIndex + 1)%animsCount;
else if (IsKeyPressed(KEY_G)) animIndex = (animIndex + animsCount - 1)%animsCount;
if (IsKeyPressed(KEY_RIGHT)) animIndex = (animIndex + 1)%animCount;
else if (IsKeyPressed(KEY_LEFT)) animIndex = (animIndex + animCount - 1)%animCount;
// Update model animation
ModelAnimation anim = modelAnimations[animIndex];
animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount;
characterModel.transform = MatrixTranslate(position.x, position.y, position.z);
UpdateModelAnimationBones(characterModel, anim, animCurrentFrame);
animCurrentFrame = (animCurrentFrame + 1)%anims[animIndex].keyframeCount;
UpdateModelAnimation(model, anims[animIndex], (float)animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
@ -95,14 +95,14 @@ int main(void)
BeginMode3D(camera);
// Draw character mesh, pose calculation is done in shader (GPU skinning)
DrawMesh(characterModel.meshes[0], characterModel.materials[1], characterModel.transform);
DrawModel(model, position, 1.0f, WHITE);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText("Use the T/G to switch animation", 10, 10, 20, GRAY);
DrawText(TextFormat("Current animation: %s", anims[animIndex].name), 10, 40, 20, MAROON);
DrawText("Use the LEFT/RIGHT keys to switch animation", 10, 10, 20, GRAY);
EndDrawing();
//----------------------------------------------------------------------------------
@ -110,8 +110,8 @@ int main(void)
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation
UnloadModel(characterModel); // Unload model and meshes/material
UnloadModelAnimations(anims, animCount); // Unload model animation
UnloadModel(model); // Unload model and meshes/material
UnloadShader(skinningShader); // Unload GPU skinning shader
CloseWindow(); // Close window and OpenGL context

View File

@ -0,0 +1,114 @@
/*******************************************************************************************
*
* raylib [models] example - animation timming
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.6, last time updated with raylib 5.6
*
* 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) 2026 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
#include "raylib.h"
#define RAYGUI_IMPLEMENTATION
#include "raygui.h" // Required for: UI controls
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - animation timming");
// Define the camera to look into our 3d world
Camera camera = { 0 };
camera.position = (Vector3){ 6.0f, 6.0f, 6.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 model
Model model = LoadModel("resources/models/gltf/robot.glb");
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model world position
// Load model animations
int animsCount = 0;
ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount);
// Animation playing variables
unsigned int animIndex = 0; // Current animation playing
float animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames)
float animFrameSpeed = 0.1f; // Animation play speed
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_ORBITAL);
// Select current animation
if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) animIndex = (animIndex + 1)%animsCount;
else if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) animIndex = (animIndex + animsCount - 1)%animsCount;
// Select animation playing speed
if (IsKeyPressed(KEY_RIGHT)) animFrameSpeed += 0.1f;
else if (IsKeyPressed(KEY_LEFT)) animFrameSpeed -= 0.1f;
// Update model animation
animCurrentFrame += animFrameSpeed;
UpdateModelAnimation(model, modelAnimations[animIndex], animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
BeginMode3D(camera);
DrawModel(model, position, 1.0f, WHITE);
DrawGrid(10, 1.0f);
EndMode3D();
// Draw UI
//GuiDropdownBox((Rectangle){ 10, 20, 240, 30 }, "text", &animIndex, editMode);
DrawText(TextFormat("FRAME SPEED: x%.1f", animFrameSpeed), 10, 40, 20, RED);
DrawText("Use the LEFT/RIGHT mouse buttons to switch animation", 10, 10, 20, GRAY);
DrawText(TextFormat("Animation: %s", modelAnimations[animIndex].name), 10, GetScreenHeight() - 20, 10, DARKGRAY);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModel(model); // Unload model and meshes/material
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -60,25 +60,25 @@ int main(void)
unsigned int animCurrentFrame = 0;
ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", &animsCount);
// indices of bones for sockets
// Indices of bones for sockets
int boneSocketIndex[BONE_SOCKETS] = { -1, -1, -1 };
// search bones for sockets
for (int i = 0; i < characterModel.boneCount; i++)
// Search bones for sockets
for (int i = 0; i < characterModel.skeleton.boneCount; i++)
{
if (TextIsEqual(characterModel.bones[i].name, "socket_hat"))
if (TextIsEqual(characterModel.skeleton.bones[i].name, "socket_hat"))
{
boneSocketIndex[BONE_SOCKET_HAT] = i;
continue;
}
if (TextIsEqual(characterModel.bones[i].name, "socket_hand_R"))
if (TextIsEqual(characterModel.skeleton.bones[i].name, "socket_hand_R"))
{
boneSocketIndex[BONE_SOCKET_HAND_R] = i;
continue;
}
if (TextIsEqual(characterModel.bones[i].name, "socket_hand_L"))
if (TextIsEqual(characterModel.skeleton.bones[i].name, "socket_hand_L"))
{
boneSocketIndex[BONE_SOCKET_HAND_L] = i;
continue;
@ -115,7 +115,7 @@ int main(void)
// Update model animation
ModelAnimation anim = modelAnimations[animIndex];
animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount;
animCurrentFrame = (animCurrentFrame + 1)%anim.keyframeCount;
UpdateModelAnimation(characterModel, anim, animCurrentFrame);
//----------------------------------------------------------------------------------
@ -137,8 +137,8 @@ int main(void)
{
if (!showEquip[i]) continue;
Transform *transform = &anim.framePoses[animCurrentFrame][boneSocketIndex[i]];
Quaternion inRotation = characterModel.bindPose[boneSocketIndex[i]].rotation;
Transform *transform = &anim.keyframePoses[animCurrentFrame][boneSocketIndex[i]];
Quaternion inRotation = characterModel.skeleton.bindPose[boneSocketIndex[i]].rotation;
Quaternion outRotation = transform->rotation;
// Calculate socket rotation (angle between bone in initial pose and same bone in current animation frame)