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_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_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_basic_lighting;★★★★;3.0;4.2;2019;2025;"Chris Camacho";@chriscamacho
|
||||
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
|
||||
*
|
||||
* 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:
|
||||
* - Blending movement animations (walk/run) with action animations (jump/attack)
|
||||
* - Creating smooth animation transitions
|
||||
@@ -27,8 +27,10 @@
|
||||
|
||||
#include "raymath.h"
|
||||
|
||||
#include <string.h> // Required for: memcpy()
|
||||
#include <stdlib.h> // Required for: NULL
|
||||
#include "rlgl.h" // Requried for: rlUpdateVertexBuffer() (CPU-skinning)
|
||||
|
||||
#include <string.h> // Required for: memcpy()
|
||||
#include <stdlib.h> // Required for: NULL
|
||||
|
||||
#if defined(PLATFORM_DESKTOP)
|
||||
#define GLSL_VERSION 330
|
||||
@@ -40,8 +42,8 @@
|
||||
// Module Functions Declaration
|
||||
//------------------------------------------------------------------------------------
|
||||
static bool IsUpperBodyBone(const char *boneName);
|
||||
static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int frame1,
|
||||
ModelAnimation *anim2, int frame2, float blendFactor, bool upperBodyBlend);
|
||||
static void UpdateModelAnimationBones(Model *model, ModelAnimation *anim1, int frame1,
|
||||
ModelAnimation *anim2, int frame2, float blend, bool upperBodyBlend);
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Program main entry point
|
||||
@@ -57,51 +59,40 @@ int main(void)
|
||||
|
||||
// Define the camera to look into our 3d world
|
||||
Camera camera = { 0 };
|
||||
camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position
|
||||
camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point
|
||||
camera.position = (Vector3){ 4.0f, 4.0f, 4.0f }; // Camera position
|
||||
camera.target = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera looking at point
|
||||
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
|
||||
camera.fovy = 45.0f; // Camera field-of-view Y
|
||||
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
|
||||
|
||||
// Load gltf model
|
||||
Model characterModel = LoadModel("resources/models/gltf/greenman.glb");
|
||||
Model model = LoadModel("resources/models/gltf/greenman.glb");
|
||||
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
|
||||
|
||||
// Load skinning shader
|
||||
// WARNING: GPU skinning must be enabled in raylib with a compilation flag,
|
||||
// if not enabled, CPU skinning will be used instead
|
||||
Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
|
||||
TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
|
||||
|
||||
characterModel.materials[1].shader = skinningShader;
|
||||
model.materials[1].shader = skinningShader;
|
||||
|
||||
// Load gltf model animations
|
||||
int animsCount = 0;
|
||||
ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", &animsCount);
|
||||
int animCount = 0;
|
||||
ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/greenman.glb", &animCount);
|
||||
|
||||
// Log all available animations for debugging
|
||||
TraceLog(LOG_INFO, "Found %d animations:", animsCount);
|
||||
for (int i = 0; i < animsCount; i++)
|
||||
{
|
||||
TraceLog(LOG_INFO, " Animation %d: %s (%d frames)", i, modelAnimations[i].name, modelAnimations[i].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)
|
||||
// Use specific animation indices: 2-walk/move, 3-attack
|
||||
unsigned int animIndex0 = 2; // Walk/Move animation (index 2)
|
||||
unsigned int animIndex1 = 3; // Attack animation (index 3)
|
||||
unsigned int animCurrentFrame0 = 0;
|
||||
unsigned int animCurrentFrame1 = 0;
|
||||
unsigned int animCurrentFrame2 = 0;
|
||||
|
||||
// Validate indices
|
||||
if (animIndex1 >= animsCount) animIndex1 = 0;
|
||||
if (animIndex2 >= animsCount) animIndex2 = (animsCount > 1) ? 1 : 0;
|
||||
|
||||
TraceLog(LOG_INFO, "Using Walk (index %d): %s", animIndex1, modelAnimations[animIndex1].name);
|
||||
TraceLog(LOG_INFO, "Using Attack (index %d): %s", animIndex2, modelAnimations[animIndex2].name);
|
||||
if (animIndex0 >= animCount) animIndex0 = 0;
|
||||
if (animIndex1 >= animCount) animIndex1 = (animCount > 1) ? 1 : 0;
|
||||
|
||||
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
|
||||
bool upperBodyBlend = true; // Toggle: true = upper/lower body blending, false = uniform blending (50/50)
|
||||
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
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// Main game loop
|
||||
@@ -109,24 +100,28 @@ int main(void)
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
UpdateCamera(&camera, CAMERA_THIRD_PERSON);
|
||||
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||
|
||||
// Toggle upper/lower body blending mode (SPACE key)
|
||||
if (IsKeyPressed(KEY_SPACE)) upperBodyBlend = !upperBodyBlend;
|
||||
|
||||
// Update animation frames
|
||||
ModelAnimation anim1 = modelAnimations[animIndex1];
|
||||
ModelAnimation anim2 = modelAnimations[animIndex2];
|
||||
ModelAnimation anim0 = anims[animIndex0];
|
||||
ModelAnimation anim1 = anims[animIndex1];
|
||||
|
||||
animCurrentFrame0 = (animCurrentFrame0 + 1)%anim0.keyframeCount;
|
||||
animCurrentFrame1 = (animCurrentFrame1 + 1)%anim1.keyframeCount;
|
||||
animCurrentFrame2 = (animCurrentFrame2 + 1)%anim2.keyframeCount;
|
||||
|
||||
// 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 OFF: uniform blend at 0.5 (50% walk, 50% attack)
|
||||
float blendFactor = upperBodyBlend ? 1.0f : 0.5f;
|
||||
BlendModelAnimationsBones(&characterModel, &anim1, animCurrentFrame1, &anim2, animCurrentFrame2, blendFactor, upperBodyBlend);
|
||||
float blendFactor = (upperBodyBlend? 1.0f : 0.5f);
|
||||
UpdateModelAnimationBones(&model, &anim0, animCurrentFrame0,
|
||||
&anim1, animCurrentFrame1, blendFactor, upperBodyBlend);
|
||||
|
||||
// raylib provided animation blending function
|
||||
//UpdateModelAnimationEx(model, anim0, (float)animCurrentFrame0,
|
||||
// anim1, (float)animCurrentFrame1, blendFactor);
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
@@ -137,19 +132,18 @@ int main(void)
|
||||
|
||||
BeginMode3D(camera);
|
||||
|
||||
// Draw character mesh, pose calculation is done in shader (GPU skinning)
|
||||
DrawMesh(characterModel.meshes[0], characterModel.materials[1], characterModel.transform);
|
||||
DrawModel(model, position, 1.0f, WHITE);
|
||||
|
||||
DrawGrid(10, 1.0f);
|
||||
|
||||
EndMode3D();
|
||||
|
||||
// Draw UI
|
||||
DrawText("BONE BLENDING EXAMPLE", 10, 10, 20, DARKGRAY);
|
||||
DrawText(TextFormat("Walk (Animation 2): %s", anim1.name), 10, 35, 10, GRAY);
|
||||
DrawText(TextFormat("Attack (Animation 3): %s", anim2.name), 10, 50, 10, GRAY);
|
||||
DrawText(TextFormat("Mode: %s", upperBodyBlend ? "Upper/Lower Body Blending" : "Uniform Blending"), 10, 65, 10, GRAY);
|
||||
DrawText("SPACE - Toggle blending mode", 10, GetScreenHeight() - 20, 10, DARKGRAY);
|
||||
DrawText(TextFormat("ANIM 0: %s", anim0.name), 10, 10, 20, GRAY);
|
||||
DrawText(TextFormat("ANIM 1: %s", anim1.name), 10, 40, 20, GRAY);
|
||||
DrawText(TextFormat("[SPACE] Toggle blending mode: %s",
|
||||
upperBodyBlend? "Upper/Lower Body Blending" : "Uniform Blending"),
|
||||
10, GetScreenHeight() - 30, 20, DARKGRAY);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
@@ -157,8 +151,8 @@ int main(void)
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation
|
||||
UnloadModel(characterModel); // Unload model and meshes/material
|
||||
UnloadModelAnimations(anims, animCount); // Unload model animation
|
||||
UnloadModel(model); // Unload model and meshes/material
|
||||
UnloadShader(skinningShader); // Unload GPU skinning shader
|
||||
|
||||
CloseWindow(); // Close window and OpenGL context
|
||||
@@ -199,74 +193,138 @@ static bool IsUpperBodyBone(const char *boneName)
|
||||
}
|
||||
|
||||
// Blend two animations per-bone with selective upper/lower body blending
|
||||
static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int frame1,
|
||||
ModelAnimation *anim2, int frame2, float blendFactor, bool upperBodyBlend)
|
||||
static void UpdateModelAnimationBones(Model *model, ModelAnimation *anim0, int frame0,
|
||||
ModelAnimation *anim1, int frame1, float blend, bool upperBodyBlend)
|
||||
{
|
||||
// Validate inputs
|
||||
if (anim1->boneCount == 0 || anim1->keyframePoses == NULL ||
|
||||
anim2->boneCount == 0 || anim2->keyframePoses == NULL ||
|
||||
model->skeleton.boneCount == 0 || model->skeleton.bindPose == NULL)
|
||||
if ((anim0->boneCount != 0) && (anim0->keyframePoses != NULL) &&
|
||||
(anim1->boneCount != 0) && (anim1->keyframePoses != NULL) &&
|
||||
(model->skeleton.boneCount != 0) && (model->skeleton.bindPose != NULL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Clamp blend factor to [0, 1]
|
||||
blendFactor = fminf(1.0f, fmaxf(0.0f, blendFactor));
|
||||
|
||||
// Ensure frame indices are valid
|
||||
if (frame1 >= anim1->keyframeCount) frame1 = anim1->keyframeCount - 1;
|
||||
if (frame2 >= anim2->keyframeCount) frame2 = anim2->keyframeCount - 1;
|
||||
if (frame1 < 0) frame1 = 0;
|
||||
if (frame2 < 0) frame2 = 0;
|
||||
|
||||
// Get bone count (use minimum of all to be safe)
|
||||
int boneCount = model->skeleton.boneCount;
|
||||
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;
|
||||
// Clamp blend factor to [0, 1]
|
||||
blend = fminf(1.0f, fmaxf(0.0f, blend));
|
||||
|
||||
// If upper body blending is enabled, use different blend factors for upper vs lower body
|
||||
if (upperBodyBlend)
|
||||
// Ensure frame indices are valid
|
||||
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;
|
||||
|
||||
// Get bone count (use minimum of all to be safe)
|
||||
int boneCount = model->skeleton.boneCount;
|
||||
if (anim0->boneCount < boneCount) boneCount = anim0->boneCount;
|
||||
if (anim1->boneCount < boneCount) boneCount = anim1->boneCount;
|
||||
|
||||
// Blend each bone
|
||||
for (int boneIndex = 0; boneIndex < boneCount; boneIndex++)
|
||||
{
|
||||
const char *boneName = model->skeleton.bones[boneId].name;
|
||||
bool isUpperBody = IsUpperBodyBone(boneName);
|
||||
// Determine blend factor for this bone
|
||||
float boneBlendFactor = blend;
|
||||
|
||||
// Upper body: use anim2 (attack), Lower body: use anim1 (walk)
|
||||
// blendFactor = 0.0 means full anim1 (walk), 1.0 means full anim2 (attack)
|
||||
if (isUpperBody) boneBlendFactor = blendFactor; // Upper body: blend towards anim2 (attack)
|
||||
else boneBlendFactor = 1.0f - blendFactor; // Lower body: blend towards anim1 (walk) - invert the blend
|
||||
// If upper body blending is enabled, use different blend factors for upper vs lower body
|
||||
if (upperBodyBlend)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// CPU skinning, updates CPU buffers and uploads them to GPU (if available)
|
||||
// NOTE: Fallback in case GPU skinning is not supported or enabled
|
||||
for (int m = 0; m < model->meshCount; m++)
|
||||
{
|
||||
Mesh mesh = model->meshes[m];
|
||||
Vector3 animVertex = { 0 };
|
||||
Vector3 animNormal = { 0 };
|
||||
const int vertexValuesCount = mesh.vertexCount*3;
|
||||
|
||||
int boneIndex = 0;
|
||||
int boneCounter = 0;
|
||||
float boneWeight = 0.0f;
|
||||
bool bufferUpdateRequired = false; // Flag to check when anim vertex information is updated
|
||||
|
||||
// Skip if missing bone data or missing anim buffers initialization
|
||||
if ((mesh.boneWeights == NULL) || (mesh.boneIndices == NULL) ||
|
||||
(mesh.animVertices == NULL) || (mesh.animNormals == NULL)) continue;
|
||||
|
||||
for (int vCounter = 0; vCounter < vertexValuesCount; vCounter += 3)
|
||||
{
|
||||
mesh.animVertices[vCounter] = 0;
|
||||
mesh.animVertices[vCounter + 1] = 0;
|
||||
mesh.animVertices[vCounter + 2] = 0;
|
||||
if (mesh.animNormals != NULL)
|
||||
{
|
||||
mesh.animNormals[vCounter] = 0;
|
||||
mesh.animNormals[vCounter + 1] = 0;
|
||||
mesh.animNormals[vCounter + 2] = 0;
|
||||
}
|
||||
|
||||
// Iterates over 4 bones per vertex
|
||||
for (int j = 0; j < 4; j++, boneCounter++)
|
||||
{
|
||||
boneWeight = mesh.boneWeights[boneCounter];
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Get transforms from both animations
|
||||
Transform *bindTransform = &model->skeleton.bindPose[boneId];
|
||||
Transform *anim1Transform = &anim1->keyframePoses[frame1][boneId];
|
||||
Transform *anim2Transform = &anim2->keyframePoses[frame2][boneId];
|
||||
|
||||
// Blend the transforms
|
||||
Transform blended = { 0 };
|
||||
blended.translation = Vector3Lerp(anim1Transform->translation, anim2Transform->translation, boneBlendFactor);
|
||||
blended.rotation = QuaternionSlerp(anim1Transform->rotation, anim2Transform->rotation, boneBlendFactor);
|
||||
blended.scale = Vector3Lerp(anim1Transform->scale, anim2Transform->scale, boneBlendFactor);
|
||||
|
||||
// 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[boneId] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 82 KiB |
@@ -21,7 +21,8 @@
|
||||
|
||||
#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)
|
||||
#define GLSL_VERSION 330
|
||||
@@ -43,31 +44,58 @@ int main(void)
|
||||
|
||||
// Define the camera to look into our 3d world
|
||||
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.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
|
||||
camera.fovy = 45.0f; // Camera field-of-view Y
|
||||
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
|
||||
|
||||
// Load model
|
||||
Model 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
|
||||
// WARNING: It requires SUPPORT_GPU_SKINNING enabled on raylib (disabled by default)
|
||||
Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
|
||||
TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
|
||||
|
||||
// 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
|
||||
int animsCount = 0;
|
||||
ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount);
|
||||
int animCount = 0;
|
||||
ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/robot.glb", &animCount);
|
||||
|
||||
// Define animation variables
|
||||
unsigned int animIndex0 = 0;
|
||||
unsigned int animIndex1 = 0;
|
||||
float animCurrentFrame = 0;
|
||||
float blendFactor = 0.5f;
|
||||
// Animation playing variables
|
||||
// NOTE: Two animations are played with a smooth transition between them
|
||||
int currentAnimPlaying = 0; // Current animation playing (0 o 1)
|
||||
int nextAnimToPlay = 1; // Next animation to play (to transition)
|
||||
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
|
||||
//--------------------------------------------------------------------------------------
|
||||
@@ -79,23 +107,100 @@ int main(void)
|
||||
//----------------------------------------------------------------------------------
|
||||
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||
|
||||
// Select current animation
|
||||
if (IsKeyPressed(KEY_T)) animIndex0 = (animIndex0 + 1)%animsCount;
|
||||
else if (IsKeyPressed(KEY_G)) animIndex0 = (animIndex0 + animsCount - 1)%animsCount;
|
||||
if (IsKeyPressed(KEY_Y)) animIndex1 = (animIndex1 + 1)%animsCount;
|
||||
else if (IsKeyPressed(KEY_H)) animIndex1 = (animIndex1 + animsCount - 1)%animsCount;
|
||||
|
||||
// Select blend factor
|
||||
if (IsKeyPressed(KEY_U)) blendFactor = clamp(blendFactor - 0.1, 0.0f, 1.0f);
|
||||
else if (IsKeyPressed(KEY_J)) blendFactor = clamp(blendFactor + 0.1, 0.0f, 1.0f);
|
||||
if (IsKeyPressed(KEY_P)) animPause = !animPause;
|
||||
|
||||
// Update animation
|
||||
animCurrentFrame += 0.2f;
|
||||
if (!animPause)
|
||||
{
|
||||
// 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 bones
|
||||
// Note: Same animation frame index is used below. By default it loops both animations
|
||||
UpdateModelAnimationEx(characterModel, modelAnimations[animIndex0], animCurrentFrame,
|
||||
modelAnimations[animIndex1], animCurrentFrame, blendFactor);
|
||||
// Set animation transition
|
||||
animTransition = true;
|
||||
animBlendTimeCounter = 0.0f;
|
||||
animBlendFactor = 0.0f;
|
||||
}
|
||||
|
||||
if (animTransition)
|
||||
{
|
||||
// Playing anim0 and anim1 at the same time
|
||||
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
|
||||
@@ -106,16 +211,47 @@ int main(void)
|
||||
|
||||
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);
|
||||
|
||||
EndMode3D();
|
||||
|
||||
DrawText("Use the U/J to adjust blend factor", 10, 10, 20, GRAY);
|
||||
DrawText("Use the T/G to switch first animation", 10, 30, 20, GRAY);
|
||||
DrawText("Use the Y/H to switch second animation", 10, 50, 20, GRAY);
|
||||
DrawText(TextFormat("Animations: %s, %s", modelAnimations[animIndex0].name, modelAnimations[animIndex1].name), 10, 70, 20, BLACK);
|
||||
DrawText(TextFormat("Blend Factor: %f", blendFactor), 10, 86, 20, BLACK);
|
||||
if (animTransition) DrawText("ANIM TRANSITION BLENDING!", 170, 50, 30, BLUE);
|
||||
|
||||
// Draw UI elements
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// 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();
|
||||
//----------------------------------------------------------------------------------
|
||||
@@ -123,9 +259,8 @@ int main(void)
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation
|
||||
UnloadModel(characterModel); // Unload model and meshes/material
|
||||
|
||||
UnloadModelAnimations(anims, animCount); // Unload model animation
|
||||
UnloadModel(model); // Unload model and meshes/material
|
||||
UnloadShader(skinningShader); // Unload GPU skinning shader
|
||||
|
||||
CloseWindow(); // Close window and OpenGL context
|
||||
|
||||
|
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,
|
||||
* if not enabled, CPU skinning will be used instead
|
||||
* NOTE: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS
|
||||
*
|
||||
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
|
||||
* BSD-like license that allows static linking with closed source software
|
||||
|
||||
|
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
|
||||
*
|
||||
@@ -16,7 +16,7 @@
|
||||
#include "raylib.h"
|
||||
|
||||
#define RAYGUI_IMPLEMENTATION
|
||||
#include "raygui.h" // Required for: UI controls
|
||||
#include "raygui.h" // Required for: UI controls
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Program main entry point
|
||||
@@ -28,7 +28,7 @@ int main(void)
|
||||
const int screenWidth = 800;
|
||||
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
|
||||
Camera camera = { 0 };
|
||||
@@ -43,13 +43,21 @@ int main(void)
|
||||
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model world position
|
||||
|
||||
// Load model animations
|
||||
int animsCount = 0;
|
||||
ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount);
|
||||
int animCount = 0;
|
||||
ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/robot.glb", &animCount);
|
||||
|
||||
// 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 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
|
||||
//--------------------------------------------------------------------------------------
|
||||
@@ -61,17 +69,20 @@ int main(void)
|
||||
//----------------------------------------------------------------------------------
|
||||
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||
|
||||
// Select current animation
|
||||
if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) animIndex = (animIndex + 1)%animsCount;
|
||||
else if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) animIndex = (animIndex + animsCount - 1)%animsCount;
|
||||
if (IsKeyPressed(KEY_P)) animPause = !animPause;
|
||||
|
||||
// Select animation playing speed
|
||||
if (IsKeyPressed(KEY_RIGHT)) animFrameSpeed += 0.1f;
|
||||
else if (IsKeyPressed(KEY_LEFT)) animFrameSpeed -= 0.1f;
|
||||
if (!animPause && (animIndex < animCount))
|
||||
{
|
||||
// Update model animation
|
||||
animCurrentFrame += animFrameSpeed;
|
||||
if (animCurrentFrame >= anims[animIndex].keyframeCount) animCurrentFrame = 0.0f;
|
||||
UpdateModelAnimation(model, anims[animIndex], animCurrentFrame);
|
||||
}
|
||||
|
||||
// Update model animation
|
||||
animCurrentFrame += animFrameSpeed;
|
||||
UpdateModelAnimation(model, modelAnimations[animIndex], animCurrentFrame);
|
||||
// NOTE: Animation and playing speed selected through UI
|
||||
|
||||
// Update progressbar value with current frame
|
||||
animFrameProgress = animCurrentFrame;
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
@@ -88,13 +99,22 @@ int main(void)
|
||||
|
||||
EndMode3D();
|
||||
|
||||
// Draw UI
|
||||
//GuiDropdownBox((Rectangle){ 10, 20, 240, 30 }, "text", &animIndex, editMode);
|
||||
// Draw UI, select anim and playing speed
|
||||
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);
|
||||
DrawText(TextFormat("Animation: %s", modelAnimations[animIndex].name), 10, GetScreenHeight() - 20, 10, DARKGRAY);
|
||||
// Draw playing timeline with keyframes
|
||||
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();
|
||||
//----------------------------------------------------------------------------------
|
||||
@@ -102,6 +122,7 @@ int main(void)
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
UnloadModelAnimations(anims, animCount); // Unload model animation
|
||||
UnloadModel(model); // Unload model and meshes/material
|
||||
|
||||
CloseWindow(); // Close window and OpenGL context
|
||||
@@ -110,5 +131,3 @@ int main(void)
|
||||
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:
|
||||
*
|
||||
* - 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)
|
||||
* - GLTF > Text/binary file format. Includes lot of information and it could
|
||||
* also reference external files, raylib will try loading mesh and materials data
|
||||
* if .obj references some .mtl materials file, it will be tried to be loaded
|
||||
* - GLTF/GLB > Text/binary file formats. Includes lot of information and it could
|
||||
* 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,
|
||||
* raylib can load .iqm animations
|
||||
* meshes and animation data can be loaded
|
||||
* - VOX > Binary file format. MagikaVoxel mesh format:
|
||||
* https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt
|
||||
* - M3D > Binary file format. Model 3D format:
|
||||
@@ -43,10 +43,10 @@ int main(void)
|
||||
// Define the camera to look into our 3d world
|
||||
Camera camera = { 0 };
|
||||
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.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
|
||||
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
|
||||
|
||||
DisableCursor(); // Limit cursor to relative movement inside the window
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
@@ -71,7 +69,7 @@ int main(void)
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
UpdateCamera(&camera, CAMERA_FIRST_PERSON);
|
||||
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||
|
||||
// Load new models/textures on drag&drop
|
||||
if (IsFileDropped())
|
||||
@@ -93,7 +91,10 @@ int main(void)
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
@@ -42,15 +42,17 @@ int main(void)
|
||||
camera.fovy = 45.0f; // Camera field-of-view Y
|
||||
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
|
||||
|
||||
// Load gltf model
|
||||
// Load model
|
||||
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
|
||||
int animsCount = 0;
|
||||
unsigned int animIndex = 0;
|
||||
unsigned int animCurrentFrame = 0;
|
||||
ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount);
|
||||
// Load model animations
|
||||
int animCount = 0;
|
||||
ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/robot.glb", &animCount);
|
||||
|
||||
// Animation playing variables
|
||||
unsigned int animIndex = 0; // Current animation playing
|
||||
unsigned int animCurrentFrame = 0; // Current animation frame
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
@@ -63,13 +65,12 @@ int main(void)
|
||||
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||
|
||||
// Select current animation
|
||||
if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) animIndex = (animIndex + 1)%animsCount;
|
||||
else if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) animIndex = (animIndex + animsCount - 1)%animsCount;
|
||||
if (IsKeyPressed(KEY_RIGHT)) animIndex = (animIndex + 1)%animCount;
|
||||
else if (IsKeyPressed(KEY_LEFT)) animIndex = (animIndex + animCount - 1)%animCount;
|
||||
|
||||
// Update model animation
|
||||
ModelAnimation anim = modelAnimations[animIndex];
|
||||
animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount;
|
||||
UpdateModelAnimation(model, anim, animCurrentFrame);
|
||||
animCurrentFrame = (animCurrentFrame + 1)%anims[animIndex].keyframeCount;
|
||||
UpdateModelAnimation(model, anims[animIndex], (float)animCurrentFrame);
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
@@ -79,12 +80,15 @@ int main(void)
|
||||
ClearBackground(RAYWHITE);
|
||||
|
||||
BeginMode3D(camera);
|
||||
DrawModel(model, position, 1.0f, WHITE); // Draw animated model
|
||||
|
||||
DrawModel(model, position, 1.0f, WHITE);
|
||||
|
||||
DrawGrid(10, 1.0f);
|
||||
|
||||
EndMode3D();
|
||||
|
||||
DrawText("Use the LEFT/RIGHT mouse buttons to switch animation", 10, 10, 20, GRAY);
|
||||
DrawText(TextFormat("Animation: %s", anim.name), 10, GetScreenHeight() - 20, 10, DARKGRAY);
|
||||
DrawText(TextFormat("Current animation: %s", anims[animIndex].name), 10, 40, 20, MAROON);
|
||||
DrawText("Use the LEFT/RIGHT keys to switch animation", 10, 10, 20, GRAY);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
@@ -92,7 +96,8 @@ int main(void)
|
||||
|
||||
// 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
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
|
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)
|
||||
*
|
||||
* 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,
|
||||
* BSD-like license that allows static linking with closed source software
|
||||
*
|
||||
* 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"
|
||||
@@ -38,7 +36,7 @@ int main(void)
|
||||
// Define the camera to look into our 3d world
|
||||
Camera camera = { 0 };
|
||||
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.fovy = 45.0f; // Camera field-of-view Y
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
int animsCount = 0;
|
||||
ModelAnimation *anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", &animsCount);
|
||||
float animFrameCounter = 0;
|
||||
int animCount = 0;
|
||||
ModelAnimation *anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", &animCount);
|
||||
|
||||
// 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
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
@@ -66,9 +65,9 @@ int main(void)
|
||||
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||
|
||||
// Play animation when spacebar is held down
|
||||
animFrameCounter += 1.0f;
|
||||
UpdateModelAnimation(model, anims[0], animFrameCounter);
|
||||
if (animFrameCounter >= anims[0].keyframeCount) animFrameCounter = 0;
|
||||
animCurrentFrame += 1.0f;
|
||||
UpdateModelAnimation(model, anims[0], animCurrentFrame);
|
||||
if (animCurrentFrame >= anims[0].keyframeCount) animCurrentFrame = 0;
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// 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);
|
||||
|
||||
for (int i = 0; i < model.skeleton.boneCount; i++)
|
||||
{
|
||||
//DrawCube(anims[0].keyframePoses[animFrameCounter][i].translation, 0.2f, 0.2f, 0.2f, RED);
|
||||
}
|
||||
|
||||
DrawGrid(10, 1.0f); // Draw a grid
|
||||
DrawGrid(10, 1.0f);
|
||||
|
||||
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);
|
||||
|
||||
EndDrawing();
|
||||
@@ -99,9 +93,9 @@ int main(void)
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
UnloadTexture(texture); // Unload texture
|
||||
UnloadModelAnimations(anims, animsCount); // Unload model animations data
|
||||
UnloadModel(model); // Unload model
|
||||
UnloadTexture(texture); // Unload texture
|
||||
UnloadModelAnimations(anims, animCount); // Unload model animations data
|
||||
UnloadModel(model); // Unload model
|
||||
|
||||
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"
|
||||
|
||||
static void DrawModelSkeleton(ModelSkeleton skeleton, ModelAnimPose pose, float scale, Color color);
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Program main entry point
|
||||
//------------------------------------------------------------------------------------
|
||||
@@ -41,22 +43,17 @@ int main(void)
|
||||
camera.fovy = 45.0f; // Camera field-of-view Y
|
||||
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
|
||||
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
|
||||
int animsCount = 0;
|
||||
int animFrameCounter = 0, animId = 0;
|
||||
ModelAnimation *anims = LoadModelAnimations(modelFileName, &animsCount); // Load skeletal animation data
|
||||
// Load animation data
|
||||
int animCount = 0;
|
||||
ModelAnimation *anims = LoadModelAnimations("resources/models/m3d/cesium_man.m3d", &animCount);
|
||||
|
||||
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
|
||||
//--------------------------------------------------------------------------------------
|
||||
@@ -66,38 +63,16 @@ int main(void)
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
UpdateCamera(&camera, CAMERA_FIRST_PERSON);
|
||||
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||
|
||||
if (animsCount)
|
||||
{
|
||||
// Play animation when spacebar is held down (or step one frame with N)
|
||||
if (IsKeyDown(KEY_SPACE) || IsKeyPressed(KEY_N))
|
||||
{
|
||||
animFrameCounter++;
|
||||
// Select current animation
|
||||
if (IsKeyPressed(KEY_RIGHT)) animIndex = (animIndex + 1)%animCount;
|
||||
else if (IsKeyPressed(KEY_LEFT)) animIndex = (animIndex + animCount - 1)%animCount;
|
||||
|
||||
if (animFrameCounter >= anims[animId].frameCount) animFrameCounter = 0;
|
||||
|
||||
UpdateModelAnimation(model, anims[animId], animFrameCounter);
|
||||
animPlaying = true;
|
||||
}
|
||||
|
||||
// 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;
|
||||
// Update model animation
|
||||
animCurrentFrame += 1.0f;
|
||||
if (animCurrentFrame >= anims[animIndex].keyframeCount) animCurrentFrame = 0.0f;
|
||||
UpdateModelAnimation(model, anims[animIndex], animCurrentFrame);
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
@@ -109,52 +84,19 @@ int main(void)
|
||||
BeginMode3D(camera);
|
||||
|
||||
// Draw 3d model with texture
|
||||
if (drawMesh) DrawModel(model, position, 1.0f, WHITE);
|
||||
|
||||
// Draw the animated skeleton
|
||||
if (drawSkeleton)
|
||||
if (!IsKeyDown(KEY_SPACE)) DrawModel(model, position, 1.0f, WHITE);
|
||||
else
|
||||
{
|
||||
// Loop to (boneCount - 1) because the last one is a special "no bone" bone,
|
||||
// needed to workaround buggy models
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Draw the animated skeleton
|
||||
DrawModelSkeleton(model.skeleton, anims[animIndex].keyframePoses[(int)animCurrentFrame], 1.0f, RED);
|
||||
}
|
||||
|
||||
DrawGrid(10, 1.0f); // Draw a grid
|
||||
DrawGrid(10, 1.0f);
|
||||
|
||||
EndMode3D();
|
||||
|
||||
DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, GetScreenHeight() - 80, 10, MAROON);
|
||||
DrawText("PRESS N to STEP ONE ANIMATION FRAME", 10, GetScreenHeight() - 60, 10, DARKGRAY);
|
||||
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(TextFormat("Current animation: %s", anims[animIndex].name), 10, 10, 20, LIGHTGRAY);
|
||||
DrawText("Press SPACE to draw skeleton", 10, 40, 20, MAROON);
|
||||
DrawText("(c) CesiumMan model by KhronosGroup", GetScreenWidth() - 210, GetScreenHeight() - 20, 10, GRAY);
|
||||
|
||||
EndDrawing();
|
||||
@@ -163,14 +105,28 @@ int main(void)
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// Unload model animations data
|
||||
UnloadModelAnimations(anims, animsCount);
|
||||
|
||||
UnloadModel(model); // Unload model
|
||||
UnloadModelAnimations(anims, animCount); // Unload model animations data
|
||||
UnloadModel(model); // Unload model
|
||||
|
||||
CloseWindow(); // Close window and OpenGL context
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
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"
|
||||
|
||||
#if defined(PLATFORM_DESKTOP)
|
||||
#define GLSL_VERSION 330
|
||||
#define GLSL_VERSION 330
|
||||
#else // PLATFORM_ANDROID, PLATFORM_WEB
|
||||
#define GLSL_VERSION 100
|
||||
#define GLSL_VERSION 100
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
@@ -130,6 +130,7 @@ int main(void)
|
||||
camerarot.y = 0;
|
||||
}
|
||||
|
||||
// Update camere movement, custom controls
|
||||
UpdateCameraPro(&camera,
|
||||
(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
|
||||
@@ -173,7 +174,7 @@ int main(void)
|
||||
DrawText("- MOUSE LEFT BUTTON: CYCLE VOX MODELS", 20, 50, 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(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();
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 36 KiB |
@@ -55,7 +55,7 @@
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>models_animation_timming</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>models_animation_timming</ProjectName>
|
||||
<ProjectName>models_animation_timing</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
@@ -553,7 +553,7 @@
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\examples\models\models_animation_timming.c" />
|
||||
<ClCompile Include="..\..\..\examples\models\models_animation_timing.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="..\..\..\examples\examples.rc" />
|
||||
@@ -435,7 +435,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_blending",
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_web", "examples\core_window_web.vcxproj", "{4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}"
|
||||
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
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
||||
35
src/config.h
@@ -131,13 +131,13 @@
|
||||
// Module: rlgl - Configuration values
|
||||
//------------------------------------------------------------------------------------
|
||||
#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)
|
||||
//#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
|
||||
//#define RLGL_SHOW_GL_DETAILS_INFO 1
|
||||
|
||||
#define RL_SUPPORT_MESH_GPU_SKINNING 1 // GPU skinning, comment if your GPU does not support more than 8 VBOs
|
||||
//#define RLGL_SHOW_GL_DETAILS_INFO 1 // Show OpenGL detailed info on initialization (limits and extensions)
|
||||
#endif
|
||||
|
||||
//#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_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_NEAR 0.05 // Default projection matrix near cull distance
|
||||
#define RL_CULL_DISTANCE_FAR 4000.0 // Default projection matrix far cull distance
|
||||
|
||||
// Default shader vertex attribute locations
|
||||
#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_TEXCOORD2 5
|
||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6
|
||||
#if defined(RL_SUPPORT_MESH_GPU_SKINNING)
|
||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS 7
|
||||
#if defined(SUPPORT_GPU_SKINNING)
|
||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES 7
|
||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8
|
||||
#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
|
||||
// NOTE: When a new shader is loaded, the following locations are tried to be set for convenience
|
||||
// Default shader vertex attribute/uniform names to set location points
|
||||
// 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_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_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_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_VIEW "matView" // view 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_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_TEXTURE1 "texture1" // texture1 (texture slot active 1)
|
||||
#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
|
||||
|
||||
#ifdef RL_SUPPORT_MESH_GPU_SKINNING
|
||||
#define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh
|
||||
#ifdef SUPPORT_GPU_SKINNING
|
||||
// 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
|
||||
#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
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
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 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 *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
|
||||
unsigned int vaoId; // OpenGL Vertex Array Object id
|
||||
@@ -393,12 +395,22 @@ typedef struct Transform {
|
||||
Vector3 scale; // Scale
|
||||
} Transform;
|
||||
|
||||
// Anim pose, an array of Transform[]
|
||||
typedef Transform *ModelAnimPose;
|
||||
|
||||
// Bone, skeletal animation bone
|
||||
typedef struct BoneInfo {
|
||||
char name[32]; // Bone name
|
||||
int parent; // Bone parent
|
||||
} 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
|
||||
typedef struct Model {
|
||||
Matrix transform; // Local transform matrix
|
||||
@@ -410,18 +422,20 @@ typedef struct Model {
|
||||
int *meshMaterial; // Mesh material number
|
||||
|
||||
// Animation data
|
||||
int boneCount; // Number of bones
|
||||
BoneInfo *bones; // Bones information (skeleton)
|
||||
Transform *bindPose; // Bones base transformation (pose)
|
||||
ModelSkeleton skeleton; // Skeleton for animation
|
||||
|
||||
// Runtime animation data (CPU/GPU skinning)
|
||||
ModelAnimPose currentPose; // Current animation pose (Transform[])
|
||||
Matrix *boneMatrices; // Bones animated transformation matrices
|
||||
} Model;
|
||||
|
||||
// ModelAnimation
|
||||
// ModelAnimation, contains a full animation sequence
|
||||
typedef struct ModelAnimation {
|
||||
char name[32]; // Animation name
|
||||
int boneCount; // Number of bones
|
||||
int frameCount; // Number of animation frames
|
||||
BoneInfo *bones; // Bones information (skeleton)
|
||||
Transform **framePoses; // Poses array by frame
|
||||
|
||||
int boneCount; // Number of bones (per pose)
|
||||
int keyframeCount; // Number of animation key frames
|
||||
ModelAnimPose *keyframePoses; // Animation sequence keyframe poses [keyframe][pose]
|
||||
} ModelAnimation;
|
||||
|
||||
// Ray, ray for raycasting
|
||||
@@ -768,6 +782,8 @@ typedef enum {
|
||||
#define MATERIAL_MAP_SPECULAR MATERIAL_MAP_METALNESS
|
||||
|
||||
// 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 {
|
||||
SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position
|
||||
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_OCCLUSION, // Shader location: sampler2d texture: occlusion
|
||||
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_IRRADIANCE, // Shader location: samplerCube texture: irradiance
|
||||
SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter
|
||||
SHADER_LOC_MAP_BRDF, // Shader location: sampler2d texture: brdf
|
||||
SHADER_LOC_VERTEX_BONEIDS, // Shader location: vertex attribute: boneIds
|
||||
SHADER_LOC_VERTEX_BONEWEIGHTS, // Shader location: vertex attribute: boneWeights
|
||||
SHADER_LOC_BONE_MATRICES, // Shader location: array of matrices uniform: boneMatrices
|
||||
SHADER_LOC_VERTEX_INSTANCE_TX // Shader location: vertex attribute: instanceTransform
|
||||
SHADER_LOC_VERTEX_BONEIDS, // Shader location: vertex attribute: bone indices
|
||||
SHADER_LOC_VERTEX_BONEWEIGHTS, // Shader location: vertex attribute: bone weights
|
||||
SHADER_LOC_MATRIX_BONETRANSFORMS, // Shader location: matrix attribute: bone transforms (animation)
|
||||
SHADER_LOC_VERTEX_INSTANCETRANSFORMS // Shader location: vertex attribute: instance transforms
|
||||
} ShaderLocationIndex;
|
||||
|
||||
#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
|
||||
RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount); // Load model animations from file
|
||||
RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose (CPU)
|
||||
RLAPI void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame); // Update model animation mesh bone matrices (GPU skinning)
|
||||
RLAPI void UpdateModelAnimationBonesLerp(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float value); // Update model animation mesh bone matrices with interpolation between two poses(GPU skinning)
|
||||
RLAPI void UpdateModelVertsToCurrentBones(Model model); // Update model vertices according to mesh bone matrices (CPU)
|
||||
RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data
|
||||
RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, float frame); // Update model animation pose (vertex buffers and bone matrices)
|
||||
RLAPI void UpdateModelAnimationEx(Model model, ModelAnimation animA, float frameA, ModelAnimation animB, float frameB, float blend); // Update model animation pose, blending two animations
|
||||
RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data
|
||||
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 tangent location = 4
|
||||
// - vertex texcoord2 location = 5
|
||||
// - vertex boneIds location = 6
|
||||
// - vertex boneIndices location = 6
|
||||
// - vertex boneWeights location = 7
|
||||
|
||||
// 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_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_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_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)
|
||||
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_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_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)
|
||||
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_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_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_UNIFORM_NAME_MVP "mvp" // model-view-projection 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_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_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_TEXTURE1 "texture1" // texture1 (texture slot active 1)
|
||||
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2)
|
||||
@@ -345,16 +345,16 @@
|
||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES
|
||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6
|
||||
#endif
|
||||
#ifdef RL_SUPPORT_MESH_GPU_SKINNING
|
||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS
|
||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS 7
|
||||
#ifdef SUPPORT_GPU_SKINNING
|
||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES
|
||||
#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
|
||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS
|
||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8
|
||||
#endif
|
||||
#endif
|
||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX
|
||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX 9
|
||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS
|
||||
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS 9
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
@@ -992,31 +992,34 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
|
||||
|
||||
// Default shader vertex attribute names to set location points
|
||||
#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
|
||||
#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
|
||||
#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
|
||||
#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
|
||||
#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
|
||||
#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
|
||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS
|
||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS "vertexBoneIds" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS
|
||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES
|
||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES "vertexBoneIndices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES
|
||||
#endif
|
||||
#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
|
||||
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX
|
||||
#define RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX "instanceTransform" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX
|
||||
#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES
|
||||
#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
|
||||
|
||||
#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
|
||||
#define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color)
|
||||
#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
|
||||
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0)
|
||||
#endif
|
||||
@@ -1049,6 +1049,9 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
|
||||
#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2
|
||||
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2)
|
||||
#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
|
||||
@@ -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_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_INSTANCE_TX, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX);
|
||||
|
||||
#ifdef RL_SUPPORT_MESH_GPU_SKINNING
|
||||
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS);
|
||||
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORMS, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORMS);
|
||||
#ifdef SUPPORT_GPU_SKINNING
|
||||
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES);
|
||||
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
|
||||
#endif
|
||||
|
||||
|
||||