From b6cec905c7d532a45b07b8ec9f49dde0b06b6d02 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sat, 17 Feb 2024 22:11:07 -0500 Subject: [PATCH] Transform tagging to missed skele drawing functions, transform tagging for particles, automatic transform tagging for all actors with only one matrix, update RT64 --- lib/RT64-HLE | 2 +- patches/actor_transform_tagging.c | 414 ++++++++++++++++++++++++++++++ patches/input.h | 22 +- patches/patch_helpers.h | 24 ++ patches/transform_ids.h | 2 +- 5 files changed, 441 insertions(+), 23 deletions(-) create mode 100644 patches/patch_helpers.h diff --git a/lib/RT64-HLE b/lib/RT64-HLE index 5e21b41..44f24ce 160000 --- a/lib/RT64-HLE +++ b/lib/RT64-HLE @@ -1 +1 @@ -Subproject commit 5e21b41fdafbf569cc1938d6560ee1c8c1cef4f3 +Subproject commit 44f24ced733dc9db61eefefa7b26187ddc7f8fda diff --git a/patches/actor_transform_tagging.c b/patches/actor_transform_tagging.c index 47dbc48..0df1831 100644 --- a/patches/actor_transform_tagging.c +++ b/patches/actor_transform_tagging.c @@ -847,6 +847,268 @@ void SkelAnime_DrawTransformFlexOpa(PlayState* play, void** skeleton, Vec3s* joi CLOSE_DISPS(play->state.gfxCtx); } +/* + * Draws the Skeleton `skeleton`'s limb at index `limbIndex`. Appends all generated graphics commands to + * `gfx`. Returns a pointer to the next gfx to be appended to. + */ +Gfx* SkelAnime_DrawLimb(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable, + OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, Actor* actor, Gfx* gfx) { + StandardLimb* limb; + Gfx* dList; + Vec3f pos; + Vec3s rot; + + Matrix_Push(); + + limb = Lib_SegmentedToVirtual(skeleton[limbIndex]); + limbIndex++; + + rot = jointTable[limbIndex]; + pos.x = limb->jointPos.x; + pos.y = limb->jointPos.y; + pos.z = limb->jointPos.z; + + dList = limb->dList; + + // @recomp Push the limb's matrix group. + gfx = push_limb_matrix_group(gfx, actor, limbIndex); + + if ((overrideLimbDraw == NULL) || !overrideLimbDraw(play, limbIndex, &dList, &pos, &rot, actor, &gfx)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (dList != NULL) { + gSPMatrix(&gfx[0], Matrix_NewMtx(play->state.gfxCtx), G_MTX_LOAD); + gSPDisplayList(&gfx[1], dList); + gfx = &gfx[2]; + } + } + + // @recomp Pop the limb's matrix group and push the post-limb matrix group. + gfx = pop_limb_matrix_group(gfx, actor); + gfx = push_post_limb_matrix_group(gfx, actor, limbIndex); + + if (postLimbDraw != NULL) { + postLimbDraw(play, limbIndex, &dList, &rot, actor, &gfx); + } + + // @recomp Pop the post-limb matrix group. + gfx = pop_post_limb_matrix_group(gfx, actor); + + if (limb->child != LIMB_DONE) { + gfx = SkelAnime_DrawLimb(play, limb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, actor, gfx); + } + + Matrix_Pop(); + + if (limb->sibling != LIMB_DONE) { + gfx = SkelAnime_DrawLimb(play, limb->sibling, skeleton, jointTable, overrideLimbDraw, postLimbDraw, actor, gfx); + } + + return gfx; +} + +/* + * Draws the Skeleton `skeleton` Appends all generated graphics to `gfx`, and returns a pointer to the + * next gfx to be appended to. + */ +Gfx* SkelAnime_Draw(PlayState* play, void** skeleton, Vec3s* jointTable, OverrideLimbDraw overrideLimbDraw, + PostLimbDraw postLimbDraw, Actor* actor, Gfx* gfx) { + StandardLimb* rootLimb; + s32 pad; + Gfx* dList; + Vec3f pos; + Vec3s rot; + + if (skeleton == NULL) { + return NULL; + } + + Matrix_Push(); + + rootLimb = Lib_SegmentedToVirtual(skeleton[0]); + + pos.x = jointTable[0].x; + pos.y = jointTable[0].y; + pos.z = jointTable[0].z; + + rot = jointTable[1]; + + dList = rootLimb->dList; + + // @recomp Push the limb's matrix group. + gfx = push_limb_matrix_group(gfx, actor, 0); + + if ((overrideLimbDraw == NULL) || !overrideLimbDraw(play, 1, &dList, &pos, &rot, actor, &gfx)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (dList != NULL) { + gSPMatrix(&gfx[0], Matrix_NewMtx(play->state.gfxCtx), G_MTX_LOAD); + gSPDisplayList(&gfx[1], dList); + gfx = &gfx[2]; + } + } + + // @recomp Pop the limb's matrix group and push the post-limb matrix group. + gfx = pop_limb_matrix_group(gfx, actor); + gfx = push_post_limb_matrix_group(gfx, actor, 0); + + if (postLimbDraw != NULL) { + postLimbDraw(play, 1, &dList, &rot, actor, &gfx); + } + + // @recomp Pop the post-limb matrix group. + gfx = pop_post_limb_matrix_group(gfx, actor); + + if (rootLimb->child != LIMB_DONE) { + gfx = + SkelAnime_DrawLimb(play, rootLimb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, actor, gfx); + } + + Matrix_Pop(); + + return gfx; +} + +/** + * Draw a limb of type `StandardLimb` contained within a flexible skeleton to the specified display buffer + */ +Gfx* SkelAnime_DrawFlexLimb(PlayState* play, s32 limbIndex, void** skeleton, Vec3s* jointTable, + OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, Actor* actor, Mtx** mtx, + Gfx* gfx) { + StandardLimb* limb; + Gfx* newDList; + Gfx* limbDList; + Vec3f pos; + Vec3s rot; + + Matrix_Push(); + + limb = Lib_SegmentedToVirtual(skeleton[limbIndex]); + limbIndex++; + rot = jointTable[limbIndex]; + + pos.x = limb->jointPos.x; + pos.y = limb->jointPos.y; + pos.z = limb->jointPos.z; + + newDList = limbDList = limb->dList; + + // @recomp Push the limb's matrix group. + gfx = push_limb_matrix_group(gfx, actor, limbIndex); + + if ((overrideLimbDraw == NULL) || !overrideLimbDraw(play, limbIndex, &newDList, &pos, &rot, actor, &gfx)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (newDList != NULL) { + gSPMatrix(&gfx[0], Matrix_ToMtx(*mtx), G_MTX_LOAD); + gSPDisplayList(&gfx[1], newDList); + gfx = &gfx[2]; + (*mtx)++; + } else { + if (limbDList != NULL) { + Matrix_ToMtx(*mtx); + (*mtx)++; + } + } + } + + // @recomp Pop the limb's matrix group and push the post-limb matrix group. + gfx = pop_limb_matrix_group(gfx, actor); + gfx = push_post_limb_matrix_group(gfx, actor, limbIndex); + + if (postLimbDraw != NULL) { + postLimbDraw(play, limbIndex, &limbDList, &rot, actor, &gfx); + } + + // @recomp Pop the post-limb matrix group. + gfx = pop_post_limb_matrix_group(gfx, actor); + + if (limb->child != LIMB_DONE) { + gfx = SkelAnime_DrawFlexLimb(play, limb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, actor, + mtx, gfx); + } + + Matrix_Pop(); + + if (limb->sibling != LIMB_DONE) { + gfx = SkelAnime_DrawFlexLimb(play, limb->sibling, skeleton, jointTable, overrideLimbDraw, postLimbDraw, actor, + mtx, gfx); + } + + return gfx; +} + +/** + * Draw all limbs of type `StandardLimb` in a given flexible skeleton to the specified display buffer + * Limbs in a flexible skeleton have meshes that can stretch to line up with other limbs. + * An array of matrices is dynamically allocated so each limb can access any transform to ensure its meshes line up. + */ +Gfx* SkelAnime_DrawFlex(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount, + OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, Actor* actor, Gfx* gfx) { + StandardLimb* rootLimb; + s32 pad; + Gfx* newDList; + Gfx* limbDList; + Vec3f pos; + Vec3s rot; + Mtx* mtx; + + if (skeleton == NULL) { + return NULL; + } + + mtx = GRAPH_ALLOC(play->state.gfxCtx, dListCount * sizeof(Mtx)); + + gSPSegment(gfx++, 0x0D, mtx); + + Matrix_Push(); + + rootLimb = Lib_SegmentedToVirtual(skeleton[0]); + + pos.x = jointTable[0].x; + pos.y = jointTable[0].y; + pos.z = jointTable[0].z; + + rot = jointTable[1]; + + newDList = limbDList = rootLimb->dList; + + // @recomp Push the limb's matrix group. + gfx = push_limb_matrix_group(gfx, actor, 0); + + if ((overrideLimbDraw == NULL) || !overrideLimbDraw(play, 1, &newDList, &pos, &rot, actor, &gfx)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (newDList != NULL) { + gSPMatrix(&gfx[0], Matrix_ToMtx(mtx), G_MTX_LOAD); + gSPDisplayList(&gfx[1], newDList); + gfx = &gfx[2]; + mtx++; + } else { + if (limbDList != NULL) { + Matrix_ToMtx(mtx); + mtx++; + } + } + } + + // @recomp Pop the limb's matrix group and push the post-limb matrix group. + gfx = pop_limb_matrix_group(gfx, actor); + gfx = push_post_limb_matrix_group(gfx, actor, 0); + + if (postLimbDraw != NULL) { + postLimbDraw(play, 1, &limbDList, &rot, actor, &gfx); + } + + // @recomp Pop the post-limb matrix group. + gfx = pop_post_limb_matrix_group(gfx, actor); + + if (rootLimb->child != LIMB_DONE) { + gfx = SkelAnime_DrawFlexLimb(play, rootLimb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, actor, + &mtx, gfx); + } + + Matrix_Pop(); + + return gfx; +} + extern MtxF gSkinLimbMatrices[]; void Skin_DrawImpl(Actor* actor, PlayState* play, Skin* skin, SkinPostDraw postDraw, @@ -908,3 +1170,155 @@ void Skin_DrawImpl(Actor* actor, PlayState* play, Skin* skin, SkinPostDraw postD close_disps:; CLOSE_DISPS(gfxCtx); } + +__attribute__((noinline)) s32 scan_for_matrices(Gfx* start, Gfx* end) { + s32 matrix_count = 0; + Gfx* cur = start; + // Count any G_MTX commands between the start and end commands. + while (cur != end) { + if ((cur->words.w0 >> 24) == G_MTX) { + matrix_count++; + } + cur++; + } + return matrix_count; +} + +void tag_actor_displaylists(Actor* actor, PlayState* play, Gfx* opa_start, Gfx* xlu_start) { + OPEN_DISPS(play->state.gfxCtx); + + // Scan the commands written by the actor to see how many matrices were added. + s32 opa_matrices = scan_for_matrices(opa_start, POLY_OPA_DISP); + s32 xlu_matrices = scan_for_matrices(xlu_start, POLY_XLU_DISP); + + // If the actor wrote at least one matrix in total and at most one matrix to each list, tag that matrix with the actor's id. + if ((opa_matrices == 1 || xlu_matrices == 1) && opa_matrices <= 1 && xlu_matrices <= 1) { + u32 cur_transform_id = + (actorIdByte0(actor) << 0) | + (actorIdByte1(actor) << 8) | + (actorIdByte2(actor) << 16) | + (actorIdByte3(actor) << 24); + + if (opa_matrices == 1) { + // Fill in the slot that was reserved for a transform id. + gEXMatrixGroup(opa_start, cur_transform_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, G_EX_ORDER_LINEAR); + + // Pop the matrix group. + gEXPopMatrixGroup(POLY_OPA_DISP++); + } + + // if (xlu_matrices == 1) { + // // Fill in the slot that was reserved for a transform id. + // gEXMatrixGroup(xlu_start, cur_transform_id + 1, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, G_EX_ORDER_LINEAR); + + // // Pop the matrix groups. + // gEXPopMatrixGroup(POLY_XLU_DISP++); + // } + } + + CLOSE_DISPS(); +} + +// @recomp Patched to automatically add transform tagging to actor matrices based on what DL commands they write in their draw function +void Actor_Draw(PlayState* play, Actor* actor) { + Lights* light; + + OPEN_DISPS(play->state.gfxCtx); + + light = LightContext_NewLights(&play->lightCtx, play->state.gfxCtx); + if ((actor->flags & ACTOR_FLAG_10000000) && (play->roomCtx.curRoom.enablePosLights || (MREG(93) != 0))) { + light->enablePosLights = true; + } + + Lights_BindAll(light, play->lightCtx.listHead, + (actor->flags & (ACTOR_FLAG_10000000 | ACTOR_FLAG_400000)) ? NULL : &actor->world.pos, play); + Lights_Draw(light, play->state.gfxCtx); + + if (actor->flags & ACTOR_FLAG_IGNORE_QUAKE) { + Matrix_SetTranslateRotateYXZ(actor->world.pos.x + play->mainCamera.quakeOffset.x, + actor->world.pos.y + + ((actor->shape.yOffset * actor->scale.y) + play->mainCamera.quakeOffset.y), + actor->world.pos.z + play->mainCamera.quakeOffset.z, &actor->shape.rot); + } else { + Matrix_SetTranslateRotateYXZ(actor->world.pos.x, actor->world.pos.y + (actor->shape.yOffset * actor->scale.y), + actor->world.pos.z, &actor->shape.rot); + } + + Matrix_Scale(actor->scale.x, actor->scale.y, actor->scale.z, MTXMODE_APPLY); + Actor_SetObjectDependency(play, actor); + + gSPSegment(POLY_OPA_DISP++, 0x06, play->objectCtx.slots[actor->objectSlot].segment); + gSPSegment(POLY_XLU_DISP++, 0x06, play->objectCtx.slots[actor->objectSlot].segment); + + if (actor->colorFilterTimer != 0) { + s32 colorFlag = COLORFILTER_GET_COLORFLAG(actor->colorFilterParams); + Color_RGBA8 actorDefaultHitColor = { 0, 0, 0, 255 }; + + if (colorFlag == COLORFILTER_COLORFLAG_GRAY) { + actorDefaultHitColor.r = actorDefaultHitColor.g = actorDefaultHitColor.b = + COLORFILTER_GET_COLORINTENSITY(actor->colorFilterParams) | 7; + } else if (colorFlag == COLORFILTER_COLORFLAG_RED) { + actorDefaultHitColor.r = COLORFILTER_GET_COLORINTENSITY(actor->colorFilterParams) | 7; + } else if (colorFlag == COLORFILTER_COLORFLAG_NONE) { + actorDefaultHitColor.b = actorDefaultHitColor.g = actorDefaultHitColor.r = 0; + } else { + actorDefaultHitColor.b = COLORFILTER_GET_COLORINTENSITY(actor->colorFilterParams) | 7; + } + + if (actor->colorFilterParams & COLORFILTER_BUFFLAG_XLU) { + func_800AE778(play, &actorDefaultHitColor, actor->colorFilterTimer, + COLORFILTER_GET_DURATION(actor->colorFilterParams)); + } else { + func_800AE434(play, &actorDefaultHitColor, actor->colorFilterTimer, + COLORFILTER_GET_DURATION(actor->colorFilterParams)); + } + } + + // @recomp Add two noops into the opa and xlu displaylists to reserve space for a transform tag before the actor is drawn. + Gfx* opa_tag_slot = POLY_OPA_DISP; + Gfx* xlu_tag_slot = POLY_XLU_DISP; + gDPNoOp(POLY_OPA_DISP++); + gDPNoOp(POLY_OPA_DISP++); + gDPNoOp(POLY_XLU_DISP++); + gDPNoOp(POLY_XLU_DISP++); + + actor->draw(actor, play); + + tag_actor_displaylists(actor, play, opa_tag_slot, xlu_tag_slot); + + if (actor->colorFilterTimer != 0) { + if (actor->colorFilterParams & COLORFILTER_BUFFLAG_XLU) { + func_800AE8EC(play); + } else { + func_800AE5A0(play); + } + } + + if (actor->shape.shadowDraw != NULL) { + actor->shape.shadowDraw(actor, light, play); + } + actor->isDrawn = true; + + CLOSE_DISPS(play->state.gfxCtx); +} + +extern EffectSsInfo sEffectSsInfo; + +// @recomp Add transform tags to particles +void EffectSS_DrawParticle(PlayState* play, s32 index) { + EffectSs* entry = &sEffectSsInfo.dataTable[index]; + + OPEN_DISPS(play->state.gfxCtx); + + gEXMatrixGroup(POLY_OPA_DISP++, PARTICLE_TRANSFORM_ID_START + index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, G_EX_ORDER_LINEAR); + gEXMatrixGroup(POLY_XLU_DISP++, PARTICLE_TRANSFORM_ID_START + index, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, G_EX_ORDER_LINEAR); + + if (entry->draw != NULL) { + entry->draw(play, index, entry); + } + + gEXPopMatrixGroup(POLY_OPA_DISP++); + gEXPopMatrixGroup(POLY_XLU_DISP++); + + CLOSE_DISPS(play->state.gfxCtx); +} diff --git a/patches/input.h b/patches/input.h index 7b5c37a..f94a942 100644 --- a/patches/input.h +++ b/patches/input.h @@ -1,11 +1,7 @@ #ifndef __INPUT_H__ #define __INPUT_H__ -#ifdef MIPS -#include "ultra64.h" -#else -#include "recomp.h" -#endif +#include "patch_helpers.h" typedef enum { RECOMP_CAMERA_NORMAL, @@ -14,18 +10,6 @@ typedef enum { extern RecompCameraMode recomp_camera_mode; -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef MIPS -# define DECLARE_FUNC(type, name, ...) \ - type name(__VA_ARGS__); -#else -# define DECLARE_FUNC(type, name, ...) \ - void name(uint8_t* rdram, recomp_context* ctx); -#endif - DECLARE_FUNC(void, recomp_get_gyro_deltas, float* x, float* y); // TODO move these DECLARE_FUNC(void, recomp_puts, const char* data, u32 size); @@ -33,8 +17,4 @@ DECLARE_FUNC(void, recomp_exit); DECLARE_FUNC(void, recomp_handle_quicksave_actions, OSMesgQueue* enter_mq, OSMesgQueue* exit_mq); DECLARE_FUNC(void, recomp_handle_quicksave_actions_main, OSMesgQueue* enter_mq, OSMesgQueue* exit_mq); -#ifdef __cplusplus -} -#endif - #endif diff --git a/patches/patch_helpers.h b/patches/patch_helpers.h new file mode 100644 index 0000000..f6b88d3 --- /dev/null +++ b/patches/patch_helpers.h @@ -0,0 +1,24 @@ +#ifndef __PATCH_HELPERS_H__ +#define __PATCH_HELPERS_H__ + +#ifdef MIPS +#include "ultra64.h" +#else +#include "recomp.h" +#endif + +#ifdef __cplusplus +# define EXTERNC extern "C" +#else +# define EXTERNC +#endif + +#ifdef MIPS +# define DECLARE_FUNC(type, name, ...) \ + EXTERNC type name(__VA_ARGS__) +#else // MIPS +# define DECLARE_FUNC(type, name, ...) \ + EXTERNC void name(uint8_t* rdram, recomp_context* ctx) +#endif + +#endif diff --git a/patches/transform_ids.h b/patches/transform_ids.h index 4768c30..b37d5dc 100644 --- a/patches/transform_ids.h +++ b/patches/transform_ids.h @@ -5,7 +5,7 @@ #define SKYBOX_TRANSFORM_ID_START 0x100U -#define TERRAIN_TRANSFORM_ID_START 0x200U +#define PARTICLE_TRANSFORM_ID_START 0x200U #define ACTOR_TRANSFORM_LIMB_COUNT 128 #define ACTOR_TRANSFORM_ID_COUNT (ACTOR_TRANSFORM_LIMB_COUNT * 2) // One ID for each limb and another for each post-draw