diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 0825c31b0..9f89bf662 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -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 diff --git a/data/dynos_mgr_anim.cpp b/data/dynos_mgr_anim.cpp index 21db5f690..58690eb55 100644 --- a/data/dynos_mgr_anim.cpp +++ b/data/dynos_mgr_anim.cpp @@ -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; } } diff --git a/docs/lua/constants.md b/docs/lua/constants.md index 78590ed1e..5c73058a4 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -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 diff --git a/include/types.h b/include/types.h index 006531203..a8bd4c792 100644 --- a/include/types.h +++ b/include/types.h @@ -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. diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c index 43c3c1f0e..f1042a21c 100644 --- a/src/game/rendering_graph_node.c +++ b/src/game/rendering_graph_node.c @@ -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]); diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index 80c6dcb85..759299ab9 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -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"