From 8808a3958a705120bea516cf8d50155e5b9d8725 Mon Sep 17 00:00:00 2001 From: Kirandeep-Singh-Khehra Date: Wed, 4 Dec 2024 23:02:28 +0530 Subject: [PATCH] [rmodels] Added implementation of `UpdateModelAnimationBonesWithBlending()` function Signed-off-by: Kirandeep-Singh-Khehra --- src/raylib.h | 1 + src/rmodels.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index 641bd10e..bb25ea94 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1605,6 +1605,7 @@ 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 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 diff --git a/src/rmodels.c b/src/rmodels.c index 24f4a4fc..b68dc0cf 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2308,6 +2308,65 @@ 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) +{ + if ((animA.frameCount > 0) && (animA.bones != NULL) && (animA.framePoses != NULL) && + (animB.frameCount > 0) && (animB.bones != NULL) && (animB.framePoses != NULL) && + (blendFactor >= 0.0f) && (blendFactor <= 1.0)) + { + frameA = frameA % animA.frameCount; + frameB = frameB % animB.frameCount; + + for (int i = 0; i < model.meshCount; i++) + { + if (model.meshes[i].boneMatrices) + { + assert(model.meshes[i].boneCount == animA.boneCount); + assert(model.meshes[i].boneCount == animB.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 outATranslation = animA.framePoses[frameA][boneId].translation; + Quaternion outARotation = animA.framePoses[frameA][boneId].rotation; + Vector3 outAScale = animA.framePoses[frameA][boneId].scale; + + Vector3 outBTranslation = animB.framePoses[frameB][boneId].translation; + 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 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; + } + } + } + } +} + // 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