Scale bone anim support (#1215)

* Scale bone anim support

Code changes provided by ExcellentGamer. Supports model anims which modify scale.

* Peachy code changes by ExcellentGamer

* Autogen constants

* changed vc3f_set to vec3f_copy
This commit is contained in:
DorfDork 2026-05-04 17:15:00 -05:00 committed by GitHub
parent 95782f447c
commit f688416483
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 87 additions and 25 deletions

View file

@ -11220,6 +11220,9 @@ ANIM_FLAG_7 = (1 << 7)
--- @type integer
ANIM_FLAG_BONE_TRANS = (1 << 8)
--- @type integer
ANIM_FLAG_BONE_SCALE = (1 << 9)
--- @type integer
OBJECT_MAX_BHV_STACK = 16

View file

@ -45,8 +45,16 @@ static s32 RetrieveCurrentAnimationIndex(struct Object *aObject) {
void DynOS_Anim_Swap(void *aPtr) {
if (!aPtr) { return; }
static Animation *pDefaultAnimation = NULL;
static Animation sGfxDataAnimation;
// Must support nested calls (held objects render inside other object render)
// and interleaving objects without corrupting swap state.
struct AnimSwapFrame {
struct Object *obj;
Animation *defaultAnim;
Animation gfxDataAnim;
};
static AnimSwapFrame sAnimSwapFrames[32] = { 0 };
static s32 sCurrAnimSwapIndex = 0;
// Does the object have a model?
struct Object *_Object = (struct Object *) aPtr;
@ -54,9 +62,20 @@ void DynOS_Anim_Swap(void *aPtr) {
return;
}
// Determine if this call is the "swap" phase or "restore" phase.
// The engine calls DynOS_Anim_Swap twice around geo_set_animation_globals.
const bool restoring = (sCurrAnimSwapIndex > 0 && sAnimSwapFrames[sCurrAnimSwapIndex - 1].obj == _Object);
// Swap the current animation with the one from the Gfx data
if (!pDefaultAnimation) {
pDefaultAnimation = _Object->header.gfx.animInfo.curAnim;
if (!restoring) {
if (sCurrAnimSwapIndex >= (s32) ARRAY_COUNT(sAnimSwapFrames)) {
return;
}
AnimSwapFrame *frame = &sAnimSwapFrames[sCurrAnimSwapIndex];
frame->obj = _Object;
frame->defaultAnim = _Object->header.gfx.animInfo.curAnim;
sCurrAnimSwapIndex++;
// ActorGfx data
ActorGfx* _ActorGfx = DynOS_Actor_GetActorGfx(_Object->header.gfx.sharedChild);
@ -100,23 +119,26 @@ void DynOS_Anim_Swap(void *aPtr) {
// Animation data
const AnimData *_AnimData = (const AnimData *) _GfxData->mAnimationTable[_AnimIndex].second;
if (_AnimData) {
sGfxDataAnimation.flags = _AnimData->mFlags;
sGfxDataAnimation.animYTransDivisor = _AnimData->mUnk02;
sGfxDataAnimation.startFrame = _AnimData->mUnk04;
sGfxDataAnimation.loopStart = _AnimData->mUnk06;
sGfxDataAnimation.loopEnd = _AnimData->mUnk08;
sGfxDataAnimation.unusedBoneCount = _AnimData->mUnk0A.second;
sGfxDataAnimation.values = (u16*)_AnimData->mValues.second.begin();
sGfxDataAnimation.index = (u16*)_AnimData->mIndex.second.begin();
sGfxDataAnimation.valuesLength = _AnimData->mValues.second.Count();
sGfxDataAnimation.indexLength = _AnimData->mIndex.second.Count();
sGfxDataAnimation.length = _AnimData->mLength;
_Object->header.gfx.animInfo.curAnim = &sGfxDataAnimation;
frame->gfxDataAnim.flags = _AnimData->mFlags;
frame->gfxDataAnim.animYTransDivisor = _AnimData->mUnk02;
frame->gfxDataAnim.startFrame = _AnimData->mUnk04;
frame->gfxDataAnim.loopStart = _AnimData->mUnk06;
frame->gfxDataAnim.loopEnd = _AnimData->mUnk08;
frame->gfxDataAnim.unusedBoneCount = _AnimData->mUnk0A.second;
frame->gfxDataAnim.values = (u16*) _AnimData->mValues.second.begin();
frame->gfxDataAnim.index = (u16*) _AnimData->mIndex.second.begin();
frame->gfxDataAnim.valuesLength = _AnimData->mValues.second.Count();
frame->gfxDataAnim.indexLength = _AnimData->mIndex.second.Count();
frame->gfxDataAnim.length = _AnimData->mLength;
_Object->header.gfx.animInfo.curAnim = &frame->gfxDataAnim;
}
// Restore the default animation
} else {
_Object->header.gfx.animInfo.curAnim = pDefaultAnimation;
pDefaultAnimation = NULL;
sCurrAnimSwapIndex--;
AnimSwapFrame *frame = &sAnimSwapFrames[sCurrAnimSwapIndex];
_Object->header.gfx.animInfo.curAnim = frame->defaultAnim;
frame->obj = NULL;
frame->defaultAnim = NULL;
}
}

View file

@ -4730,6 +4730,7 @@
- ANIM_FLAG_6
- ANIM_FLAG_7
- ANIM_FLAG_BONE_TRANS
- ANIM_FLAG_BONE_SCALE
- OBJECT_MAX_BHV_STACK
- OBJECT_NUM_REGULAR_FIELDS
- OBJECT_NUM_CUSTOM_FIELDS

View file

@ -112,6 +112,7 @@ struct VblankHandler
#define ANIM_FLAG_6 (1 << 6) // 0x40
#define ANIM_FLAG_7 (1 << 7) // 0x80
#define ANIM_FLAG_BONE_TRANS (1 << 8)
#define ANIM_FLAG_BONE_SCALE (1 << 9)
struct Animation {
// TODO: Optimize this later if possible.

View file

@ -383,7 +383,6 @@ void patch_mtx_interpolated(f32 delta) {
/**
* Graph node interpolation
*/
static void *sGraphNodeInterpDataMap = NULL;
struct GraphNodeInterpData *geo_get_interp_data(void *node, struct GraphNodeObject *obj) {
@ -1059,7 +1058,7 @@ static void geo_process_background(struct GraphNodeBackground *node) {
}
}
static void anim_process(Vec3f translation, Vec3s rotation, u8 *animType, s16 animFrame, u16 **animAttribute) {
static void anim_process(Vec3f translation, Vec3s rotation, Vec3f scale, u8 *animType, s16 animFrame, u16 **animAttribute) {
if (*animType == ANIM_TYPE_TRANSLATION) {
translation[0] += retrieve_animation_value(gCurAnim, animFrame, animAttribute) * gCurAnimTranslationMultiplier;
translation[1] += retrieve_animation_value(gCurAnim, animFrame, animAttribute) * gCurAnimTranslationMultiplier;
@ -1090,6 +1089,19 @@ static void anim_process(Vec3f translation, Vec3s rotation, u8 *animType, s16 an
rotation[0] += retrieve_animation_value(gCurAnim, animFrame, animAttribute);
rotation[1] += retrieve_animation_value(gCurAnim, animFrame, animAttribute);
rotation[2] += retrieve_animation_value(gCurAnim, animFrame, animAttribute);
if (gCurAnim->flags & ANIM_FLAG_BONE_SCALE) {
s16 scaleX = retrieve_animation_value(gCurAnim, animFrame, animAttribute);
s16 scaleY = retrieve_animation_value(gCurAnim, animFrame, animAttribute);
s16 scaleZ = retrieve_animation_value(gCurAnim, animFrame, animAttribute);
if (scale != NULL) {
scale[0] *= ((f32) scaleX) / 256.0f;
scale[1] *= ((f32) scaleY) / 256.0f;
scale[2] *= ((f32) scaleZ) / 256.0f;
}
}
if (gCurAnim->flags & ANIM_FLAG_BONE_TRANS) {
*animType = ANIM_TYPE_TRANSLATION;
}
@ -1108,6 +1120,7 @@ static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) {
Mat4 matrix;
Vec3s rotation;
Vec3f translation;
Vec3f scale;
// Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\.
if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; }
@ -1118,8 +1131,10 @@ static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) {
// current frame
vec3s_copy(rotation, gVec3sZero);
vec3s_to_vec3f(translation, node->translation);
anim_process(translation, rotation, &gCurAnimType, gCurrAnimFrame, &gCurrAnimAttribute);
vec3f_copy(scale, gVec3fOne);
anim_process(translation, rotation, scale, &gCurAnimType, gCurrAnimFrame, &gCurrAnimAttribute);
mtxf_rotate_xyz_and_translate(matrix, translation, rotation);
mtxf_scale_vec3f(matrix, matrix, scale);
mtxf_mul(gMatStack[gMatStackIndex + 1], matrix, gMatStack[gMatStackIndex]);
// previous frame
@ -1130,8 +1145,10 @@ static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) {
node->translation
);
vec3s_copy(rotation, gVec3sZero);
anim_process(translation, rotation, &animType, gPrevAnimFrame, &animAttribute);
vec3f_copy(scale, gVec3fOne);
anim_process(translation, rotation, scale, &animType, gPrevAnimFrame, &animAttribute);
mtxf_rotate_xyz_and_translate(matrix, translation, rotation);
mtxf_scale_vec3f(matrix, matrix, scale);
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], matrix, gMatStackPrev[gMatStackIndex]);
);
@ -1420,8 +1437,12 @@ static s32 obj_is_in_view(struct GraphNodeObject *node, Mat4 matrix) {
static void geo_sanitize_object_gfx(void) {
geo_append_display_list(obj_sanitize_gfx, LAYER_OPAQUE);
geo_append_display_list(obj_sanitize_gfx, LAYER_OPAQUE_DECAL);
geo_append_display_list(obj_sanitize_gfx, LAYER_OPAQUE_INTER);
geo_append_display_list(obj_sanitize_gfx, LAYER_ALPHA);
geo_append_display_list(obj_sanitize_gfx, LAYER_TRANSPARENT);
geo_append_display_list(obj_sanitize_gfx, LAYER_TRANSPARENT_DECAL);
geo_append_display_list(obj_sanitize_gfx, LAYER_TRANSPARENT_INTER);
}
static struct MarioBodyState *get_mario_body_state_from_mario_object(struct Object *marioObj) {
@ -1688,6 +1709,7 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) {
node->fnNode.func(GEO_CONTEXT_HELD_OBJ, &node->fnNode.node, (struct DynamicPool *) gMatStack[gMatStackIndex + 1]);
}
s32 savedMatStackIndex = gMatStackIndex;
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
@ -1708,7 +1730,13 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) {
}
geo_sanitize_object_gfx();
// While rendering the held object's geo tree, ensure "current object" globals
// refer to the held object, otherwise Lua geo callbacks can accidentally
// mutate the holder's render state (e.g. make Wario limbs disappear).
struct GraphNodeObject *savedCurGraphNodeObject = gCurGraphNodeObject;
gCurGraphNodeObject = &node->objNode->header.gfx;
geo_process_node_and_siblings(node->objNode->header.gfx.sharedChild);
gCurGraphNodeObject = savedCurGraphNodeObject;
gCurGraphNodeHeldObject = NULL;
gCurAnimType = gGeoTempState.type;
gCurAnimEnabled = gGeoTempState.enabled;
@ -1717,7 +1745,13 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) {
gCurrAnimAttribute = gGeoTempState.attribute;
gCurAnim = gGeoTempState.anim;
gPrevAnimFrame = gGeoTempState.prevFrame;
gMatStackIndex--;
// Force-restore matrix stack index to avoid any imbalance caused by
// held object geo trees (including Lua geo callbacks).
gMatStackIndex = savedMatStackIndex;
// Reset any render-state changes performed by the held object's geo tree
// so the holder's remaining body parts render correctly.
geo_sanitize_object_gfx();
}
if (node->fnNode.node.children != NULL) {
@ -1748,7 +1782,7 @@ static void geo_process_bone(struct GraphNodeBone *node) {
vec3s_copy(rotation, node->rotation);
vec3s_to_vec3f(translation, node->translation);
vec3f_copy(scale, node->scale);
anim_process(translation, rotation, &gCurAnimType, gCurrAnimFrame, &gCurrAnimAttribute);
anim_process(translation, rotation, scale, &gCurAnimType, gCurrAnimFrame, &gCurrAnimAttribute);
mtxf_rotate_xyz_and_translate(matrix, translation, rotation);
mtxf_scale_vec3f(matrix, matrix, scale);
mtxf_mul(gMatStack[gMatStackIndex + 1], matrix, gMatStack[gMatStackIndex]);
@ -1764,7 +1798,7 @@ static void geo_process_bone(struct GraphNodeBone *node) {
vec3s_to_vec3f(translation, node->translation);
vec3f_copy(scale, node->scale);
}
anim_process(translation, rotation, &animType, gPrevAnimFrame, &animAttribute);
anim_process(translation, rotation, scale, &animType, gPrevAnimFrame, &animAttribute);
mtxf_rotate_xyz_and_translate(matrix, translation, rotation);
mtxf_scale_vec3f(matrix, matrix, scale);
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], matrix, gMatStackPrev[gMatStackIndex]);

View file

@ -4666,6 +4666,7 @@ char gSmluaConstants[] = ""
"ANIM_FLAG_6=(1 << 6)\n"
"ANIM_FLAG_7=(1 << 7)\n"
"ANIM_FLAG_BONE_TRANS=(1 << 8)\n"
"ANIM_FLAG_BONE_SCALE=(1 << 9)\n"
"OBJECT_MAX_BHV_STACK=16\n"
"OBJECT_NUM_REGULAR_FIELDS=0x50\n"
"OBJECT_NUM_CUSTOM_FIELDS=0x40\n"