From cdd25e5bf7b551b7954105a1751ecd62788c04c0 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Tue, 9 Sep 2025 22:14:30 -0400 Subject: [PATCH] Implement prop extension API, SpriteProp and ModelProp transform tagging --- patches/bk_api.h | 14 ++ patches/marker_extension_patches.c | 5 - patches/prop_extension_patches.c | 325 ++++++++++++++++++++++++++++- patches/prop_transform_tagging.c | 119 +++++++++++ patches/transform_ids.h | 3 + 5 files changed, 459 insertions(+), 7 deletions(-) diff --git a/patches/bk_api.h b/patches/bk_api.h index 4ddfc82..26e4721 100644 --- a/patches/bk_api.h +++ b/patches/bk_api.h @@ -5,6 +5,12 @@ #include "enums.h" #include "prop.h" +typedef enum { + EXTENSION_TYPE_MARKER, + EXTENSION_TYPE_PROP, +} ExtensionType; + +// ActorMarkers typedef u32 MarkerExtensionId; MarkerExtensionId bkrecomp_extend_marker(enum marker_e type, u32 size); @@ -13,4 +19,12 @@ MarkerExtensionId bkrecomp_extend_marker_all(u32 size); void* bkrecomp_get_extended_marker_data(ActorMarker* marker, MarkerExtensionId extension); u32 bkrecomp_get_marker_spawn_index(ActorMarker* marker); +// Props +typedef u32 PropExtensionId; + +PropExtensionId bkrecomp_extend_prop_all(u32 size); + +void *bkrecomp_get_extended_prop_data(Cube* cube, Prop* prop, PropExtensionId extension_id); +u32 bkrecomp_get_prop_spawn_index(Cube* cube, Prop* prop); + #endif diff --git a/patches/marker_extension_patches.c b/patches/marker_extension_patches.c index fdc968d..c9d4202 100644 --- a/patches/marker_extension_patches.c +++ b/patches/marker_extension_patches.c @@ -5,11 +5,6 @@ #include "object_extension_funcs.h" #include "bk_api.h" -typedef enum { - EXTENSION_TYPE_MARKER, - EXTENSION_TYPE_PROP, -} ExtensionType; - // Array of handles for ActorMarker instances. // Normally the game only has at most 0xE0 ActorMarker instances, but this is larger to account for mods increasing // the ActorMarker count. diff --git a/patches/prop_extension_patches.c b/patches/prop_extension_patches.c index c7bc2e5..4be27c1 100644 --- a/patches/prop_extension_patches.c +++ b/patches/prop_extension_patches.c @@ -5,15 +5,33 @@ #include "object_extension_funcs.h" #include "bk_api.h" #include "core2/coords.h" +#include "core2/file.h" // Max props per cube, limited by cube->prop2Cnt which is only 6 bits. #define CUBE_MAX_PROPS 63 // Hardcoded cube limit, TODO implement a better solution that doesn't involve dedicating this much memory and allows for larger cube counts. #define MAX_CUBES 4000 -u32 prop_handles[MAX_CUBES][CUBE_MAX_PROPS]; + +typedef struct { + u32 prop_handles[CUBE_MAX_PROPS]; +} CubeHandle; + +CubeHandle all_cube_handles[MAX_CUBES]; +CubeHandle cube_3C_handle; +CubeHandle cube_40_handle; + +extern s32 *D_8036A9E0; +extern s32 D_80383450[0x40]; void vtxList_getBounds_s32(BKVertexList *, s32[3], s32[3]); enum map_e map_get(void); +void code7AF80_initCubeFromFile(File *file_ptr, Cube *cube); +bool func_80305D14(void); +void func_80305CD8(s32 idx, s32 count); +s32 func_803058C0(f32 arg0); +void code_A5BC0_initCubePropActorProp(Cube*); +void func_80332B2C(ActorMarker * arg0); +void bitfield_free(s32 *arg0); extern struct { Cube *cubes; @@ -78,8 +96,311 @@ RECOMP_PATCH void mapModel_getCubeBounds(s32 min[3], s32 max[3]) { u32 stride1 = stride0 * width1; u32 cubeCnt = stride1 * width2; - recomp_printf("Cube count for map %d: %u\n", map_get(), cubeCnt); if (cubeCnt > MAX_CUBES) { recomp_abort("Cube count too high\n"); } } + +CubeHandle* get_cube_handle(Cube *cube) { + if (cube == sCubeList.unk3C) { + return &cube_3C_handle; + } + else if (cube == sCubeList.unk40) { + return &cube_40_handle; + } + else { + s32 cube_index = cube - sCubeList.cubes; + if (cube_index < 0 || cube_index >= sCubeList.cubeCnt) { + recomp_printf("Invalid cube index %d\n", cube_index); + recomp_abort("Got invalid cube\n"); + } + return &all_cube_handles[cube_index]; + } +} + +// Alloc prop handles: +// __codeA5BC0_initProp2Ptr +// code7AF80_initCubeFromFile (covered by __code7AF80_initCubeFromFile) + +// Delete prop handles: +// func_8032D9C0 +// cube_free + +// Swap prop handles: +// __cube_sort + +// Reset all prop handles candidates: +// cubeList_free + +// @recomp Patched to create a handle for the new prop. +RECOMP_PATCH Prop *__codeA5BC0_initProp2Ptr(Cube *cube) { + Prop *sp1C; + + if (cube->prop2Ptr != NULL) { + cube->prop2Cnt++; + cube->prop2Ptr = realloc(cube->prop2Ptr, cube->prop2Cnt * sizeof(Prop)); + } else { + cube->prop2Cnt = 1; + cube->prop2Ptr = malloc(sizeof(Prop)); + } + sp1C = &cube->prop2Ptr[cube->prop2Cnt-1]; + sp1C->is_actor = FALSE; + code_A5BC0_initCubePropActorProp(cube); + + // @recomp Get the cube's prop handle list. + CubeHandle* cube_handle = get_cube_handle(cube); + u32 prop_index = cube->prop2Cnt - 1; + // TODO prop subtypes. + cube_handle->prop_handles[prop_index] = recomp_create_object_data(EXTENSION_TYPE_PROP, 0); + + return sp1C; +} + +// @recomp Patched to initialize prop handles after a cube has been loaded. +RECOMP_PATCH void __code7AF80_initCubeFromFile(Cube *cube, File* file_ptr) { + s32 pad[3]; + + // @recomp Get the cube's prop handle list. + CubeHandle* cube_handle = get_cube_handle(cube); + + // @recomp Free any prop handles that the cube may already have, as this initialization resets the cube's prop list. + for (u32 i = 0; i < cube->prop2Cnt; i++) { + recomp_destroy_object_data(EXTENSION_TYPE_PROP, cube_handle->prop_handles[i]); + cube_handle->prop_handles[i] = 0; + } + + while(!file_isNextByteExpected(file_ptr, 1)) { + if (file_getNWords_ifExpected(file_ptr, 0, pad, 3)) { + file_getNWords(file_ptr, pad, 3); + } else if (!file_getNWords_ifExpected(file_ptr, 2, pad, 3) && file_isNextByteExpected(file_ptr, 3) + ) { + code7AF80_initCubeFromFile(file_ptr, cube); + } + } + + // @recomp Initialize prop handles after loading the cube. + for (u32 i = 0; i < cube->prop2Cnt; i++) { + // TODO prop subtypes. + cube_handle->prop_handles[i] = recomp_create_object_data(EXTENSION_TYPE_PROP, 0); + } +} + +// @recomp Patched to delete the handle for the deleted prop and shift the remaining prop handles. +RECOMP_PATCH s32 func_8032D9C0(Cube *cube, Prop* prop){ + s32 sp24; + s32 tmp; + + // @recomp Get the cube's prop handle list. + CubeHandle* cube_handle = get_cube_handle(cube); + + sp24 = 0; + if(cube->prop2Cnt != 0){ + // @recomp Delete the handle for the deleted prop. + u32 prop_index = prop - cube->prop2Ptr; + + sp24 = prop->is_3d; + if(func_80305D14()){ + func_80305CD8(func_803058C0(prop->unk4[1]), -1); + } + + // @recomp Destroy the prop's extension data. + recomp_destroy_object_data(EXTENSION_TYPE_PROP, cube_handle->prop_handles[prop_index]); + + if((prop - cube->prop2Ptr) < (cube->prop2Cnt - 1)){ + memcpy(prop, prop + 1, (s32)(&cube->prop2Ptr[cube->prop2Cnt-1]) - (s32)(prop)); + // @recomp Shift the prop handles back by 1 to remove the gap. + for (u32 cur_prop_index = prop_index; cur_prop_index < cube->prop2Cnt - 1; cur_prop_index++) { + cube_handle->prop_handles[cur_prop_index] = cube_handle->prop_handles[cur_prop_index + 1]; + } + // @recomp Clear the handle at the end of the list to account for the list decreasing in size. + cube_handle->prop_handles[cube->prop2Cnt - 1] = 0; + } + cube->prop2Cnt--; + if(cube->prop2Cnt){ + cube->prop2Ptr = realloc(cube->prop2Ptr, cube->prop2Cnt*sizeof(Prop)); + code_A5BC0_initCubePropActorProp(cube); + }else{ + free(cube->prop2Ptr); + cube->prop2Ptr = NULL; + } + return sp24; + } + return 0; +} + +// @recomp Patched to clear all prop handles for a cube when freeing a cube. +RECOMP_PATCH void cube_free(Cube *cube){ + Prop *iProp; + + // @recomp Get the cube's prop handle list. + CubeHandle* cube_handle = get_cube_handle(cube); + + // @recomp Delete and clear the cube's prop handles. + for (u32 prop_index = 0; prop_index < cube->prop2Cnt; prop_index++) { + recomp_destroy_object_data(EXTENSION_TYPE_PROP, cube_handle->prop_handles[prop_index]); + cube_handle->prop_handles[prop_index] = 0; + } + + if(cube->prop2Ptr){ + for(iProp = cube->prop2Ptr; iProp < cube->prop2Ptr +cube->prop2Cnt; iProp++){ + if(iProp->is_actor){ + func_80332B2C(iProp->actorProp.marker); + } + } + free(cube->prop2Ptr); + cube->prop2Ptr = NULL; + } + if(cube->prop1Ptr){ + free(cube->prop1Ptr); + cube->prop1Ptr = NULL; + } + cube->prop2Cnt = 0; + cube->prop1Cnt = 0; + cube->unk0_4 = 0; +} + +// @recomp Patched to swap prop handles when sorting a cube's props. +RECOMP_PATCH void __cube_sort(Cube *cube, bool global) { + s32 ref_position[3]; + Prop *var_v1; + Prop *start_prop; + s32 temp_a2; + Prop *var_t1; + Prop * var_a3; + Prop * var_t0; + s32 i; + Prop *new_var; + + // @recomp Get the cube's prop handle list. + CubeHandle* cube_handle = get_cube_handle(cube); + + if (cube->prop2Cnt >= 2) { + if (global == 0) { + viewport_getPosition_vec3w(ref_position); //distance from viewport + } else { + ref_position[0] = 0; + ref_position[1] = 0; + ref_position[2] = 0; + } + + //calculate prop distances + new_var = var_v1 = cube->prop2Ptr; + for(i = 0; i < cube->prop2Cnt; var_v1++, i++){ + D_80383450[i] = (var_v1->actorProp.x - ref_position[0])*(var_v1->actorProp.x - ref_position[0]) + + (var_v1->actorProp.y - ref_position[1])* (var_v1->actorProp.y - ref_position[1]) + + (var_v1->actorProp.z - ref_position[2])* (var_v1->actorProp.z - ref_position[2]); + } + + //sort prop list + start_prop = cube->prop2Ptr; + var_t0 = cube->prop2Ptr + (cube->prop2Cnt - 1); + do { + new_var = start_prop; + var_t1 = var_t0; + start_prop = NULL; + var_v1 = new_var; + i = (new_var - cube->prop2Ptr); + while(var_v1 < var_t1){ + if(D_80383450[i] < D_80383450[i + 1]){ + var_t0 = var_v1 + 1; + if (start_prop != 0) { + var_t0 = var_v1; + } else { + start_prop = (var_v1 == cube->prop2Ptr) ? var_v1 : var_v1 - 1; + } + + //swap_distances + temp_a2 = D_80383450[i]; + D_80383450[i] = D_80383450[i + 1]; + D_80383450[i + 1] = temp_a2; + + //swap_props + temp_a2 = ((s32*)(&var_v1[0]))[0]; + ((s32*)(&var_v1[0]))[0] = ((s32*)(&var_v1[1]))[0]; + ((s32*)(&var_v1[1]))[0] = temp_a2; + + temp_a2 = ((s32*)(&var_v1[0]))[1]; + ((s32*)(&var_v1[0]))[1] = ((s32*)(&var_v1[1]))[1]; + ((s32*)(&var_v1[1]))[1] = temp_a2; + + temp_a2 = ((s32*)(&var_v1[0]))[2]; + ((s32*)(&var_v1[0]))[2] = ((s32*)(&var_v1[1]))[2]; + ((s32*)(&var_v1[1]))[2] = temp_a2; + + // @recomp Swap prop extension data handles. + u32 temp_handle = cube_handle->prop_handles[i]; + cube_handle->prop_handles[i] = cube_handle->prop_handles[i + 1]; + cube_handle->prop_handles[i + 1] = temp_handle; + } + + var_v1++; + i++; + } + } while (start_prop != NULL); + code_A5BC0_initCubePropActorProp(cube); + } +} + +// @recomp Patched to reset the prop handle list. +RECOMP_PATCH void cubeList_free(){ + Cube *iCube; + + for(iCube = sCubeList.cubes; iCube < sCubeList.cubes + sCubeList.cubeCnt; iCube++){ + cube_free(iCube); + } + free(sCubeList.cubes); + + if(sCubeList.unk3C){ + cube_free(sCubeList.unk3C); + free(sCubeList.unk3C); + } + + if(sCubeList.unk40){ + cube_free(sCubeList.unk40); + free(sCubeList.unk40); + } + bitfield_free(D_8036A9E0); + D_8036A9E0 = NULL; + + // @recomp Reset the prop handle list. + recomp_clear_all_object_data(EXTENSION_TYPE_PROP); +} + +RECOMP_EXPORT PropExtensionId bkrecomp_extend_prop_all(u32 size) { + return recomp_register_object_extension_generic(EXTENSION_TYPE_PROP, size); +} + +RECOMP_EXPORT void *bkrecomp_get_extended_prop_data(Cube* cube, Prop* prop, PropExtensionId extension_id) { + CubeHandle* cube_handle = get_cube_handle(cube); + s32 prop_index = prop - cube->prop2Ptr; + if (prop_index < 0 || prop_index >= cube->prop2Cnt) { + recomp_printf("bkrecomp_get_extended_prop_data: Invalid Cube/Prop pair\n"); + recomp_abort("Fatal error in mod - Invalid call to bkrecomp_get_extended_prop_data"); + } + + // TODO prop subtypes. + void* data = recomp_get_object_data(EXTENSION_TYPE_PROP, 0, cube_handle->prop_handles[prop_index], extension_id); + if (data == NULL) { + recomp_printf("bkrecomp_get_extended_prop_data: Invalid extension id 0x%08X\n", extension_id); + recomp_abort("Fatal error in mod - Invalid call to bkrecomp_get_extended_prop_data"); + } + + return data; +} + +RECOMP_EXPORT u32 bkrecomp_get_prop_spawn_index(Cube* cube, Prop* prop) { + CubeHandle* cube_handle = get_cube_handle(cube); + s32 prop_index = prop - cube->prop2Ptr; + if (prop_index < 0 || prop_index >= cube->prop2Cnt) { + recomp_printf("bkrecomp_get_prop_spawn_index: Invalid Cube/Prop pair\n"); + recomp_abort("Fatal error in mod - Invalid call to bkrecomp_get_prop_spawn_index"); + } + + u32 spawn_index = recomp_get_object_spawn_index(EXTENSION_TYPE_PROP, cube_handle->prop_handles[prop_index]); + if (spawn_index == 0xFFFFFFFF) { + recomp_printf("bkrecomp_get_prop_spawn_index: Internal error\n"); + recomp_abort("Fatal error - Internal error in bkrecomp_get_prop_spawn_index"); + } + + return spawn_index; +} diff --git a/patches/prop_transform_tagging.c b/patches/prop_transform_tagging.c index 95435d1..8116b49 100644 --- a/patches/prop_transform_tagging.c +++ b/patches/prop_transform_tagging.c @@ -1,3 +1,122 @@ #include "patches.h" #include "bk_api.h" +#include "transform_ids.h" +#include "functions.h" +#include "core2/vla.h" +typedef union{ + struct{ + u32 pad31: 27; + u32 unk4: 1; + u32 pad3: 1; + u32 unk2: 1; + u32 unk1: 1; + u32 unk0: 1; + }; + u32 word; +} tmp_bitfield; + +extern vector(ActorMarker *) *D_80383550; +extern vector(ActorMarker *) *D_80383554; + +void propModelList_drawSprite(Gfx **, Mtx **, Vtx **, f32[3], f32, s32, Cube*,s32 ,s32, s32, s32, s32); +void propModelList_drawModel(Gfx **, Mtx **, Vtx **, f32[3], f32[3], f32, s32, Cube*); +void __marker_draw(ActorMarker *this, Gfx **gfx, Mtx **mtx, Vtx **vtx); +void __cube_sort(Cube *cube, bool global); +void func_8032CD60(Prop *); + +// @recomp Patched to add transform tagging when drawing sprite and model props. +RECOMP_PATCH void func_8032D510(Cube *cube, Gfx **gfx, Mtx **mtx, Vtx **vtx){ + Prop *iProp; + int i; + f32 position[3]; + f32 rotation[3]; + tmp_bitfield tmp_v0; + int iOffset; + ActorMarker **markerPtr; + + if(cube->prop2Cnt == 0 ) return; + + __cube_sort(cube, 0); + iOffset = 0; + for(i = 0; i < cube->prop2Cnt; i++){//L8032D5A0 + iOffset = i * 0xC; + iProp = (Prop *)((s32)cube->prop2Ptr + iOffset); + tmp_v0.word = *(u32 *)((s32)iProp + 0x8); + if(!tmp_v0.unk4){ + + }else{ + if(!tmp_v0.unk1){ + func_8032CD60(iProp); + } + tmp_v0.word = *(u32 *)((s32)iProp + 0x8); + if(tmp_v0.unk0){//actorProp; + if(iProp->actorProp.marker->unk40_22){ + markerPtr = (ActorMarker **)vector_pushBackNew(&D_80383550); + *markerPtr = iProp->actorProp.marker; + } + else if(iProp->actorProp.marker->unk40_19){ + markerPtr = (ActorMarker **)vector_pushBackNew(&D_80383554); + *markerPtr = iProp->actorProp.marker; + } + else{ + __marker_draw(iProp->actorProp.marker, gfx, mtx, vtx); + }//L8032D62C + } + else{//L8032D640 + // @recomp Calculate the base transform ID for the prop. + u32 spawn_index = bkrecomp_get_prop_spawn_index(cube, iProp); + u32 base_transform_id = spawn_index * PROP_TRANSFORM_ID_COUNT + PROP_TRANSFORM_ID_START; + + position[0] = (f32)iProp->modelProp.position[0]; + position[1] = (f32)iProp->modelProp.position[1]; + position[2] = (f32)iProp->modelProp.position[2]; + if(iProp->is_3d){ + rotation[0] = 0.0f; + rotation[1] = (f32)((s32)iProp->modelProp.yaw*2); + rotation[2] = (f32)((s32)iProp->modelProp.roll*2); + + // @recomp Set the model render transform ID before drawing the model. + cur_drawn_model_transform_id = base_transform_id; + + propModelList_drawModel(gfx, mtx, vtx, + position, rotation, (f32)iProp->modelProp.scale/100.0, + iProp->modelProp.model_index, cube + ); + + + // @recomp Clear the model render transform ID after drawing the model. + cur_drawn_model_transform_id = 0; + } + else{//L8032D72C + // @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. + // TODO rotation temporarily disabled for now, reenable it when RT64 decomposition is improved. + gEXMatrixGroupDecomposed((*gfx)++, base_transform_id, G_EX_PUSH, G_MTX_MODELVIEW, + G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_SKIP, 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); + + // @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; + + propModelList_drawSprite(gfx, mtx, vtx, + position, (f32)iProp->spriteProp.scale/100.0, iProp->spriteProp.sprite_index, cube, + iProp->spriteProp.r, iProp->spriteProp.b, iProp->spriteProp.g, + iProp->spriteProp.mirrored, iProp->spriteProp.frame + ); + + // @recomp Pop the sprite's matrix group. + gEXPopMatrixGroup((*gfx)++, G_MTX_MODELVIEW); + + // @recomp Clear the model render transform ID after drawing the sprite. + cur_drawn_model_transform_id = 0; + } + }//L8032D7C4 + } + iOffset+=0xC; + }//L8032D7D4 +} diff --git a/patches/transform_ids.h b/patches/transform_ids.h index bb6a5d5..dcd6ce5 100644 --- a/patches/transform_ids.h +++ b/patches/transform_ids.h @@ -10,6 +10,9 @@ #define BANJO_TRANSFORM_ID_START 0x01000000 #define MARKER_TRANSFORM_ID_START (BANJO_TRANSFORM_ID_START + MARKER_TRANSFORM_ID_COUNT) +#define PROP_TRANSFORM_ID_COUNT 256 +#define PROP_TRANSFORM_ID_START 0x01200000 + extern u32 cur_drawn_model_transform_id; #endif