diff options
| author | Adam Bird <Archez@users.noreply.github.com> | 2023-11-23 09:07:30 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-11-23 09:07:30 -0500 |
| commit | f4e4545180c7fb643f8a2012a36c6346c495f510 (patch) | |
| tree | 54aef7539436722902517c6e9b121d92c9c9b02a /soh/src | |
| parent | 0ddb0711ad1b46e434e1587003f1c749b9a222e7 (diff) | |
Re-implement King Dodongo's Lava texture effects (#3434)
* fix alt backgrounds not always loading
* include gfx lookup with the original unload
* Add hook for alt toggle
* handle cpu modified texture for kd lava
* malloc array instead of illegal initialize
Diffstat (limited to 'soh/src')
| -rw-r--r-- | soh/src/code/z_room.c | 5 | ||||
| -rw-r--r-- | soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c | 228 |
2 files changed, 203 insertions, 30 deletions
diff --git a/soh/src/code/z_room.c b/soh/src/code/z_room.c index ccbc93d0d..772978f09 100644 --- a/soh/src/code/z_room.c +++ b/soh/src/code/z_room.c @@ -277,6 +277,11 @@ void func_8009638C(Gfx** displayList, void* source, void* tlut, u16 width, u16 h bg->b.imagePal = 0; bg->b.imageFlip = CVarGetInteger("gMirroredWorld", 0) ? G_BG_FLAG_FLIPS : 0; + // When an alt resource exists for the background, we need to unload the original asset + // to clear the cache so the alt asset will be loaded instead + // OTRTODO: If Alt loading over original cache is fixed, this line can most likely be removed + ResourceMgr_UnloadOriginalWhenAltExists((char*) source); + if (ResourceMgr_ResourceIsBackground((char*) source)) { char* blob = (char*) ResourceGetDataByName((char *) source); swapAndConvertJPEG(blob); diff --git a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c index e90e36e80..be9778c6c 100644 --- a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c +++ b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c @@ -5,6 +5,14 @@ #include "scenes/dungeons/ddan_boss/ddan_boss_room_1.h" #include "soh/frame_interpolation.h" #include "soh/Enhancements/boss-rush/BossRush.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" + +#include <stdlib.h> // malloc +#include <string.h> // memcpy + +// OTRTODO: Replace usage of this method when we can clear the cache +// for a single texture without the need of a DL opcode in the render code +void gfx_texture_cache_clear(); #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) @@ -59,6 +67,13 @@ static u8 sMaskTex8x8[8 * 8] = { { 0 } }; static u8 sMaskTex8x32[8 * 32] = { { 0 } }; static u8 sMaskTexLava[32 * 64] = { { 0 } }; +static u32* sLavaFloorModifiedTexRaw = NULL; +static u32* sLavaWavyTexRaw = NULL; +static u16 sLavaFloorModifiedTex[4096]; +static u16 sLavaWavyTex[2048]; + +static u8 hasRegisteredBlendedHook = 0; + static InitChainEntry sInitChain[] = { ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE), ICHAIN_S8(naviEnemyId, 0x0C, ICHAIN_CONTINUE), @@ -66,6 +81,70 @@ static InitChainEntry sInitChain[] = { ICHAIN_F32(targetArrowOffset, 8200.0f, ICHAIN_STOP), }; +void BossDodongo_RegisterBlendedLavaTextureUpdate() { + // Not in scene so there is nothing to do + if (gPlayState == NULL || gPlayState->sceneNum != SCENE_DODONGOS_CAVERN_BOSS) { + return; + } + + // Free old textures + if (sLavaFloorModifiedTexRaw != NULL) { + free(sLavaFloorModifiedTexRaw); + sLavaFloorModifiedTexRaw = NULL; + } + if (sLavaWavyTexRaw != NULL) { + free(sLavaWavyTexRaw); + sLavaWavyTexRaw = NULL; + } + + // Unload original textures to bypass cache result for lookups + ResourceMgr_UnloadOriginalWhenAltExists(sLavaFloorLavaTex); + ResourceMgr_UnloadOriginalWhenAltExists(sLavaFloorRockTex); + ResourceMgr_UnloadOriginalWhenAltExists(gDodongosCavernBossLavaFloorTex); + + // When the texture is HD (raw) we need to work with u32 values for RGBA32 + // Otherwise the original asset is u16 for RGBA16 + if (ResourceMgr_TexIsRaw(sLavaFloorLavaTex)) { + u32* lavaTex = ResourceGetDataByName(sLavaFloorLavaTex); + size_t lavaSize = ResourceGetSizeByName(sLavaFloorLavaTex); + size_t floorSize = ResourceGetSizeByName(gDodongosCavernBossLavaFloorTex); + + sLavaFloorModifiedTexRaw = malloc(lavaSize); + sLavaWavyTexRaw = malloc(floorSize); + + memcpy(sLavaFloorModifiedTexRaw, lavaTex, lavaSize); + + // When KD is dead, just immediately copy the rock texture + if (Flags_GetClear(gPlayState, gPlayState->roomCtx.curRoom.num)) { + u32* rockTex = ResourceGetDataByName(sLavaFloorRockTex); + size_t rockSize = ResourceGetSizeByName(sLavaFloorRockTex); + memcpy(sLavaFloorModifiedTexRaw, rockTex, rockSize); + } + + memcpy(sLavaWavyTexRaw, sLavaFloorModifiedTexRaw, floorSize); + + // Register the blended effect for the raw texture + Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, sLavaWavyTexRaw); + } else { + u16* lavaTex = ResourceGetDataByName(sLavaFloorLavaTex); + memcpy(sLavaFloorModifiedTex, lavaTex, sizeof(sLavaFloorModifiedTex)); + + // When KD is dead, just immediately copy the rock texture + if (Flags_GetClear(gPlayState, gPlayState->roomCtx.curRoom.num)) { + u16* rockTex = ResourceGetDataByName(sLavaFloorRockTex); + size_t rockSize = ResourceGetSizeByName(sLavaFloorRockTex); + memcpy(sLavaFloorModifiedTex, rockTex, rockSize); + } + + // Register the blended effect for the non-raw texture + memcpy(sLavaWavyTex, sLavaFloorModifiedTex, sizeof(sLavaWavyTex)); + + Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, sLavaWavyTex); + } + + gfx_texture_cache_clear(); +} + void func_808C12C4(u8* arg1, s16 arg2) { if (arg2[arg1] != 0) { sMaskTex8x16[arg2 / 2] = 1; @@ -86,12 +165,51 @@ void func_808C12C4(u8* arg1, s16 arg2) { } } -void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) { - arg0 = GetResourceDataByNameHandlingMQ(arg0); - floorTex = ResourceGetDataByName(floorTex); +// Same as func_808C1554 but works with u32 values for RGBA32 raw textures +void func_808C1554_Raw(void* arg0, void* floorTex, s32 arg2, f32 arg3) { + u16 width = ResourceGetTexWidthByName(arg0); + s32 size = ResourceGetTexHeightByName(arg0) * width; + + u32* temp_s3 = sLavaWavyTexRaw; + u32* temp_s1 = sLavaFloorModifiedTexRaw; + s32 i; + s32 i2; + u32* sp54 = malloc(size * sizeof(u32)); // Match the size for lava floor tex + s32 temp; + s32 temp2; + + // Multiplier is used to try to scale the wavy effect to match the scale of the HD texture + // Applying sqrt(multiplier) to arg3 is to control how many pixels move left/right for the selected row + // Applying to arg2 and M_PI help to space out the wave effect + // It's not perfect but close enough + u16 multiplier = width / 32; + + for (i = 0; i < size; i += width) { + temp = sinf((((i / width) + (s32)(((arg2 * multiplier) * 50.0f) / 100.0f)) & (width - 1)) * (M_PI / (16 * multiplier))) * (arg3 * sqrt(multiplier)); + for (i2 = 0; i2 < width; i2++) { + sp54[i + ((temp + i2) & (width - 1))] = temp_s1[i + i2]; + } + } + for (i = 0; i < width; i++) { + temp = sinf(((i + (s32)(((arg2 * multiplier) * 80.0f) / 100.0f)) & (width - 1)) * (M_PI / (16 * multiplier))) * (arg3 * sqrt(multiplier)); + temp *= width; + for (i2 = 0; i2 < size; i2 += width) { + temp2 = (temp + i2) & (size - 1); + temp_s3[i + temp2] = sp54[i + i2]; + } + } + + free(sp54); + + // Need to clear the cache after updating sLavaWavyTexRaw + gfx_texture_cache_clear(); +} - u16* temp_s3 = SEGMENTED_TO_VIRTUAL(arg0); - u16* temp_s1 = SEGMENTED_TO_VIRTUAL(floorTex); +// Modified to support CPU modified texture with the resource system +// Used for the original non-raw asset working with u16 values +void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) { + u16* temp_s3 = sLavaWavyTex; + u16* temp_s1 = sLavaFloorModifiedTex; s16 i; s16 i2; u16 sp54[2048]; @@ -112,6 +230,9 @@ void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) { temp_s3[i + temp2] = sp54[i + i2]; } } + + // Need to clear the cache after updating sLavaWavyTex + gfx_texture_cache_clear(); } void func_808C17C8(PlayState* play, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3, f32 arg4, s16 arg5) { @@ -187,27 +308,21 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) { Collider_SetJntSph(play, &this->collider, &this->actor, &sJntSphInit, this->items); if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { // KD is dead - u16* LavaFloorTex = ResourceGetDataByName(gDodongosCavernBossLavaFloorTex); - u16* LavaFloorRockTex = ResourceGetDataByName(sLavaFloorRockTex); - temp_s1_3 = SEGMENTED_TO_VIRTUAL(LavaFloorTex); - temp_s2 = SEGMENTED_TO_VIRTUAL(LavaFloorRockTex); + // SOH [General] + // Applying the "cooled off" lava rock CPU modified texture for re-visiting the scene + // is now handled by BossDodongo_RegisterBlendedLavaTextureUpdate below + Actor_Kill(&this->actor); Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, -890.0f, -1523.76f, -3304.0f, 0, 0, 0, WARP_DUNGEON_CHILD); Actor_Spawn(&play->actorCtx, play, ACTOR_BG_BREAKWALL, -890.0f, -1523.76f, -3304.0f, 0, 0, 0, 0x6000, true); Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, -690.0f, -1523.76f, -3304.0f, 0, 0, 0, 0, true); - - for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) { - sMaskTexLava[i] = 1; - } - } else { - for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) { - sMaskTexLava[i] = 0; - } } this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; + // #region SOH [General] + // Init mask values for all blended textures for (int i = 0; i < ARRAY_COUNT(sMaskTex8x16); i++) { sMaskTex8x16[i] = 0; } @@ -223,6 +338,12 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) { for (int i = 0; i < ARRAY_COUNT(sMaskTex32x16); i++) { sMaskTex32x16[i] = 0; } + // Set all true for the lava as it will always replace the scene texture + for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) { + sMaskTexLava[i] = 1; + } + + // Register all blended textures Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_015890, sMaskTex8x16, NULL); Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_017210, sMaskTex8x32, NULL); Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_015D90, sMaskTex16x16, NULL); @@ -234,10 +355,14 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) { Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016990, sMaskTex32x16, NULL); Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016E10, sMaskTex32x16, NULL); - // OTRTODO: This is causing OOB memory reads with HD assets - // commenting this out means the lava will stay lava even after beating king d - // - // Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, sLavaFloorRockTex); + BossDodongo_RegisterBlendedLavaTextureUpdate(); + + // Register alt listener to update the blended lava for the replacement texture based on alt path + if (!hasRegisteredBlendedHook) { + GameInteractor_RegisterOnAssetAltChange(BossDodongo_RegisterBlendedLavaTextureUpdate); + hasRegisteredBlendedHook = 1; + } + // #endregion } void BossDodongo_Destroy(Actor* thisx, PlayState* play) { @@ -1014,21 +1139,64 @@ void BossDodongo_Update(Actor* thisx, PlayState* play2) { } } - // TODO The lave floor bubbles with an effect that modifies the texture. This needs to be recreated shader-side. - //func_808C1554(gDodongosCavernBossLavaFloorTex, sLavaFloorLavaTex, this->unk_19E, this->unk_224); + // The lava bubbles with a wavy effect as a CPU modified texture + // This has been done by maintaining copied/modified texture values in the actor code + // The "cooling off" effect for the lava is pre-applied to the lava texture before applying + // the wavy effect. Since this is two effects and closely related to the actor, I've opted + // to handle them here rather than as a shader effect. + // + // Apply the corresponding wavy effect based on the texture being raw or not + if (ResourceMgr_TexIsRaw(gDodongosCavernBossLavaFloorTex)) { + func_808C1554_Raw(gDodongosCavernBossLavaFloorTex, sLavaFloorLavaTex, this->unk_19E, this->unk_224); + } else { + func_808C1554(gDodongosCavernBossLavaFloorTex, sLavaFloorLavaTex, this->unk_19E, this->unk_224); + } } + // Apply the "cooling off" effect for the lava if (this->unk_1C6 != 0) { - u16* ptr1 = ResourceGetDataByName(sLavaFloorLavaTex); - u16* ptr2 = ResourceGetDataByName(sLavaFloorRockTex); - s16 i2; + // Similar to above, the cooling off effect is a CPU modified texture effect + // Apply corresponding to the texture being raw or not + if (ResourceMgr_TexIsRaw(sLavaFloorRockTex)) { + u32* ptr1 = sLavaFloorModifiedTexRaw; + u32* ptr2 = ResourceGetDataByName(sLavaFloorRockTex); + u16 width = ResourceGetTexWidthByName(sLavaFloorRockTex); + u16 height = ResourceGetTexHeightByName(sLavaFloorRockTex); + s16 i2; + + // Get the scale based on the original texture size + u16 widthScale = width / 32; + u16 heightScale = height / 64; + u32 size = width * height; + + for (i2 = 0; i2 < 20; i2++) { + s16 new_var = this->unk_1C2 & 0x7FF; + // Compute the index to a scaled position (scaling pseudo x,y as a 1D value) + s32 indexStart = ((new_var % 32) * widthScale) + ((new_var / 32) * width * heightScale); + + // From the starting index, apply extra pixels right/down based on the scale + for (size_t j = 0; j < heightScale; j++) { + for (size_t i3 = 0; i3 < widthScale; i3++) { + s32 scaledIndex = (indexStart + i3 + (j * width)) & (size - 1); + ptr1[scaledIndex] = ptr2[scaledIndex]; + } + } + + this->unk_1C2 += 37; + } + } else { + u16* ptr1 = sLavaFloorModifiedTex; + u16* ptr2 = ResourceGetDataByName(sLavaFloorRockTex); + s16 i2; - for (i2 = 0; i2 < 20; i2++) { - s16 new_var = this->unk_1C2 & 0x7FF; + for (i2 = 0; i2 < 20; i2++) { + s16 new_var = this->unk_1C2 & 0x7FF; - sMaskTexLava[new_var] = 1; - this->unk_1C2 += 37; + ptr1[new_var] = ptr2[new_var]; + this->unk_1C2 += 37; + } } + Math_SmoothStepToF(&this->unk_224, 0.0f, 1.0f, 0.01f, 0.0f); } |
