diff --git a/patches/culling_patches.c b/patches/culling_patches.c index 0dd0528..b92285d 100644 --- a/patches/culling_patches.c +++ b/patches/culling_patches.c @@ -188,10 +188,10 @@ 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); + // @recomp Set up skinning data for this actor. + recomp_setup_marker_skinning(this->marker); + if(this->unkF4_30){ sp40 = func_80330C74(this); if(this->unk138_29){ sp34[0] = this->pitch; diff --git a/patches/graphics_patches.c b/patches/graphics_patches.c index 179ce97..757c3c8 100644 --- a/patches/graphics_patches.c +++ b/patches/graphics_patches.c @@ -44,6 +44,7 @@ extern u32 heap_occupiedBytes; extern u32 dynamic_camera_target_index; extern void recomp_reset_skinning_stack(); +extern void recomp_reset_map_model_skinning(); extern void recomp_advance_dynamic_camera_targets(); // @recomp Patched to not free anything. @@ -89,8 +90,9 @@ RECOMP_PATCH void game_draw(s32 arg0){ getGraphicsStacks(&gfx, &mtx, &vtx); } - // @recomp Reset the high precision position skinning stack. + // @recomp Reset the high precision position skinning stacks. recomp_reset_skinning_stack(); + recomp_reset_map_model_skinning(); // @recomp Advance the frame used as reference by the dynamic camera target changes for analog camera. recomp_advance_dynamic_camera_targets(); diff --git a/patches/map_transform_tagging.c b/patches/map_transform_tagging.c index 552eb9b..42a11b2 100644 --- a/patches/map_transform_tagging.c +++ b/patches/map_transform_tagging.c @@ -4,6 +4,27 @@ #include "core2/anctrl.h" #include "core2/modelRender.h" +typedef struct struct_1D_s { + BKModel *model; + s16 unk4; + u8 xform_id; + // u8 pad7[1]; + Struct70s unk8;//union of subtypes +}struct1Ds; + +extern struct { + u8 unk0; + vector(struct1Ds) *unk4; +} D_80386140; + +typedef struct struct_1E_s { + void (*unk0)(void *arg0, s32 arg1, s32 arg2, s32 arg3); + void (*unk4)(void *arg0, s32 arg1, s32 arg2); + void (*unk8)(void *arg0); +}struct1Es; + +extern struct1Es D_80372030[]; + s32 func_80320708(void); s32 levelSpecificFlags_validateCRC2(void); s32 dummy_func_80320248(void); @@ -18,6 +39,11 @@ bool fileProgressFlag_get(enum file_progress_e index); void modelRender_setAnimatedTexturesCacheId(s32 arg0); bool mapModel_has_xlu_bin(void); void func_802F7BC0(Gfx **, Mtx **, Vtx **); +void func_8034E660(s32 arg0, BKVtxRef *src, Vtx *dst, s32 arg3); +void BKModel_transformMesh(BKModel *model, s32 mesh_id, void (*fn)(s32, BKVtxRef *, Vtx *, s32), s32 arg3); + +void recomp_setup_map_skinning(int map_model_id, float *pos_floats); +void recomp_clear_map_skinning(); typedef struct { s16 map_id; //enum map_e @@ -173,6 +199,97 @@ RECOMP_PATCH void mapModel_opa_draw(Gfx **gfx, Mtx **mtx, Vtx **vtx) { } } +// @recomp Reserve a static amount of memory to be used for storing the current modifications to the map model with higher precision floats. +#define MAP_MODEL_XLU_VERTEX_COUNT_MAX 0x400 +f32 map_model_xlu_pos_floats[MAP_MODEL_XLU_VERTEX_COUNT_MAX * 3]; +u32 map_model_xlu_pos_floats_count = 0; + +void recomp_reset_map_model_skinning() { + map_model_xlu_pos_floats_count = 0; +} + +// @recomp Patched to store the moved water vertices with higher precision floats. +RECOMP_PATCH void func_8034E8E4(Struct73s *arg0, BKModel *arg1, s32 arg2) { + f32 sp3C; + f32 sp38; + f32 sp30[2]; + f32 sin; + f32 cos; + f32 sp2C; + f32 sp28; + + sp38 = time_getDelta(); + arg0->unk4 += sp38; + cos = cosf(arg0->unk4 * 0.2 * BAD_PI); + sin = sinf(arg0->unk4 * 0.08 * BAD_PI); + sp30[0] = (sin * 100.0f + 150.0f * cos) * 0.8; + + + cos = sinf(arg0->unk4 * 0.5 * BAD_PI); + sin = cosf(arg0->unk4 * 0.22 * BAD_PI); + sp30[1] = (sin * 100.0f + 50.0f * cos) * 0.8; + + arg0->d_tc[0] = (sp30[0] >= 0.0) ? sp30[0] + 0.5 : sp30[0] - 0.5; + arg0->d_tc[1] = (sp30[1] >= 0.0) ? sp30[1] + 0.5 : sp30[1] - 0.5; + + cos = cosf(arg0->unk4 * 0.5 * BAD_PI); + sin = sinf(arg0->unk4 * 0.11 * BAD_PI); + sp2C = sin * (arg0->unk8 * 0.25) + (arg0->unk8 * 0.75) * cos; + + if (arg0->unk14 < arg0->unk1C) { + arg0->unk18 = arg0->unk14; + arg0->unk14 += sp38; + if (arg0->unk1C < arg0->unk14) { + arg0->unk14 = arg0->unk1C; + } + } + sp28 = ((arg0->unk14 < arg0->unk1C) ? arg0->unkC + ((arg0->unk14 / arg0->unk1C) * (arg0->unkE - arg0->unkC)) : arg0->unkE); + sp28 += sp2C; + arg0->dy = (sp28 >= 0.0) ? sp28 + 0.5 : sp28 - 0.5; + BKModel_transformMesh(arg1, arg2, func_8034E660, (s32)arg0); + + // @recomp Don't use high precision floats if the model exceeds the bounds of the floats array, in case the model was modified. + if (arg1->vtxList_4->count <= MAP_MODEL_XLU_VERTEX_COUNT_MAX) { + // @recomp Make sure to copy all the vertices from the model to the higher precision floats at least once per frame. + s32 i, j = 0; + if (map_model_xlu_pos_floats_count < arg1->vtxList_4->count) { + Vtx *vtx = vtxList_getVertices(arg1->vtxList_4); + for (i = 0; i < arg1->vtxList_4->count; i++) { + map_model_xlu_pos_floats[j++] = vtx->v.ob[0]; + map_model_xlu_pos_floats[j++] = vtx->v.ob[1]; + map_model_xlu_pos_floats[j++] = vtx->v.ob[2]; + vtx++; + } + + map_model_xlu_pos_floats_count = arg1->vtxList_4->count; + } + + // @recomp Run the logic of BKModel_transformMesh again with only the modification of the Y component as seen in func_8034E660. + // The result is stored in the higher precision floats instead of the model binary itself. The original value of dy is used + // before it's rounded to make the animation smoother. + BKMesh *iMesh = (BKMesh *)(arg1 + 1); + BKVtxRef *iVtx; + BKVtxRef *start_vtx_ref; + BKVtxRef *end_vtx_ref; + for (i = 0; i < arg1->meshList_0->meshCount_0; i++) { + if (arg2 == iMesh->uid_0) { + start_vtx_ref = (BKVtxRef *)(iMesh + 1); + end_vtx_ref = start_vtx_ref + iMesh->vtxCount_2; + for (iVtx = start_vtx_ref; iVtx < end_vtx_ref; iVtx++) { + j = iVtx->unk10 * 3; + map_model_xlu_pos_floats[j + 0] = iVtx->v.v.ob[0]; + map_model_xlu_pos_floats[j + 1] = iVtx->v.v.ob[1] + sp28; + map_model_xlu_pos_floats[j + 2] = iVtx->v.v.ob[2]; + } + + break; + } + + iMesh = (BKMesh *)(((BKVtxRef *)(iMesh + 1)) + iMesh->vtxCount_2); + }; + } +} + // @recomp Patched to set the transform ID when drawing the map's translucent model. RECOMP_PATCH void mapModel_xlu_draw(Gfx **gfx, Mtx **mtx, Vtx **vtx) { s32 temp_a0; @@ -192,11 +309,27 @@ RECOMP_PATCH void mapModel_xlu_draw(Gfx **gfx, Mtx **mtx, Vtx **vtx) { cur_drawn_model_is_map = TRUE; cur_drawn_model_transform_id = MAP_MODEL_XLU_TRANSFORM_ID_START; + // @recomp Because func_8034E8E4 runs after this function, run through the vector and see if there's a model that uses + // the vertex modification function that was patched. The address must be hardcoded as it's not possible to retrieve + // the right function address inside a recompiled patch by referencing func_8034E8E4 instead. + BKVertexList *vtxList = (BKVertexList *)((s32)mapModel.model_bin_xlu + mapModel.model_bin_xlu->vtx_list_offset_10); + if (vtxList->count <= MAP_MODEL_XLU_VERTEX_COUNT_MAX) { + struct1Ds *iPtr; + struct1Ds *endPtr = vector_getEnd(D_80386140.unk4); + for (iPtr = vector_getBegin(D_80386140.unk4); iPtr < endPtr; iPtr++) { + if (D_80372030[iPtr->xform_id].unk4 == 0x8034E8E4) { + recomp_setup_map_skinning(mapModel.description->xlu_model_id, map_model_xlu_pos_floats); + break; + } + } + } + modelRender_draw(gfx, mtx, NULL, NULL, mapModel.description->scale, NULL, mapModel.model_bin_xlu); // @recomp Clear the current model transform id after drawing. cur_drawn_model_is_map = FALSE; cur_drawn_model_transform_id = 0; + recomp_clear_map_skinning(); func_802F7BC0(gfx, mtx, vtx); } diff --git a/patches/transform_tagging.c b/patches/transform_tagging.c index ca7e73d..8a98f98 100644 --- a/patches/transform_tagging.c +++ b/patches/transform_tagging.c @@ -217,7 +217,7 @@ bool set_model_matrix_group(Gfx **gfx, void *geo_list, bool skip_rotation) { // 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 vertex_interpolation_mode = cur_drawn_model_is_map && !cur_model_uses_ex_vertex ? 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, @@ -464,6 +464,8 @@ ModelSkinningData *sCurModelSkinningData; u32 sCurModelId; ModelSkinningData sOverlaySkinningData; +ModelSkinningData sMapSkinningData; +f32 *sMapSkinningPosFloats; void recomp_setup_overlay_skinning(int overlay_id) { sCurModelSkinningData = &sOverlaySkinningData; @@ -489,7 +491,19 @@ void recomp_setup_marker_skinning(ActorMarker *marker) { sCurModelId = marker->modelId; } -bool recomp_apply_cpu_skinning(BKModelUnk28List *arg0, BKVertexList *arg1, AnimMtxList *mtx_list, float **pos, float **vel) { +void recomp_setup_map_skinning(int map_model_id, float *pos_floats) { + sCurModelSkinningData = &sMapSkinningData; + sCurModelId = map_model_id; + sMapSkinningPosFloats = pos_floats; +} + +void recomp_clear_map_skinning() { + sCurModelSkinningData = NULL; + sCurModelId = 0; + sMapSkinningPosFloats = NULL; +} + +bool recomp_apply_cpu_skinning(BKModelUnk28List *arg0, BKVertexList *arg1, AnimMtxList *mtx_list, float *pos_override, float **pos, float **vel) { *pos = NULL; *vel = NULL; @@ -509,12 +523,18 @@ bool recomp_apply_cpu_skinning(BKModelUnk28List *arg0, BKVertexList *arg1, AnimM } // Copy unmodified positions. - float *dst_pos = &sSkinningPosFloats[sSkinningFloatFrame & 0x1][sSkinningFloatCount]; s32 i, j = 0; - for (i = 0; i < arg1->count; i++) { - 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]; + float *dst_pos = &sSkinningPosFloats[sSkinningFloatFrame & 0x1][sSkinningFloatCount]; + float *dst_vel = &sSkinningVelFloats[sSkinningFloatCount]; + if (pos_override != NULL) { + memcpy(dst_pos, pos_override, sizeof(float) * arg1->count * 3); + } + else { + for (i = 0; i < arg1->count; i++) { + 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]; + } } // Increase the current float count. Always align to multiples of 2. @@ -523,38 +543,39 @@ bool recomp_apply_cpu_skinning(BKModelUnk28List *arg0, BKVertexList *arg1, AnimM sSkinningFloatCount++; } - // Apply animation. - BKModelUnk28 *i_ptr = (BKModelUnk28 *)(arg0 + 1); - s32 mtx_index = -2; - f32 src_coord[3]; - f32 dst_coord[3]; - s32 vertex_index; - for (i = 0; i < arg0->count; i++) { - if (mtx_index != i_ptr->anim_index) { - mtx_index = i_ptr->anim_index; - mlMtxSet(animMtxList_get(mtx_list, mtx_index)); + if (arg0 != NULL) { + // Apply animation. + BKModelUnk28 *i_ptr = (BKModelUnk28 *)(arg0 + 1); + s32 mtx_index = -2; + f32 src_coord[3]; + f32 dst_coord[3]; + s32 vertex_index; + for (i = 0; i < arg0->count; i++) { + if (mtx_index != i_ptr->anim_index) { + mtx_index = i_ptr->anim_index; + mlMtxSet(animMtxList_get(mtx_list, mtx_index)); + } + + src_coord[0] = i_ptr->coord[0]; + src_coord[1] = i_ptr->coord[1]; + src_coord[2] = i_ptr->coord[2]; + mlMtx_apply_vec3f(dst_coord, src_coord); + + for (j = 0; j < i_ptr->vtx_count; j++) { + vertex_index = i_ptr->vtx_list[j] * 3; + dst_pos[vertex_index++] = dst_coord[0]; + dst_pos[vertex_index++] = dst_coord[1]; + dst_pos[vertex_index] = dst_coord[2]; + } + + i_ptr = (BKModelUnk28 *)((s16 *)(i_ptr + 1) + (i_ptr->vtx_count - 1)); } - - src_coord[0] = i_ptr->coord[0]; - src_coord[1] = i_ptr->coord[1]; - src_coord[2] = i_ptr->coord[2]; - mlMtx_apply_vec3f(dst_coord, src_coord); - - for (j = 0; j < i_ptr->vtx_count; j++) { - vertex_index = i_ptr->vtx_list[j] * 3; - dst_pos[vertex_index++] = dst_coord[0]; - dst_pos[vertex_index++] = dst_coord[1]; - dst_pos[vertex_index] = dst_coord[2]; - } - - i_ptr = (BKModelUnk28 *)((s16 *)(i_ptr + 1) + (i_ptr->vtx_count - 1)); } // 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++) { @@ -562,6 +583,7 @@ bool recomp_apply_cpu_skinning(BKModelUnk28List *arg0, BKVertexList *arg1, AnimM j++; } } + *vel = dst_vel; } @@ -826,23 +848,28 @@ RECOMP_PATCH BKModelBin *modelRender_draw(Gfx **gfx, Mtx **mtx, f32 position[3], } // @recomp Use higher precision vertex buffer when the model requires CPU skinning. + f32 *skinned_pos = NULL, *skinned_vel = NULL; 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, *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); - 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; + recomp_apply_cpu_skinning((u8 *)modelRenderModelBin + modelRenderModelBin->unk28, modelRendervertexList, D_8038371C, NULL, &skinned_pos, &skinned_vel); + } + // @recomp Apply skinning using the floats applied by the map model. + else if (sMapSkinningPosFloats != NULL) { + recomp_apply_cpu_skinning(NULL, modelRendervertexList, NULL, sMapSkinningPosFloats, &skinned_pos, &skinned_vel); + } + + if (skinned_pos != NULL) { + gEXSetVertexSegment((*gfx)++, G_EX_VERTEX_POSITION, G_EX_ENABLED, skinned_pos, &modelRendervertexList->vtx_18); + + if (skinned_vel != NULL) { + gEXSetVertexSegment((*gfx)++, G_EX_VERTEX_VELOCITY, G_EX_ENABLED, skinned_vel, &modelRendervertexList->vtx_18); } + + cur_model_uses_ex_vertex = TRUE; } mlMtxIdent(); @@ -874,7 +901,7 @@ RECOMP_PATCH BKModelBin *modelRender_draw(Gfx **gfx, Mtx **mtx, f32 position[3], 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) { + else if (cur_drawn_model_is_map && !cur_model_uses_ex_vertex) { gEXMatrixGroupDecomposedVerts((*gfx)++, cur_drawn_model_transform_id, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE); pushed_matrix_group = TRUE; }