diff --git a/lib/rt64 b/lib/rt64 index b5b1980..1583c50 160000 --- a/lib/rt64 +++ b/lib/rt64 @@ -1 +1 @@ -Subproject commit b5b19802c0e5ec9bc80677201e0f087cddccc6c6 +Subproject commit 1583c508d396eab94f9043060d81c88178ca377a diff --git a/patches/clanker_patches.c b/patches/clanker_patches.c index b66af70..1c42358 100644 --- a/patches/clanker_patches.c +++ b/patches/clanker_patches.c @@ -14,6 +14,9 @@ extern struct { void (*unk14)(s32, s32); } sOverlay; +void recomp_setup_overlay_skinning(int overlay_id); +void recomp_clear_overlay_skinning(); + // @recomp: Patched to assign an ID to Clanker. RECOMP_PATCH void func_80322E64(Gfx **gfx, Mtx **mtx, Vtx **vtx){ // @recomp If the current level is Clanker's Cavern, set up the model ID for Clanker. @@ -22,8 +25,14 @@ RECOMP_PATCH void func_80322E64(Gfx **gfx, Mtx **mtx, Vtx **vtx){ cur_drawn_model_transform_id = CLANKER_TRANSFORM_ID_START; } + // @recomp Setup the overlay skinning data. + recomp_setup_overlay_skinning(overlayManagergetLoadedId()); + if(sOverlay.draw) sOverlay.draw(gfx, mtx, vtx); + + // @recomp Clear the overlay skinning setup. + recomp_clear_overlay_skinning(); // @recomp Reset the current transform ID. cur_drawn_model_transform_id = prev_transform_id; diff --git a/patches/culling_patches.c b/patches/culling_patches.c index 2b8b896..0dd0528 100644 --- a/patches/culling_patches.c +++ b/patches/culling_patches.c @@ -11,6 +11,7 @@ void animMtxList_setBoned(AnimMtxList **this_ptr, BKAnimationList *anim_list, Bo extern s32 cur_model_would_have_been_culled_in_demo; bool recomp_in_demo_playback_game_mode(); +void recomp_setup_marker_skinning(ActorMarker *marker); int frustum_checks_enabled = TRUE; void set_frustum_checks_enabled(int enabled) { @@ -188,6 +189,9 @@ RECOMP_PATCH void actor_predrawMethod(Actor *this){ } if(this->unkF4_30){ + // @recomp Set up skinning data for this actor. + recomp_setup_marker_skinning(this->marker); + sp40 = func_80330C74(this); if(this->unk138_29){ sp34[0] = this->pitch; diff --git a/patches/init_patches.c b/patches/init_patches.c index 6e40cac..1d4a5a5 100644 --- a/patches/init_patches.c +++ b/patches/init_patches.c @@ -10,12 +10,17 @@ RECOMP_DECLARE_EVENT(recomp_on_init()); +void recomp_init_vertex_skinning(void); + // @recomp Patched to perform some initialization after core2 has been loaded. RECOMP_PATCH void dummy_func_8025AFB0(void) { // @recomp Initialize note saving data before the init event is run. // This will allow mods to override it as necessary. init_note_saving(); + // @recomp Perform the necessary initialization for vertex skinning. + recomp_init_vertex_skinning(); + // @recomp Register actor extension data and call the init event. recomp_on_init(); diff --git a/patches/patches.h b/patches/patches.h index cb6fa38..a405481 100644 --- a/patches/patches.h +++ b/patches/patches.h @@ -74,6 +74,9 @@ void osWriteBackDCacheAll(void); #define gEXMatrixGroupDecomposedNormal(cmd, id, push, proj, edit) \ gEXMatrixGroupDecomposed(cmd, id, 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_COMPONENT_SKIP, G_EX_COMPONENT_AUTO) +#define gEXMatrixGroupDecomposedNormalTcs(cmd, id, push, proj, edit) \ + gEXMatrixGroupDecomposed(cmd, id, 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_COMPONENT_INTERPOLATE, G_EX_COMPONENT_AUTO) + #define gEXMatrixGroupDecomposedSkipRot(cmd, id, push, proj, edit) \ gEXMatrixGroupDecomposed(cmd, id, push, proj, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, 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_COMPONENT_SKIP, G_EX_COMPONENT_AUTO) diff --git a/patches/transform_tagging.c b/patches/transform_tagging.c index 414ab2d..ca7e73d 100644 --- a/patches/transform_tagging.c +++ b/patches/transform_tagging.c @@ -35,6 +35,7 @@ s32 cur_drawn_model_skip_interpolation = FALSE; s32 cur_model_transform_id_offset = 0; s32 cur_model_uses_bones = FALSE; s32 cur_model_would_have_been_culled_in_demo = FALSE; +s32 cur_model_uses_ex_vertex = FALSE; Mtx identity_fixed_mtx = {{ { @@ -211,24 +212,18 @@ bool set_model_matrix_group(Gfx **gfx, void *geo_list, bool skip_rotation) { // 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 if (cur_model_uses_bones) { - // Tag the matrix with simple matrix interpolation if the model uses bones. - if (skip_rotation) { - gEXMatrixGroupSimpleVertsSkipRot((*gfx)++, group_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); - } - else { - gEXMatrixGroupSimpleVerts((*gfx)++, group_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); - } - } else { - // Tag the matrix with decomposed matrix interpolation on any other model. - if (skip_rotation) { - gEXMatrixGroupDecomposedVertsSkipRot((*gfx)++, group_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); - } - else { - gEXMatrixGroupDecomposedVerts((*gfx)++, group_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); - } + // Tag the matrix with simple matrix interpolation if the model uses bones. + // Use decomposed matrix interpolation on any other model. + u8 interpolation_mode = cur_model_uses_bones ? G_EX_INTERPOLATE_SIMPLE : G_EX_INTERPOLATE_DECOMPOSE; + u8 rotation_mode = skip_rotation ? G_EX_COMPONENT_SKIP : G_EX_COMPONENT_INTERPOLATE; + u8 vertex_interpolation_mode = cur_drawn_model_is_map ? G_EX_COMPONENT_INTERPOLATE : G_EX_COMPONENT_SKIP; + u8 texcoord_interpolation_mode = cur_drawn_model_is_map ? G_EX_COMPONENT_INTERPOLATE : G_EX_COMPONENT_SKIP; + gEXMatrixGroup((*gfx)++, group_id, interpolation_mode, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_COMPONENT_INTERPOLATE, rotation_mode, + G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, vertex_interpolation_mode, G_EX_COMPONENT_INTERPOLATE, + G_EX_ORDER_LINEAR, G_EX_EDIT_NONE, G_EX_ASPECT_AUTO, texcoord_interpolation_mode, G_EX_COMPONENT_AUTO); } + return TRUE; } else if (skip_all_interpolation) { @@ -451,31 +446,79 @@ RECOMP_PATCH void func_80339124(Gfx ** gfx, Mtx ** mtx, BKGeoList *geo_list){ }while(1); } +typedef struct { + u32 frameCount; + u32 modelId; + u32 floatStart; +} ModelSkinningData; + // @recomp Applies CPU skinning and saves the result to a higher precision vertex buffer. #define SKINNING_POSITIONS_MAX 65536 -f32 sSkinningFloats[SKINNING_POSITIONS_MAX] __attribute__((aligned(8))); +f32 sSkinningPosFloats[2][SKINNING_POSITIONS_MAX] __attribute__((aligned(8))); +f32 sSkinningVelFloats[SKINNING_POSITIONS_MAX] __attribute__((aligned(8))); u32 sSkinningFloatCount; +u32 sSkinningFloatFrame; +MarkerExtensionId sMarkerSkinningExtensionId; +ModelSkinningData *sCurModelSkinningData; +u32 sCurModelId; + +ModelSkinningData sOverlaySkinningData; + +void recomp_setup_overlay_skinning(int overlay_id) { + sCurModelSkinningData = &sOverlaySkinningData; + sCurModelId = overlay_id; // Using the overlay ID as the model ID since there's no easy way to get the model ID for overlay draws. +} + +void recomp_clear_overlay_skinning() { + sCurModelSkinningData = NULL; + sCurModelId = 0; +} void recomp_reset_skinning_stack() { sSkinningFloatCount = 0; + sSkinningFloatFrame++; } -float *recomp_apply_cpu_skinning(BKModelUnk28List *arg0, BKVertexList *arg1, AnimMtxList *mtx_list) { +void recomp_init_vertex_skinning() { + sMarkerSkinningExtensionId = bkrecomp_extend_marker_all(sizeof(ModelSkinningData)); +} + +void recomp_setup_marker_skinning(ActorMarker *marker) { + sCurModelSkinningData = bkrecomp_get_extended_marker_data(marker, sMarkerSkinningExtensionId); + sCurModelId = marker->modelId; +} + +bool recomp_apply_cpu_skinning(BKModelUnk28List *arg0, BKVertexList *arg1, AnimMtxList *mtx_list, float **pos, float **vel) { + *pos = NULL; + *vel = NULL; + + ModelSkinningData prev_skinning_data = {0}; + u32 cur_model_id = sCurModelId; + if (sCurModelSkinningData != NULL) { + prev_skinning_data = *sCurModelSkinningData; + sCurModelSkinningData->frameCount = sSkinningFloatFrame; + sCurModelSkinningData->modelId = cur_model_id; + sCurModelSkinningData->floatStart = sSkinningFloatCount; + } + sCurModelSkinningData = NULL; + sCurModelId = 0; + if (sSkinningFloatCount + (arg1->count * 3) > SKINNING_POSITIONS_MAX) { - return NULL; + return FALSE; } // Copy unmodified positions. - float *dst_pos = &sSkinningFloats[sSkinningFloatCount]; - s32 i, j; + float *dst_pos = &sSkinningPosFloats[sSkinningFloatFrame & 0x1][sSkinningFloatCount]; + s32 i, j = 0; for (i = 0; i < arg1->count; i++) { - sSkinningFloats[sSkinningFloatCount++] = arg1->vtx_18[i].v.ob[0]; - sSkinningFloats[sSkinningFloatCount++] = arg1->vtx_18[i].v.ob[1]; - sSkinningFloats[sSkinningFloatCount++] = arg1->vtx_18[i].v.ob[2]; + dst_pos[j++] = arg1->vtx_18[i].v.ob[0]; + dst_pos[j++] = arg1->vtx_18[i].v.ob[1]; + dst_pos[j++] = arg1->vtx_18[i].v.ob[2]; } - - // Always align to multiples of 2. + + // Increase the current float count. Always align to multiples of 2. + sSkinningFloatCount += arg1->count * 3; if (sSkinningFloatCount & 0x1) { sSkinningFloatCount++; } @@ -507,7 +550,25 @@ float *recomp_apply_cpu_skinning(BKModelUnk28List *arg0, BKVertexList *arg1, Ani i_ptr = (BKModelUnk28 *)((s16 *)(i_ptr + 1) + (i_ptr->vtx_count - 1)); } - return dst_pos; + // Compute velocities if applicable. + // To apply, the frame index stored on the extended marker data must be exactly the previous frame and the model ID must match. + if (prev_skinning_data.frameCount == sSkinningFloatFrame - 1 && prev_skinning_data.modelId == cur_model_id) { + const float *prev_pos = &sSkinningPosFloats[(sSkinningFloatFrame & 0x1) ^ 1][prev_skinning_data.floatStart]; + float *dst_vel = &sSkinningVelFloats[sSkinningFloatCount]; + j = 0; + for (i = 0; i < arg1->count; i++) { + for (s32 k = 0; k < 3; k++) { + dst_vel[j] = dst_pos[j] - prev_pos[j]; + j++; + } + } + *vel = dst_vel; + } + + // Return the vertex float segments that should be used. + *pos = dst_pos; + + return TRUE; } @@ -765,15 +826,22 @@ RECOMP_PATCH BKModelBin *modelRender_draw(Gfx **gfx, Mtx **mtx, f32 position[3], } // @recomp Use higher precision vertex buffer when the model requires CPU skinning. - bool ex_vertex_components_used = FALSE; + cur_model_uses_ex_vertex = FALSE; + if(model_bin->unk28 != NULL && D_8038371C != NULL){ func_802E6BD0((u8*)modelRenderModelBin + modelRenderModelBin->unk28, modelRendervertexList, D_8038371C); // @recomp Do the skinning again on a high precision version of the vertex buffer. Force its usage for any subsequent display lists. - f32 *skinned_pos = recomp_apply_cpu_skinning((u8 *)modelRenderModelBin + modelRenderModelBin->unk28, modelRendervertexList, D_8038371C); - if (skinned_pos != NULL) { + f32 *skinned_pos, *skinned_vel; + if (recomp_apply_cpu_skinning((u8 *)modelRenderModelBin + modelRenderModelBin->unk28, modelRendervertexList, D_8038371C, &skinned_pos, &skinned_vel)) { gEXSetVertexSegment((*gfx)++, G_EX_VERTEX_POSITION, G_EX_ENABLED, skinned_pos, &modelRendervertexList->vtx_18); - ex_vertex_components_used = TRUE; + if (skinned_vel != NULL) { + gEXSetVertexSegment((*gfx)++, G_EX_VERTEX_VELOCITY, G_EX_ENABLED, skinned_vel, &modelRendervertexList->vtx_18); + } + else { + gEXSetVertexSegment((*gfx)++, G_EX_VERTEX_VELOCITY, G_EX_DISABLED, 0, 0); + } + cur_model_uses_ex_vertex = TRUE; } } @@ -800,14 +868,21 @@ 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. + bool pushed_matrix_group = FALSE; if (skip_all_interpolation || cur_drawn_model_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); + pushed_matrix_group = TRUE; + } + else if (cur_drawn_model_is_map) { + gEXMatrixGroupDecomposedVerts((*gfx)++, cur_drawn_model_transform_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); + pushed_matrix_group = TRUE; } else if (cur_drawn_model_transform_id != 0) { - gEXMatrixGroupDecomposedVerts((*gfx)++, cur_drawn_model_transform_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); + gEXMatrixGroupDecomposedNormalTcs((*gfx)++, cur_drawn_model_transform_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); + pushed_matrix_group = TRUE; } - + modelRenderScale = scale; if(rotation){ modelRenderRotation[0] = rotation[0]; @@ -833,13 +908,14 @@ RECOMP_PATCH BKModelBin *modelRender_draw(Gfx **gfx, Mtx **mtx, f32 position[3], gSPPopMatrix((*gfx)++, G_MTX_MODELVIEW); // @recomp Pop the matrix group if a transform id is set. - if (cur_drawn_model_transform_id != 0) { + if (pushed_matrix_group) { gEXPopMatrixGroup((*gfx)++, G_MTX_MODELVIEW); } // @recomp Clear use of high precision vertex buffers. - if (ex_vertex_components_used) { + if (cur_model_uses_ex_vertex) { gEXSetVertexSegment((*gfx)++, G_EX_VERTEX_POSITION, G_EX_DISABLED, 0, 0); + gEXSetVertexSegment((*gfx)++, G_EX_VERTEX_VELOCITY, G_EX_DISABLED, 0, 0); } if(modelRenderCallback.post_method != NULL){