From 498002a4259c1e29a9e0081d6b5740b5d3ef067f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo?= Date: Sat, 20 Dec 2025 14:41:47 -0300 Subject: [PATCH] Add patch for skipping camera interpolation during non-gameplay segments. (#6) * Add patch for skipping camera interpolation during non-gameplay segments. * Fix ignore interpolation patches. Add skip for zoombox. --- patches/camera_transform_tagging.c | 129 +++++++++++++++++++++---- patches/particle_transform_tagging.c | 2 +- patches/patches.h | 4 + patches/projection_transform_tagging.c | 5 +- patches/prop_transform_tagging.c | 16 +-- patches/snow_patches.c | 5 +- patches/transform_ids.h | 1 + patches/transform_tagging.c | 32 +++--- 8 files changed, 153 insertions(+), 41 deletions(-) diff --git a/patches/camera_transform_tagging.c b/patches/camera_transform_tagging.c index 44c91d4..282baf6 100644 --- a/patches/camera_transform_tagging.c +++ b/patches/camera_transform_tagging.c @@ -1,6 +1,7 @@ #include "patches.h" #include "transform_ids.h" #include "core1/core1.h" +#include "core2/nc/camera.h" s32 cur_perspective_projection_transform_id = 0; s32 backup_perspective_projection_transform_id = 0; @@ -13,7 +14,11 @@ void reset_projection_ids() { cur_ortho_projection_transform_id = 0; } -bool perspective_interpolation_skipped = FALSE; +bool skip_perspective_interpolation = FALSE; + +bool perspective_interpolation_skipped() { + return skip_perspective_interpolation; +} s32 getGameMode(void); @@ -75,20 +80,26 @@ RECOMP_PATCH void viewport_setRenderPerspectiveMatrix(Gfx **gfx, Mtx **mtx, f32 gEXSetViewMatrixFloat((*gfx)++, view->m); // @recomp If a perspective projection transform ID is set, apply it as the projection matrix group. Otherwise, use auto as the projection matrix group. - if (all_interpolation_skipped()) { - gEXMatrixGroupNoInterpolate((*gfx)++, G_EX_NOPUSH, G_MTX_PROJECTION, G_EX_EDIT_NONE); - } - else if (cur_perspective_projection_transform_id != 0) { - // Force the projection to not adjust itself for a wider aspect ratio when it's being rendered for the Bottles' bonus puzzle or the Mumbo photo. - u16 aspect = G_EX_ASPECT_AUTO; - bool inPictureGameMode = (getGameMode() == GAME_MODE_8_BOTTLES_BONUS) || (getGameMode() == GAME_MODE_A_SNS_PICTURE); - bool isGameplayTransformId = (cur_perspective_projection_transform_id == PROJECTION_GAMEPLAY_TRANSFORM_ID); - bool isTransitionTransformId = (cur_perspective_projection_transform_id == PROJECTION_TRANSITION_TRANSFORM_ID); - if (inPictureGameMode && (isGameplayTransformId || isTransitionTransformId)) { - aspect = G_EX_ASPECT_STRETCH; + bool skip_interpolation = all_interpolation_skipped() || perspective_interpolation_skipped(); + if (cur_perspective_projection_transform_id != 0) { + if (skip_interpolation) { + gEXMatrixGroupSkipAll((*gfx)++, cur_perspective_projection_transform_id, G_EX_NOPUSH, G_MTX_PROJECTION, G_EX_EDIT_NONE); } + else { + // Force the projection to not adjust itself for a wider aspect ratio when it's being rendered for the Bottles' bonus puzzle or the Mumbo photo. + u16 aspect = G_EX_ASPECT_AUTO; + bool inPictureGameMode = (getGameMode() == GAME_MODE_8_BOTTLES_BONUS) || (getGameMode() == GAME_MODE_A_SNS_PICTURE); + bool isGameplayTransformId = (cur_perspective_projection_transform_id == PROJECTION_GAMEPLAY_TRANSFORM_ID); + bool isTransitionTransformId = (cur_perspective_projection_transform_id == PROJECTION_TRANSITION_TRANSFORM_ID); + if (inPictureGameMode && (isGameplayTransformId || isTransitionTransformId)) { + aspect = G_EX_ASPECT_STRETCH; + } - gEXMatrixGroup((*gfx)++, cur_perspective_projection_transform_id, G_EX_INTERPOLATE_SIMPLE, G_EX_NOPUSH, G_MTX_PROJECTION, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_INTERPOLATE, G_EX_ORDER_LINEAR, G_EX_EDIT_NONE, aspect, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_AUTO); + gEXMatrixGroup((*gfx)++, cur_perspective_projection_transform_id, G_EX_INTERPOLATE_SIMPLE, G_EX_NOPUSH, G_MTX_PROJECTION, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_INTERPOLATE, G_EX_ORDER_LINEAR, G_EX_EDIT_NONE, aspect, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_AUTO); + } + } + else if (skip_interpolation) { + gEXMatrixGroupNoInterpolate((*gfx)++, G_EX_NOPUSH, G_MTX_PROJECTION, G_EX_EDIT_NONE); } else { gEXMatrixGroupSimpleNormal((*gfx)++, G_EX_ID_AUTO, G_EX_NOPUSH, G_MTX_PROJECTION, G_EX_EDIT_NONE); @@ -116,11 +127,16 @@ RECOMP_PATCH void viewport_setRenderViewportAndOrthoMatrix(Gfx **gfx, Mtx **mtx) gEXSetViewMatrixFloat((*gfx)++, identity_matrix); // @recomp If an ortho projection transform ID is set, apply it as the projection matrix group. Otherwise, use auto as the projection matrix group. - if (all_interpolation_skipped()) { - gEXMatrixGroupNoInterpolate((*gfx)++, G_EX_NOPUSH, G_MTX_PROJECTION, G_EX_EDIT_NONE); + if (cur_ortho_projection_transform_id != 0) { + if (all_interpolation_skipped()) { + gEXMatrixGroupSkipAll((*gfx)++, cur_ortho_projection_transform_id, G_EX_NOPUSH, G_MTX_PROJECTION, G_EX_EDIT_NONE); + } + else { + gEXMatrixGroupSimpleNormal((*gfx)++, cur_ortho_projection_transform_id, G_EX_NOPUSH, G_MTX_PROJECTION, G_EX_EDIT_NONE); + } } - else if (cur_ortho_projection_transform_id != 0) { - gEXMatrixGroupSimpleNormal((*gfx)++, cur_ortho_projection_transform_id, G_EX_NOPUSH, G_MTX_PROJECTION, G_EX_EDIT_NONE); + else if (all_interpolation_skipped()) { + gEXMatrixGroupNoInterpolate((*gfx)++, G_EX_NOPUSH, G_MTX_PROJECTION, G_EX_EDIT_NONE); } else { gEXMatrixGroupSimpleNormal((*gfx)++, G_EX_ID_AUTO, G_EX_NOPUSH, G_MTX_PROJECTION, G_EX_EDIT_NONE); @@ -168,3 +184,82 @@ RECOMP_PATCH void viewport_restoreState(void) { cur_perspective_projection_transform_id = backup_perspective_projection_transform_id; cur_ortho_projection_transform_id = backup_ortho_projection_transform_id; } + +extern s32 ncCameraType; +extern u8 D_8037D8C5; +extern u8 D_8037D8C6; +void ncDynamicCamera_update(void); +void ncStaticCamera_update(void); +void ncRandomCamera_update(void); +void func_802BB4D8(f32 position[3], f32 rotation[3]); +void func_802BEFB0(void); +void func_802BBA84(void); + +// @recomp Patched to detect camera type changes or sudden camera movements and skip perspective interpolation when they happen. +RECOMP_PATCH void ncCamera_update(void) { + f32 vpPos[3]; + f32 vpRot[3]; + s32 camType; + + camType = ncCameraType; + if (!D_8037D8C5 && !D_8037D8C6) { + camType = 0; + } + + switch (camType) { + case CAMERA_TYPE_2_DYNAMIC://dynamic viewport position + ncDynamicCamera_update(); + break; + case CAMERA_TYPE_3_STATIC://set viewport to static location + ncStaticCamera_update(); + break; + case CAMERA_TYPE_4_RANDOM: //set viewport to random location + ncRandomCamera_update(); + break; + } + + viewport_getPosition_vec3f(vpPos); + viewport_getRotation_vec3f(vpRot); + func_802BB4D8(vpPos, vpRot); + viewport_setPosition_vec3f(vpPos); + viewport_setRotation_vec3f(vpRot); + + // @recomp Detect camera type changes or sudden camera movements. + static s32 camTypePrev = 0; + static f32 vpPosPrev[3] = {}; + static f32 vpPosVel[3] = {}; + f32 vpPosDelta[3]; + ml_vec3f_diff_copy(vpPosDelta, vpPos, vpPosPrev); + if (camType != camTypePrev) { + // Always skip perspective interpolation on camera type changes. + skip_perspective_interpolation = TRUE; + } + else if (camType == CAMERA_TYPE_2_DYNAMIC) { + // Never skip interpolation during controllable gameplay. + skip_perspective_interpolation = FALSE; + } + else { + // Check for sudden position differences. They must be within an arbitrary threshold o + // the projected position from using the previous frame's velocity. + f32 vpPosProjected[3]; + ml_vec3f_add(vpPosProjected, vpPosPrev, vpPosVel); + + f32 distToProjected = ml_vec3f_distance(vpPos, vpPosProjected); + const f32 SkipThreshold = 100.0f; + if (distToProjected > SkipThreshold) { + ml_vec3f_clear(vpPosVel); + skip_perspective_interpolation = TRUE; + } + else { + ml_vec3f_copy(vpPosVel, vpPosDelta); + skip_perspective_interpolation = FALSE; + } + } + + camTypePrev = camType; + ml_vec3f_copy(vpPosPrev, vpPos); + + viewport_update(); + func_802BEFB0(); + func_802BBA84(); +} diff --git a/patches/particle_transform_tagging.c b/patches/particle_transform_tagging.c index 4a569c7..0fd3496 100644 --- a/patches/particle_transform_tagging.c +++ b/patches/particle_transform_tagging.c @@ -132,7 +132,7 @@ RECOMP_PATCH void __particleEmitter_drawOnPass(ParticleEmitter *this, Gfx **gfx, func_80344C2C(this->unk0_16); // @recomp Get the particle's ID and use it to set a matrix group for this particle. u32 transform_id = NORMAL_PARTICLE_TRANSFORM_ID_START + get_particle_id(iPtr); - gEXMatrixGroupDecomposedNormal((*gfx)++, transform_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW); + gEXMatrixGroupDecomposedNormal((*gfx)++, transform_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); if(this->draw_mode & PART_EMIT_ROTATABLE){ func_80344720(this->unk34, (s32)iPtr->frame, 0, position, flat_rotation, scale, gfx, mtx); diff --git a/patches/patches.h b/patches/patches.h index f0d9224..365aec3 100644 --- a/patches/patches.h +++ b/patches/patches.h @@ -50,6 +50,9 @@ void osWriteBackDCacheAll(void); ) #endif +#define gEXMatrixGroupSkipAll(cmd, id, push, proj, edit) \ + gEXMatrixGroup(cmd, id, G_EX_INTERPOLATE_SIMPLE, push, proj, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, G_EX_ORDER_LINEAR, edit, G_EX_ASPECT_AUTO, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP) + #define gEXMatrixGroupSimpleNormal(cmd, id, push, proj, edit) \ gEXMatrixGroup(cmd, id, G_EX_INTERPOLATE_SIMPLE, push, proj, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_INTERPOLATE, G_EX_ORDER_LINEAR, edit, G_EX_ASPECT_AUTO, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_AUTO) @@ -82,5 +85,6 @@ void set_additional_model_scale(f32 x, f32 y, f32 z); void set_frustum_checks_enabled(int enabled); void set_all_interpolation_skipped(bool skipped); bool all_interpolation_skipped(); +bool perspective_interpolation_skipped(); #endif diff --git a/patches/projection_transform_tagging.c b/patches/projection_transform_tagging.c index 9737d52..9af7034 100644 --- a/patches/projection_transform_tagging.c +++ b/patches/projection_transform_tagging.c @@ -291,14 +291,17 @@ RECOMP_PATCH void func_803163A8(GcZoombox *this, Gfx **gfx, Mtx **mtx) { anctrl_drawSetup(this->anim_ctrl, sp50, 1); } - // @recomp Set the model transform ID. + // @recomp Set the model transform ID. Skip interpolation when the camera does a cut. u32 prev_transform_id = cur_drawn_model_transform_id; + s32 prev_skip = cur_drawn_model_transform_id_skip_interpolation; cur_drawn_model_transform_id = ZOOMBOX_TRANSFORM_ID_START + this->portrait_id; + cur_drawn_model_transform_id_skip_interpolation = perspective_interpolation_skipped(); modelRender_draw(gfx, mtx, sp50, sp5C, this->unk198 * sp34, sp38, this->model); // @recomp Reset the model transform ID. cur_drawn_model_transform_id = prev_transform_id; + cur_drawn_model_transform_id_skip_interpolation = prev_skip; } // @recomp Patched to set the zoombox portrait's model and ortho projection transform IDs. diff --git a/patches/prop_transform_tagging.c b/patches/prop_transform_tagging.c index da11d6f..d2c52e3 100644 --- a/patches/prop_transform_tagging.c +++ b/patches/prop_transform_tagging.c @@ -92,12 +92,16 @@ RECOMP_PATCH void func_8032D510(Cube *cube, Gfx **gfx, Mtx **mtx, Vtx **vtx){ // @recomp Set the matrix group before drawing the sprite. // Skip interpolation on vertices to account for vertex lists changing between frames of the sprite. // Also skip interpolation on scale to account for the scale inverting when sprites are mirrored. - // TODO track this matrix for skipping interpolation when camera interpolation is skipped. - gEXMatrixGroupDecomposed((*gfx)++, base_transform_id, G_EX_PUSH, G_MTX_MODELVIEW, - G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_INTERPOLATE, - G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_INTERPOLATE, - G_EX_ORDER_LINEAR, G_EX_EDIT_ALLOW, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_AUTO); - + if (perspective_interpolation_skipped()) { + gEXMatrixGroupSkipAll((*gfx)++, base_transform_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); + } + else { + gEXMatrixGroupDecomposed((*gfx)++, base_transform_id, G_EX_PUSH, G_MTX_MODELVIEW, + G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_INTERPOLATE, + G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_INTERPOLATE, + G_EX_ORDER_LINEAR, G_EX_EDIT_NONE, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_AUTO); + } + // @recomp Also set the model render transform ID before drawing the sprite. This won't have any effect // in the unmodified game, but will allow transform tagging for mods that draw models in place of sprites. cur_drawn_model_transform_id = base_transform_id; diff --git a/patches/snow_patches.c b/patches/snow_patches.c index 27e1b93..120159a 100644 --- a/patches/snow_patches.c +++ b/patches/snow_patches.c @@ -198,12 +198,13 @@ RECOMP_PATCH bool func_802F989C(Gfx **gfx, Mtx **mtx, f32 arg2[3]) { // @recomp Set a matrix group before drawing the snow particle. If the interpolation skip bit is enabled as the snow particle was just // created, do not interpolate and clear the bit instead. s32 snowIndex = (struct struct_4D_s *)(arg2)-D_80369280->unk1C; + u32 snowId = SNOW_TRANSFORM_ID_START + snowIDArray[snowIndex]; if (snowIDArray[snowIndex] & SNOW_SKIP_INTERPOLATION_MASK) { - gEXMatrixGroupNoInterpolate((*gfx)++, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); + gEXMatrixGroupSkipAll((*gfx)++, snowId, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); snowIDArray[snowIndex] ^= SNOW_SKIP_INTERPOLATION_MASK; } else { - gEXMatrixGroupSimpleNormal((*gfx)++, SNOW_TRANSFORM_ID_START + snowIDArray[snowIndex], G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); + gEXMatrixGroupSimpleNormal((*gfx)++, snowId, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); } gSPMatrix((*gfx)++, (*mtx)++, G_MTX_PUSH | G_MTX_LOAD | G_MTX_MODELVIEW); diff --git a/patches/transform_ids.h b/patches/transform_ids.h index 39a3f05..0bf7aab 100644 --- a/patches/transform_ids.h +++ b/patches/transform_ids.h @@ -72,6 +72,7 @@ void reset_projection_ids(); extern s32 cur_drawn_model_is_map; extern s32 cur_drawn_model_transform_id; +extern s32 cur_drawn_model_transform_id_skip_interpolation; extern s32 cur_perspective_projection_transform_id; extern s32 cur_ortho_projection_transform_id; diff --git a/patches/transform_tagging.c b/patches/transform_tagging.c index 40ea3dd..a82c590 100644 --- a/patches/transform_tagging.c +++ b/patches/transform_tagging.c @@ -29,6 +29,7 @@ void set_additional_model_scale(f32 x, f32 y, f32 z) { s32 cur_drawn_model_is_map = FALSE; s32 cur_drawn_model_transform_id = 0; +s32 cur_drawn_model_transform_id_skip_interpolation = FALSE; Mtx identity_fixed_mtx = {{ { @@ -92,11 +93,7 @@ void func_802E6BD0(BKModelUnk28List *arg0, BKVertexList *arg1, AnimMtxList *mtx_ void assetCache_free(void *arg0); void set_model_matrix_group(Gfx **gfx, void *geo_list, u32 bone_index) { - if (skip_all_interpolation || cur_drawn_model_transform_id == -1) { - // @recomp Skip interpolation if all interpolation is currently skipped or the transform id is -1. - gEXMatrixGroupNoInterpolate((*gfx)++, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW); - } - else if (cur_drawn_model_transform_id != 0) { + if (cur_drawn_model_transform_id != 0) { u32 group_id; // Pick a group ID based on whether this is a map or not. if (cur_drawn_model_is_map) { @@ -107,11 +104,18 @@ void set_model_matrix_group(Gfx **gfx, void *geo_list, u32 bone_index) { // Other models use a group ID determined by the bone index. group_id = cur_drawn_model_transform_id + bone_index; } - // @recomp Tag the matrix. - // gEXMatrixGroupSimpleNormal((*gfx)++, group_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW); - gEXMatrixGroupSimpleVerts((*gfx)++, group_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW); - // gEXMatrixGroupDecomposedNormal((*gfx)++, group_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW); - // gEXMatrixGroupDecomposedVerts((*gfx)++, group_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW); + + if (skip_all_interpolation || cur_drawn_model_transform_id_skip_interpolation) { + // @recomp Skip interpolation if all interpolation is currently skipped or the transform was specified to be skipped. + gEXMatrixGroupSkipAll((*gfx)++, group_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); + } + else { + // @recomp Tag the matrix. + gEXMatrixGroupSimpleVerts((*gfx)++, group_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); + } + } + else if (skip_all_interpolation) { + gEXMatrixGroupNoInterpolate((*gfx)++, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); } } @@ -639,12 +643,12 @@ RECOMP_PATCH BKModelBin *modelRender_draw(Gfx **gfx, Mtx **mtx, f32 position[3], gSPMatrix((*gfx)++, (*mtx)++, G_MTX_PUSH | G_MTX_LOAD | G_MTX_MODELVIEW); // @recomp Create a matrix group if a transform id is set. - if (skip_all_interpolation || cur_drawn_model_transform_id == -1) { - // @recomp Skip interpolation if all interpolation is currently skipped or the transform id is -1. - gEXMatrixGroupNoInterpolate((*gfx)++, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW); + if (skip_all_interpolation || cur_drawn_model_transform_id_skip_interpolation) { + // @recomp Skip interpolation if all interpolation is currently skipped or the transform was specified to be skipped. + gEXMatrixGroupSkipAll((*gfx)++, cur_drawn_model_transform_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); } else if (cur_drawn_model_transform_id != 0) { - gEXMatrixGroupDecomposedVerts((*gfx)++, cur_drawn_model_transform_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW); + gEXMatrixGroupDecomposedVerts((*gfx)++, cur_drawn_model_transform_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); } modelRenderScale = scale;