Add system for skipping skeleton interpolation for a given actor and applied it to Happy Mask Salesman, tagged fireworks

This commit is contained in:
Mr-Wiseguy 2024-03-31 16:39:47 -04:00
parent 31d2424774
commit 30e66ab12b
6 changed files with 385 additions and 5 deletions

View File

@ -83,7 +83,11 @@ void Actor_Init(Actor* actor, PlayState* play) {
Gfx* push_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) {
if (actor != NULL) {
u32 cur_transform_id = actor_transform_id(actor);
gEXMatrixGroupDecomposedNormal(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
if (actor_get_interpolation_skipped(actor)) {
gEXMatrixGroupDecomposedSkip(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
} else {
gEXMatrixGroupDecomposedNormal(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
}
}
return dlist;
}
@ -92,7 +96,11 @@ Gfx* push_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) {
Gfx* push_post_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) {
if (actor != NULL) {
u32 cur_transform_id = actor_transform_id(actor);
gEXMatrixGroupDecomposedNormal(dlist++, cur_transform_id + limb_index + ACTOR_TRANSFORM_LIMB_COUNT, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
if (actor_get_interpolation_skipped(actor)) {
gEXMatrixGroupDecomposedSkip(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
} else {
gEXMatrixGroupDecomposedNormal(dlist++, cur_transform_id + limb_index + ACTOR_TRANSFORM_LIMB_COUNT, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
}
}
return dlist;
}
@ -101,7 +109,11 @@ Gfx* push_post_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) {
Gfx* push_skin_limb_matrix_group(Gfx* dlist, Actor* actor, u32 limb_index) {
if (actor != NULL) {
u32 cur_transform_id = actor_transform_id(actor);
gEXMatrixGroupDecomposedVerts(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
if (actor_get_interpolation_skipped(actor)) {
gEXMatrixGroupDecomposedSkip(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
} else {
gEXMatrixGroupDecomposedVerts(dlist++, cur_transform_id + limb_index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
}
}
return dlist;
}
@ -1257,5 +1269,8 @@ void Actor_Draw(PlayState* play, Actor* actor) {
}
actor->isDrawn = true;
// @recomp Clear the actor's interpolation skipped flag.
actor_clear_interpolation_skipped(actor);
CLOSE_DISPS(play->state.gfxCtx);
}

View File

@ -0,0 +1,31 @@
// Required to include MM decomp headers without having built the repo
#ifndef OBJECT_FALL2_H
#define OBJECT_FALL2_H 1
extern Vtx object_fall2Vtx_000000[];
extern Gfx gOpenMouthMoonDL[];
extern u64 gOpenMouthMoonFarSideTLUT[];
extern u64 gOpenMouthMoonFaceTLUT[];
extern u64 gOpenMouthMoonEyesTex[];
extern u64 gOpenMouthMoonFarSideTex[];
extern u64 gOpenMouthMoonFaceTex[];
extern u64 gOpenMouthMoonTeethTex[];
extern u8 object_fall2_Blob_005EF4[];
extern Vtx object_fall2_Vtx_005F10[];
extern Gfx object_fall2_DL_006E00[];
extern Gfx object_fall2_DL_006EF0[];
extern Gfx object_fall2_DL_006FF8[];
extern Gfx object_fall2_DL_007100[];
extern Gfx object_fall2_DL_007208[];
extern Gfx object_fall2_DL_007310[];
extern Gfx object_fall2_DL_007418[];
extern Gfx object_fall2_DL_007520[];
extern Gfx object_fall2_DL_007628[];
extern Gfx object_fall2_DL_007730[];
extern u64 object_fall2_Tex_007838[];
extern u64 object_fall2_Tex_008038[];
extern AnimatedMatTexScrollParams object_fall2_Matanimheader_008840TexScrollParams_008838[];
extern AnimatedMaterial object_fall2_Matanimheader_008840[];
extern u8 object_fall2_Blob_008898[];
#endif

View File

@ -1,5 +1,6 @@
#include "patches.h"
#include "transform_ids.h"
#include "overlays/actors/ovl_En_Hanabi/z_en_hanabi.h"
extern EffectSsInfo sEffectSsInfo;
@ -90,3 +91,86 @@ void EffectSS_DrawParticle(PlayState* play, s32 index) {
CLOSE_DISPS(play->state.gfxCtx);
}
extern u64 gSun1Tex[];
extern Gfx gSunSparkleMaterialDL[];
extern Gfx gSunSparkleModelDL[];
extern u8 D_80B23C40[];
extern u8 D_80B23C2C[];
// @recomp Modified to take the actor as an argument for relocation and to tag firework transforms.
void func_80B22FA8_patched(Actor* thisx, EnHanabiStruct* arg0, PlayState* play2) {
PlayState* play = play2;
GraphicsContext* gfxCtx = play->state.gfxCtx;
s32 i;
u8 sp53;
OPEN_DISPS(gfxCtx);
Gfx_SetupDL25_Xlu(play->state.gfxCtx);
POLY_XLU_DISP = Gfx_SetupDL(POLY_XLU_DISP, SETUPDL_20);
gSPSegment(POLY_XLU_DISP++, 0x08, Lib_SegmentedToVirtual(gSun1Tex));
gSPDisplayList(POLY_XLU_DISP++, gSunSparkleMaterialDL);
sp53 = 0xFF;
// @recomp Manually relocate, TODO remove when automated by recompiler.
u8* D_80B23C40_relocated = (u8*)actor_relocate(thisx, D_80B23C40);
u8* D_80B23C2C_relocated = (u8*)actor_relocate(thisx, D_80B23C2C);
for (i = 0; i < 400; i++, arg0++) {
if (arg0->unk_00 != 1) {
continue;
}
Matrix_Translate(arg0->unk_08.x, arg0->unk_08.y, arg0->unk_08.z, MTXMODE_NEW);
Matrix_ReplaceRotation(&play->billboardMtxF);
if (arg0->unk_01 < 40) {
Matrix_Scale(arg0->unk_04 * 0.025f * arg0->unk_01, arg0->unk_04 * 0.025f * arg0->unk_01, 1.0f,
MTXMODE_APPLY);
} else {
Matrix_Scale(arg0->unk_04, arg0->unk_04, 1.0f, MTXMODE_APPLY);
}
Matrix_RotateZS(play->gameplayFrames * 4864, MTXMODE_APPLY);
// @recomp Tag the matrix.
gEXMatrixGroupDecomposedNormal(POLY_XLU_DISP++, actor_transform_id(thisx) + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
if (sp53 != arg0->unk_02) {
gDPPipeSync(POLY_XLU_DISP++);
gDPSetEnvColor(POLY_XLU_DISP++, D_80B23C40_relocated[arg0->unk_02], D_80B23C40_relocated[arg0->unk_02 + 1],
D_80B23C40_relocated[arg0->unk_02 + 2], 255);
sp53 = arg0->unk_02;
}
if (arg0->unk_01 < 6) {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, D_80B23C2C_relocated[arg0->unk_02], D_80B23C2C_relocated[arg0->unk_02 + 1],
D_80B23C2C_relocated[arg0->unk_02 + 2], arg0->unk_01 * 50);
} else {
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, D_80B23C2C_relocated[arg0->unk_02], D_80B23C2C_relocated[arg0->unk_02 + 1],
D_80B23C2C_relocated[arg0->unk_02 + 2], 255);
}
gSPDisplayList(POLY_XLU_DISP++, gSunSparkleModelDL);
// @recomp Pop the matrix group.
gEXPopMatrixGroup(POLY_XLU_DISP++, G_MTX_MODELVIEW);
}
CLOSE_DISPS(gfxCtx);
}
// @recomp Patched to call a custom version of a vanilla function.
void EnHanabi_Draw(Actor* thisx, PlayState* play) {
EnHanabi* this = (EnHanabi*)thisx;
Matrix_Push();
// @recomp Call a modified version of the function that takes the actor for relocation purposes.
func_80B22FA8_patched(thisx, this->unk_148, play);
Matrix_Pop();
}

View File

@ -5,6 +5,8 @@
#include "overlays/actors/ovl_Boss_04/z_boss_04.h"
#include "overlays/actors/ovl_Boss_Hakugin/z_boss_hakugin.h"
#include "overlays/actors/ovl_En_Water_Effect/z_en_water_effect.h"
#include "overlays/actors/ovl_En_Osn/z_en_osn.h"
#include "overlays/actors/ovl_En_Fall2/z_en_fall2.h"
extern EnTanron2* D_80BB8458[82];
extern Boss04* D_80BB8450;
@ -684,3 +686,233 @@ void func_80B0C398(BossHakugin* this, PlayState* play) {
CLOSE_DISPS(play->state.gfxCtx);
}
// @recomp Skip interpolation for the Happy Mask Salesman when his animation changes.
extern AnimationInfo sHappyMaskSalesmanAnimationInfo[];
typedef enum {
/* 0 */ OSN_ANIM_IDLE,
/* 1 */ OSN_ANIM_ARMS_OUT,
/* 2 */ OSN_ANIM_BOWING,
/* 3 */ OSN_ANIM_REMINISCE,
/* 4 */ OSN_ANIM_HANDS_CLASPED,
/* 5 */ OSN_ANIM_BELIEVE,
/* 6 */ OSN_ANIM_THINK,
/* 7 */ OSN_ANIM_SHAKE_HEAD,
/* 8 */ OSN_ANIM_ORGAN_TALK,
/* 9 */ OSN_ANIM_ORGAN_PLAY,
/* 10 */ OSN_ANIM_SHAKE,
/* 11 */ OSN_ANIM_CHOKE,
/* 12 */ OSN_ANIM_DESPAIR,
/* 13 */ OSN_ANIM_FAST_BOWS,
/* 14 */ OSN_ANIM_HAND_OUT,
/* 15 */ OSN_ANIM_LYING_DOWN_FACE_UP,
/* 16 */ OSN_ANIM_LYING_DOWN_FACE_DOWN,
/* 17 */ OSN_ANIM_MASK_LOOK_AT,
/* 18 */ OSN_ANIM_TURN_AROUND_START,
/* 19 */ OSN_ANIM_TURN_AROUND_LOOP,
/* 20 */ OSN_ANIM_WALK_AWAY,
/* 21 */ OSN_ANIM_MASK_LOOK_FROM_START,
/* 22 */ OSN_ANIM_MASK_LOOK_FROM_LOOP,
/* 23 */ OSN_ANIM_HAND_OUT_2, // Exact same as OSN_ANIM_HAND_OUT
/* 24 */ OSN_ANIM_WALK_AWAY_END, // Only the last frame of OSN_ANIM_WALK_AWAY
/* 25 */ OSN_ANIM_MAX
} OsnAnimation;
void EnOsn_HandleCsAction(EnOsn* this, PlayState* play);
void EnOsn_Idle(EnOsn* this, PlayState* play);
// @recomp Patched to skip interpolation when the Happy Mask Salesman changes animations.
void EnOsn_ChooseAction(EnOsn* this, PlayState* play) {
// @recomp Manually relocate the static symbol.
AnimationInfo* sAnimationInfo = (AnimationInfo*)actor_relocate(&this->actor, sHappyMaskSalesmanAnimationInfo);
u32 isSwitchFlagSet = Flags_GetSwitch(play, 0);
this->csId = this->actor.csId;
Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, OSN_ANIM_IDLE);
if (!isSwitchFlagSet) {
// @recomp Manual relocation, TODO remove when automated by the recompiler.
this->actionFunc = (EnOsnActionFunc)actor_relocate(&this->actor, EnOsn_HandleCsAction);
} else {
// @recomp Manual relocation, TODO remove when automated by the recompiler.
this->actionFunc = (EnOsnActionFunc)actor_relocate(&this->actor, EnOsn_Idle);
}
// @recomp Skip interpolation this frame.
actor_set_interpolation_skipped(&this->actor);
}
void EnOsn_TurnAround(EnOsn* this);
void EnOsn_LookFromMask(EnOsn* this);
void EnOsn_FadeOut(EnOsn* this);
// @recomp Patched to skip interpolation when the Happy Mask Salesman changes animations.
void EnOsn_HandleCsAction(EnOsn* this, PlayState* play) {
u8 pad;
s32 cueChannel;
if (Cutscene_IsCueInChannel(play, CS_CMD_ACTOR_CUE_130)) {
cueChannel = Cutscene_GetCueChannel(play, CS_CMD_ACTOR_CUE_130);
this->shouldRotateHead = false;
if (this->cueId != play->csCtx.actorCues[cueChannel]->id) {
this->cueId = play->csCtx.actorCues[cueChannel]->id;
switch (play->csCtx.actorCues[cueChannel]->id) {
case 1:
this->animIndex = OSN_ANIM_BOWING;
break;
case 2:
this->animIndex = OSN_ANIM_ARMS_OUT;
break;
case 3:
this->animIndex = OSN_ANIM_SHAKE_HEAD;
break;
case 4:
this->animIndex = OSN_ANIM_REMINISCE;
break;
case 5:
this->animIndex = OSN_ANIM_THINK;
break;
case 6:
this->animIndex = OSN_ANIM_BELIEVE;
break;
case 7:
this->animIndex = OSN_ANIM_HANDS_CLASPED;
break;
case 8:
this->animIndex = OSN_ANIM_IDLE;
break;
case 10:
this->animIndex = OSN_ANIM_ORGAN_TALK;
break;
case 11:
this->animIndex = OSN_ANIM_ORGAN_PLAY;
break;
case 13:
this->animIndex = OSN_ANIM_SHAKE;
break;
case 15:
this->animIndex = OSN_ANIM_CHOKE;
break;
case 16:
this->animIndex = OSN_ANIM_DESPAIR;
break;
case 17:
this->animIndex = OSN_ANIM_FAST_BOWS;
break;
case 18:
this->animIndex = OSN_ANIM_HAND_OUT;
break;
case 19:
this->animIndex = OSN_ANIM_MASK_LOOK_AT;
break;
case 20:
this->animIndex = OSN_ANIM_TURN_AROUND_START;
break;
case 21:
this->animIndex = OSN_ANIM_WALK_AWAY;
break;
case 22:
this->animIndex = OSN_ANIM_MASK_LOOK_FROM_START;
break;
case 23:
this->animIndex = OSN_ANIM_HAND_OUT_2;
break;
case 24:
this->animIndex = OSN_ANIM_WALK_AWAY_END;
break;
default:
this->animIndex = OSN_ANIM_IDLE;
break;
}
// @recomp Manually relocate the static symbol.
AnimationInfo* sAnimationInfo = (AnimationInfo*)actor_relocate(&this->actor, sHappyMaskSalesmanAnimationInfo);
Actor_ChangeAnimationByInfo(&this->skelAnime, sAnimationInfo, this->animIndex);
// @recomp Skip interpolation this frame.
actor_set_interpolation_skipped(&this->actor);
}
if ((this->animIndex == OSN_ANIM_BELIEVE) && (play->sceneId == SCENE_SPOT00) &&
(gSaveContext.sceneLayer == 0xB) && (play->csCtx.curFrame == 400)) {
Actor_PlaySfx(&this->actor, NA_SE_VO_OMVO00);
}
if (this->animIndex == OSN_ANIM_TURN_AROUND_START) {
EnOsn_TurnAround(this);
}
if (this->animIndex == OSN_ANIM_MASK_LOOK_FROM_START) {
EnOsn_LookFromMask(this);
}
if (this->animIndex == OSN_ANIM_WALK_AWAY_END) {
EnOsn_FadeOut(this);
}
if ((this->animIndex == OSN_ANIM_WALK_AWAY) &&
(Animation_OnFrame(&this->skelAnime, 17.0f) || Animation_OnFrame(&this->skelAnime, 27.0f) ||
Animation_OnFrame(&this->skelAnime, 37.0f) || Animation_OnFrame(&this->skelAnime, 47.0f) ||
Animation_OnFrame(&this->skelAnime, 57.0f) || Animation_OnFrame(&this->skelAnime, 67.0f))) {
Actor_PlaySfx(&this->actor, NA_SE_EV_OMENYA_WALK);
}
Cutscene_ActorTranslateAndYaw(&this->actor, play, cueChannel);
} else {
this->shouldRotateHead = true;
this->cueId = 99;
EnOsn_ChooseAction(this, play);
}
}
// @recomp Patched to tag this actor's draws using linear order matching.
void EnFall2_Draw(Actor* thisx, PlayState* play) {
s32 pad;
EnFall2* this = (EnFall2*)thisx;
Mtx* mtx;
if (!(this->alphaLevel <= 0.0f)) {
Gfx_SetupDL25_Xlu(play->state.gfxCtx);
AnimatedMat_DrawXlu(play, Lib_SegmentedToVirtual(object_fall2_Matanimheader_008840));
mtx = GRAPH_ALLOC(play->state.gfxCtx, this->skeletonInfo.unk_18->unk_1 * sizeof(Mtx));
if (mtx != NULL) {
Gfx_SetupDL25_Xlu(play->state.gfxCtx);
Matrix_RotateYS((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(play)) + 0x8000), MTXMODE_APPLY);
// @recomp Tag this actor's matrices.
OPEN_DISPS(play->state.gfxCtx);
gEXMatrixGroupDecomposedNormal(POLY_XLU_DISP++, actor_transform_id(thisx), G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
func_8018450C(play, &this->skeletonInfo, mtx, NULL, NULL, &this->actor);
// @recomp Pop the matrix group.
gEXPopMatrixGroup(POLY_XLU_DISP++, G_MTX_MODELVIEW);
CLOSE_DISPS(play->state.gfxCtx);
}
}
}

View File

@ -3,6 +3,7 @@ __start = 0x80000000;
/* Static symbols that aren't in the elf */
sSceneEntranceTable = 0x801C5720;
D_808DE5B0 = 0x808DE5B0;
sHappyMaskSalesmanAnimationInfo = 0x80AD22C0;
/* Dummy addresses that get recompiled into function calls */
recomp_puts = 0x8F000000;

View File

@ -28,7 +28,7 @@
#define EFFECT_SHIELD_PARTICLE_TRANSFORM_ID_START (EFFECT_BLURE_TRANSFORM_ID_START + 2 * EFFECT_TRANSFORM_ID_COUNT)
#define EFFECT_TIRE_MARK_TRANSFORM_ID_START (EFFECT_SHIELD_PARTICLE_TRANSFORM_ID_START + 2 * EFFECT_TRANSFORM_ID_COUNT)
#define ACTOR_TRANSFORM_LIMB_COUNT 128
#define ACTOR_TRANSFORM_LIMB_COUNT 256
#define ACTOR_TRANSFORM_ID_COUNT (ACTOR_TRANSFORM_LIMB_COUNT * 2) // One ID for each limb and another for each post-draw
#define ACTOR_TRANSFORM_ID_START 0x1000000U
@ -37,9 +37,10 @@
#define actorIdByte0(actor) ((u8*)(actor))[0x22]
// 0x23 between halfDaysBits and world
#define actorIdByte1(actor) ((u8*)(actor))[0x23]
// 0x3A between audioFlags and focus
#define actorIdByte2(actor) ((u8*)(actor))[0x3A]
// Other unused padding:
// 0x3A between audioFlags and focus
// 0x3B between audioFlags and focus
static inline u32 actor_transform_id(Actor* actor) {
@ -50,6 +51,22 @@ static inline u32 actor_transform_id(Actor* actor) {
return (actor_id * ACTOR_TRANSFORM_ID_COUNT) + ACTOR_TRANSFORM_ID_START;
}
typedef enum {
ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED = 1 << 0,
} ActorTransformFlags;
static inline u32 actor_get_interpolation_skipped(Actor* actor) {
return (actorIdByte2(actor) & ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED) != 0;
}
static inline void actor_set_interpolation_skipped(Actor* actor) {
actorIdByte2(actor) |= ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED;
}
static inline void actor_clear_interpolation_skipped(Actor* actor) {
actorIdByte2(actor) &= ~ACTOR_TRANSFORM_FLAG_INTERPOLATION_SKIPPED;
}
void force_camera_interpolation();
void force_camera_skip_interpolation();