From 3f156f32df2b37a17e69a4a20d1281e548ce8a9c Mon Sep 17 00:00:00 2001 From: Yanis <35189056+Yanis002@users.noreply.github.com> Date: Mon, 6 Jan 2025 00:17:24 +0100 Subject: [PATCH] Animated Materials from Majora's Mask (#160) * animated materials * fix build errors with CI * fix issues in scene when disabled --- include/animated_materials.h | 84 +++++ include/config/config_game.h | 5 + include/helpers.h | 2 + include/macros.h | 1 + include/ultra64/ultratypes.h | 2 + include/z64play.h | 10 +- include/z64scene.h | 20 +- spec | 1 + src/audio/general.c | 2 - src/code/animated_materials.c | 399 ++++++++++++++++++++ src/code/helpers.c | 54 +++ src/code/z_play.c | 7 + src/code/z_room.c | 6 + src/code/z_scene.c | 7 + src/code/z_scene_table.c | 89 ++--- src/overlays/gamestates/ovl_title/z_title.c | 2 +- 16 files changed, 624 insertions(+), 67 deletions(-) create mode 100644 include/animated_materials.h create mode 100644 src/code/animated_materials.c diff --git a/include/animated_materials.h b/include/animated_materials.h new file mode 100644 index 0000000000..01ceaa60ea --- /dev/null +++ b/include/animated_materials.h @@ -0,0 +1,84 @@ +#ifndef ANIMATED_MATERIALS_H +#define ANIMATED_MATERIALS_H + +#include "ultra64.h" + +typedef enum AnimatedMatType { + /* 0 */ ANIM_MAT_TYPE_TEX_SCROLL, + /* 1 */ ANIM_MAT_TYPE_TWO_TEX_SCROLL, + /* 2 */ ANIM_MAT_TYPE_COLOR, + /* 3 */ ANIM_MAT_TYPE_COLOR_LERP, + /* 4 */ ANIM_MAT_TYPE_COLOR_NON_LINEAR_INTERP, + /* 5 */ ANIM_MAT_TYPE_TEX_CYCLE, + /* 6 */ ANIM_MAT_TYPE_MAX +} AnimatedMatType; + +typedef struct { + /* 0x0 */ u8 r; + /* 0x1 */ u8 g; + /* 0x2 */ u8 b; + /* 0x3 */ u8 a; + /* 0x4 */ u8 lodFrac; +} F3DPrimColor; // size = 0x5 + +typedef struct { + /* 0x0 */ u8 r; + /* 0x1 */ u8 g; + /* 0x2 */ u8 b; + /* 0x3 */ u8 a; +} F3DEnvColor; // size = 0x4 + +typedef struct { + /* 0x0 */ u16 keyFrameLength; + /* 0x2 */ u16 keyFrameCount; + /* 0x4 */ F3DPrimColor* primColors; + /* 0x8 */ F3DEnvColor* envColors; + /* 0xC */ u16* keyFrames; +} AnimatedMatColorParams; // size = 0x10 + +typedef struct { + /* 0x0 */ s8 xStep; + /* 0x1 */ s8 yStep; + /* 0x2 */ u8 width; + /* 0x3 */ u8 height; +} AnimatedMatTexScrollParams; // size = 0x4 + +typedef struct { + /* 0x0 */ u16 keyFrameLength; + /* 0x4 */ TexturePtr* textureList; + /* 0x8 */ u8* textureIndexList; +} AnimatedMatTexCycleParams; // size = 0xC + +typedef struct { + /* 0x0 */ s8 segment; + /* 0x2 */ AnimatedMatType type; + /* 0x4 */ void* params; +} AnimatedMaterial; // size = 0x8 + +struct PlayState; + +Gfx* AnimatedMat_TexScroll(struct PlayState* play, AnimatedMatTexScrollParams* params); +void AnimatedMat_DrawTexScroll(struct PlayState* play, s32 segment, void* params); +Gfx* AnimatedMat_TwoLayerTexScroll(struct PlayState* play, AnimatedMatTexScrollParams* params); +void AnimatedMat_DrawTwoTexScroll(struct PlayState* play, s32 segment, void* params); +void AnimatedMat_SetColor(struct PlayState* play, s32 segment, F3DPrimColor* primColorResult, F3DEnvColor* envColor); +void AnimatedMat_DrawColor(struct PlayState* play, s32 segment, void* params); +s32 AnimatedMat_Lerp(s32 min, s32 max, f32 norm); +void AnimatedMat_DrawColorLerp(struct PlayState* play, s32 segment, void* params); +void AnimatedMat_DrawColorNonLinearInterp(struct PlayState* play, s32 segment, void* params); +void AnimatedMat_DrawTexCycle(struct PlayState* play, s32 segment, void* params); +void AnimatedMat_DrawMain(struct PlayState* play, AnimatedMaterial* matAnim, f32 alphaRatio, u32 step, u32 flags); +void AnimatedMat_Draw(struct PlayState* play, AnimatedMaterial* matAnim); +void AnimatedMat_DrawOpa(struct PlayState* play, AnimatedMaterial* matAnim); +void AnimatedMat_DrawXlu(struct PlayState* play, AnimatedMaterial* matAnim); +void AnimatedMat_DrawAlpha(struct PlayState* play, AnimatedMaterial* matAnim, f32 alphaRatio); +void AnimatedMat_DrawAlphaOpa(struct PlayState* play, AnimatedMaterial* matAnim, f32 alphaRatio); +void AnimatedMat_DrawAlphaXlu(struct PlayState* play, AnimatedMaterial* matAnim, f32 alphaRatio); +void AnimatedMat_DrawStep(struct PlayState* play, AnimatedMaterial* matAnim, u32 step); +void AnimatedMat_DrawStepOpa(struct PlayState* play, AnimatedMaterial* matAnim, u32 step); +void AnimatedMat_DrawStepXlu(struct PlayState* play, AnimatedMaterial* matAnim, u32 step); +void AnimatedMat_DrawAlphaStep(struct PlayState* play, AnimatedMaterial* matAnim, f32 alphaRatio, u32 step); +void AnimatedMat_DrawAlphaStepOpa(struct PlayState* play, AnimatedMaterial* matAnim, f32 alphaRatio, u32 step); +void AnimatedMat_DrawAlphaStepXlu(struct PlayState* play, AnimatedMaterial* matAnim, f32 alphaRatio, u32 step); + +#endif diff --git a/include/config/config_game.h b/include/config/config_game.h index 170c1d2009..9be9c08fd9 100644 --- a/include/config/config_game.h +++ b/include/config/config_game.h @@ -69,4 +69,9 @@ // Make `Camera_KeepOn4`'s `case 9`, mimic how getting an item looks in Majora's Mask (Perspective from above) #define MM_GETITEM_CAM false +/** + * Enable Animated Materials (from Majora's Mask) + */ +#define ENABLE_ANIMATED_MATERIALS true + #endif diff --git a/include/helpers.h b/include/helpers.h index c9f1a52d5a..0c4cbc9e5b 100644 --- a/include/helpers.h +++ b/include/helpers.h @@ -12,5 +12,7 @@ void Helpers_LoadMapSelect(GameState* gameState); void Helpers_SetView(View* view, f32 eyeX, f32 eyeY, f32 eyeZ); void Helpers_InitSkybox(GameState* gameState, EnvironmentContext* envCtx, SkyboxContext* skyboxCtx, s16 skyboxId); void Helpers_DrawSkybox(GameState* gameState, View* view, EnvironmentContext* envCtx, SkyboxContext* skyboxCtx, s16 skyboxId, f32 eyeY, f32 angleIncrement); +f32 Helpers_LagrangeInterp(s32 n, f32 x[], f32 fx[], f32 xp); +u8 Helpers_LagrangeInterpColor(s32 n, f32 x[], f32 fx[], f32 xp); #endif diff --git a/include/macros.h b/include/macros.h index 9bd72bda5f..da78aa7502 100644 --- a/include/macros.h +++ b/include/macros.h @@ -24,6 +24,7 @@ #define VIRTUAL_TO_PHYSICAL(addr) (uintptr_t)((u8*)(addr) - 0x80000000) #define ABS(x) ((x) >= 0 ? (x) : -(x)) +#define ABS_ALT(x) ((x) < 0 ? -(x) : (x)) #define DECR(x) ((x) == 0 ? 0 : --(x)) #define CLAMP(x, min, max) ((x) < (min) ? (min) : (x) > (max) ? (max) : (x)) #define CLAMP_MAX(x, max) ((x) > (max) ? (max) : (x)) diff --git a/include/ultra64/ultratypes.h b/include/ultra64/ultratypes.h index 743e755ae9..180f7dc103 100644 --- a/include/ultra64/ultratypes.h +++ b/include/ultra64/ultratypes.h @@ -38,6 +38,8 @@ typedef union MtxF { }; } MtxF; +typedef void* TexturePtr; + #endif #endif diff --git a/include/z64play.h b/include/z64play.h index 310c4134c2..6a36fa1ed1 100644 --- a/include/z64play.h +++ b/include/z64play.h @@ -24,6 +24,8 @@ #include "z64sram.h" #include "z64transition.h" #include "z64view.h" +#include "animated_materials.h" +#include "config.h" union Color_RGBA8_u32; struct Player; @@ -96,23 +98,21 @@ typedef struct PlayState { /* 0x11E16 */ s16 unk_11E16; /* 0x11E18 */ s16 bgCoverAlpha; /* 0x11E1A */ s16 nextEntranceIndex; - /* 0x11E1C */ char unk_11E1C[0x40]; /* 0x11E5C */ s8 shootingGalleryStatus; /* 0x11E5D */ s8 bombchuBowlingStatus; // "bombchu_game_flag" /* 0x11E5E */ u8 transitionType; /* 0x11E60 */ CollisionCheckContext colChkCtx; /* 0x120FC */ u16 cutsceneFlags[20]; /* 0x12124 */ PreRender pauseBgPreRender; - /* 0x12174 */ char unk_12174[0x53]; /* 0x121C7 */ s8 unk_121C7; /* 0x121C8 */ TransitionContext transitionCtx; - /* 0x12418 */ char unk_12418[0x3]; /* 0x1241B */ u8 transitionMode; // "fbdemo_wipe_modem" /* 0x1241C */ TransitionFade transitionFadeFlash; // Transition fade instance which flashes screen, see R_TRANS_FADE_FLASH_ALPHA_STEP - /* 0x12428 */ char unk_12428[0x3]; /* 0x1242B */ u8 viewpoint; // toggleable camera setting by shops or player. Is also equal to the bgCamIndex + 1 /* 0x1242C */ SceneTableEntry* loadedScene; - /* 0x12430 */ char unk_12430[0xE8]; +#if ENABLE_ANIMATED_MATERIALS + AnimatedMaterial* sceneMaterialAnims; +#endif } PlayState; // size = 0x12518 #define GET_ACTIVE_CAM(play) ((play)->cameraPtrs[(play)->activeCamId]) diff --git a/include/z64scene.h b/include/z64scene.h index e5d3744c9b..7d4744bd6e 100644 --- a/include/z64scene.h +++ b/include/z64scene.h @@ -390,6 +390,14 @@ typedef struct SCmdOccPlaneCandList { } SCmdOccPlaneCandList; #endif +#if ENABLE_ANIMATED_MATERIALS +typedef struct { + /* 0x0 */ u8 code; + /* 0x1 */ u8 data1; + /* 0x4 */ void* segment; +} SCmdTextureAnimations; // size = 0x8 +#endif + typedef union SceneCmd { SCmdBase base; SCmdPlayerEntryList playerEntryList; @@ -420,6 +428,9 @@ typedef union SceneCmd { #if ENABLE_F3DEX3 SCmdOccPlaneCandList occPlaneCandList; #endif +#if ENABLE_ANIMATED_MATERIALS + SCmdTextureAnimations textureAnimations; +#endif } SceneCmd; // size = 0x8 typedef BAD_RETURN(s32) (*SceneCmdHandlerFunc)(struct PlayState*, SceneCmd*); @@ -545,7 +556,9 @@ typedef enum SceneDrawConfig { /* 50 */ SDC_FISHING_POND, /* 51 */ SDC_GANONS_TOWER_COLLAPSE_INTERIOR, /* 52 */ SDC_INSIDE_GANONS_CASTLE_COLLAPSE, - /* 53 */ SDC_MAX + /* 53 */ SDC_MAT_ANIM, + /* 54 */ SDC_MAT_ANIM_MANUAL_STEP, + /* 55 */ SDC_MAX } SceneDrawConfig; typedef void (*SceneDrawConfigFunc)(struct PlayState*); @@ -597,6 +610,7 @@ typedef enum SceneCommandTypeID { #if ENABLE_F3DEX3 SCENE_CMD_ID_OCC_PLANE_CAND_LIST, #endif + SCENE_CMD_ID_ANIMATED_MATERIAL_LIST, /* 0x1A */ SCENE_CMD_ID_MAX } SceneCommandTypeID; @@ -684,6 +698,10 @@ typedef enum SceneCommandTypeID { { SCENE_CMD_ID_OCC_PLANE_CAND_LIST, numPlanes, CMD_PTR(planeList) } #endif +#define SCENE_CMD_ANIMATED_MATERIAL_LIST(matAnimList) \ + { SCENE_CMD_ID_ANIMATED_MATERIAL_LIST, 0, CMD_PTR(matAnimList) } + + s32 Scene_ExecuteCommands(struct PlayState* play, SceneCmd* sceneCmd); void Scene_ResetTransitionActorList(struct GameState* state, TransitionActorList* transitionActors); void Scene_SetTransitionForNextEntrance(struct PlayState* play); diff --git a/spec b/spec index cefd81b5c0..28b254d29a 100644 --- a/spec +++ b/spec @@ -963,6 +963,7 @@ beginseg #endif include "$(BUILD_DIR)/src/code/rainbow.o" include "$(BUILD_DIR)/src/code/helpers.o" + include "$(BUILD_DIR)/src/code/animated_materials.o" endseg #if ENABLE_HACKER_DEBUG diff --git a/src/audio/general.c b/src/audio/general.c index de0875553f..a95ec453f4 100644 --- a/src/audio/general.c +++ b/src/audio/general.c @@ -2,8 +2,6 @@ #include "global.h" #include "versions.h" -#define ABS_ALT(x) ((x) < 0 ? -(x) : (x)) - #if !PLATFORM_N64 #define AUDIO_PRINTF osSyncPrintf #elif IDO_PRINTF_WORKAROUND diff --git a/src/code/animated_materials.c b/src/code/animated_materials.c new file mode 100644 index 0000000000..fbe802eb5d --- /dev/null +++ b/src/code/animated_materials.c @@ -0,0 +1,399 @@ +#include "animated_materials.h" +#include "global.h" +#include "z64.h" +#include "helpers.h" + +static s32 sMatAnimStep; +static u32 sMatAnimFlags; +static f32 sMatAnimAlphaRatio; + +/** + * Returns a pointer to a single layer texture scroll displaylist. + */ +Gfx* AnimatedMat_TexScroll(PlayState* play, AnimatedMatTexScrollParams* params) { + return Gfx_TexScroll(play->state.gfxCtx, params->xStep * sMatAnimStep, -(params->yStep * sMatAnimStep), + params->width, params->height); +} + +/** + * Animated Material Type 0: + * Scrolls a single layer texture using the provided `AnimatedMatTexScrollParams`. + */ +void AnimatedMat_DrawTexScroll(PlayState* play, s32 segment, void* params) { + AnimatedMatTexScrollParams* texScrollParams = (AnimatedMatTexScrollParams*)params; + Gfx* texScrollDList = AnimatedMat_TexScroll(play, texScrollParams); + + OPEN_DISPS(play->state.gfxCtx); + + if (sMatAnimFlags & 1) { + gSPSegment(POLY_OPA_DISP++, segment, texScrollDList); + } + + if (sMatAnimFlags & 2) { + gSPSegment(POLY_XLU_DISP++, segment, texScrollDList); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +/** + * Returns a pointer to a two layer texture scroll displaylist. + */ +Gfx* AnimatedMat_TwoLayerTexScroll(PlayState* play, AnimatedMatTexScrollParams* params) { + return Gfx_TwoTexScroll(play->state.gfxCtx, 0, params[0].xStep * sMatAnimStep, -(params[0].yStep * sMatAnimStep), + params[0].width, params[0].height, 1, params[1].xStep * sMatAnimStep, + -(params[1].yStep * sMatAnimStep), params[1].width, params[1].height); +} + +/** + * Animated Material Type 1: + * Scrolls a two layer texture using the provided `AnimatedMatTexScrollParams`. + */ +void AnimatedMat_DrawTwoTexScroll(PlayState* play, s32 segment, void* params) { + AnimatedMatTexScrollParams* texScrollParams = (AnimatedMatTexScrollParams*)params; + Gfx* texScrollDList = AnimatedMat_TwoLayerTexScroll(play, texScrollParams); + + OPEN_DISPS(play->state.gfxCtx); + + if (sMatAnimFlags & 1) { + gSPSegment(POLY_OPA_DISP++, segment, texScrollDList); + } + + if (sMatAnimFlags & 2) { + gSPSegment(POLY_XLU_DISP++, segment, texScrollDList); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +/** + * Generates a displaylist that sets the prim and env color, and stores it in the provided segment ID. + */ +void AnimatedMat_SetColor(PlayState* play, s32 segment, F3DPrimColor* primColorResult, F3DEnvColor* envColor) { + Gfx* gfx = GRAPH_ALLOC(play->state.gfxCtx, 3 * sizeof(Gfx)); + + OPEN_DISPS(play->state.gfxCtx); + + // clang-format off + if (sMatAnimFlags & 1) { gSPSegment(POLY_OPA_DISP++, segment, gfx); } + if (sMatAnimFlags & 2) { gSPSegment(POLY_XLU_DISP++, segment, gfx); } + // clang-format on + + gDPSetPrimColor(gfx++, 0, primColorResult->lodFrac, primColorResult->r, primColorResult->g, primColorResult->b, + (u8)(primColorResult->a * sMatAnimAlphaRatio)); + + if (envColor != NULL) { + gDPSetEnvColor(gfx++, envColor->r, envColor->g, envColor->b, envColor->a); + } + + gSPEndDisplayList(gfx++); + + CLOSE_DISPS(play->state.gfxCtx); +} + +/** + * Animated Material Type 2: + * Color key frame animation without linear interpolation. + */ +void AnimatedMat_DrawColor(PlayState* play, s32 segment, void* params) { + AnimatedMatColorParams* colorAnimParams = (AnimatedMatColorParams*)params; + F3DPrimColor* primColor = SEGMENTED_TO_VIRTUAL(colorAnimParams->primColors); + F3DEnvColor* envColor; + s32 curFrame = sMatAnimStep % colorAnimParams->keyFrameLength; + + primColor += curFrame; + envColor = (colorAnimParams->envColors != NULL) + ? (F3DEnvColor*)SEGMENTED_TO_VIRTUAL(colorAnimParams->envColors) + curFrame + : NULL; + + AnimatedMat_SetColor(play, segment, primColor, envColor); +} + +/** + * Linear Interpolation + */ +s32 AnimatedMat_Lerp(s32 min, s32 max, f32 norm) { + return (s32)((max - min) * norm) + min; +} + +/** + * Animated Material Type 3: + * Color key frame animation with linear interpolation. + */ +void AnimatedMat_DrawColorLerp(PlayState* play, s32 segment, void* params) { + AnimatedMatColorParams* colorAnimParams = (AnimatedMatColorParams*)params; + F3DPrimColor* primColorMax = SEGMENTED_TO_VIRTUAL(colorAnimParams->primColors); + F3DEnvColor* envColorMax; + u16* keyFrames = SEGMENTED_TO_VIRTUAL(colorAnimParams->keyFrames); + s32 curFrame = sMatAnimStep % colorAnimParams->keyFrameLength; + s32 endFrame; + s32 relativeFrame; // relative to the start frame + s32 startFrame; + f32 norm; + F3DPrimColor* primColorMin; + F3DPrimColor primColorResult; + F3DEnvColor* envColorMin; + F3DEnvColor envColorResult; + s32 i; + + keyFrames++; + i = 1; + + while (colorAnimParams->keyFrameCount > i) { + if (curFrame < *keyFrames) { + break; + } + i++; + keyFrames++; + } + + startFrame = keyFrames[-1]; + endFrame = keyFrames[0] - startFrame; + relativeFrame = curFrame - startFrame; + norm = (f32)relativeFrame / (f32)endFrame; + + primColorMax += i; + primColorMin = primColorMax - 1; + primColorResult.r = AnimatedMat_Lerp(primColorMin->r, primColorMax->r, norm); + primColorResult.g = AnimatedMat_Lerp(primColorMin->g, primColorMax->g, norm); + primColorResult.b = AnimatedMat_Lerp(primColorMin->b, primColorMax->b, norm); + primColorResult.a = AnimatedMat_Lerp(primColorMin->a, primColorMax->a, norm); + primColorResult.lodFrac = AnimatedMat_Lerp(primColorMin->lodFrac, primColorMax->lodFrac, norm); + + if (colorAnimParams->envColors) { + envColorMax = SEGMENTED_TO_VIRTUAL(colorAnimParams->envColors); + envColorMax += i; + envColorMin = envColorMax - 1; + envColorResult.r = AnimatedMat_Lerp(envColorMin->r, envColorMax->r, norm); + envColorResult.g = AnimatedMat_Lerp(envColorMin->g, envColorMax->g, norm); + envColorResult.b = AnimatedMat_Lerp(envColorMin->b, envColorMax->b, norm); + envColorResult.a = AnimatedMat_Lerp(envColorMin->a, envColorMax->a, norm); + } else { + envColorMax = NULL; + } + + AnimatedMat_SetColor(play, segment, &primColorResult, (envColorMax != NULL) ? &envColorResult : NULL); +} + +/** + * Animated Material Type 4: + * Color key frame animation with non-linear interpolation. + */ +void AnimatedMat_DrawColorNonLinearInterp(PlayState* play, s32 segment, void* params) { + AnimatedMatColorParams* colorAnimParams = (AnimatedMatColorParams*)params; + F3DPrimColor* primColorCur = SEGMENTED_TO_VIRTUAL(colorAnimParams->primColors); + F3DEnvColor* envColorCur = SEGMENTED_TO_VIRTUAL(colorAnimParams->envColors); + u16* keyFrames = SEGMENTED_TO_VIRTUAL(colorAnimParams->keyFrames); + f32 curFrame = sMatAnimStep % colorAnimParams->keyFrameLength; + F3DPrimColor primColorResult; + F3DEnvColor envColorResult; + f32 x[50]; + f32 fxPrimR[50]; + f32 fxPrimG[50]; + f32 fxPrimB[50]; + f32 fxPrimA[50]; + f32 fxPrimLodFrac[50]; + f32 fxEnvR[50]; + f32 fxEnvG[50]; + f32 fxEnvB[50]; + f32 fxEnvA[50]; + f32* xPtr = x; + f32* fxPrimRPtr = fxPrimR; + f32* fxPrimGPtr = fxPrimG; + f32* fxPrimBPtr = fxPrimB; + f32* fxPrimAPtr = fxPrimA; + f32* fxPrimLodFracPtr = fxPrimLodFrac; + f32* fxEnvRPtr = fxEnvR; + f32* fxEnvGPtr = fxEnvG; + f32* fxEnvBPtr = fxEnvB; + f32* fxEnvAPtr = fxEnvA; + s32 i; + + for (i = 0; i < colorAnimParams->keyFrameCount; i++) { + *xPtr = *keyFrames; + *fxPrimRPtr = primColorCur->r; + *fxPrimGPtr = primColorCur->g; + *fxPrimBPtr = primColorCur->b; + *fxPrimAPtr = primColorCur->a; + *fxPrimLodFracPtr = primColorCur->lodFrac; + + primColorCur++; + fxPrimRPtr++; + fxPrimGPtr++; + fxPrimBPtr++; + fxPrimAPtr++; + fxPrimLodFracPtr++; + + if (envColorCur != NULL) { + *fxEnvRPtr = envColorCur->r; + *fxEnvGPtr = envColorCur->g; + *fxEnvBPtr = envColorCur->b; + *fxEnvAPtr = envColorCur->a; + + envColorCur++; + fxEnvRPtr++; + fxEnvGPtr++; + fxEnvBPtr++; + fxEnvAPtr++; + } + + keyFrames++; + xPtr++; + } + + primColorResult.r = Helpers_LagrangeInterpColor(colorAnimParams->keyFrameCount, x, fxPrimR, curFrame); + primColorResult.g = Helpers_LagrangeInterpColor(colorAnimParams->keyFrameCount, x, fxPrimG, curFrame); + primColorResult.b = Helpers_LagrangeInterpColor(colorAnimParams->keyFrameCount, x, fxPrimB, curFrame); + primColorResult.a = Helpers_LagrangeInterpColor(colorAnimParams->keyFrameCount, x, fxPrimA, curFrame); + primColorResult.lodFrac = Helpers_LagrangeInterpColor(colorAnimParams->keyFrameCount, x, fxPrimLodFrac, curFrame); + + if (colorAnimParams->envColors != NULL) { + envColorCur = SEGMENTED_TO_VIRTUAL(colorAnimParams->envColors); + envColorResult.r = Helpers_LagrangeInterpColor(colorAnimParams->keyFrameCount, x, fxEnvR, curFrame); + envColorResult.g = Helpers_LagrangeInterpColor(colorAnimParams->keyFrameCount, x, fxEnvG, curFrame); + envColorResult.b = Helpers_LagrangeInterpColor(colorAnimParams->keyFrameCount, x, fxEnvB, curFrame); + envColorResult.a = Helpers_LagrangeInterpColor(colorAnimParams->keyFrameCount, x, fxEnvA, curFrame); + } else { + envColorCur = NULL; + } + + AnimatedMat_SetColor(play, segment, &primColorResult, (envColorCur != NULL) ? &envColorResult : NULL); +} + +/** + * Animated Material Type 5: + * Cycles between a list of textures (imagine like a GIF) + */ +void AnimatedMat_DrawTexCycle(PlayState* play, s32 segment, void* params) { + AnimatedMatTexCycleParams* texAnimParams = params; + TexturePtr* texList = SEGMENTED_TO_VIRTUAL(texAnimParams->textureList); + u8* texId = SEGMENTED_TO_VIRTUAL(texAnimParams->textureIndexList); + s32 curFrame = sMatAnimStep % texAnimParams->keyFrameLength; + TexturePtr tex = SEGMENTED_TO_VIRTUAL(texList[texId[curFrame]]); + + OPEN_DISPS(play->state.gfxCtx); + + if (sMatAnimFlags & 1) { + gSPSegment(POLY_OPA_DISP++, segment, tex); + } + + if (sMatAnimFlags & 2) { + gSPSegment(POLY_XLU_DISP++, segment, tex); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +/** + * This is the main function that handles the animated material system. + * There are six different animated material types, which should be set in the provided `AnimatedMaterial`. + */ +void AnimatedMat_DrawMain(PlayState* play, AnimatedMaterial* matAnim, f32 alphaRatio, u32 step, u32 flags) { + static void (*sMatAnimDrawHandlers[ANIM_MAT_TYPE_MAX])(PlayState*, s32 segment, void* params) = { + AnimatedMat_DrawTexScroll, // ANIM_MAT_TYPE_TEX_SCROLL + AnimatedMat_DrawTwoTexScroll, // ANIM_MAT_TYPE_TWO_TEX_SCROLL + AnimatedMat_DrawColor, // ANIM_MAT_TYPE_COLOR + AnimatedMat_DrawColorLerp, // ANIM_MAT_TYPE_COLOR_LERP + AnimatedMat_DrawColorNonLinearInterp, // ANIM_MAT_TYPE_COLOR_NON_LINEAR_INTERP + AnimatedMat_DrawTexCycle, // ANIM_MAT_TYPE_TEX_CYCLE + }; + s32 segmentAbs; + s32 segment; + + sMatAnimAlphaRatio = alphaRatio; + sMatAnimStep = step; + sMatAnimFlags = flags; + + if ((matAnim != NULL) && (matAnim->segment != 0)) { + do { + segment = matAnim->segment; + segmentAbs = ABS_ALT(segment) + 7; + sMatAnimDrawHandlers[matAnim->type](play, segmentAbs, SEGMENTED_TO_VIRTUAL(matAnim->params)); + matAnim++; + } while (segment >= 0); + } +} + +/** + * Draws an animated material to both OPA and XLU buffers. + */ +void AnimatedMat_Draw(PlayState* play, AnimatedMaterial* matAnim) { + AnimatedMat_DrawMain(play, matAnim, 1, play->gameplayFrames, 3); +} + +/** + * Draws an animated material to only the OPA buffer. + */ +void AnimatedMat_DrawOpa(PlayState* play, AnimatedMaterial* matAnim) { + AnimatedMat_DrawMain(play, matAnim, 1, play->gameplayFrames, 1); +} + +/** + * Draws an animated material to only the XLU buffer. + */ +void AnimatedMat_DrawXlu(PlayState* play, AnimatedMaterial* matAnim) { + AnimatedMat_DrawMain(play, matAnim, 1, play->gameplayFrames, 2); +} + +/** + * Draws an animated material with an alpha ratio (0.0 - 1.0) both OPA and XLU buffers. + */ +void AnimatedMat_DrawAlpha(PlayState* play, AnimatedMaterial* matAnim, f32 alphaRatio) { + AnimatedMat_DrawMain(play, matAnim, alphaRatio, play->gameplayFrames, 3); +} + +/** + * Draws an animated material with an alpha ratio (0.0 - 1.0) to only the OPA buffer. + */ +void AnimatedMat_DrawAlphaOpa(PlayState* play, AnimatedMaterial* matAnim, f32 alphaRatio) { + AnimatedMat_DrawMain(play, matAnim, alphaRatio, play->gameplayFrames, 1); +} + +/** + * Draws an animated material with an alpha ratio (0.0 - 1.0) to only the XLU buffer. + */ +void AnimatedMat_DrawAlphaXlu(PlayState* play, AnimatedMaterial* matAnim, f32 alphaRatio) { + AnimatedMat_DrawMain(play, matAnim, alphaRatio, play->gameplayFrames, 2); +} + +/** + * Draws an animated material with a step to both the OPA and XLU buffers. + */ +void AnimatedMat_DrawStep(PlayState* play, AnimatedMaterial* matAnim, u32 step) { + AnimatedMat_DrawMain(play, matAnim, 1, step, 3); +} + +/** + * Draws an animated material with a step to only the OPA buffer. + */ +void AnimatedMat_DrawStepOpa(PlayState* play, AnimatedMaterial* matAnim, u32 step) { + AnimatedMat_DrawMain(play, matAnim, 1, step, 1); +} + +/** + * Draws an animated material with a step to only the XLU buffer. + */ +void AnimatedMat_DrawStepXlu(PlayState* play, AnimatedMaterial* matAnim, u32 step) { + AnimatedMat_DrawMain(play, matAnim, 1, step, 2); +} + +/** + * Draws an animated material with an alpha ratio (0.0 - 1.0) and a step to both the OPA and XLU buffers. + */ +void AnimatedMat_DrawAlphaStep(PlayState* play, AnimatedMaterial* matAnim, f32 alphaRatio, u32 step) { + AnimatedMat_DrawMain(play, matAnim, alphaRatio, step, 3); +} + +/** + * Draws an animated material with an alpha ratio (0.0 - 1.0) and a step to only the OPA buffer. + */ +void AnimatedMat_DrawAlphaStepOpa(PlayState* play, AnimatedMaterial* matAnim, f32 alphaRatio, u32 step) { + AnimatedMat_DrawMain(play, matAnim, alphaRatio, step, 1); +} + +/** + * Draws an animated material with an alpha ratio (0.0 - 1.0) and a step to only the XLU buffer. + */ +void AnimatedMat_DrawAlphaStepXlu(PlayState* play, AnimatedMaterial* matAnim, f32 alphaRatio, u32 step) { + AnimatedMat_DrawMain(play, matAnim, alphaRatio, step, 2); +} diff --git a/src/code/helpers.c b/src/code/helpers.c index 513d63f853..f10dd990fe 100644 --- a/src/code/helpers.c +++ b/src/code/helpers.c @@ -104,3 +104,57 @@ void Helpers_DrawSkybox(GameState* gameState, View* view, EnvironmentContext* en view->eye.z); Environment_UpdateSkybox(skyboxId, envCtx, skyboxCtx); } + +/** + * Lagrange interpolation + */ +f32 Helpers_LagrangeInterp(s32 n, f32 x[], f32 fx[], f32 xp) { + f32 weights[50]; + f32 xVal; + f32 m; + f32 intp; + f32* xPtr1; + f32* fxPtr; + f32* weightsPtr; + f32* xPtr2; + s32 i; + s32 j; + + for (i = 0, xPtr1 = x, fxPtr = fx, weightsPtr = weights; i < n; i++) { + for (xVal = *xPtr1, m = 1.0f, j = 0, xPtr2 = x; j < n; j++) { + if (j != i) { + m *= xVal - (*xPtr2); + } + xPtr2++; + } + + xPtr1++; + *weightsPtr = (*fxPtr) / m; + fxPtr++; + weightsPtr++; + } + + for (intp = 0.0f, i = 0, weightsPtr = weights; i < n; i++) { + for (m = 1.0f, j = 0, xPtr2 = x; j < n; j++) { + if (j != i) { + m *= xp - (*xPtr2); + } + xPtr2++; + } + + intp += (*weightsPtr) * m; + weightsPtr++; + } + + return intp; +} + +/** + * Lagrange interpolation specifically for colors + */ +u8 Helpers_LagrangeInterpColor(s32 n, f32 x[], f32 fx[], f32 xp) { + s32 intp = Helpers_LagrangeInterp(n, x, fx, xp); + + // Clamp between 0 and 255 to ensure the color value does not overflow in either direction + return CLAMP(intp, 0, 255); +} diff --git a/src/code/z_play.c b/src/code/z_play.c index 1f8d3aee10..e6465607de 100644 --- a/src/code/z_play.c +++ b/src/code/z_play.c @@ -531,6 +531,9 @@ void Play_Init(GameState* thisx) { DmaMgr_DmaRomToRam(0x03FEB000, gDebugCutsceneScript, sizeof(sDebugCutsceneScriptBuf)); } #endif + + //! TODO: investigate issue with this variable set to a random value + this->gameplayFrames = 0; } void Play_Update(PlayState* this) { @@ -1771,6 +1774,10 @@ void Play_InitScene(PlayState* this, s32 spawn) { this->naviQuestHints = NULL; this->pathList = NULL; +#if ENABLE_ANIMATED_MATERIALS + this->sceneMaterialAnims = NULL; +#endif + this->numActorEntries = 0; Object_InitContext(this, &this->objectCtx); diff --git a/src/code/z_room.c b/src/code/z_room.c index 25578a631d..6d523137cc 100644 --- a/src/code/z_room.c +++ b/src/code/z_room.c @@ -562,11 +562,17 @@ void Room_DrawImage(PlayState* play, Room* room, u32 flags) { } void Room_Init(PlayState* play, Room* room) { + u8 i; + room->num = -1; room->segment = NULL; #if ENABLE_F3DEX3 room->occPlaneCount = 0; #endif + + for (i = 0; i < ARRAY_COUNT(play->roomCtx.drawParams); i++) { + play->roomCtx.drawParams[i] = 0; + } } /** diff --git a/src/code/z_scene.c b/src/code/z_scene.c index 949e4c9d7b..151d2e220e 100644 --- a/src/code/z_scene.c +++ b/src/code/z_scene.c @@ -527,6 +527,12 @@ void Scene_SetTransitionForNextEntrance(PlayState* play) { play->transitionType = ENTRANCE_INFO_START_TRANS_TYPE(gEntranceTable[entranceIndex].field); } +void Scene_CommandAnimatedMaterials(PlayState* play, SceneCmd* cmd) { +#if ENABLE_ANIMATED_MATERIALS + play->sceneMaterialAnims = SEGMENTED_TO_VIRTUAL(cmd->textureAnimations.segment); +#endif +} + SceneCmdHandlerFunc sSceneCmdHandlers[SCENE_CMD_ID_MAX] = { Scene_CommandPlayerEntryList, // SCENE_CMD_ID_SPAWN_LIST Scene_CommandActorEntryList, // SCENE_CMD_ID_ACTOR_LIST @@ -557,6 +563,7 @@ SceneCmdHandlerFunc sSceneCmdHandlers[SCENE_CMD_ID_MAX] = { #if ENABLE_F3DEX3 Scene_CommandOccPlaneCandList, // SCENE_CMD_ID_OCC_PLANE_CAND_LIST #endif + Scene_CommandAnimatedMaterials, // SCENE_CMD_ID_ANIMATED_MATERIAL_LIST }; RomFile sNaviQuestHintFiles[] = { diff --git a/src/code/z_scene_table.c b/src/code/z_scene_table.c index b10223500a..c616926c0d 100644 --- a/src/code/z_scene_table.c +++ b/src/code/z_scene_table.c @@ -2,6 +2,7 @@ #include "quake.h" #include "versions.h" #include "z64frame_advance.h" +#include "animated_materials.h" #include "config.h" #if PLATFORM_N64 #include "n64dd.h" @@ -85,6 +86,9 @@ void Scene_DrawConfigFishingPond(PlayState* play); void Scene_DrawConfigGanonsTowerCollapseInterior(PlayState* play); void Scene_DrawConfigInsideGanonsCastleCollapse(PlayState* play); +void Scene_DrawConfigMatAnim(PlayState* play); +void Scene_DrawConfigMatAnimManualStep(PlayState* play); + // Entrance Table definition #define DEFINE_ENTRANCE(_0, sceneId, spawn, continueBgm, displayTitleCard, endTransType, startTransType) \ { sceneId, spawn, \ @@ -138,8 +142,6 @@ Gfx sDefaultDisplayList[] = { gsSPEndDisplayList(), }; -#if PLATFORM_N64 // Scene_Draw is at end of file in GC/iQue versions - SceneDrawConfigFunc sSceneDrawConfigs[SDC_MAX] = { Scene_DrawConfigDefault, // SDC_DEFAULT Scene_DrawConfigHyruleField, // SDC_HYRULE_FIELD @@ -194,8 +196,12 @@ SceneDrawConfigFunc sSceneDrawConfigs[SDC_MAX] = { Scene_DrawConfigFishingPond, // SDC_FISHING_POND Scene_DrawConfigGanonsTowerCollapseInterior, // SDC_GANONS_TOWER_COLLAPSE_INTERIOR Scene_DrawConfigInsideGanonsCastleCollapse, // SDC_INSIDE_GANONS_CASTLE_COLLAPSE + Scene_DrawConfigMatAnim, // SDC_MAT_ANIM + Scene_DrawConfigMatAnimManualStep, // SDC_MAT_ANIM_MANUAL_STEP }; +#if PLATFORM_N64 // Scene_Draw is at end of file in GC/iQue versions + void Scene_Draw(PlayState* play) { if ((B_80121220 != NULL) && (B_80121220->unk_6C != NULL)) { B_80121220->unk_6C(play, sSceneDrawConfigs); @@ -1703,63 +1709,30 @@ void Scene_DrawConfigBesitu(PlayState* play) { CLOSE_DISPS(play->state.gfxCtx, "../z_scene_table.c", 7910); } -#if !PLATFORM_N64 // Scene_Draw is at beginning of file in N64 versions +/** + * Allows the usage of the animated material system in scenes. + */ +void Scene_DrawConfigMatAnim(PlayState* play) { +#if ENABLE_ANIMATED_MATERIALS + AnimatedMat_Draw(play, play->sceneMaterialAnims); +#else + Scene_DrawConfigDefault(play); +#endif +} -SceneDrawConfigFunc sSceneDrawConfigs[SDC_MAX] = { - Scene_DrawConfigDefault, // SDC_DEFAULT - Scene_DrawConfigHyruleField, // SDC_HYRULE_FIELD - Scene_DrawConfigKakarikoVillage, // SDC_KAKARIKO_VILLAGE - Scene_DrawConfigZorasRiver, // SDC_ZORAS_RIVER - Scene_DrawConfigKokiriForest, // SDC_KOKIRI_FOREST - Scene_DrawConfigLakeHylia, // SDC_LAKE_HYLIA - Scene_DrawConfigZorasDomain, // SDC_ZORAS_DOMAIN - Scene_DrawConfigZorasFountain, // SDC_ZORAS_FOUNTAIN - Scene_DrawConfigGerudoValley, // SDC_GERUDO_VALLEY - Scene_DrawConfigLostWoods, // SDC_LOST_WOODS - Scene_DrawConfigDesertColossus, // SDC_DESERT_COLOSSUS - Scene_DrawConfigGerudosFortress, // SDC_GERUDOS_FORTRESS - Scene_DrawConfigHauntedWasteland, // SDC_HAUNTED_WASTELAND - Scene_DrawConfigHyruleCastle, // SDC_HYRULE_CASTLE - Scene_DrawConfigDeathMountainTrail, // SDC_DEATH_MOUNTAIN_TRAIL - Scene_DrawConfigDeathMountainCrater, // SDC_DEATH_MOUNTAIN_CRATER - Scene_DrawConfigGoronCity, // SDC_GORON_CITY - Scene_DrawConfigLonLonRanch, // SDC_LON_LON_RANCH - Scene_DrawConfigFireTemple, // SDC_FIRE_TEMPLE - Scene_DrawConfigDekuTree, // SDC_DEKU_TREE - Scene_DrawConfigDodongosCavern, // SDC_DODONGOS_CAVERN - Scene_DrawConfigJabuJabu, // SDC_JABU_JABU - Scene_DrawConfigForestTemple, // SDC_FOREST_TEMPLE - Scene_DrawConfigWaterTemple, // SDC_WATER_TEMPLE - Scene_DrawConfigShadowTempleAndWell, // SDC_SHADOW_TEMPLE_AND_WELL - Scene_DrawConfigSpiritTemple, // SDC_SPIRIT_TEMPLE - Scene_DrawConfigInsideGanonsCastle, // SDC_INSIDE_GANONS_CASTLE - Scene_DrawConfigGerudoTrainingGround, // SDC_GERUDO_TRAINING_GROUND - Scene_DrawConfigDekuTreeBoss, // SDC_DEKU_TREE_BOSS - Scene_DrawConfigWaterTempleBoss, // SDC_WATER_TEMPLE_BOSS - Scene_DrawConfigTempleOfTime, // SDC_TEMPLE_OF_TIME - Scene_DrawConfigGrottos, // SDC_GROTTOS - Scene_DrawConfigChamberOfTheSages, // SDC_CHAMBER_OF_THE_SAGES - Scene_DrawConfigGreatFairyFountain, // SDC_GREAT_FAIRYS_FOUNTAIN - Scene_DrawConfigShootingGallery, // SDC_SHOOTING_GALLERY - Scene_DrawConfigCastleCourtyardGuards, // SDC_CASTLE_COURTYARD_GUARDS - Scene_DrawConfigOutsideGanonsCastle, // SDC_OUTSIDE_GANONS_CASTLE - Scene_DrawConfigIceCavern, // SDC_ICE_CAVERN - Scene_DrawConfigGanonsTowerCollapseExterior, // SDC_GANONS_TOWER_COLLAPSE_EXTERIOR - Scene_DrawConfigFairysFountain, // SDC_FAIRYS_FOUNTAIN - Scene_DrawConfigThievesHideout, // SDC_THIEVES_HIDEOUT - Scene_DrawConfigBombchuBowlingAlley, // SDC_BOMBCHU_BOWLING_ALLEY - Scene_DrawConfigRoyalFamilysTomb, // SDC_ROYAL_FAMILYS_TOMB - Scene_DrawConfigLakesideLaboratory, // SDC_LAKESIDE_LABORATORY - Scene_DrawConfigLonLonBuildings, // SDC_LON_LON_BUILDINGS - Scene_DrawConfigMarketGuardHouse, // SDC_MARKET_GUARD_HOUSE - Scene_DrawConfigPotionShopGranny, // SDC_POTION_SHOP_GRANNY - Scene_DrawConfigCalmWater, // SDC_CALM_WATER - Scene_DrawConfigGraveExitLightShining, // SDC_GRAVE_EXIT_LIGHT_SHINING - Scene_DrawConfigBesitu, // SDC_BESITU - Scene_DrawConfigFishingPond, // SDC_FISHING_POND - Scene_DrawConfigGanonsTowerCollapseInterior, // SDC_GANONS_TOWER_COLLAPSE_INTERIOR - Scene_DrawConfigInsideGanonsCastleCollapse, // SDC_INSIDE_GANONS_CASTLE_COLLAPSE -}; +/** + * This is a special draw config for Sakon's Hideout, as well as the Music Box House. Its step value is set manually + * rather than always animating like `Scene_DrawConfigMatAnim`. + */ +void Scene_DrawConfigMatAnimManualStep(PlayState* play) { +#if ENABLE_ANIMATED_MATERIALS + AnimatedMat_DrawStep(play, play->sceneMaterialAnims, play->roomCtx.drawParams[0]); +#else + Scene_DrawConfigDefault(play); +#endif +} + +#if !PLATFORM_N64 // Scene_Draw is at beginning of file in N64 versions void Scene_Draw(PlayState* play) { #if DEBUG_FEATURES diff --git a/src/overlays/gamestates/ovl_title/z_title.c b/src/overlays/gamestates/ovl_title/z_title.c index f792b9e433..6aa88c349f 100644 --- a/src/overlays/gamestates/ovl_title/z_title.c +++ b/src/overlays/gamestates/ovl_title/z_title.c @@ -137,7 +137,7 @@ void ConsoleLogo_Main(GameState* thisx) { if (this->exit) { #if ENABLE_HACKER_DEBUG #if BOOT_TO_SCENE - Helpers_LoadDefinedScene(thisx), + Helpers_LoadDefinedScene(thisx); #elif IS_DEBUG_BOOT_ENABLED && BOOT_TO_DEBUG_OPENING this->state.running = false; SET_NEXT_GAMESTATE(&this->state, DebugOpening_Init, DebugOpeningState);