Skip to content

Commit

Permalink
[rmodels] Optional GPU skinning (#4321)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
orangeduck authored Sep 20, 2024
1 parent 2f0cf8f commit 86ead96
Show file tree
Hide file tree
Showing 11 changed files with 437 additions and 23 deletions.
3 changes: 2 additions & 1 deletion examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
117 changes: 117 additions & 0 deletions examples/models/models_gpu_skinning.c
Original file line number Diff line number Diff line change
@@ -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;
}
17 changes: 17 additions & 0 deletions examples/models/resources/shaders/glsl100/skinning.fs
Original file line number Diff line number Diff line change
@@ -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;
}
34 changes: 34 additions & 0 deletions examples/models/resources/shaders/glsl100/skinning.vs
Original file line number Diff line number Diff line change
@@ -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;
}
17 changes: 17 additions & 0 deletions examples/models/resources/shaders/glsl330/skinning.fs
Original file line number Diff line number Diff line change
@@ -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;
}
34 changes: 34 additions & 0 deletions examples/models/resources/shaders/glsl330/skinning.vs
Original file line number Diff line number Diff line change
@@ -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;
}
16 changes: 9 additions & 7 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,15 @@
#define RL_CULL_DISTANCE_FAR 1000.0 // Default projection matrix far cull distance

// Default shader vertex attribute locations
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION 0
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD 1
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL 2
#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_POSITION 0
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD 1
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL 2
#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_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
Expand Down
12 changes: 9 additions & 3 deletions src/raylib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions src/rcore.c
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -1318,13 +1320,16 @@ 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);
shader.locs[SHADER_LOC_MATRIX_VIEW] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW);
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);
Expand Down
Loading

0 comments on commit 86ead96

Please sign in to comment.