[rmodels] Optional GPU skinning (#4321)

* Added optional GPU skinning

* Added skinned bone matrices support for different file formats.

* Moved new shader locations to end of enum to avoid breaking existing examples. Added gpu skinning on drawing of instanced meshes.

* Added GPU skinning example.

* Removed variable declaration to avoid shadowing warning.
This commit is contained in:
Daniel Holden 2024-09-20 11:30:37 -04:00 committed by GitHub
parent 2f0cf8fbe1
commit 86ead96263
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 437 additions and 23 deletions

View File

@ -587,7 +587,8 @@ MODELS = \
models/models_rlgl_solar_system \
models/models_skybox \
models/models_waving_cubes \
models/models_yaw_pitch_roll
models/models_yaw_pitch_roll \
models/models_gpu_skinning
SHADERS = \
shaders/shaders_basic_lighting \

View File

@ -0,0 +1,117 @@
/*******************************************************************************************
*
* raylib [core] example - Doing skinning on the gpu using a vertex shader
*
* Example originally created with raylib 4.5, last time updated with raylib 4.5
*
* Example contributed by Daniel Holden (@orangeduck) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2024 Daniel Holden (@orangeduck)
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
#if defined(PLATFORM_DESKTOP)
#define GLSL_VERSION 330
#else // PLATFORM_ANDROID, PLATFORM_WEB
#define GLSL_VERSION 100
#endif
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - GPU skinning");
// 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.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
camera.fovy = 45.0f; // Camera field-of-view Y
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
// Load gltf model
Model characterModel = LoadModel("resources/models/gltf/greenman.glb"); // Load character model
// Load skinning shader
Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
characterModel.materials[1].shader = skinningShader;
// Load gltf model animations
int animsCount = 0;
unsigned int animIndex = 0;
unsigned int animCurrentFrame = 0;
ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", &animsCount);
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
DisableCursor(); // Limit cursor to relative movement inside the window
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(&camera, CAMERA_THIRD_PERSON);
// Select current animation
if (IsKeyPressed(KEY_T)) animIndex = (animIndex + 1)%animsCount;
else if (IsKeyPressed(KEY_G)) animIndex = (animIndex + animsCount - 1)%animsCount;
// Update model animation
ModelAnimation anim = modelAnimations[animIndex];
animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount;
UpdateModelAnimationBoneMatrices(characterModel, anim, animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
BeginMode3D(camera);
// Draw character
characterModel.transform = MatrixTranslate(position.x, position.y, position.z);
UpdateModelAnimationBoneMatrices(characterModel, anim, animCurrentFrame);
DrawMesh(characterModel.meshes[0], characterModel.materials[1], characterModel.transform);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText("Use the T/G to switch animation", 10, 10, 20, GRAY);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModelAnimations(modelAnimations, animsCount);
UnloadModel(characterModel); // Unload character model and meshes/material
UnloadShader(skinningShader);
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}

View File

@ -0,0 +1,17 @@
#version 100
// Input vertex attributes (from vertex shader)
in vec2 fragTexCoord;
in vec4 fragColor;
// Output fragment color
out vec4 finalColor;
uniform sampler2D texture0;
uniform vec4 colDiffuse;
void main()
{
vec4 texelColor = texture(texture0, fragTexCoord);
finalColor = texelColor*colDiffuse*fragColor;
}

View File

@ -0,0 +1,34 @@
#version 100
in vec3 vertexPosition;
in vec2 vertexTexCoord;
in vec4 vertexColor;
in vec4 vertexBoneIds;
in vec4 vertexBoneWeights;
#define MAX_BONE_NUM 128
uniform mat4 boneMatrices[MAX_BONE_NUM];
uniform mat4 mvp;
out vec2 fragTexCoord;
out vec4 fragColor;
void main()
{
int boneIndex0 = int(vertexBoneIds.x);
int boneIndex1 = int(vertexBoneIds.y);
int boneIndex2 = int(vertexBoneIds.z);
int boneIndex3 = int(vertexBoneIds.w);
vec4 skinnedPosition =
vertexBoneWeights.x * (boneMatrices[boneIndex0] * vec4(vertexPosition, 1.0f)) +
vertexBoneWeights.y * (boneMatrices[boneIndex1] * vec4(vertexPosition, 1.0f)) +
vertexBoneWeights.z * (boneMatrices[boneIndex2] * vec4(vertexPosition, 1.0f)) +
vertexBoneWeights.w * (boneMatrices[boneIndex3] * vec4(vertexPosition, 1.0f));
fragTexCoord = vertexTexCoord;
fragColor = vertexColor;
gl_Position = mvp * skinnedPosition;
}

View File

@ -0,0 +1,17 @@
#version 330
// Input vertex attributes (from vertex shader)
in vec2 fragTexCoord;
in vec4 fragColor;
// Output fragment color
out vec4 finalColor;
uniform sampler2D texture0;
uniform vec4 colDiffuse;
void main()
{
vec4 texelColor = texture(texture0, fragTexCoord);
finalColor = texelColor*colDiffuse*fragColor;
}

View File

@ -0,0 +1,34 @@
#version 330
in vec3 vertexPosition;
in vec2 vertexTexCoord;
in vec4 vertexColor;
in vec4 vertexBoneIds;
in vec4 vertexBoneWeights;
#define MAX_BONE_NUM 128
uniform mat4 boneMatrices[MAX_BONE_NUM];
uniform mat4 mvp;
out vec2 fragTexCoord;
out vec4 fragColor;
void main()
{
int boneIndex0 = int(vertexBoneIds.x);
int boneIndex1 = int(vertexBoneIds.y);
int boneIndex2 = int(vertexBoneIds.z);
int boneIndex3 = int(vertexBoneIds.w);
vec4 skinnedPosition =
vertexBoneWeights.x * (boneMatrices[boneIndex0] * vec4(vertexPosition, 1.0f)) +
vertexBoneWeights.y * (boneMatrices[boneIndex1] * vec4(vertexPosition, 1.0f)) +
vertexBoneWeights.z * (boneMatrices[boneIndex2] * vec4(vertexPosition, 1.0f)) +
vertexBoneWeights.w * (boneMatrices[boneIndex3] * vec4(vertexPosition, 1.0f));
fragTexCoord = vertexTexCoord;
fragColor = vertexColor;
gl_Position = mvp * skinnedPosition;
}

View File

@ -119,7 +119,9 @@
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR 3
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT 4
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 5
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS 6
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 7
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 8
// 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

View File

@ -352,8 +352,10 @@ typedef struct Mesh {
// Animation vertex data
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)
float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning)
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
@ -790,7 +792,10 @@ typedef enum {
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_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
} ShaderLocationIndex;
#define SHADER_LOC_MAP_DIFFUSE SHADER_LOC_MAP_ALBEDO
@ -1591,6 +1596,7 @@ RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame);
RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data
RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data
RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match
RLAPI void UpdateModelAnimationBoneMatrices(Model model, ModelAnimation anim, int frame); // Update model animation mesh bone matrices
// Collision detection functions
RLAPI bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2); // Check collision between two spheres

View File

@ -1303,6 +1303,8 @@ 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 boneWeights location = 7
// NOTE: If any location is not found, loc point becomes -1
@ -1318,6 +1320,8 @@ 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_BONEWEIGHTS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
// Get handles to GLSL uniform locations (vertex shader)
shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP);
@ -1325,6 +1329,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);
// Get handles to GLSL uniform locations (fragment shader)
shader.locs[SHADER_LOC_COLOR_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR);

View File

@ -68,12 +68,15 @@
* #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_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_BONE_MATRICES "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)
@ -341,6 +344,12 @@
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 5
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS 6
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 7
#endif
//----------------------------------------------------------------------------------
// Types and Structures Definition
@ -759,6 +768,7 @@ RLAPI int rlGetLocationUniform(unsigned int shaderId, const char *uniformName);
RLAPI int rlGetLocationAttrib(unsigned int shaderId, const char *attribName); // Get shader location attribute
RLAPI void rlSetUniform(int locIndex, const void *value, int uniformType, int count); // Set shader value uniform
RLAPI void rlSetUniformMatrix(int locIndex, Matrix mat); // Set shader value matrix
RLAPI void rlSetUniformMatrices(int locIndex, const Matrix *mat, int count); // Set shader value matrices
RLAPI void rlSetUniformSampler(int locIndex, unsigned int textureId); // Set shader value sampler
RLAPI void rlSetShader(unsigned int id, int *locs); // Set shader currently active (id and locations)
@ -977,6 +987,12 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
#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
#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
#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
#endif
#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MVP
#define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix
@ -996,6 +1012,9 @@ 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
@ -4148,6 +4167,8 @@ unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId)
glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS);
glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
// NOTE: If some attrib name is no found on the shader, it locations becomes -1
@ -4283,6 +4304,14 @@ void rlSetUniformMatrix(int locIndex, Matrix mat)
#endif
}
// Set shader value uniform matrix
void rlSetUniformMatrices(int locIndex, const Matrix *matrices, int count)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
glUniformMatrix4fv(locIndex, count, true, (const float*)matrices);
#endif
}
// Set shader value uniform sampler
void rlSetUniformSampler(int locIndex, unsigned int textureId)
{

View File

@ -130,7 +130,7 @@
#define MAX_MATERIAL_MAPS 12 // Maximum number of maps supported
#endif
#ifndef MAX_MESH_VERTEX_BUFFERS
#define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh
#define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh
#endif
//----------------------------------------------------------------------------------
@ -1252,6 +1252,8 @@ void UploadMesh(Mesh *mesh, bool dynamic)
mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] = 0; // Vertex buffer: colors
mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT] = 0; // Vertex buffer: tangents
mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2] = 0; // Vertex buffer: texcoords2
mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS] = 0; // Vertex buffer: boneIds
mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS] = 0; // Vertex buffer: boneWeights
mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES] = 0; // Vertex buffer: indices
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
@ -1339,6 +1341,38 @@ void UploadMesh(Mesh *mesh, bool dynamic)
rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2);
}
if (mesh->boneIds != NULL)
{
// Enable vertex attribute: boneIds (shader-location = 6)
mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS] = rlLoadVertexBuffer(mesh->boneIds, mesh->vertexCount*4*sizeof(unsigned char), dynamic);
rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, 4, RL_UNSIGNED_BYTE, 0, 0, 0);
rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS);
}
else
{
// Default vertex attribute: boneIds
// WARNING: Default value provided to shader if location available
float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, value, SHADER_ATTRIB_VEC4, 4);
rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS);
}
if (mesh->boneWeights != NULL)
{
// Enable vertex attribute: boneWeights (shader-location = 7)
mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS] = rlLoadVertexBuffer(mesh->boneWeights, mesh->vertexCount*4*sizeof(float), dynamic);
rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, 4, RL_FLOAT, 0, 0, 0);
rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS);
}
else
{
// Default vertex attribute: boneWeights
// WARNING: Default value provided to shader if location available
float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, value, SHADER_ATTRIB_VEC4, 2);
rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS);
}
if (mesh->indices != NULL)
{
mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES] = rlLoadVertexBufferElement(mesh->indices, mesh->triangleCount*3*sizeof(unsigned short), dynamic);
@ -1451,6 +1485,13 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
// Upload model normal matrix (if locations available)
if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel)));
// Upload Bone Transforms
if (material.shader.locs[SHADER_LOC_BONE_MATRICES] != -1 && mesh.boneMatrices)
{
rlSetUniformMatrices(material.shader.locs[SHADER_LOC_BONE_MATRICES], mesh.boneMatrices, mesh.boneCount);
}
//-----------------------------------------------------
// Bind active texture maps (if available)
@ -1530,6 +1571,22 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]);
}
// Bind mesh VBO data: vertex bone ids (shader-location = 6, if available)
if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1)
{
rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS]);
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS], 4, RL_UNSIGNED_BYTE, 0, 0, 0);
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS]);
}
// Bind mesh VBO data: vertex bone weights (shader-location = 7, if available)
if (material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] != -1)
{
rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS]);
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS], 4, RL_FLOAT, 0, 0, 0);
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS]);
}
if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES]);
}
@ -1671,6 +1728,13 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i
// Upload model normal matrix (if locations available)
if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel)));
// Upload Bone Transforms
if (material.shader.locs[SHADER_LOC_BONE_MATRICES] != -1 && mesh.boneMatrices)
{
rlSetUniformMatrices(material.shader.locs[SHADER_LOC_BONE_MATRICES], mesh.boneMatrices, mesh.boneCount);
}
//-----------------------------------------------------
// Bind active texture maps (if available)
@ -1748,6 +1812,22 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]);
}
// Bind mesh VBO data: vertex bone ids (shader-location = 6, if available)
if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1)
{
rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS]);
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS], 4, RL_UNSIGNED_BYTE, 0, 0, 0);
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS]);
}
// Bind mesh VBO data: vertex bone weights (shader-location = 7, if available)
if (material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] != -1)
{
rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS]);
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS], 4, RL_FLOAT, 0, 0, 0);
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS]);
}
if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES]);
}
@ -1825,6 +1905,7 @@ void UnloadMesh(Mesh mesh)
RL_FREE(mesh.animNormals);
RL_FREE(mesh.boneWeights);
RL_FREE(mesh.boneIds);
RL_FREE(mesh.boneMatrices);
}
// Export mesh data to file
@ -2255,6 +2336,50 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame)
}
}
void UpdateModelAnimationBoneMatrices(Model model, ModelAnimation anim, int frame)
{
if ((anim.frameCount > 0) && (anim.bones != NULL) && (anim.framePoses != NULL))
{
if (frame >= anim.frameCount) frame = frame%anim.frameCount;
for (int i = 0; i < model.meshCount; i++)
{
if (model.meshes[i].boneMatrices)
{
assert(model.meshes[i].boneCount == anim.boneCount);
for (int boneId = 0; boneId < model.meshes[i].boneCount; boneId++)
{
Vector3 inTranslation = model.bindPose[boneId].translation;
Quaternion inRotation = model.bindPose[boneId].rotation;
Vector3 inScale = model.bindPose[boneId].scale;
Vector3 outTranslation = anim.framePoses[frame][boneId].translation;
Quaternion outRotation = anim.framePoses[frame][boneId].rotation;
Vector3 outScale = anim.framePoses[frame][boneId].scale;
Vector3 invTranslation = Vector3RotateByQuaternion(Vector3Negate(inTranslation), QuaternionInvert(inRotation));
Quaternion invRotation = QuaternionInvert(inRotation);
Vector3 invScale = Vector3Divide((Vector3){ 1.0f, 1.0f, 1.0f }, inScale);
Vector3 boneTranslation = Vector3Add(
Vector3RotateByQuaternion(Vector3Multiply(outScale, invTranslation),
outRotation), outTranslation);
Quaternion boneRotation = QuaternionMultiply(outRotation, invRotation);
Vector3 boneScale = Vector3Multiply(outScale, invScale);
Matrix boneMatrix = MatrixMultiply(MatrixMultiply(
QuaternionToMatrix(boneRotation),
MatrixTranslate(boneTranslation.x, boneTranslation.y, boneTranslation.z)),
MatrixScale(boneScale.x, boneScale.y, boneScale.z));
model.meshes[i].boneMatrices[boneId] = boneMatrix;
}
}
}
}
}
// Unload animation array data
void UnloadModelAnimations(ModelAnimation *animations, int animCount)
{
@ -4672,6 +4797,17 @@ static Model LoadIQM(const char *fileName)
BuildPoseFromParentJoints(model.bones, model.boneCount, model.bindPose);
for (int i = 0; i < model.meshCount; i++)
{
model.meshes[i].boneCount = model.boneCount;
model.meshes[i].boneMatrices = RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix));
for (int j = 0; j < model.meshes[i].boneCount; j++)
{
model.meshes[i].boneMatrices[j] = MatrixIdentity();
}
}
UnloadFileData(fileData);
RL_FREE(imesh);
@ -5755,6 +5891,15 @@ static Model LoadGLTF(const char *fileName)
memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, model.meshes[meshIndex].vertexCount*3*sizeof(float));
}
// Bone Transform Matrices
model.meshes[meshIndex].boneCount = model.boneCount;
model.meshes[meshIndex].boneMatrices = RL_CALLOC(model.meshes[meshIndex].boneCount, sizeof(Matrix));
for (int j = 0; j < model.meshes[meshIndex].boneCount; j++)
{
model.meshes[meshIndex].boneMatrices[j] = MatrixIdentity();
}
meshIndex++; // Move to next mesh
}
@ -6522,6 +6667,13 @@ static Model LoadM3D(const char *fileName)
{
memcpy(model.meshes[i].animVertices, model.meshes[i].vertices, model.meshes[i].vertexCount*3*sizeof(float));
memcpy(model.meshes[i].animNormals, model.meshes[i].normals, model.meshes[i].vertexCount*3*sizeof(float));
model.meshes[i].boneCount = model.boneCount;
model.meshes[i].boneMatrices = RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix));
for (j = 0; j < model.meshes[i].boneCount; j++)
{
model.meshes[i].boneMatrices[j] = MatrixIdentity();
}
}
}