WARNING: BREAKING: REDESIGNED: **Animation System** #4606
REVIEWED: Reorganized structures for a clearer distinction between "skeleton", "skin" and "skinning" data ADDED: New structures: `ModelSkeleton`, `ModelAnimPose` (alias `Transform*`) ADDED: Runtime data `currentPose` and `boneMatrices` to `Model` structure ADDED: Support animation frames-blending, for timing control ADDED: Support animations blending, between two animations REVIEWED: All models animation loading functions ADDED: `UpdateModelAnimationEx()` for two animations blending REMOVED: `UpdateModelAnimationBones*()`, simplified API REVIEWED: Shader attributes/uniforms names for animations, for consistency REVIEWED: Multiple tweaks on animations loading for consistency between formats ADDED: example: `models_animation_timing` ADDED: example: `models_animation_blending` REVIEWED: example: `models_animation_gpu_skinning` REVIEWED: example: `models_animation_blend_custom` REVIEWED: All animated models loading examples
@ -173,7 +173,7 @@ models;models_decals;★★★★;5.6-dev;5.6-dev;2025;2025;"JP Mortiboys";@them
|
|||||||
models;models_directional_billboard;★★☆☆;5.6-dev;5.6;2025;2025;"Robin";@RobinsAviary
|
models;models_directional_billboard;★★☆☆;5.6-dev;5.6;2025;2025;"Robin";@RobinsAviary
|
||||||
models;models_animation_blend_custom;★★★★;5.5;5.5;2026;2026;"dmitrii-brand";@dmitrii-brand
|
models;models_animation_blend_custom;★★★★;5.5;5.5;2026;2026;"dmitrii-brand";@dmitrii-brand
|
||||||
models;models_animation_blending;☆☆☆☆;5.5;5.6-dev;2024;2024;"Kirandeep";@Kirandeep-Singh-Khehra
|
models;models_animation_blending;☆☆☆☆;5.5;5.6-dev;2024;2024;"Kirandeep";@Kirandeep-Singh-Khehra
|
||||||
models;models_animation_timming;★★☆☆;5.6;5.6;2026;2026;"Ramon Santamaria";@raysan5
|
models;models_animation_timing;★★☆☆;5.6;5.6;2026;2026;"Ramon Santamaria";@raysan5
|
||||||
shaders;shaders_ascii_rendering;★★☆☆;5.5;5.6;2025;2025;"Maicon Santana";@maiconpintoabreu
|
shaders;shaders_ascii_rendering;★★☆☆;5.5;5.6;2025;2025;"Maicon Santana";@maiconpintoabreu
|
||||||
shaders;shaders_basic_lighting;★★★★;3.0;4.2;2019;2025;"Chris Camacho";@chriscamacho
|
shaders;shaders_basic_lighting;★★★★;3.0;4.2;2019;2025;"Chris Camacho";@chriscamacho
|
||||||
shaders;shaders_model_shader;★★☆☆;1.3;3.7;2014;2025;"Ramon Santamaria";@raysan5
|
shaders;shaders_model_shader;★★☆☆;1.3;3.7;2014;2025;"Ramon Santamaria";@raysan5
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
* Example originally created with raylib 5.5, last time updated with raylib 5.5
|
* Example originally created with raylib 5.5, last time updated with raylib 5.5
|
||||||
*
|
*
|
||||||
* This example demonstrates per-bone animation blending, allowing smooth transitions
|
* DETAILS: Example demonstrates per-bone animation blending, allowing smooth transitions
|
||||||
* between two animations by interpolating bone transforms. This is useful for:
|
* between two animations by interpolating bone transforms. This is useful for:
|
||||||
* - Blending movement animations (walk/run) with action animations (jump/attack)
|
* - Blending movement animations (walk/run) with action animations (jump/attack)
|
||||||
* - Creating smooth animation transitions
|
* - Creating smooth animation transitions
|
||||||
@ -27,8 +27,10 @@
|
|||||||
|
|
||||||
#include "raymath.h"
|
#include "raymath.h"
|
||||||
|
|
||||||
#include <string.h> // Required for: memcpy()
|
#include "rlgl.h" // Requried for: rlUpdateVertexBuffer() (CPU-skinning)
|
||||||
#include <stdlib.h> // Required for: NULL
|
|
||||||
|
#include <string.h> // Required for: memcpy()
|
||||||
|
#include <stdlib.h> // Required for: NULL
|
||||||
|
|
||||||
#if defined(PLATFORM_DESKTOP)
|
#if defined(PLATFORM_DESKTOP)
|
||||||
#define GLSL_VERSION 330
|
#define GLSL_VERSION 330
|
||||||
@ -40,8 +42,8 @@
|
|||||||
// Module Functions Declaration
|
// Module Functions Declaration
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
static bool IsUpperBodyBone(const char *boneName);
|
static bool IsUpperBodyBone(const char *boneName);
|
||||||
static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int frame1,
|
static void UpdateModelAnimationBones(Model *model, ModelAnimation *anim1, int frame1,
|
||||||
ModelAnimation *anim2, int frame2, float blendFactor, bool upperBodyBlend);
|
ModelAnimation *anim2, int frame2, float blend, bool upperBodyBlend);
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Program main entry point
|
// Program main entry point
|
||||||
@ -57,51 +59,40 @@ int main(void)
|
|||||||
|
|
||||||
// Define the camera to look into our 3d world
|
// Define the camera to look into our 3d world
|
||||||
Camera camera = { 0 };
|
Camera camera = { 0 };
|
||||||
camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position
|
camera.position = (Vector3){ 4.0f, 4.0f, 4.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.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
|
||||||
camera.fovy = 45.0f; // Camera field-of-view Y
|
camera.fovy = 45.0f; // Camera field-of-view Y
|
||||||
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
|
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
|
||||||
|
|
||||||
// Load gltf model
|
// Load gltf model
|
||||||
Model characterModel = LoadModel("resources/models/gltf/greenman.glb");
|
Model model = LoadModel("resources/models/gltf/greenman.glb");
|
||||||
|
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
|
||||||
|
|
||||||
// Load skinning shader
|
// 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),
|
Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
|
||||||
TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
|
TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
|
||||||
|
model.materials[1].shader = skinningShader;
|
||||||
characterModel.materials[1].shader = skinningShader;
|
|
||||||
|
|
||||||
// Load gltf model animations
|
// Load gltf model animations
|
||||||
int animsCount = 0;
|
int animCount = 0;
|
||||||
ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", &animsCount);
|
ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/greenman.glb", &animCount);
|
||||||
|
|
||||||
// Log all available animations for debugging
|
// Use specific animation indices: 2-walk/move, 3-attack
|
||||||
TraceLog(LOG_INFO, "Found %d animations:", animsCount);
|
unsigned int animIndex0 = 2; // Walk/Move animation (index 2)
|
||||||
for (int i = 0; i < animsCount; i++)
|
unsigned int animIndex1 = 3; // Attack animation (index 3)
|
||||||
{
|
unsigned int animCurrentFrame0 = 0;
|
||||||
TraceLog(LOG_INFO, " Animation %d: %s (%d frames)", i, modelAnimations[i].name, modelAnimations[i].keyframeCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use specific indices: walk/move = 2, attack = 3
|
|
||||||
unsigned int animIndex1 = 2; // Walk/Move animation (index 2)
|
|
||||||
unsigned int animIndex2 = 3; // Attack animation (index 3)
|
|
||||||
unsigned int animCurrentFrame1 = 0;
|
unsigned int animCurrentFrame1 = 0;
|
||||||
unsigned int animCurrentFrame2 = 0;
|
|
||||||
|
|
||||||
// Validate indices
|
// Validate indices
|
||||||
if (animIndex1 >= animsCount) animIndex1 = 0;
|
if (animIndex0 >= animCount) animIndex0 = 0;
|
||||||
if (animIndex2 >= animsCount) animIndex2 = (animsCount > 1) ? 1 : 0;
|
if (animIndex1 >= animCount) animIndex1 = (animCount > 1) ? 1 : 0;
|
||||||
|
|
||||||
TraceLog(LOG_INFO, "Using Walk (index %d): %s", animIndex1, modelAnimations[animIndex1].name);
|
bool upperBodyBlend = true; // Toggle: true = upper/lower body blending, false = uniform blending (50/50)
|
||||||
TraceLog(LOG_INFO, "Using Attack (index %d): %s", animIndex2, modelAnimations[animIndex2].name);
|
|
||||||
|
|
||||||
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
|
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||||
bool upperBodyBlend = true; // Toggle: true = upper/lower body blending, false = uniform blending (50/50)
|
|
||||||
|
|
||||||
DisableCursor(); // Limit cursor to relative movement inside the window
|
|
||||||
|
|
||||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Main game loop
|
// Main game loop
|
||||||
@ -109,24 +100,28 @@ int main(void)
|
|||||||
{
|
{
|
||||||
// Update
|
// Update
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
UpdateCamera(&camera, CAMERA_THIRD_PERSON);
|
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||||
|
|
||||||
// Toggle upper/lower body blending mode (SPACE key)
|
// Toggle upper/lower body blending mode (SPACE key)
|
||||||
if (IsKeyPressed(KEY_SPACE)) upperBodyBlend = !upperBodyBlend;
|
if (IsKeyPressed(KEY_SPACE)) upperBodyBlend = !upperBodyBlend;
|
||||||
|
|
||||||
// Update animation frames
|
// Update animation frames
|
||||||
ModelAnimation anim1 = modelAnimations[animIndex1];
|
ModelAnimation anim0 = anims[animIndex0];
|
||||||
ModelAnimation anim2 = modelAnimations[animIndex2];
|
ModelAnimation anim1 = anims[animIndex1];
|
||||||
|
|
||||||
|
animCurrentFrame0 = (animCurrentFrame0 + 1)%anim0.keyframeCount;
|
||||||
animCurrentFrame1 = (animCurrentFrame1 + 1)%anim1.keyframeCount;
|
animCurrentFrame1 = (animCurrentFrame1 + 1)%anim1.keyframeCount;
|
||||||
animCurrentFrame2 = (animCurrentFrame2 + 1)%anim2.keyframeCount;
|
|
||||||
|
|
||||||
// Blend the two animations
|
// Blend the two animations
|
||||||
characterModel.transform = MatrixTranslate(position.x, position.y, position.z);
|
|
||||||
// When upperBodyBlend is ON: upper body = attack (1.0), lower body = walk (0.0)
|
// When upperBodyBlend is ON: upper body = attack (1.0), lower body = walk (0.0)
|
||||||
// When upperBodyBlend is OFF: uniform blend at 0.5 (50% walk, 50% attack)
|
// When upperBodyBlend is OFF: uniform blend at 0.5 (50% walk, 50% attack)
|
||||||
float blendFactor = upperBodyBlend ? 1.0f : 0.5f;
|
float blendFactor = (upperBodyBlend? 1.0f : 0.5f);
|
||||||
BlendModelAnimationsBones(&characterModel, &anim1, animCurrentFrame1, &anim2, animCurrentFrame2, blendFactor, upperBodyBlend);
|
UpdateModelAnimationBones(&model, &anim0, animCurrentFrame0,
|
||||||
|
&anim1, animCurrentFrame1, blendFactor, upperBodyBlend);
|
||||||
|
|
||||||
|
// raylib provided animation blending function
|
||||||
|
//UpdateModelAnimationEx(model, anim0, (float)animCurrentFrame0,
|
||||||
|
// anim1, (float)animCurrentFrame1, blendFactor);
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
@ -137,19 +132,18 @@ int main(void)
|
|||||||
|
|
||||||
BeginMode3D(camera);
|
BeginMode3D(camera);
|
||||||
|
|
||||||
// Draw character mesh, pose calculation is done in shader (GPU skinning)
|
DrawModel(model, position, 1.0f, WHITE);
|
||||||
DrawMesh(characterModel.meshes[0], characterModel.materials[1], characterModel.transform);
|
|
||||||
|
|
||||||
DrawGrid(10, 1.0f);
|
DrawGrid(10, 1.0f);
|
||||||
|
|
||||||
EndMode3D();
|
EndMode3D();
|
||||||
|
|
||||||
// Draw UI
|
// Draw UI
|
||||||
DrawText("BONE BLENDING EXAMPLE", 10, 10, 20, DARKGRAY);
|
DrawText(TextFormat("ANIM 0: %s", anim0.name), 10, 10, 20, GRAY);
|
||||||
DrawText(TextFormat("Walk (Animation 2): %s", anim1.name), 10, 35, 10, GRAY);
|
DrawText(TextFormat("ANIM 1: %s", anim1.name), 10, 40, 20, GRAY);
|
||||||
DrawText(TextFormat("Attack (Animation 3): %s", anim2.name), 10, 50, 10, GRAY);
|
DrawText(TextFormat("[SPACE] Toggle blending mode: %s",
|
||||||
DrawText(TextFormat("Mode: %s", upperBodyBlend ? "Upper/Lower Body Blending" : "Uniform Blending"), 10, 65, 10, GRAY);
|
upperBodyBlend? "Upper/Lower Body Blending" : "Uniform Blending"),
|
||||||
DrawText("SPACE - Toggle blending mode", 10, GetScreenHeight() - 20, 10, DARKGRAY);
|
10, GetScreenHeight() - 30, 20, DARKGRAY);
|
||||||
|
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
@ -157,8 +151,8 @@ int main(void)
|
|||||||
|
|
||||||
// De-Initialization
|
// De-Initialization
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation
|
UnloadModelAnimations(anims, animCount); // Unload model animation
|
||||||
UnloadModel(characterModel); // Unload model and meshes/material
|
UnloadModel(model); // Unload model and meshes/material
|
||||||
UnloadShader(skinningShader); // Unload GPU skinning shader
|
UnloadShader(skinningShader); // Unload GPU skinning shader
|
||||||
|
|
||||||
CloseWindow(); // Close window and OpenGL context
|
CloseWindow(); // Close window and OpenGL context
|
||||||
@ -199,74 +193,138 @@ static bool IsUpperBodyBone(const char *boneName)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Blend two animations per-bone with selective upper/lower body blending
|
// Blend two animations per-bone with selective upper/lower body blending
|
||||||
static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int frame1,
|
static void UpdateModelAnimationBones(Model *model, ModelAnimation *anim0, int frame0,
|
||||||
ModelAnimation *anim2, int frame2, float blendFactor, bool upperBodyBlend)
|
ModelAnimation *anim1, int frame1, float blend, bool upperBodyBlend)
|
||||||
{
|
{
|
||||||
// Validate inputs
|
// Validate inputs
|
||||||
if (anim1->boneCount == 0 || anim1->keyframePoses == NULL ||
|
if ((anim0->boneCount != 0) && (anim0->keyframePoses != NULL) &&
|
||||||
anim2->boneCount == 0 || anim2->keyframePoses == NULL ||
|
(anim1->boneCount != 0) && (anim1->keyframePoses != NULL) &&
|
||||||
model->skeleton.boneCount == 0 || model->skeleton.bindPose == NULL)
|
(model->skeleton.boneCount != 0) && (model->skeleton.bindPose != NULL))
|
||||||
{
|
{
|
||||||
return;
|
// Clamp blend factor to [0, 1]
|
||||||
}
|
blend = fminf(1.0f, fmaxf(0.0f, blend));
|
||||||
|
|
||||||
// Clamp blend factor to [0, 1]
|
// Ensure frame indices are valid
|
||||||
blendFactor = fminf(1.0f, fmaxf(0.0f, blendFactor));
|
if (frame0 >= anim0->keyframeCount) frame0 = anim0->keyframeCount - 1;
|
||||||
|
if (frame1 >= anim1->keyframeCount) frame1 = anim1->keyframeCount - 1;
|
||||||
|
if (frame0 < 0) frame0 = 0;
|
||||||
|
if (frame1 < 0) frame1 = 0;
|
||||||
|
|
||||||
// Ensure frame indices are valid
|
// Get bone count (use minimum of all to be safe)
|
||||||
if (frame1 >= anim1->keyframeCount) frame1 = anim1->keyframeCount - 1;
|
int boneCount = model->skeleton.boneCount;
|
||||||
if (frame2 >= anim2->keyframeCount) frame2 = anim2->keyframeCount - 1;
|
if (anim0->boneCount < boneCount) boneCount = anim0->boneCount;
|
||||||
if (frame1 < 0) frame1 = 0;
|
if (anim1->boneCount < boneCount) boneCount = anim1->boneCount;
|
||||||
if (frame2 < 0) frame2 = 0;
|
|
||||||
|
|
||||||
// Get bone count (use minimum of all to be safe)
|
// Blend each bone
|
||||||
int boneCount = model->skeleton.boneCount;
|
for (int boneIndex = 0; boneIndex < boneCount; boneIndex++)
|
||||||
if (anim1->boneCount < boneCount) boneCount = anim1->boneCount;
|
|
||||||
if (anim2->boneCount < boneCount) boneCount = anim2->boneCount;
|
|
||||||
|
|
||||||
// Blend each bone
|
|
||||||
for (int boneId = 0; boneId < boneCount; boneId++)
|
|
||||||
{
|
|
||||||
// Determine blend factor for this bone
|
|
||||||
float boneBlendFactor = blendFactor;
|
|
||||||
|
|
||||||
// If upper body blending is enabled, use different blend factors for upper vs lower body
|
|
||||||
if (upperBodyBlend)
|
|
||||||
{
|
{
|
||||||
const char *boneName = model->skeleton.bones[boneId].name;
|
// Determine blend factor for this bone
|
||||||
bool isUpperBody = IsUpperBodyBone(boneName);
|
float boneBlendFactor = blend;
|
||||||
|
|
||||||
// Upper body: use anim2 (attack), Lower body: use anim1 (walk)
|
// If upper body blending is enabled, use different blend factors for upper vs lower body
|
||||||
// blendFactor = 0.0 means full anim1 (walk), 1.0 means full anim2 (attack)
|
if (upperBodyBlend)
|
||||||
if (isUpperBody) boneBlendFactor = blendFactor; // Upper body: blend towards anim2 (attack)
|
{
|
||||||
else boneBlendFactor = 1.0f - blendFactor; // Lower body: blend towards anim1 (walk) - invert the blend
|
const char *boneName = model->skeleton.bones[boneIndex].name;
|
||||||
|
bool isUpperBody = IsUpperBodyBone(boneName);
|
||||||
|
|
||||||
|
// Upper body: use anim1 (attack), Lower body: use anim0 (walk)
|
||||||
|
// blend = 0.0 means full anim0 (walk), 1.0 means full anim1 (attack)
|
||||||
|
if (isUpperBody) boneBlendFactor = blend; // Upper body: blend towards anim1 (attack)
|
||||||
|
else boneBlendFactor = 1.0f - blend; // Lower body: blend towards anim0 (walk) - invert the blend
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get transforms from both animations
|
||||||
|
Transform *bindTransform = &model->skeleton.bindPose[boneIndex];
|
||||||
|
Transform *animTransform0 = &anim0->keyframePoses[frame0][boneIndex];
|
||||||
|
Transform *animTransform1 = &anim1->keyframePoses[frame1][boneIndex];
|
||||||
|
|
||||||
|
// Blend the transforms
|
||||||
|
Transform blended = { 0 };
|
||||||
|
blended.translation = Vector3Lerp(animTransform0->translation, animTransform1->translation, boneBlendFactor);
|
||||||
|
blended.rotation = QuaternionSlerp(animTransform0->rotation, animTransform1->rotation, boneBlendFactor);
|
||||||
|
blended.scale = Vector3Lerp(animTransform0->scale, animTransform1->scale, boneBlendFactor);
|
||||||
|
|
||||||
|
// Convert bind pose to matrix
|
||||||
|
Matrix bindMatrix = MatrixMultiply(MatrixMultiply(
|
||||||
|
MatrixScale(bindTransform->scale.x, bindTransform->scale.y, bindTransform->scale.z),
|
||||||
|
QuaternionToMatrix(bindTransform->rotation)),
|
||||||
|
MatrixTranslate(bindTransform->translation.x, bindTransform->translation.y, bindTransform->translation.z));
|
||||||
|
|
||||||
|
// Convert blended transform to matrix
|
||||||
|
Matrix blendedMatrix = MatrixMultiply(MatrixMultiply(
|
||||||
|
MatrixScale(blended.scale.x, blended.scale.y, blended.scale.z),
|
||||||
|
QuaternionToMatrix(blended.rotation)),
|
||||||
|
MatrixTranslate(blended.translation.x, blended.translation.y, blended.translation.z));
|
||||||
|
|
||||||
|
// Calculate final bone matrix (similar to UpdateModelAnimationBones)
|
||||||
|
model->boneMatrices[boneIndex] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get transforms from both animations
|
// CPU skinning, updates CPU buffers and uploads them to GPU (if available)
|
||||||
Transform *bindTransform = &model->skeleton.bindPose[boneId];
|
// NOTE: Fallback in case GPU skinning is not supported or enabled
|
||||||
Transform *anim1Transform = &anim1->keyframePoses[frame1][boneId];
|
for (int m = 0; m < model->meshCount; m++)
|
||||||
Transform *anim2Transform = &anim2->keyframePoses[frame2][boneId];
|
{
|
||||||
|
Mesh mesh = model->meshes[m];
|
||||||
|
Vector3 animVertex = { 0 };
|
||||||
|
Vector3 animNormal = { 0 };
|
||||||
|
const int vertexValuesCount = mesh.vertexCount*3;
|
||||||
|
|
||||||
// Blend the transforms
|
int boneIndex = 0;
|
||||||
Transform blended = { 0 };
|
int boneCounter = 0;
|
||||||
blended.translation = Vector3Lerp(anim1Transform->translation, anim2Transform->translation, boneBlendFactor);
|
float boneWeight = 0.0f;
|
||||||
blended.rotation = QuaternionSlerp(anim1Transform->rotation, anim2Transform->rotation, boneBlendFactor);
|
bool bufferUpdateRequired = false; // Flag to check when anim vertex information is updated
|
||||||
blended.scale = Vector3Lerp(anim1Transform->scale, anim2Transform->scale, boneBlendFactor);
|
|
||||||
|
|
||||||
// Convert bind pose to matrix
|
// Skip if missing bone data or missing anim buffers initialization
|
||||||
Matrix bindMatrix = MatrixMultiply(MatrixMultiply(
|
if ((mesh.boneWeights == NULL) || (mesh.boneIndices == NULL) ||
|
||||||
MatrixScale(bindTransform->scale.x, bindTransform->scale.y, bindTransform->scale.z),
|
(mesh.animVertices == NULL) || (mesh.animNormals == NULL)) continue;
|
||||||
QuaternionToMatrix(bindTransform->rotation)),
|
|
||||||
MatrixTranslate(bindTransform->translation.x, bindTransform->translation.y, bindTransform->translation.z));
|
|
||||||
|
|
||||||
// Convert blended transform to matrix
|
for (int vCounter = 0; vCounter < vertexValuesCount; vCounter += 3)
|
||||||
Matrix blendedMatrix = MatrixMultiply(MatrixMultiply(
|
{
|
||||||
MatrixScale(blended.scale.x, blended.scale.y, blended.scale.z),
|
mesh.animVertices[vCounter] = 0;
|
||||||
QuaternionToMatrix(blended.rotation)),
|
mesh.animVertices[vCounter + 1] = 0;
|
||||||
MatrixTranslate(blended.translation.x, blended.translation.y, blended.translation.z));
|
mesh.animVertices[vCounter + 2] = 0;
|
||||||
|
if (mesh.animNormals != NULL)
|
||||||
|
{
|
||||||
|
mesh.animNormals[vCounter] = 0;
|
||||||
|
mesh.animNormals[vCounter + 1] = 0;
|
||||||
|
mesh.animNormals[vCounter + 2] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate final bone matrix (similar to UpdateModelAnimationBones)
|
// Iterates over 4 bones per vertex
|
||||||
model->boneMatrices[boneId] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix);
|
for (int j = 0; j < 4; j++, boneCounter++)
|
||||||
|
{
|
||||||
|
boneWeight = mesh.boneWeights[boneCounter];
|
||||||
|
boneIndex = mesh.boneIndices[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->boneMatrices[boneIndex]);
|
||||||
|
mesh.animVertices[vCounter] += animVertex.x*boneWeight;
|
||||||
|
mesh.animVertices[vCounter + 1] += animVertex.y*boneWeight;
|
||||||
|
mesh.animVertices[vCounter + 2] += animVertex.z*boneWeight;
|
||||||
|
bufferUpdateRequired = true;
|
||||||
|
|
||||||
|
// Normals processing
|
||||||
|
// NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
|
||||||
|
if ((mesh.normals != NULL) && (mesh.animNormals != NULL ))
|
||||||
|
{
|
||||||
|
animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] };
|
||||||
|
animNormal = Vector3Transform(animNormal, MatrixTranspose(MatrixInvert(model->boneMatrices[boneIndex])));
|
||||||
|
mesh.animNormals[vCounter] += animNormal.x*boneWeight;
|
||||||
|
mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight;
|
||||||
|
mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bufferUpdateRequired)
|
||||||
|
{
|
||||||
|
// Update GPU vertex buffers with updated data (position + normals)
|
||||||
|
rlUpdateVertexBuffer(mesh.vboId[SHADER_LOC_VERTEX_POSITION], mesh.animVertices, mesh.vertexCount*3*sizeof(float), 0);
|
||||||
|
if (mesh.normals != NULL) rlUpdateVertexBuffer(mesh.vboId[SHADER_LOC_VERTEX_NORMAL], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 82 KiB |
@ -21,7 +21,8 @@
|
|||||||
|
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
|
|
||||||
#define clamp(x,a,b) ((x < a)? a : (x > b)? b : x)
|
#define RAYGUI_IMPLEMENTATION
|
||||||
|
#include "raygui.h" // Required for: UI controls
|
||||||
|
|
||||||
#if defined(PLATFORM_DESKTOP)
|
#if defined(PLATFORM_DESKTOP)
|
||||||
#define GLSL_VERSION 330
|
#define GLSL_VERSION 330
|
||||||
@ -43,31 +44,58 @@ int main(void)
|
|||||||
|
|
||||||
// Define the camera to look into our 3d world
|
// Define the camera to look into our 3d world
|
||||||
Camera camera = { 0 };
|
Camera camera = { 0 };
|
||||||
camera.position = (Vector3){ 8.0f, 8.0f, 8.0f }; // Camera position
|
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.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.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
|
||||||
camera.fovy = 45.0f; // Camera field-of-view Y
|
camera.fovy = 45.0f; // Camera field-of-view Y
|
||||||
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
|
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
|
||||||
|
|
||||||
// Load model
|
// Load model
|
||||||
Model characterModel = LoadModel("resources/models/gltf/robot.glb"); // Load character model
|
Model model = LoadModel("resources/models/gltf/robot.glb"); // Load character model
|
||||||
|
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model world position
|
||||||
|
|
||||||
// Load skinning shader
|
// Load skinning shader
|
||||||
|
// WARNING: It requires SUPPORT_GPU_SKINNING enabled on raylib (disabled by default)
|
||||||
Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
|
Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
|
||||||
TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
|
TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
|
||||||
|
|
||||||
// Assign skinning shader to all materials shaders
|
// Assign skinning shader to all materials shaders
|
||||||
for (int i = 0; i < characterModel.materialCount; i++) characterModel.materials[i].shader = skinningShader;
|
//for (int i = 0; i < model.materialCount; i++) model.materials[i].shader = skinningShader;
|
||||||
|
|
||||||
// Load model animations
|
// Load model animations
|
||||||
int animsCount = 0;
|
int animCount = 0;
|
||||||
ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount);
|
ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/robot.glb", &animCount);
|
||||||
|
|
||||||
// Define animation variables
|
// Animation playing variables
|
||||||
unsigned int animIndex0 = 0;
|
// NOTE: Two animations are played with a smooth transition between them
|
||||||
unsigned int animIndex1 = 0;
|
int currentAnimPlaying = 0; // Current animation playing (0 o 1)
|
||||||
float animCurrentFrame = 0;
|
int nextAnimToPlay = 1; // Next animation to play (to transition)
|
||||||
float blendFactor = 0.5f;
|
bool animTransition = false; // Flag to register anim transition state
|
||||||
|
|
||||||
|
int animIndex0 = 10; // Current animation playing (walking)
|
||||||
|
float animCurrentFrame0 = 0.0f; // Current animation frame (supporting interpolated frames)
|
||||||
|
float animFrameSpeed0 = 0.5f; // Current animation play speed
|
||||||
|
int animIndex1 = 6; // Next animation to play (running)
|
||||||
|
float animCurrentFrame1 = 0.0f; // Next animation frame (supporting interpolated frames)
|
||||||
|
float animFrameSpeed1 = 0.5f; // Next animation play speed
|
||||||
|
|
||||||
|
float animBlendFactor = 0.0f; // Blend factor from anim0[frame0] --> anim1[frame1], [0.0f..1.0f]
|
||||||
|
// NOTE: 0.0f results in full anim0[] and 1.0f in full anim1[]
|
||||||
|
|
||||||
|
float animBlendTime = 2.0f; // Time to blend from one playing animation to another (in seconds)
|
||||||
|
float animBlendTimeCounter = 0.0f; // Time counter (delta time)
|
||||||
|
|
||||||
|
bool animPause = false; // Pause animation
|
||||||
|
|
||||||
|
// UI required variables
|
||||||
|
char *animNames[64] = { 0 }; // Pointers to animation names for dropdown box
|
||||||
|
for (int i = 0; i < animCount; i++) animNames[i] = anims[i].name;
|
||||||
|
|
||||||
|
bool dropdownEditMode0 = false;
|
||||||
|
bool dropdownEditMode1 = false;
|
||||||
|
float animFrameProgress0 = 0.0f;
|
||||||
|
float animFrameProgress1 = 0.0f;
|
||||||
|
float animBlendProgress = 0.0f;
|
||||||
|
|
||||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
@ -79,23 +107,100 @@ int main(void)
|
|||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
UpdateCamera(&camera, CAMERA_ORBITAL);
|
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||||
|
|
||||||
// Select current animation
|
if (IsKeyPressed(KEY_P)) animPause = !animPause;
|
||||||
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 (!animPause)
|
||||||
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);
|
// Start transition from anim0[] to anim1[]
|
||||||
|
if (IsKeyPressed(KEY_SPACE) && !animTransition)
|
||||||
|
{
|
||||||
|
if (currentAnimPlaying == 0)
|
||||||
|
{
|
||||||
|
// Transition anim0 --> anim1
|
||||||
|
nextAnimToPlay = 1;
|
||||||
|
animCurrentFrame1 = 0.0f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Transition anim1 --> anim0
|
||||||
|
nextAnimToPlay = 0;
|
||||||
|
animCurrentFrame0 = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
// Update animation
|
// Set animation transition
|
||||||
animCurrentFrame += 0.2f;
|
animTransition = true;
|
||||||
|
animBlendTimeCounter = 0.0f;
|
||||||
|
animBlendFactor = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
// Update bones
|
if (animTransition)
|
||||||
// Note: Same animation frame index is used below. By default it loops both animations
|
{
|
||||||
UpdateModelAnimationEx(characterModel, modelAnimations[animIndex0], animCurrentFrame,
|
// Playing anim0 and anim1 at the same time
|
||||||
modelAnimations[animIndex1], animCurrentFrame, blendFactor);
|
animCurrentFrame0 += animFrameSpeed0;
|
||||||
|
if (animCurrentFrame0 >= anims[animIndex0].keyframeCount) animCurrentFrame0 = 0.0f;
|
||||||
|
animCurrentFrame1 += animFrameSpeed1;
|
||||||
|
if (animCurrentFrame1 >= anims[animIndex1].keyframeCount) animCurrentFrame1 = 0.0f;
|
||||||
|
|
||||||
|
// Increment blend factor over time to transition from anim0 --> anim1 over time
|
||||||
|
// NOTE: Time blending could be other than linear, using some easing
|
||||||
|
animBlendFactor = animBlendTimeCounter/animBlendTime;
|
||||||
|
animBlendTimeCounter += GetFrameTime();
|
||||||
|
animBlendProgress = animBlendFactor;
|
||||||
|
|
||||||
|
// Update model with animations blending
|
||||||
|
if (nextAnimToPlay == 1)
|
||||||
|
{
|
||||||
|
// Blend anim0 --> anim1
|
||||||
|
UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0,
|
||||||
|
anims[animIndex1], animCurrentFrame1, animBlendFactor);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Blend anim1 --> anim0
|
||||||
|
UpdateModelAnimationEx(model, anims[animIndex1], animCurrentFrame1,
|
||||||
|
anims[animIndex0], animCurrentFrame0, animBlendFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if transition completed
|
||||||
|
if (animBlendFactor > 1.0f)
|
||||||
|
{
|
||||||
|
// Reset frame states
|
||||||
|
if (currentAnimPlaying == 0) animCurrentFrame0 = 0.0f;
|
||||||
|
else if (currentAnimPlaying == 1) animCurrentFrame1 = 0.0f;
|
||||||
|
currentAnimPlaying = nextAnimToPlay; // Update current animation playing
|
||||||
|
|
||||||
|
animBlendFactor = 0.0f; // Reset blend factor
|
||||||
|
animTransition = false; // Exit transition mode
|
||||||
|
animBlendTimeCounter = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Play only one anim, the current one
|
||||||
|
if (currentAnimPlaying == 0)
|
||||||
|
{
|
||||||
|
// Playing anim0 at defined speed
|
||||||
|
animCurrentFrame0 += animFrameSpeed0;
|
||||||
|
if (animCurrentFrame0 >= anims[animIndex0].keyframeCount) animCurrentFrame0 = 0.0f;
|
||||||
|
UpdateModelAnimation(model, anims[animIndex0], animCurrentFrame0);
|
||||||
|
//UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0,
|
||||||
|
// anims[animIndex1], animCurrentFrame1, 0.0f);
|
||||||
|
}
|
||||||
|
else if (currentAnimPlaying == 1)
|
||||||
|
{
|
||||||
|
// Playing anim1 at defined speed
|
||||||
|
animCurrentFrame1 += animFrameSpeed1;
|
||||||
|
if (animCurrentFrame1 >= anims[animIndex1].keyframeCount) animCurrentFrame1 = 0.0f;
|
||||||
|
UpdateModelAnimation(model, anims[animIndex1], animCurrentFrame1);
|
||||||
|
//UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0,
|
||||||
|
// anims[animIndex1], animCurrentFrame1, 1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update progress bars values with current frame for each animation
|
||||||
|
float animFrameProgress0 = animCurrentFrame0;
|
||||||
|
float animFrameProgress1 = animCurrentFrame1;
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
@ -106,16 +211,47 @@ int main(void)
|
|||||||
|
|
||||||
BeginMode3D(camera);
|
BeginMode3D(camera);
|
||||||
|
|
||||||
DrawModel(characterModel, (Vector3){0.0f, 0.0f, 0.0f}, 1.0f, WHITE);
|
DrawModel(model, position, 1.0f, WHITE); // Draw animated model
|
||||||
|
|
||||||
DrawGrid(10, 1.0f);
|
DrawGrid(10, 1.0f);
|
||||||
|
|
||||||
EndMode3D();
|
EndMode3D();
|
||||||
|
|
||||||
DrawText("Use the U/J to adjust blend factor", 10, 10, 20, GRAY);
|
if (animTransition) DrawText("ANIM TRANSITION BLENDING!", 170, 50, 30, BLUE);
|
||||||
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);
|
// Draw UI elements
|
||||||
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);
|
// Draw animation selectors for blending transition
|
||||||
|
// NOTE: Transition does not start until requested
|
||||||
|
GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 1);
|
||||||
|
if (GuiDropdownBox((Rectangle){ 10, 10, 160, 24 }, TextJoin(animNames, animCount, ";"),
|
||||||
|
&animIndex0, dropdownEditMode0)) dropdownEditMode0 = !dropdownEditMode0;
|
||||||
|
|
||||||
|
// Blending process progress bar
|
||||||
|
if (nextAnimToPlay == 1) GuiSetStyle(PROGRESSBAR, PROGRESS_SIDE, 0); // Left-->Right
|
||||||
|
else GuiSetStyle(PROGRESSBAR, PROGRESS_SIDE, 1); // Right-->Left
|
||||||
|
GuiProgressBar((Rectangle){ 180, 14, 440, 16 }, NULL, NULL, &animBlendProgress, 0.0f, 1.0f);
|
||||||
|
GuiSetStyle(PROGRESSBAR, PROGRESS_SIDE, 0); // Reset to Left-->Right
|
||||||
|
|
||||||
|
if (GuiDropdownBox((Rectangle){ GetScreenWidth() - 170, 10, 160, 24 }, TextJoin(animNames, animCount, ";"),
|
||||||
|
&animIndex1, dropdownEditMode1)) dropdownEditMode1 = !dropdownEditMode1;
|
||||||
|
|
||||||
|
// Draw playing timeline with keyframes for anim0[]
|
||||||
|
GuiProgressBar((Rectangle){ 60, GetScreenHeight() - 60, GetScreenWidth() - 180, 20 }, "ANIM 0",
|
||||||
|
TextFormat("FRAME: %.2f / %i", animFrameProgress0, anims[animIndex0].keyframeCount),
|
||||||
|
&animFrameProgress0, 0.0f, (float)anims[animIndex0].keyframeCount);
|
||||||
|
for (int i = 0; i < anims[animIndex0].keyframeCount; i++)
|
||||||
|
DrawRectangle(60 + ((float)(GetScreenWidth() - 180)/(float)anims[animIndex0].keyframeCount)*(float)i,
|
||||||
|
GetScreenHeight() - 60, 1, 20, BLUE);
|
||||||
|
|
||||||
|
// Draw playing timeline with keyframes for anim1[]
|
||||||
|
GuiProgressBar((Rectangle){ 60, GetScreenHeight() - 30, GetScreenWidth() - 180, 20 }, "ANIM 1",
|
||||||
|
TextFormat("FRAME: %.2f / %i", animFrameProgress1, anims[animIndex1].keyframeCount),
|
||||||
|
&animFrameProgress1, 0.0f, (float)anims[animIndex1].keyframeCount);
|
||||||
|
for (int i = 0; i < anims[animIndex1].keyframeCount; i++)
|
||||||
|
DrawRectangle(60 + ((float)(GetScreenWidth() - 180)/(float)anims[animIndex1].keyframeCount)*(float)i,
|
||||||
|
GetScreenHeight() - 30, 1, 20, BLUE);
|
||||||
|
//---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
@ -123,9 +259,8 @@ int main(void)
|
|||||||
|
|
||||||
// De-Initialization
|
// De-Initialization
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation
|
UnloadModelAnimations(anims, animCount); // Unload model animation
|
||||||
UnloadModel(characterModel); // Unload model and meshes/material
|
UnloadModel(model); // Unload model and meshes/material
|
||||||
|
|
||||||
UnloadShader(skinningShader); // Unload GPU skinning shader
|
UnloadShader(skinningShader); // Unload GPU skinning shader
|
||||||
|
|
||||||
CloseWindow(); // Close window and OpenGL context
|
CloseWindow(); // Close window and OpenGL context
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 27 KiB |
@ -10,7 +10,6 @@
|
|||||||
*
|
*
|
||||||
* WARNING: GPU skinning must be enabled in raylib with a compilation flag,
|
* WARNING: GPU skinning must be enabled in raylib with a compilation flag,
|
||||||
* if not enabled, CPU skinning will be used instead
|
* 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,
|
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
|
||||||
* BSD-like license that allows static linking with closed source software
|
* BSD-like license that allows static linking with closed source software
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 65 KiB |
@ -1,6 +1,6 @@
|
|||||||
/*******************************************************************************************
|
/*******************************************************************************************
|
||||||
*
|
*
|
||||||
* raylib [models] example - animation timming
|
* raylib [models] example - animation timing
|
||||||
*
|
*
|
||||||
* Example complexity rating: [★★☆☆] 2/4
|
* Example complexity rating: [★★☆☆] 2/4
|
||||||
*
|
*
|
||||||
@ -16,7 +16,7 @@
|
|||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
|
|
||||||
#define RAYGUI_IMPLEMENTATION
|
#define RAYGUI_IMPLEMENTATION
|
||||||
#include "raygui.h" // Required for: UI controls
|
#include "raygui.h" // Required for: UI controls
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Program main entry point
|
// Program main entry point
|
||||||
@ -28,7 +28,7 @@ int main(void)
|
|||||||
const int screenWidth = 800;
|
const int screenWidth = 800;
|
||||||
const int screenHeight = 450;
|
const int screenHeight = 450;
|
||||||
|
|
||||||
InitWindow(screenWidth, screenHeight, "raylib [models] example - animation timming");
|
InitWindow(screenWidth, screenHeight, "raylib [models] example - animation timing");
|
||||||
|
|
||||||
// Define the camera to look into our 3d world
|
// Define the camera to look into our 3d world
|
||||||
Camera camera = { 0 };
|
Camera camera = { 0 };
|
||||||
@ -43,13 +43,21 @@ int main(void)
|
|||||||
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model world position
|
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model world position
|
||||||
|
|
||||||
// Load model animations
|
// Load model animations
|
||||||
int animsCount = 0;
|
int animCount = 0;
|
||||||
ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount);
|
ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/robot.glb", &animCount);
|
||||||
|
|
||||||
// Animation playing variables
|
// Animation playing variables
|
||||||
unsigned int animIndex = 0; // Current animation playing
|
int animIndex = 10; // Current animation playing
|
||||||
float animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames)
|
float animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames)
|
||||||
float animFrameSpeed = 0.1f; // Animation play speed
|
float animFrameSpeed = 0.5f; // Animation play speed
|
||||||
|
bool animPause = false; // Pause animation
|
||||||
|
|
||||||
|
// UI required variables
|
||||||
|
char *animNames[64] = { 0 };
|
||||||
|
for (int i = 0; i < animCount; i++) animNames[i] = anims[i].name;
|
||||||
|
|
||||||
|
bool dropdownEditMode = false;
|
||||||
|
float animFrameProgress = 0.0f;
|
||||||
|
|
||||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
@ -61,17 +69,20 @@ int main(void)
|
|||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
UpdateCamera(&camera, CAMERA_ORBITAL);
|
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||||
|
|
||||||
// Select current animation
|
if (IsKeyPressed(KEY_P)) animPause = !animPause;
|
||||||
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 (!animPause && (animIndex < animCount))
|
||||||
if (IsKeyPressed(KEY_RIGHT)) animFrameSpeed += 0.1f;
|
{
|
||||||
else if (IsKeyPressed(KEY_LEFT)) animFrameSpeed -= 0.1f;
|
// Update model animation
|
||||||
|
animCurrentFrame += animFrameSpeed;
|
||||||
|
if (animCurrentFrame >= anims[animIndex].keyframeCount) animCurrentFrame = 0.0f;
|
||||||
|
UpdateModelAnimation(model, anims[animIndex], animCurrentFrame);
|
||||||
|
}
|
||||||
|
|
||||||
// Update model animation
|
// NOTE: Animation and playing speed selected through UI
|
||||||
animCurrentFrame += animFrameSpeed;
|
|
||||||
UpdateModelAnimation(model, modelAnimations[animIndex], animCurrentFrame);
|
// Update progressbar value with current frame
|
||||||
|
animFrameProgress = animCurrentFrame;
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
@ -88,13 +99,22 @@ int main(void)
|
|||||||
|
|
||||||
EndMode3D();
|
EndMode3D();
|
||||||
|
|
||||||
// Draw UI
|
// Draw UI, select anim and playing speed
|
||||||
//GuiDropdownBox((Rectangle){ 10, 20, 240, 30 }, "text", &animIndex, editMode);
|
GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 1);
|
||||||
|
if (GuiDropdownBox((Rectangle){ 10, 10, 140, 24 }, TextJoin(animNames, animCount, ";"),
|
||||||
|
&animIndex, dropdownEditMode)) dropdownEditMode = !dropdownEditMode;
|
||||||
|
|
||||||
DrawText(TextFormat("FRAME SPEED: x%.1f", animFrameSpeed), 10, 40, 20, RED);
|
GuiSlider((Rectangle){ 260, 10, 500, 24 }, "FRAME SPEED: ", TextFormat("x%.1f", animFrameSpeed),
|
||||||
|
&animFrameSpeed, 0.1f, 2.0f);
|
||||||
|
|
||||||
DrawText("Use the LEFT/RIGHT mouse buttons to switch animation", 10, 10, 20, GRAY);
|
// Draw playing timeline with keyframes
|
||||||
DrawText(TextFormat("Animation: %s", modelAnimations[animIndex].name), 10, GetScreenHeight() - 20, 10, DARKGRAY);
|
GuiLabel((Rectangle){ 10, GetScreenHeight() - 64, GetScreenWidth() - 20, 24 },
|
||||||
|
TextFormat("CURRENT FRAME: %.2f / %i", animFrameProgress, anims[animIndex].keyframeCount));
|
||||||
|
GuiProgressBar((Rectangle){ 10, GetScreenHeight() - 40, GetScreenWidth() - 20, 24 }, NULL, NULL,
|
||||||
|
&animFrameProgress, 0.0f, (float)anims[animIndex].keyframeCount);
|
||||||
|
for (int i = 0; i < anims[animIndex].keyframeCount; i++)
|
||||||
|
DrawRectangle(10 + ((float)(GetScreenWidth() - 20)/(float)anims[animIndex].keyframeCount)*(float)i,
|
||||||
|
GetScreenHeight() - 40, 1, 24, BLUE);
|
||||||
|
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
@ -102,6 +122,7 @@ int main(void)
|
|||||||
|
|
||||||
// De-Initialization
|
// De-Initialization
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
|
UnloadModelAnimations(anims, animCount); // Unload model animation
|
||||||
UnloadModel(model); // Unload model and meshes/material
|
UnloadModel(model); // Unload model and meshes/material
|
||||||
|
|
||||||
CloseWindow(); // Close window and OpenGL context
|
CloseWindow(); // Close window and OpenGL context
|
||||||
@ -110,5 +131,3 @@ int main(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BIN
examples/models/models_animation_timing.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 22 KiB |
@ -7,11 +7,11 @@
|
|||||||
* NOTE: raylib supports multiple models file formats:
|
* NOTE: raylib supports multiple models file formats:
|
||||||
*
|
*
|
||||||
* - OBJ > Text file format. Must include vertex position-texcoords-normals information,
|
* - OBJ > Text file format. Must include vertex position-texcoords-normals information,
|
||||||
* if files references some .mtl materials file, it will be loaded (or try to)
|
* if .obj references some .mtl materials file, it will be tried to be loaded
|
||||||
* - GLTF > Text/binary file format. Includes lot of information and it could
|
* - GLTF/GLB > Text/binary file formats. Includes lot of information and it could
|
||||||
* also reference external files, raylib will try loading mesh and materials data
|
* also reference external files, mesh and materials data will be tried to be loaded
|
||||||
* - IQM > Binary file format. Includes mesh vertex data but also animation data,
|
* - IQM > Binary file format. Includes mesh vertex data but also animation data,
|
||||||
* raylib can load .iqm animations
|
* meshes and animation data can be loaded
|
||||||
* - VOX > Binary file format. MagikaVoxel mesh format:
|
* - VOX > Binary file format. MagikaVoxel mesh format:
|
||||||
* https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt
|
* https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt
|
||||||
* - M3D > Binary file format. Model 3D format:
|
* - M3D > Binary file format. Model 3D format:
|
||||||
@ -43,10 +43,10 @@ int main(void)
|
|||||||
// Define the camera to look into our 3d world
|
// Define the camera to look into our 3d world
|
||||||
Camera camera = { 0 };
|
Camera camera = { 0 };
|
||||||
camera.position = (Vector3){ 50.0f, 50.0f, 50.0f }; // Camera position
|
camera.position = (Vector3){ 50.0f, 50.0f, 50.0f }; // Camera position
|
||||||
camera.target = (Vector3){ 0.0f, 10.0f, 0.0f }; // Camera looking at point
|
camera.target = (Vector3){ 0.0f, 12.0f, 0.0f }; // Camera looking at point
|
||||||
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
|
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.fovy = 45.0f; // Camera field-of-view Y
|
||||||
camera.projection = CAMERA_PERSPECTIVE; // Camera mode type
|
camera.projection = CAMERA_PERSPECTIVE; // Camera mode type
|
||||||
|
|
||||||
Model model = LoadModel("resources/models/obj/castle.obj"); // Load model
|
Model model = LoadModel("resources/models/obj/castle.obj"); // Load model
|
||||||
Texture2D texture = LoadTexture("resources/models/obj/castle_diffuse.png"); // Load model texture
|
Texture2D texture = LoadTexture("resources/models/obj/castle_diffuse.png"); // Load model texture
|
||||||
@ -61,8 +61,6 @@ int main(void)
|
|||||||
|
|
||||||
bool selected = false; // Selected object flag
|
bool selected = false; // Selected object flag
|
||||||
|
|
||||||
DisableCursor(); // Limit cursor to relative movement inside the window
|
|
||||||
|
|
||||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -71,7 +69,7 @@ int main(void)
|
|||||||
{
|
{
|
||||||
// Update
|
// Update
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
UpdateCamera(&camera, CAMERA_FIRST_PERSON);
|
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||||
|
|
||||||
// Load new models/textures on drag&drop
|
// Load new models/textures on drag&drop
|
||||||
if (IsFileDropped())
|
if (IsFileDropped())
|
||||||
@ -93,7 +91,10 @@ int main(void)
|
|||||||
|
|
||||||
bounds = GetMeshBoundingBox(model.meshes[0]);
|
bounds = GetMeshBoundingBox(model.meshes[0]);
|
||||||
|
|
||||||
// TODO: Move camera position from target enough distance to visualize model properly
|
// Move camera position from target enough distance to visualize model properly
|
||||||
|
camera.position.x = bounds.max.x + 10.0f;
|
||||||
|
camera.position.y = bounds.max.y + 10.0f;
|
||||||
|
camera.position.z = bounds.max.z + 10.0f;
|
||||||
}
|
}
|
||||||
else if (IsFileExtension(droppedFiles.paths[0], ".png")) // Texture file formats supported
|
else if (IsFileExtension(droppedFiles.paths[0], ".png")) // Texture file formats supported
|
||||||
{
|
{
|
||||||
|
|||||||
@ -42,15 +42,17 @@ int main(void)
|
|||||||
camera.fovy = 45.0f; // Camera field-of-view Y
|
camera.fovy = 45.0f; // Camera field-of-view Y
|
||||||
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
|
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
|
||||||
|
|
||||||
// Load gltf model
|
// Load model
|
||||||
Model model = LoadModel("resources/models/gltf/robot.glb");
|
Model model = LoadModel("resources/models/gltf/robot.glb");
|
||||||
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
|
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model world position
|
||||||
|
|
||||||
// Load gltf model animations
|
// Load model animations
|
||||||
int animsCount = 0;
|
int animCount = 0;
|
||||||
unsigned int animIndex = 0;
|
ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/robot.glb", &animCount);
|
||||||
unsigned int animCurrentFrame = 0;
|
|
||||||
ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount);
|
// 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
|
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
@ -63,13 +65,12 @@ int main(void)
|
|||||||
UpdateCamera(&camera, CAMERA_ORBITAL);
|
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||||
|
|
||||||
// Select current animation
|
// Select current animation
|
||||||
if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) animIndex = (animIndex + 1)%animsCount;
|
if (IsKeyPressed(KEY_RIGHT)) animIndex = (animIndex + 1)%animCount;
|
||||||
else if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) animIndex = (animIndex + animsCount - 1)%animsCount;
|
else if (IsKeyPressed(KEY_LEFT)) animIndex = (animIndex + animCount - 1)%animCount;
|
||||||
|
|
||||||
// Update model animation
|
// Update model animation
|
||||||
ModelAnimation anim = modelAnimations[animIndex];
|
animCurrentFrame = (animCurrentFrame + 1)%anims[animIndex].keyframeCount;
|
||||||
animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount;
|
UpdateModelAnimation(model, anims[animIndex], (float)animCurrentFrame);
|
||||||
UpdateModelAnimation(model, anim, animCurrentFrame);
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
@ -79,12 +80,15 @@ int main(void)
|
|||||||
ClearBackground(RAYWHITE);
|
ClearBackground(RAYWHITE);
|
||||||
|
|
||||||
BeginMode3D(camera);
|
BeginMode3D(camera);
|
||||||
DrawModel(model, position, 1.0f, WHITE); // Draw animated model
|
|
||||||
|
DrawModel(model, position, 1.0f, WHITE);
|
||||||
|
|
||||||
DrawGrid(10, 1.0f);
|
DrawGrid(10, 1.0f);
|
||||||
|
|
||||||
EndMode3D();
|
EndMode3D();
|
||||||
|
|
||||||
DrawText("Use the LEFT/RIGHT mouse buttons to switch animation", 10, 10, 20, GRAY);
|
DrawText(TextFormat("Current animation: %s", anims[animIndex].name), 10, 40, 20, MAROON);
|
||||||
DrawText(TextFormat("Animation: %s", anim.name), 10, GetScreenHeight() - 20, 10, DARKGRAY);
|
DrawText("Use the LEFT/RIGHT keys to switch animation", 10, 10, 20, GRAY);
|
||||||
|
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
@ -92,7 +96,8 @@ int main(void)
|
|||||||
|
|
||||||
// De-Initialization
|
// De-Initialization
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
UnloadModel(model); // Unload model and meshes/material
|
UnloadModelAnimations(anims, animCount); // Unload model animations data
|
||||||
|
UnloadModel(model); // Unload model
|
||||||
|
|
||||||
CloseWindow(); // Close window and OpenGL context
|
CloseWindow(); // Close window and OpenGL context
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 22 KiB |
@ -8,17 +8,15 @@
|
|||||||
*
|
*
|
||||||
* Example contributed by Culacant (@culacant) and reviewed by Ramon Santamaria (@raysan5)
|
* Example contributed by Culacant (@culacant) and reviewed by Ramon Santamaria (@raysan5)
|
||||||
*
|
*
|
||||||
|
* NOTES: To export an IQM model from blender, make sure it is not posed, the vertices need
|
||||||
|
* to be in the same position as they would be in edit mode and the scale of the models is
|
||||||
|
* set to 0; scaling can be set from the export menu
|
||||||
|
*
|
||||||
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
|
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
|
||||||
* BSD-like license that allows static linking with closed source software
|
* BSD-like license that allows static linking with closed source software
|
||||||
*
|
*
|
||||||
* Copyright (c) 2019-2025 Culacant (@culacant) and Ramon Santamaria (@raysan5)
|
* Copyright (c) 2019-2025 Culacant (@culacant) and Ramon Santamaria (@raysan5)
|
||||||
*
|
*
|
||||||
********************************************************************************************
|
|
||||||
*
|
|
||||||
* NOTE: To export a model from blender, make sure it is not posed, the vertices need to be
|
|
||||||
* in the same position as they would be in edit mode and the scale of your models is
|
|
||||||
* set to 0. Scaling can be done from the export menu
|
|
||||||
*
|
|
||||||
********************************************************************************************/
|
********************************************************************************************/
|
||||||
|
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
@ -38,7 +36,7 @@ int main(void)
|
|||||||
// Define the camera to look into our 3d world
|
// Define the camera to look into our 3d world
|
||||||
Camera camera = { 0 };
|
Camera camera = { 0 };
|
||||||
camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position
|
camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position
|
||||||
camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point
|
camera.target = (Vector3){ 0.0f, 4.0f, 0.0f }; // Camera looking at point
|
||||||
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
|
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.fovy = 45.0f; // Camera field-of-view Y
|
||||||
camera.projection = CAMERA_PERSPECTIVE; // Camera mode type
|
camera.projection = CAMERA_PERSPECTIVE; // Camera mode type
|
||||||
@ -46,15 +44,16 @@ int main(void)
|
|||||||
Model model = LoadModel("resources/models/iqm/guy.iqm"); // Load the animated model mesh and basic data
|
Model model = LoadModel("resources/models/iqm/guy.iqm"); // Load the animated model mesh and basic data
|
||||||
Texture2D texture = LoadTexture("resources/models/iqm/guytex.png"); // Load model texture and set material
|
Texture2D texture = LoadTexture("resources/models/iqm/guytex.png"); // Load model texture and set material
|
||||||
SetMaterialTexture(&model.materials[0], MATERIAL_MAP_DIFFUSE, texture); // Set model material map texture
|
SetMaterialTexture(&model.materials[0], MATERIAL_MAP_DIFFUSE, texture); // Set model material map texture
|
||||||
|
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
|
||||||
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
|
|
||||||
|
|
||||||
// Load animation data
|
// Load animation data
|
||||||
int animsCount = 0;
|
int animCount = 0;
|
||||||
ModelAnimation *anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", &animsCount);
|
ModelAnimation *anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", &animCount);
|
||||||
float animFrameCounter = 0;
|
|
||||||
|
// Animation playing variables
|
||||||
|
unsigned int animIndex = 0; // Current animation playing
|
||||||
|
float animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames)
|
||||||
|
|
||||||
DisableCursor(); // Catch cursor
|
|
||||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -66,9 +65,9 @@ int main(void)
|
|||||||
UpdateCamera(&camera, CAMERA_ORBITAL);
|
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||||
|
|
||||||
// Play animation when spacebar is held down
|
// Play animation when spacebar is held down
|
||||||
animFrameCounter += 1.0f;
|
animCurrentFrame += 1.0f;
|
||||||
UpdateModelAnimation(model, anims[0], animFrameCounter);
|
UpdateModelAnimation(model, anims[0], animCurrentFrame);
|
||||||
if (animFrameCounter >= anims[0].keyframeCount) animFrameCounter = 0;
|
if (animCurrentFrame >= anims[0].keyframeCount) animCurrentFrame = 0;
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
@ -81,16 +80,11 @@ int main(void)
|
|||||||
|
|
||||||
DrawModelEx(model, position, (Vector3){ 1.0f, 0.0f, 0.0f }, -90.0f, (Vector3){ 1.0f, 1.0f, 1.0f }, WHITE);
|
DrawModelEx(model, position, (Vector3){ 1.0f, 0.0f, 0.0f }, -90.0f, (Vector3){ 1.0f, 1.0f, 1.0f }, WHITE);
|
||||||
|
|
||||||
for (int i = 0; i < model.skeleton.boneCount; i++)
|
DrawGrid(10, 1.0f);
|
||||||
{
|
|
||||||
//DrawCube(anims[0].keyframePoses[animFrameCounter][i].translation, 0.2f, 0.2f, 0.2f, RED);
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawGrid(10, 1.0f); // Draw a grid
|
|
||||||
|
|
||||||
EndMode3D();
|
EndMode3D();
|
||||||
|
|
||||||
DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, 10, 20, MAROON);
|
DrawText(TextFormat("Current animation: %s", anims[animIndex].name), 10, 10, 20, MAROON);
|
||||||
DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, GRAY);
|
DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, GRAY);
|
||||||
|
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
@ -99,9 +93,9 @@ int main(void)
|
|||||||
|
|
||||||
// De-Initialization
|
// De-Initialization
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
UnloadTexture(texture); // Unload texture
|
UnloadTexture(texture); // Unload texture
|
||||||
UnloadModelAnimations(anims, animsCount); // Unload model animations data
|
UnloadModelAnimations(anims, animCount); // Unload model animations data
|
||||||
UnloadModel(model); // Unload model
|
UnloadModel(model); // Unload model
|
||||||
|
|
||||||
CloseWindow(); // Close window and OpenGL context
|
CloseWindow(); // Close window and OpenGL context
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 58 KiB |
@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
|
|
||||||
|
static void DrawModelSkeleton(ModelSkeleton skeleton, ModelAnimPose pose, float scale, Color color);
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Program main entry point
|
// Program main entry point
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
@ -41,22 +43,17 @@ int main(void)
|
|||||||
camera.fovy = 45.0f; // Camera field-of-view Y
|
camera.fovy = 45.0f; // Camera field-of-view Y
|
||||||
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
|
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
|
||||||
|
|
||||||
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
|
|
||||||
|
|
||||||
char modelFileName[128] = "resources/models/m3d/cesium_man.m3d";
|
|
||||||
bool drawMesh = 1;
|
|
||||||
bool drawSkeleton = 1;
|
|
||||||
bool animPlaying = false; // Store anim state, what to draw
|
|
||||||
|
|
||||||
// Load model
|
// Load model
|
||||||
Model model = LoadModel(modelFileName); // Load the bind-pose model mesh and basic data
|
Model model = LoadModel("resources/models/m3d/cesium_man.m3d"); // Load the animated model mesh and basic data
|
||||||
|
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
|
||||||
|
|
||||||
// Load animations
|
// Load animation data
|
||||||
int animsCount = 0;
|
int animCount = 0;
|
||||||
int animFrameCounter = 0, animId = 0;
|
ModelAnimation *anims = LoadModelAnimations("resources/models/m3d/cesium_man.m3d", &animCount);
|
||||||
ModelAnimation *anims = LoadModelAnimations(modelFileName, &animsCount); // Load skeletal animation data
|
|
||||||
|
|
||||||
DisableCursor(); // Limit cursor to relative movement inside the window
|
// Animation playing variables
|
||||||
|
unsigned int animIndex = 0; // Current animation playing
|
||||||
|
float animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames)
|
||||||
|
|
||||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
@ -66,38 +63,16 @@ int main(void)
|
|||||||
{
|
{
|
||||||
// Update
|
// Update
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
UpdateCamera(&camera, CAMERA_FIRST_PERSON);
|
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||||
|
|
||||||
if (animsCount)
|
// Select current animation
|
||||||
{
|
if (IsKeyPressed(KEY_RIGHT)) animIndex = (animIndex + 1)%animCount;
|
||||||
// Play animation when spacebar is held down (or step one frame with N)
|
else if (IsKeyPressed(KEY_LEFT)) animIndex = (animIndex + animCount - 1)%animCount;
|
||||||
if (IsKeyDown(KEY_SPACE) || IsKeyPressed(KEY_N))
|
|
||||||
{
|
|
||||||
animFrameCounter++;
|
|
||||||
|
|
||||||
if (animFrameCounter >= anims[animId].frameCount) animFrameCounter = 0;
|
// Update model animation
|
||||||
|
animCurrentFrame += 1.0f;
|
||||||
UpdateModelAnimation(model, anims[animId], animFrameCounter);
|
if (animCurrentFrame >= anims[animIndex].keyframeCount) animCurrentFrame = 0.0f;
|
||||||
animPlaying = true;
|
UpdateModelAnimation(model, anims[animIndex], animCurrentFrame);
|
||||||
}
|
|
||||||
|
|
||||||
// Select animation by pressing C
|
|
||||||
if (IsKeyPressed(KEY_C))
|
|
||||||
{
|
|
||||||
animFrameCounter = 0;
|
|
||||||
animId++;
|
|
||||||
|
|
||||||
if (animId >= (int)animsCount) animId = 0;
|
|
||||||
UpdateModelAnimation(model, anims[animId], 0);
|
|
||||||
animPlaying = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle skeleton drawing
|
|
||||||
if (IsKeyPressed(KEY_B)) drawSkeleton ^= 1;
|
|
||||||
|
|
||||||
// Toggle mesh drawing
|
|
||||||
if (IsKeyPressed(KEY_M)) drawMesh ^= 1;
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
@ -109,52 +84,19 @@ int main(void)
|
|||||||
BeginMode3D(camera);
|
BeginMode3D(camera);
|
||||||
|
|
||||||
// Draw 3d model with texture
|
// Draw 3d model with texture
|
||||||
if (drawMesh) DrawModel(model, position, 1.0f, WHITE);
|
if (!IsKeyDown(KEY_SPACE)) DrawModel(model, position, 1.0f, WHITE);
|
||||||
|
else
|
||||||
// Draw the animated skeleton
|
|
||||||
if (drawSkeleton)
|
|
||||||
{
|
{
|
||||||
// Loop to (boneCount - 1) because the last one is a special "no bone" bone,
|
// Draw the animated skeleton
|
||||||
// needed to workaround buggy models
|
DrawModelSkeleton(model.skeleton, anims[animIndex].keyframePoses[(int)animCurrentFrame], 1.0f, RED);
|
||||||
// without a -1, we would always draw a cube at the origin
|
|
||||||
for (int i = 0; i < model.boneCount - 1; i++)
|
|
||||||
{
|
|
||||||
// By default the model is loaded in bind-pose by LoadModel()
|
|
||||||
// But if UpdateModelAnimation() has been called at least once
|
|
||||||
// then the model is already in animation pose, so we need the animated skeleton
|
|
||||||
if (!animPlaying || !animsCount)
|
|
||||||
{
|
|
||||||
// Display the bind-pose skeleton
|
|
||||||
DrawCube(model.bindPose[i].translation, 0.04f, 0.04f, 0.04f, RED);
|
|
||||||
|
|
||||||
if (model.bones[i].parent >= 0)
|
|
||||||
{
|
|
||||||
DrawLine3D(model.bindPose[i].translation,
|
|
||||||
model.bindPose[model.bones[i].parent].translation, RED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Display the frame-pose skeleton
|
|
||||||
DrawCube(anims[animId].framePoses[animFrameCounter][i].translation, 0.05f, 0.05f, 0.05f, RED);
|
|
||||||
|
|
||||||
if (anims[animId].bones[i].parent >= 0)
|
|
||||||
{
|
|
||||||
DrawLine3D(anims[animId].framePoses[animFrameCounter][i].translation,
|
|
||||||
anims[animId].framePoses[animFrameCounter][anims[animId].bones[i].parent].translation, RED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawGrid(10, 1.0f); // Draw a grid
|
DrawGrid(10, 1.0f);
|
||||||
|
|
||||||
EndMode3D();
|
EndMode3D();
|
||||||
|
|
||||||
DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, GetScreenHeight() - 80, 10, MAROON);
|
DrawText(TextFormat("Current animation: %s", anims[animIndex].name), 10, 10, 20, LIGHTGRAY);
|
||||||
DrawText("PRESS N to STEP ONE ANIMATION FRAME", 10, GetScreenHeight() - 60, 10, DARKGRAY);
|
DrawText("Press SPACE to draw skeleton", 10, 40, 20, MAROON);
|
||||||
DrawText("PRESS C to CYCLE THROUGH ANIMATIONS", 10, GetScreenHeight() - 40, 10, DARKGRAY);
|
|
||||||
DrawText("PRESS M to toggle MESH, B to toggle SKELETON DRAWING", 10, GetScreenHeight() - 20, 10, DARKGRAY);
|
|
||||||
DrawText("(c) CesiumMan model by KhronosGroup", GetScreenWidth() - 210, GetScreenHeight() - 20, 10, GRAY);
|
DrawText("(c) CesiumMan model by KhronosGroup", GetScreenWidth() - 210, GetScreenHeight() - 20, 10, GRAY);
|
||||||
|
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
@ -163,14 +105,28 @@ int main(void)
|
|||||||
|
|
||||||
// De-Initialization
|
// De-Initialization
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
|
UnloadModelAnimations(anims, animCount); // Unload model animations data
|
||||||
// Unload model animations data
|
UnloadModel(model); // Unload model
|
||||||
UnloadModelAnimations(anims, animsCount);
|
|
||||||
|
|
||||||
UnloadModel(model); // Unload model
|
|
||||||
|
|
||||||
CloseWindow(); // Close window and OpenGL context
|
CloseWindow(); // Close window and OpenGL context
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw model skeleton
|
||||||
|
static void DrawModelSkeleton(ModelSkeleton skeleton, ModelAnimPose pose, float scale, Color color)
|
||||||
|
{
|
||||||
|
// Loop to (boneCount - 1) because the last one is a special "no bone" bone,
|
||||||
|
// needed to workaround buggy models without a -1, a cube is always drawn at the origin
|
||||||
|
for (int i = 0; i < skeleton.boneCount - 1; i++)
|
||||||
|
{
|
||||||
|
// Display the frame-pose skeleton
|
||||||
|
DrawCube(pose[i].translation, scale*0.05f, scale*0.05f, scale*0.05f, color);
|
||||||
|
|
||||||
|
if (skeleton.bones[i].parent >= 0)
|
||||||
|
{
|
||||||
|
DrawLine3D(pose[i].translation, pose[skeleton.bones[i].parent].translation, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 22 KiB |
@ -25,9 +25,9 @@
|
|||||||
#include "rlights.h"
|
#include "rlights.h"
|
||||||
|
|
||||||
#if defined(PLATFORM_DESKTOP)
|
#if defined(PLATFORM_DESKTOP)
|
||||||
#define GLSL_VERSION 330
|
#define GLSL_VERSION 330
|
||||||
#else // PLATFORM_ANDROID, PLATFORM_WEB
|
#else // PLATFORM_ANDROID, PLATFORM_WEB
|
||||||
#define GLSL_VERSION 100
|
#define GLSL_VERSION 100
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
@ -130,6 +130,7 @@ int main(void)
|
|||||||
camerarot.y = 0;
|
camerarot.y = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update camere movement, custom controls
|
||||||
UpdateCameraPro(&camera,
|
UpdateCameraPro(&camera,
|
||||||
(Vector3){ (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))*0.1f - (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))*0.1f, // Move forward-backward
|
(Vector3){ (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))*0.1f - (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))*0.1f, // Move forward-backward
|
||||||
(IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))*0.1f - (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))*0.1f, // Move right-left
|
(IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))*0.1f - (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))*0.1f, // Move right-left
|
||||||
@ -173,7 +174,7 @@ int main(void)
|
|||||||
DrawText("- MOUSE LEFT BUTTON: CYCLE VOX MODELS", 20, 50, 10, BLUE);
|
DrawText("- MOUSE LEFT BUTTON: CYCLE VOX MODELS", 20, 50, 10, BLUE);
|
||||||
DrawText("- MOUSE MIDDLE BUTTON: ZOOM OR ROTATE CAMERA", 20, 70, 10, BLUE);
|
DrawText("- MOUSE MIDDLE BUTTON: ZOOM OR ROTATE CAMERA", 20, 70, 10, BLUE);
|
||||||
DrawText("- UP-DOWN-LEFT-RIGHT KEYS: MOVE CAMERA", 20, 90, 10, BLUE);
|
DrawText("- UP-DOWN-LEFT-RIGHT KEYS: MOVE CAMERA", 20, 90, 10, BLUE);
|
||||||
DrawText(TextFormat("Model file: %s", GetFileName(voxFileNames[currentModel])), 10, 10, 20, GRAY);
|
DrawText(TextFormat("VOX model file: %s", GetFileName(voxFileNames[currentModel])), 10, 10, 20, GRAY);
|
||||||
|
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 36 KiB |
@ -55,7 +55,7 @@
|
|||||||
<Keyword>Win32Proj</Keyword>
|
<Keyword>Win32Proj</Keyword>
|
||||||
<RootNamespace>models_animation_timming</RootNamespace>
|
<RootNamespace>models_animation_timming</RootNamespace>
|
||||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
<ProjectName>models_animation_timming</ProjectName>
|
<ProjectName>models_animation_timing</ProjectName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
@ -553,7 +553,7 @@
|
|||||||
</PostBuildEvent>
|
</PostBuildEvent>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\..\..\examples\models\models_animation_timming.c" />
|
<ClCompile Include="..\..\..\examples\models\models_animation_timing.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="..\..\..\examples\examples.rc" />
|
<ResourceCompile Include="..\..\..\examples\examples.rc" />
|
||||||
@ -435,7 +435,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_blending",
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_web", "examples\core_window_web.vcxproj", "{4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_web", "examples\core_window_web.vcxproj", "{4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_timming", "examples\models_animation_timming.vcxproj", "{89D5A0E9-683C-465C-BF85-A880865175C8}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_timing", "examples\models_animation_timing.vcxproj", "{89D5A0E9-683C-465C-BF85-A880865175C8}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|||||||
35
src/config.h
@ -131,13 +131,13 @@
|
|||||||
// Module: rlgl - Configuration values
|
// Module: rlgl - Configuration values
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
#if !defined(EXTERNAL_CONFIG_FLAGS)
|
#if !defined(EXTERNAL_CONFIG_FLAGS)
|
||||||
|
//#define SUPPORT_GPU_SKINNING 1 // GPU skinning, comment if your GPU does not support more than 8 VBOs
|
||||||
|
|
||||||
// Enable OpenGL Debug Context (only available on OpenGL 4.3)
|
// Enable OpenGL Debug Context (only available on OpenGL 4.3)
|
||||||
//#define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT 1
|
//#define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT 1 // OpenGL debug context requested
|
||||||
|
|
||||||
// Show OpenGL extensions and capabilities detailed logs on init
|
// Show OpenGL extensions and capabilities detailed logs on init
|
||||||
//#define RLGL_SHOW_GL_DETAILS_INFO 1
|
//#define RLGL_SHOW_GL_DETAILS_INFO 1 // Show OpenGL detailed info on initialization (limits and extensions)
|
||||||
|
|
||||||
#define RL_SUPPORT_MESH_GPU_SKINNING 1 // GPU skinning, comment if your GPU does not support more than 8 VBOs
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//#define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 4096 // Default internal render batch elements limits
|
//#define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 4096 // Default internal render batch elements limits
|
||||||
@ -149,8 +149,8 @@
|
|||||||
|
|
||||||
#define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported
|
#define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported
|
||||||
|
|
||||||
#define RL_CULL_DISTANCE_NEAR 0.05 // Default projection matrix near cull distance
|
#define RL_CULL_DISTANCE_NEAR 0.05 // Default projection matrix near cull distance
|
||||||
#define RL_CULL_DISTANCE_FAR 4000.0 // Default projection matrix far cull distance
|
#define RL_CULL_DISTANCE_FAR 4000.0 // Default projection matrix far cull distance
|
||||||
|
|
||||||
// Default shader vertex attribute locations
|
// Default shader vertex attribute locations
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION 0
|
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION 0
|
||||||
@ -160,27 +160,31 @@
|
|||||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT 4
|
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT 4
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 5
|
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 5
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6
|
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6
|
||||||
#if defined(RL_SUPPORT_MESH_GPU_SKINNING)
|
#if defined(SUPPORT_GPU_SKINNING)
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS 7
|
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES 7
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8
|
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8
|
||||||
#endif
|
#endif
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX 9
|
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS 9
|
||||||
|
|
||||||
// Default shader vertex attribute names to set location points
|
// Default shader vertex attribute/uniform names to set location points
|
||||||
// NOTE: When a new shader is loaded, the following locations are tried to be set for convenience
|
// NOTE: When a new shader is loaded, locations are tried to be set for convenience,
|
||||||
|
// if the following names are found in the shader, if not, it's up to the user to set locations
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2
|
||||||
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES "vertexBoneIndices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES
|
||||||
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS
|
||||||
|
|
||||||
#define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix
|
#define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix
|
||||||
#define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix
|
#define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix
|
||||||
#define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix
|
#define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix
|
||||||
#define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix
|
#define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix
|
||||||
#define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView))
|
#define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView))
|
||||||
#define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color)
|
#define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (tint color, multiplied by texture color)
|
||||||
|
#define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // bone matrices
|
||||||
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0)
|
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0)
|
||||||
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1)
|
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1)
|
||||||
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2)
|
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2)
|
||||||
@ -278,10 +282,11 @@
|
|||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
#define MAX_MATERIAL_MAPS 12 // Maximum number of shader maps supported
|
#define MAX_MATERIAL_MAPS 12 // Maximum number of shader maps supported
|
||||||
|
|
||||||
#ifdef RL_SUPPORT_MESH_GPU_SKINNING
|
#ifdef SUPPORT_GPU_SKINNING
|
||||||
#define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh
|
// NOTE: Two additional vertex buffers required to store bone indices and bone weights
|
||||||
|
#define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh
|
||||||
#else
|
#else
|
||||||
#define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh
|
#define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
|
|||||||
59
src/raylib.h
@ -353,13 +353,15 @@ typedef struct Mesh {
|
|||||||
unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
|
unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
|
||||||
unsigned short *indices; // Vertex indices (in case vertex data comes indexed)
|
unsigned short *indices; // Vertex indices (in case vertex data comes indexed)
|
||||||
|
|
||||||
// Animation vertex data
|
// Skin data for animation
|
||||||
|
int boneCount; // Number of bones (MAX: 256 bones)
|
||||||
|
unsigned char *boneIndices; // Vertex bone indices, up to 4 bones influence by vertex (skinning) (shader-location = 6)
|
||||||
|
float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)
|
||||||
|
|
||||||
|
// Runtime animation vertex data (CPU skinning)
|
||||||
|
// NOTE: In case of GPU skinning, not used, pointers are NULL
|
||||||
float *animVertices; // Animated vertex positions (after bones transformations)
|
float *animVertices; // Animated vertex positions (after bones transformations)
|
||||||
float *animNormals; // Animated normals (after bones transformations)
|
float *animNormals; // Animated normals (after bones transformations)
|
||||||
unsigned char *boneIds; // Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) (shader-location = 6)
|
|
||||||
float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)
|
|
||||||
Matrix *boneMatrices; // Bones animated transformation matrices
|
|
||||||
int boneCount; // Number of bones
|
|
||||||
|
|
||||||
// OpenGL identifiers
|
// OpenGL identifiers
|
||||||
unsigned int vaoId; // OpenGL Vertex Array Object id
|
unsigned int vaoId; // OpenGL Vertex Array Object id
|
||||||
@ -393,12 +395,22 @@ typedef struct Transform {
|
|||||||
Vector3 scale; // Scale
|
Vector3 scale; // Scale
|
||||||
} Transform;
|
} Transform;
|
||||||
|
|
||||||
|
// Anim pose, an array of Transform[]
|
||||||
|
typedef Transform *ModelAnimPose;
|
||||||
|
|
||||||
// Bone, skeletal animation bone
|
// Bone, skeletal animation bone
|
||||||
typedef struct BoneInfo {
|
typedef struct BoneInfo {
|
||||||
char name[32]; // Bone name
|
char name[32]; // Bone name
|
||||||
int parent; // Bone parent
|
int parent; // Bone parent
|
||||||
} BoneInfo;
|
} BoneInfo;
|
||||||
|
|
||||||
|
// Skeleton, animation bones hierarchy
|
||||||
|
typedef struct ModelSkeleton {
|
||||||
|
int boneCount; // Number of bones
|
||||||
|
BoneInfo *bones; // Bones information (skeleton)
|
||||||
|
ModelAnimPose bindPose; // Bones base transformation (Transform[])
|
||||||
|
} ModelSkeleton;
|
||||||
|
|
||||||
// Model, meshes, materials and animation data
|
// Model, meshes, materials and animation data
|
||||||
typedef struct Model {
|
typedef struct Model {
|
||||||
Matrix transform; // Local transform matrix
|
Matrix transform; // Local transform matrix
|
||||||
@ -410,18 +422,20 @@ typedef struct Model {
|
|||||||
int *meshMaterial; // Mesh material number
|
int *meshMaterial; // Mesh material number
|
||||||
|
|
||||||
// Animation data
|
// Animation data
|
||||||
int boneCount; // Number of bones
|
ModelSkeleton skeleton; // Skeleton for animation
|
||||||
BoneInfo *bones; // Bones information (skeleton)
|
|
||||||
Transform *bindPose; // Bones base transformation (pose)
|
// Runtime animation data (CPU/GPU skinning)
|
||||||
|
ModelAnimPose currentPose; // Current animation pose (Transform[])
|
||||||
|
Matrix *boneMatrices; // Bones animated transformation matrices
|
||||||
} Model;
|
} Model;
|
||||||
|
|
||||||
// ModelAnimation
|
// ModelAnimation, contains a full animation sequence
|
||||||
typedef struct ModelAnimation {
|
typedef struct ModelAnimation {
|
||||||
char name[32]; // Animation name
|
char name[32]; // Animation name
|
||||||
int boneCount; // Number of bones
|
|
||||||
int frameCount; // Number of animation frames
|
int boneCount; // Number of bones (per pose)
|
||||||
BoneInfo *bones; // Bones information (skeleton)
|
int keyframeCount; // Number of animation key frames
|
||||||
Transform **framePoses; // Poses array by frame
|
ModelAnimPose *keyframePoses; // Animation sequence keyframe poses [keyframe][pose]
|
||||||
} ModelAnimation;
|
} ModelAnimation;
|
||||||
|
|
||||||
// Ray, ray for raycasting
|
// Ray, ray for raycasting
|
||||||
@ -768,6 +782,8 @@ typedef enum {
|
|||||||
#define MATERIAL_MAP_SPECULAR MATERIAL_MAP_METALNESS
|
#define MATERIAL_MAP_SPECULAR MATERIAL_MAP_METALNESS
|
||||||
|
|
||||||
// Shader location index
|
// Shader location index
|
||||||
|
// NOTE: Some locations are tried to be set automatically on shader loading,
|
||||||
|
// but only if default attributes/uniforms names are found, check config.h for names
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position
|
SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position
|
||||||
SHADER_LOC_VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01
|
SHADER_LOC_VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01
|
||||||
@ -790,15 +806,15 @@ typedef enum {
|
|||||||
SHADER_LOC_MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness
|
SHADER_LOC_MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness
|
||||||
SHADER_LOC_MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion
|
SHADER_LOC_MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion
|
||||||
SHADER_LOC_MAP_EMISSION, // Shader location: sampler2d texture: emission
|
SHADER_LOC_MAP_EMISSION, // Shader location: sampler2d texture: emission
|
||||||
SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: height
|
SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: heightmap
|
||||||
SHADER_LOC_MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap
|
SHADER_LOC_MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap
|
||||||
SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance
|
SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance
|
||||||
SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter
|
SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter
|
||||||
SHADER_LOC_MAP_BRDF, // Shader location: sampler2d texture: brdf
|
SHADER_LOC_MAP_BRDF, // Shader location: sampler2d texture: brdf
|
||||||
SHADER_LOC_VERTEX_BONEIDS, // Shader location: vertex attribute: boneIds
|
SHADER_LOC_VERTEX_BONEIDS, // Shader location: vertex attribute: bone indices
|
||||||
SHADER_LOC_VERTEX_BONEWEIGHTS, // Shader location: vertex attribute: boneWeights
|
SHADER_LOC_VERTEX_BONEWEIGHTS, // Shader location: vertex attribute: bone weights
|
||||||
SHADER_LOC_BONE_MATRICES, // Shader location: array of matrices uniform: boneMatrices
|
SHADER_LOC_MATRIX_BONETRANSFORMS, // Shader location: matrix attribute: bone transforms (animation)
|
||||||
SHADER_LOC_VERTEX_INSTANCE_TX // Shader location: vertex attribute: instanceTransform
|
SHADER_LOC_VERTEX_INSTANCETRANSFORMS // Shader location: vertex attribute: instance transforms
|
||||||
} ShaderLocationIndex;
|
} ShaderLocationIndex;
|
||||||
|
|
||||||
#define SHADER_LOC_MAP_DIFFUSE SHADER_LOC_MAP_ALBEDO
|
#define SHADER_LOC_MAP_DIFFUSE SHADER_LOC_MAP_ALBEDO
|
||||||
@ -1619,11 +1635,8 @@ RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId);
|
|||||||
|
|
||||||
// Model animations loading/unloading functions
|
// Model animations loading/unloading functions
|
||||||
RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount); // Load model animations from file
|
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 UpdateModelAnimation(Model model, ModelAnimation anim, float frame); // Update model animation pose (vertex buffers and bone matrices)
|
||||||
RLAPI void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame); // Update model animation mesh bone matrices (GPU skinning)
|
RLAPI void UpdateModelAnimationEx(Model model, ModelAnimation animA, float frameA, ModelAnimation animB, float frameB, float blend); // Update model animation pose, blending two animations
|
||||||
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 void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data
|
||||||
RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match
|
RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match
|
||||||
|
|
||||||
|
|||||||
@ -1286,7 +1286,7 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
|
|||||||
// - vertex color location = 3
|
// - vertex color location = 3
|
||||||
// - vertex tangent location = 4
|
// - vertex tangent location = 4
|
||||||
// - vertex texcoord2 location = 5
|
// - vertex texcoord2 location = 5
|
||||||
// - vertex boneIds location = 6
|
// - vertex boneIndices location = 6
|
||||||
// - vertex boneWeights location = 7
|
// - vertex boneWeights location = 7
|
||||||
|
|
||||||
// NOTE: If any location is not found, loc point becomes -1
|
// NOTE: If any location is not found, loc point becomes -1
|
||||||
@ -1303,9 +1303,9 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
|
|||||||
shader.locs[SHADER_LOC_VERTEX_NORMAL] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
|
shader.locs[SHADER_LOC_VERTEX_NORMAL] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
|
||||||
shader.locs[SHADER_LOC_VERTEX_TANGENT] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
|
shader.locs[SHADER_LOC_VERTEX_TANGENT] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
|
||||||
shader.locs[SHADER_LOC_VERTEX_COLOR] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
|
shader.locs[SHADER_LOC_VERTEX_COLOR] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
|
||||||
shader.locs[SHADER_LOC_VERTEX_BONEIDS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS);
|
shader.locs[SHADER_LOC_VERTEX_BONEIDS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES);
|
||||||
shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
|
shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
|
||||||
shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX);
|
shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORMS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORMS);
|
||||||
|
|
||||||
// Get handles to GLSL uniform locations (vertex shader)
|
// Get handles to GLSL uniform locations (vertex shader)
|
||||||
shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP);
|
shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP);
|
||||||
@ -1313,7 +1313,7 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
|
|||||||
shader.locs[SHADER_LOC_MATRIX_PROJECTION] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION);
|
shader.locs[SHADER_LOC_MATRIX_PROJECTION] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION);
|
||||||
shader.locs[SHADER_LOC_MATRIX_MODEL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL);
|
shader.locs[SHADER_LOC_MATRIX_MODEL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL);
|
||||||
shader.locs[SHADER_LOC_MATRIX_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL);
|
shader.locs[SHADER_LOC_MATRIX_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL);
|
||||||
shader.locs[SHADER_LOC_BONE_MATRICES] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES);
|
shader.locs[SHADER_LOC_MATRIX_BONETRANSFORMS] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES);
|
||||||
|
|
||||||
// Get handles to GLSL uniform locations (fragment shader)
|
// Get handles to GLSL uniform locations (fragment shader)
|
||||||
shader.locs[SHADER_LOC_COLOR_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR);
|
shader.locs[SHADER_LOC_COLOR_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR);
|
||||||
|
|||||||
60
src/rlgl.h
@ -65,7 +65,7 @@
|
|||||||
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR
|
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR
|
||||||
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT
|
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT
|
||||||
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2
|
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2
|
||||||
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS "vertexBoneIds" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS
|
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES "vertexBoneIndices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES
|
||||||
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS
|
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS
|
||||||
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix
|
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix
|
||||||
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix
|
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix
|
||||||
@ -73,7 +73,7 @@
|
|||||||
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix
|
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix
|
||||||
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)))
|
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)))
|
||||||
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color)
|
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color)
|
||||||
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES "boneMatrices" // bone matrices
|
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // bone matrices
|
||||||
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0)
|
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0)
|
||||||
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1)
|
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1)
|
||||||
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2)
|
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2)
|
||||||
@ -345,16 +345,16 @@
|
|||||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES
|
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6
|
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6
|
||||||
#endif
|
#endif
|
||||||
#ifdef RL_SUPPORT_MESH_GPU_SKINNING
|
#ifdef SUPPORT_GPU_SKINNING
|
||||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS
|
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS 7
|
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES 7
|
||||||
|
#endif
|
||||||
|
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS
|
||||||
|
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS
|
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8
|
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS 9
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX
|
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX 9
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
@ -992,31 +992,34 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
|
|||||||
|
|
||||||
// Default shader vertex attribute names to set location points
|
// Default shader vertex attribute names to set location points
|
||||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION
|
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION
|
||||||
#endif
|
#endif
|
||||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD
|
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD
|
||||||
#endif
|
#endif
|
||||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL
|
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL
|
||||||
#endif
|
#endif
|
||||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR
|
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR
|
||||||
#endif
|
#endif
|
||||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT
|
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT
|
||||||
#endif
|
#endif
|
||||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2
|
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2
|
||||||
#endif
|
#endif
|
||||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS
|
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS "vertexBoneIds" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES "vertexBoneIndices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES
|
||||||
#endif
|
#endif
|
||||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS
|
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS
|
||||||
#endif
|
#endif
|
||||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX
|
#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES
|
||||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX "instanceTransform" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX
|
#define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEMATRICES
|
||||||
|
#endif
|
||||||
|
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORMS
|
||||||
|
#define RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORMS "instanceTransform" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MVP
|
#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MVP
|
||||||
@ -1037,9 +1040,6 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
|
|||||||
#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR
|
#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR
|
||||||
#define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color)
|
#define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color)
|
||||||
#endif
|
#endif
|
||||||
#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES
|
|
||||||
#define RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES "boneMatrices" // bone matrices
|
|
||||||
#endif
|
|
||||||
#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0
|
#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0
|
||||||
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0)
|
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0)
|
||||||
#endif
|
#endif
|
||||||
@ -1049,6 +1049,9 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
|
|||||||
#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2
|
#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2
|
||||||
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2)
|
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2)
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES
|
||||||
|
#define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // bone matrices (required for GPU skinning)
|
||||||
|
#endif
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
// Module Types and Structures Definition
|
// Module Types and Structures Definition
|
||||||
@ -4339,10 +4342,9 @@ unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId)
|
|||||||
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
|
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
|
||||||
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
|
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
|
||||||
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
|
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
|
||||||
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX);
|
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORMS);
|
||||||
|
#ifdef SUPPORT_GPU_SKINNING
|
||||||
#ifdef RL_SUPPORT_MESH_GPU_SKINNING
|
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES);
|
||||||
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS);
|
|
||||||
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
|
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||