Browse Source

[rmodels] Updated blend function signature and added function to update verts from bones

Signed-off-by: Kirandeep-Singh-Khehra <kirandeepsinghkhehra@gmail.com>
pull/4578/head
Kirandeep-Singh-Khehra 6 days ago
parent
commit
7f7815e3a2
3 changed files with 83 additions and 9 deletions
  1. +15
    -3
      examples/models/models_animation_blending.c
  2. +2
    -1
      src/raylib.h
  3. +66
    -5
      src/rmodels.c

+ 15
- 3
examples/models/models_animation_blending.c View File

@ -48,6 +48,7 @@ int main(void)
// Load gltf model
Model characterModel = LoadModel("resources/models/gltf/robot.glb"); // Load character model
/* INFO: Uncomment this to use GPU skinning
// Load skinning shader
Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
@ -56,6 +57,7 @@ int main(void)
{
characterModel.materials[i].shader = skinningShader;
}
*/
// Load gltf model animations
int animsCount = 0;
@ -93,7 +95,10 @@ int main(void)
// Update bones
// Note: Same animation frame index is used below. By default it loops both animations
UpdateModelAnimationBonesWithBlending(characterModel, modelAnimations[animIndex0], animCurrentFrame, modelAnimations[animIndex1], animCurrentFrame, blendFactor);
UpdateModelAnimationBonesLerp(characterModel, modelAnimations[animIndex0], animCurrentFrame, modelAnimations[animIndex1], animCurrentFrame, blendFactor);
// INFO: Comment the following line to use GPU skinning
UpdateModelVertsToCurrentBones(characterModel);
//----------------------------------------------------------------------------------
// Draw
@ -103,12 +108,17 @@ int main(void)
ClearBackground(RAYWHITE);
BeginMode3D(camera);
/* INFO: Uncomment this to use GPU skinning
// Draw character mesh, pose calculation is done in shader (GPU skinning)
for (int i = 0; i < characterModel.meshCount; i++)
{
DrawMesh(characterModel.meshes[i], characterModel.materials[characterModel.meshMaterial[i]], characterModel.transform);
}
*/
// INFO: Comment the following line to use GPU skinning
DrawModel(characterModel, (Vector3){0.0f, 0.0f, 0.0f}, 1.0f, WHITE);
DrawGrid(10, 1.0f);
@ -129,7 +139,9 @@ int main(void)
//--------------------------------------------------------------------------------------
UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation
UnloadModel(characterModel); // Unload model and meshes/material
UnloadShader(skinningShader); // Unload GPU skinning shader
// INFO: Uncomment the following line to use GPU skinning
// UnloadShader(skinningShader); // Unload GPU skinning shader
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------

+ 2
- 1
src/raylib.h View File

@ -1605,7 +1605,8 @@ RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId);
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 UpdateModelAnimationBonesWithBlending(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float blendFactor); // Update model animation mesh bone matrices with blending between two poses(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 UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data
RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match

+ 66
- 5
src/rmodels.c View File

@ -2311,11 +2311,11 @@ void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame)
// Update model animated bones transform matrices by blendin between two different given frames of different ModelAnimation(could be same too)
// NOTE: Updated data is not uploaded to GPU but kept at model.meshes[i].boneMatrices[boneId],
// to be uploaded to shader at drawing, in case GPU skinning is enabled
void UpdateModelAnimationBonesWithBlending(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float blendFactor)
void UpdateModelAnimationBonesLerp(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float value)
{
if ((animA.frameCount > 0) && (animA.bones != NULL) && (animA.framePoses != NULL) &&
(animB.frameCount > 0) && (animB.bones != NULL) && (animB.framePoses != NULL) &&
(blendFactor >= 0.0f) && (blendFactor <= 1.0f))
(value >= 0.0f) && (value <= 1.0f))
{
frameA = frameA % animA.frameCount;
frameB = frameB % animB.frameCount;
@ -2341,9 +2341,9 @@ void UpdateModelAnimationBonesWithBlending(Model model, ModelAnimation animA, in
Quaternion outBRotation = animB.framePoses[frameB][boneId].rotation;
Vector3 outBScale = animB.framePoses[frameB][boneId].scale;
Vector3 outTranslation = Vector3Lerp(outATranslation, outBTranslation, blendFactor);
Quaternion outRotation = QuaternionSlerp(outARotation, outBRotation, blendFactor);
Vector3 outScale = Vector3Lerp(outAScale, outBScale, blendFactor);
Vector3 outTranslation = Vector3Lerp(outATranslation, outBTranslation, value);
Quaternion outRotation = QuaternionSlerp(outARotation, outBRotation, value);
Vector3 outScale = Vector3Lerp(outAScale, outBScale, value);
Vector3 invTranslation = Vector3RotateByQuaternion(Vector3Negate(inTranslation), QuaternionInvert(inRotation));
Quaternion invRotation = QuaternionInvert(inRotation);
@ -2367,6 +2367,67 @@ void UpdateModelAnimationBonesWithBlending(Model model, ModelAnimation animA, in
}
}
// Update model vertex data (positions and normals) from mesh bone data
// NOTE: Updated data is uploaded to GPU
void UpdateModelVertsToCurrentBones(Model model)
{
for (int m = 0; m < model.meshCount; m++)
{
Mesh mesh = model.meshes[m];
Vector3 animVertex = { 0 };
Vector3 animNormal = { 0 };
int boneId = 0;
int boneCounter = 0;
float boneWeight = 0.0;
bool updated = false; // Flag to check when anim vertex information is updated
const int vValues = mesh.vertexCount*3;
for (int vCounter = 0; vCounter < vValues; 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];
boneId = mesh.boneIds[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.meshes[m].boneMatrices[boneId]);
mesh.animVertices[vCounter] += animVertex.x*boneWeight;
mesh.animVertices[vCounter+1] += animVertex.y*boneWeight;
mesh.animVertices[vCounter+2] += animVertex.z*boneWeight;
updated = true;
// Normals processing
// NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
if (mesh.normals != NULL)
{
animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] };
animNormal = Vector3Transform(animNormal,model.meshes[m].boneMatrices[boneId]);
mesh.animNormals[vCounter] += animNormal.x*boneWeight;
mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight;
mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight;
}
}
}
if (updated)
{
rlUpdateVertexBuffer(mesh.vboId[0], mesh.animVertices, mesh.vertexCount*3*sizeof(float), 0); // Update vertex position
rlUpdateVertexBuffer(mesh.vboId[2], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0); // Update vertex normals
}
}
}
// at least 2x speed up vs the old method
// Update model animated vertex data (positions and normals) for a given frame
// NOTE: Updated data is uploaded to GPU

Loading…
Cancel
Save