diff --git a/README.md b/README.md index 08694cefd..37874e56e 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ Most discussions happen on our [Discord Server](https://discord.gg/brETAakcXr), List of every HackerOoT contributors, from most recent to oldest contribution: +- Thar0 - recardo-7 - HailToDodongo - CrashOverride95 diff --git a/include/config/config_debug.h b/include/config/config_debug.h index 3931fe1f4..bf7f53ec8 100644 --- a/include/config/config_debug.h +++ b/include/config/config_debug.h @@ -65,4 +65,9 @@ */ #define ENABLE_DMA_PRINTF false +/** + * Enable motion blur debug +*/ +#define ENABLE_MOTION_BLUR_DEBUG false + #endif diff --git a/include/config/config_game.h b/include/config/config_game.h index b8b2385bd..355ed1b8b 100644 --- a/include/config/config_game.h +++ b/include/config/config_game.h @@ -103,4 +103,9 @@ */ #define MM_N64_BOOT_LOGO false +/* + * Motion Blur +*/ +#define ENABLE_MOTION_BLUR true + #endif diff --git a/include/config/config_safeguards.h b/include/config/config_safeguards.h index 33cf4844b..c957dfad8 100644 --- a/include/config/config_safeguards.h +++ b/include/config/config_safeguards.h @@ -48,6 +48,7 @@ #undef ENABLE_DEBUG_SAVE #undef MAP_SELECT_ON_FILE_1 #undef ENABLE_DEBUG_HEAP + #undef ENABLE_MOTION_BLUR_DEBUG #define SHOW_CS_INFOS false #define SHOW_INPUT_DISPLAY false @@ -67,6 +68,7 @@ #define ENABLE_DEBUG_SAVE false #define MAP_SELECT_ON_FILE_1 false #define ENABLE_DEBUG_HEAP false + #define ENABLE_MOTION_BLUR_DEBUG false #endif @@ -151,6 +153,7 @@ #undef ENABLE_MSG_DEBUGGER #undef ENABLE_DEBUG_SAVE #undef MAP_SELECT_ON_FILE_1 + #undef ENABLE_MOTION_BLUR_DEBUG #define DETERMINISTIC_BUILD false #define SKIP_N64_BOOT_LOGO true @@ -175,6 +178,7 @@ #define ENABLE_MSG_DEBUGGER false #define ENABLE_DEBUG_SAVE false #define MAP_SELECT_ON_FILE_1 true + #define ENABLE_MOTION_BLUR_DEBUG false #endif /** @@ -186,6 +190,15 @@ #define IS_DEBUG OOT_DEBUG #endif +/** + * Game +*/ +#define IS_MOTION_BLUR_ENABLED (ENABLE_HACKEROOT && ENABLE_MOTION_BLUR) + +/** + * Debug +*/ + // General features #define IS_DEBUG_HEAP_ENABLED (IS_DEBUG && ENABLE_DEBUG_HEAP) #define IS_NO_CLIP_ENABLED (IS_DEBUG && ENABLE_NO_CLIP) diff --git a/include/functions.h b/include/functions.h index 57c04635c..691b90adc 100644 --- a/include/functions.h +++ b/include/functions.h @@ -1975,4 +1975,16 @@ void TitleSetup_Destroy(GameState* thisx); void FileSelect_Init(GameState* thisx); void FileSelect_Destroy(GameState* thisx); +#if ENABLE_MOTION_BLUR +void Play_DrawMotionBlur(PlayState* this); +void Play_InitMotionBlur(PlayState* this); +void Play_DestroyMotionBlur(void); +void Play_SetMotionBlurAlpha(u32 alpha); +void Play_EnableMotionBlur(u32 alpha); +void Play_DisableMotionBlur(void); +void Play_SetMotionBlurPriorityAlpha(u32 alpha); +void Play_EnableMotionBlurPriority(u32 alpha); +void Play_DisableMotionBlurPriority(void); +#endif + #endif diff --git a/include/regs.h b/include/regs.h index 49af09a87..b87c6d9ff 100644 --- a/include/regs.h +++ b/include/regs.h @@ -357,4 +357,9 @@ typedef enum { #define R_VI_CUR_ADDI_SCAN_LINES HREG(83) #define R_VI_CUR_Y_SCALE_MODE HREG(84) +#define R_MOTION_BLUR_ALPHA SREG(90) +#define R_MOTION_BLUR_PRIORITY_ALPHA SREG(92) +#define R_MOTION_BLUR_PRIORITY_ENABLED SREG(93) +#define R_MOTION_BLUR_ENABLED SREG(95) + #endif diff --git a/include/z64cutscene.h b/include/z64cutscene.h index 99cca6263..2301a320c 100644 --- a/include/z64cutscene.h +++ b/include/z64cutscene.h @@ -2,6 +2,7 @@ #define Z64CUTSCENE_H #include "ultra64.h" +#include "config.h" typedef union CutsceneData { s32 i; @@ -147,6 +148,7 @@ typedef enum { /* 0x008F */ CS_CMD_ACTOR_CUE_9_0, /* 0x0090 */ CS_CMD_ACTOR_CUE_0_17, /* 0x03E8 */ CS_CMD_DESTINATION = 0x03E8, + /* 0x03E9 */ CS_CMD_MOTION_BLUR, /* 0xFFFF */ CS_CMD_END = 0xFFFF } CutsceneCmd; @@ -510,6 +512,22 @@ typedef struct { /* 0x20 */ CutsceneCameraPoint* camEyePoints; /* 0x24 */ CsCmdActorCue* playerCue; /* 0x28 */ CsCmdActorCue* actorCues[10]; // "npcdemopnt" + /* 0x38 */ u16 originalBlurAlpha; } CutsceneContext; // size = 0x50 +typedef union { + struct { + /* 0x0 */ u16 type; + /* 0x2 */ u16 alpha; + /* 0x4 */ u16 startFrame; + /* 0x6 */ u16 endFrame; + }; + s32 _words[2]; +} CsCmdMotionBlur; // size = 0x8 + +typedef enum { + /* 1 */ CS_MOTION_BLUR_ENABLE = 1, + /* 2 */ CS_MOTION_BLUR_DISABLE +} CsMotionBlurType; + #endif diff --git a/include/z64cutscene_commands.h b/include/z64cutscene_commands.h index 5de3d3c83..1e974b318 100644 --- a/include/z64cutscene_commands.h +++ b/include/z64cutscene_commands.h @@ -266,6 +266,30 @@ #define CS_DESTINATION(destination, startFrame, endFrame) \ CS_CMD_DESTINATION, 1, CMD_HH(destination, startFrame), CMD_HH(endFrame, endFrame) +/** + * Declares a list of `CS_MOTION_BLUR` entries + */ +#define CS_MOTION_BLUR_LIST(entries) \ + CS_CMD_MOTION_BLUR, CMD_W(entries) + +#define CS_MOTION_BLUR(type, alpha, startFrame, endFrame) \ + CMD_HH(type, alpha), CMD_HH(startFrame, endFrame) + +/** + * Enables motion blur + * ``alpha`` is how visible the motion blur is, MM uses 180 for every instance of motion blur. + * Note: this can happen gradually + */ +#define CS_MOTION_BLUR_ENABLE(startFrame, endFrame, alpha) \ + CS_MOTION_BLUR(CS_MOTION_BLUR_ENABLE, alpha, startFrame, endFrame) + +/** + * Disables motion blur + * Note: this can happen gradually +*/ +#define CS_MOTION_BLUR_DISABLE(startFrame, endFrame) \ + CS_MOTION_BLUR(CS_MOTION_BLUR_DISABLE, 0, startFrame, endFrame) + /** * Marks the end of a cutscene script. */ diff --git a/src/code/PreRender.c b/src/code/PreRender.c index 2ef603fa7..c7601cbdd 100644 --- a/src/code/PreRender.c +++ b/src/code/PreRender.c @@ -815,3 +815,81 @@ void PreRender_ApplyFilters(PreRender* this) { } } } + +#if ENABLE_MOTION_BLUR +void PreRender_MotionBlurImpl(PreRender* this, Gfx** gfxp, void* buf, void* bufSave, s32 envR, s32 envG, s32 envB, s32 envA) { + Gfx* gfx = *gfxp; + uObjBg* bg; + + gDPPipeSync(gfx++); + + if (envA == 255) { + gDPSetOtherMode(gfx++, + G_AD_DISABLE | G_CD_DISABLE | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | G_RM_OPA_SURF | G_RM_OPA_SURF2); + } else { + gDPSetOtherMode(gfx++, + G_AD_NOISE | G_CD_NOISE | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | G_RM_CLD_SURF | G_RM_CLD_SURF2); + } + + gDPSetEnvColor(gfx++, envR, envG, envB, envA); + gDPSetCombineLERP(gfx++, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, ENVIRONMENT, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, + ENVIRONMENT); + gDPSetColorImage(gfx++, G_IM_FMT_RGBA, G_IM_SIZ_16b, this->width, bufSave); + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, this->width, this->height); + + // Setup BG Obj + bg = Gfx_Alloc(&gfx, sizeof(uObjBg)); + + bg->s.imageX = 0; + bg->s.imageW = this->width * 4 + 1; + bg->s.frameX = 0; + + bg->s.imageY = 0; + bg->s.imageH = this->height * 4 + 1; + bg->s.frameY = 0; + + bg->s.imagePtr = buf; + bg->s.imageLoad = G_BGLT_LOADTILE; + bg->s.imageFmt = G_IM_FMT_RGBA; + bg->s.imageSiz = G_IM_SIZ_16b; + bg->s.imagePal = 0; + bg->s.imageFlip = 0; + + bg->s.frameW = this->width * 4; + bg->s.frameH = this->height * 4; + bg->s.scaleW = 1024; + bg->s.scaleH = 1024; + bg->s.imageYorig = bg->s.imageY; + + // Load S2DEX + gSPLoadUcodeL(gfx++, gspS2DEX2d_fifo); + gDPPipeSync(gfx++); + + gSPObjRenderMode(gfx++, G_OBJRM_ANTIALIAS | G_OBJRM_BILERP); + gSPBgRect1Cyc(gfx++, bg); + gDPPipeSync(gfx++); + + // Reload F3DZEX + gSPLoadUcodeEx(gfx++, SysUcode_GetUCode(), SysUcode_GetUCodeData(), 0x800); + gDPPipeSync(gfx++); + + gDPSetColorImage(gfx++, G_IM_FMT_RGBA, G_IM_SIZ_16b, this->width, this->fbuf); + + *gfxp = gfx; +} + +// TODO this could do with a better name but whatever +void PreRender_MotionBlurOpaque(PreRender* this, Gfx** gfxP) { + if (this->fbuf != NULL && this->fbufSave != NULL) { + PreRender_MotionBlurImpl(this, gfxP, this->fbuf, this->fbufSave, 255, 255, 255, 255); + } +} + +void PreRender_MotionBlur(PreRender* this, Gfx** gfxP, s32 alpha) { + PreRender_MotionBlurImpl(this, gfxP, this->fbufSave, this->fbuf, 255, 255, 255, alpha); +} +#endif diff --git a/src/code/graph.c b/src/code/graph.c index 61e0b89c5..31cf62c20 100644 --- a/src/code/graph.c +++ b/src/code/graph.c @@ -35,6 +35,10 @@ UCodeInfo D_8012D248[3] = { { UCODE_S2DEX, gspS2DEX2d_fifoTextStart }, }; +#if ENABLE_MOTION_BLUR +u16 (*gWorkBuf)[SCREEN_WIDTH * SCREEN_HEIGHT]; // pointer-to-array, array itself is allocated (see below) +#endif + void Graph_FaultClient(void) { void* nextFb = osViGetNextFramebuffer(); void* newFb = (SysCfb_GetFbPtr(0) != nextFb) ? SysCfb_GetFbPtr(0) : SysCfb_GetFbPtr(1); @@ -459,6 +463,11 @@ void Graph_ThreadEntry(void* arg0) { GameStateOverlay* nextOvl = &gGameStateOverlayTable[GAMESTATE_SETUP]; GameStateOverlay* ovl; +#if ENABLE_MOTION_BLUR + gWorkBuf = SYSTEM_ARENA_MALLOC(sizeof(*gWorkBuf) + 64 - 1, __FILE__, __LINE__); + gWorkBuf = (void*)ALIGN64((u32)gWorkBuf); +#endif + PRINTF("グラフィックスレッド実行開始\n"); // "Start graphic thread execution" Graph_Init(&gfxCtx); diff --git a/src/code/z_demo.c b/src/code/z_demo.c index c8875edef..f3fd355a6 100644 --- a/src/code/z_demo.c +++ b/src/code/z_demo.c @@ -1,6 +1,7 @@ #include "global.h" #include "quake.h" #include "z64camera.h" +#include "config.h" #include "assets/scenes/indoors/tokinoma/tokinoma_scene.h" #include "assets/scenes/overworld/spot00/spot00_scene.h" @@ -30,6 +31,7 @@ #include "assets/scenes/misc/hakaana_ouke/hakaana_ouke_scene.h" + u16 sCurTextId = 0; u16 sCurOcarinaAction = 0; @@ -1776,6 +1778,37 @@ void CutsceneCmd_Text(PlayState* play, CutsceneContext* csCtx, CsCmdText* cmd) { } } +void CutsceneCmd_MotionBlur(PlayState* play, CutsceneContext* csCtx, CsCmdMotionBlur* cmd) { + if (ENABLE_MOTION_BLUR) { + if ((csCtx->curFrame >= cmd->startFrame) && (cmd->endFrame >= csCtx->curFrame)) { + f32 lerp = Environment_LerpWeight(cmd->endFrame, cmd->startFrame, csCtx->curFrame); + + if (ENABLE_MOTION_BLUR_DEBUG) { + PRINTF("[HackerOoT:INFO]: originalBlurAlpha: 0x%X, lerp: %f\n", play->csCtx.originalBlurAlpha, lerp); + } + + if (cmd->type == CS_MOTION_BLUR_ENABLE) { + if (csCtx->originalBlurAlpha == 0) { + csCtx->originalBlurAlpha = cmd->alpha; + } + + Play_EnableMotionBlur(lerp * cmd->alpha); + } else if (cmd->type == CS_MOTION_BLUR_DISABLE) { + if (lerp >= 0.9f) { + Play_DisableMotionBlur(); + csCtx->originalBlurAlpha = 0; + } else { + Play_SetMotionBlurAlpha((1.0f - lerp) * csCtx->originalBlurAlpha); + } + } + } + } else if (ENABLE_MOTION_BLUR_DEBUG) { + PRINTF("[HackerOoT:INFO]: Warning: Motion Blur is disabled\ntype: %d, startFrame: %d, endFrame: %d, curFrame: %d\n", + cmd->type, cmd->startFrame, cmd->endFrame, csCtx->curFrame + ); + } +} + void Cutscene_ProcessScript(PlayState* play, CutsceneContext* csCtx, u8* script) { s16 i; s32 totalEntries; @@ -2196,6 +2229,18 @@ void Cutscene_ProcessScript(PlayState* play, CutsceneContext* csCtx, u8* script) script += sizeof(CsCmdTransition); break; + case CS_CMD_MOTION_BLUR: + if (ENABLE_MOTION_BLUR) { + MemCpy(&cmdEntries, script, sizeof(cmdEntries)); + script += sizeof(cmdEntries); + + for (j = 0; j < cmdEntries; j++) { + CutsceneCmd_MotionBlur(play, csCtx, (CsCmdMotionBlur*)script); + script += sizeof(CsCmdMotionBlur); + } + } + break; + default: MemCpy(&cmdEntries, script, 4); script += sizeof(cmdEntries); diff --git a/src/code/z_play.c b/src/code/z_play.c index 6d97002c1..d1da2db2b 100644 --- a/src/code/z_play.c +++ b/src/code/z_play.c @@ -190,6 +190,10 @@ void Play_Destroy(GameState* thisx) { this->state.gfxCtx->callback = NULL; this->state.gfxCtx->callbackParam = NULL; +#if ENABLE_MOTION_BLUR + Play_DestroyMotionBlur(); +#endif + SREG(91) = 0; R_PAUSE_BG_PRERENDER_STATE = PAUSE_BG_PRERENDER_OFF; @@ -358,6 +362,10 @@ void Play_Init(GameState* thisx) { KaleidoScopeCall_Init(this); Interface_Init(this); +#if ENABLE_MOTION_BLUR + Play_InitMotionBlur(this); +#endif + if (gSaveContext.nextDayTime != NEXT_TIME_NONE) { if (gSaveContext.nextDayTime == NEXT_TIME_DAY) { gSaveContext.save.totalDays++; @@ -1050,6 +1058,26 @@ skip: PLAY_LOG(3816); Environment_Update(this, &this->envCtx, &this->lightCtx, &this->pauseCtx, &this->msgCtx, &this->gameOverCtx, this->state.gfxCtx); + + if (ENABLE_MOTION_BLUR && ENABLE_MOTION_BLUR_DEBUG) { + // motion blur testing controls + if (CHECK_BTN_ALL(this->state.input[0].press.button, BTN_DUP)) { + R_MOTION_BLUR_ENABLED ^= 1; + } + if (R_MOTION_BLUR_ENABLED != 0) { + if (CHECK_BTN_ALL(this->state.input[0].cur.button, BTN_DRIGHT)) { + R_MOTION_BLUR_ALPHA++; + if (R_MOTION_BLUR_ALPHA > 255) { + R_MOTION_BLUR_ALPHA = 255; + } + } else if (CHECK_BTN_ALL(this->state.input[0].cur.button, BTN_DLEFT)) { + R_MOTION_BLUR_ALPHA--; + if (R_MOTION_BLUR_ALPHA < 0) { + R_MOTION_BLUR_ALPHA = 0; + } + } + } + } } void Play_DrawOverlayElements(PlayState* this) { @@ -1068,6 +1096,109 @@ void Play_DrawOverlayElements(PlayState* this) { } } +#if ENABLE_MOTION_BLUR +void PreRender_MotionBlurOpaque(PreRender* this, Gfx** gfxP); +void PreRender_MotionBlur(PreRender* this, Gfx** gfxp, s32 alpha); + +extern u16 (*gWorkBuf)[SCREEN_WIDTH * SCREEN_HEIGHT]; + +static u8 sMotionBlurStatus; + +typedef enum { + /* 0 */ MOTION_BLUR_OFF, + /* 1 */ MOTION_BLUR_SETUP, + /* 2 */ MOTION_BLUR_PROCESS +} MotionBlurStatus; + +void Play_DrawMotionBlur(PlayState* this) { + GraphicsContext* gfxCtx = this->state.gfxCtx; + s32 alpha; + Gfx* gfx; + Gfx* gfxHead; + + if (R_MOTION_BLUR_PRIORITY_ENABLED) { + alpha = R_MOTION_BLUR_PRIORITY_ALPHA; + + if (sMotionBlurStatus == MOTION_BLUR_OFF) { + sMotionBlurStatus = MOTION_BLUR_SETUP; + } + } else if (R_MOTION_BLUR_ENABLED) { + alpha = R_MOTION_BLUR_ALPHA; + + if (sMotionBlurStatus == MOTION_BLUR_OFF) { + sMotionBlurStatus = MOTION_BLUR_SETUP; + } + } else { + alpha = 0; + sMotionBlurStatus = MOTION_BLUR_OFF; + } + + if (sMotionBlurStatus != MOTION_BLUR_OFF) { + OPEN_DISPS(gfxCtx, __FILE__, __LINE__); + + gfxHead = POLY_OPA_DISP; + gfx = Gfx_Open(gfxHead); + gSPDisplayList(OVERLAY_DISP++, gfx); + + this->pauseBgPreRender.fbuf = gfxCtx->curFrameBuffer; + this->pauseBgPreRender.fbufSave = (u16*)gWorkBuf; + + if (sMotionBlurStatus == MOTION_BLUR_PROCESS) { + PreRender_MotionBlur(&this->pauseBgPreRender, &gfx, alpha); + } else { + sMotionBlurStatus = MOTION_BLUR_PROCESS; + } + + PreRender_MotionBlurOpaque(&this->pauseBgPreRender, &gfx); + + gSPEndDisplayList(gfx++); + Gfx_Close(gfxHead, gfx); + POLY_OPA_DISP = gfx; + + CLOSE_DISPS(gfxCtx, __FILE__, __LINE__); + } +} + +void Play_InitMotionBlur(PlayState* this) { + R_MOTION_BLUR_ENABLED = false; + R_MOTION_BLUR_PRIORITY_ENABLED = false; + sMotionBlurStatus = MOTION_BLUR_OFF; + this->csCtx.originalBlurAlpha = R_MOTION_BLUR_ALPHA = 0; +} + +void Play_DestroyMotionBlur(void) { + R_MOTION_BLUR_ENABLED = false; + R_MOTION_BLUR_PRIORITY_ENABLED = false; + sMotionBlurStatus = MOTION_BLUR_OFF; +} + +void Play_SetMotionBlurAlpha(u32 alpha) { + R_MOTION_BLUR_ALPHA = alpha; +} + +void Play_EnableMotionBlur(u32 alpha) { + R_MOTION_BLUR_ALPHA = alpha; + R_MOTION_BLUR_ENABLED = true; +} + +void Play_DisableMotionBlur(void) { + R_MOTION_BLUR_ENABLED = false; +} + +void Play_SetMotionBlurPriorityAlpha(u32 alpha) { + R_MOTION_BLUR_PRIORITY_ALPHA = alpha; +} + +void Play_EnableMotionBlurPriority(u32 alpha) { + R_MOTION_BLUR_PRIORITY_ALPHA = alpha; + R_MOTION_BLUR_PRIORITY_ENABLED = true; +} + +void Play_DisableMotionBlurPriority(void) { + R_MOTION_BLUR_PRIORITY_ENABLED = false; +} +#endif + void Play_Draw(PlayState* this) { GraphicsContext* gfxCtx = this->state.gfxCtx; Lights* sp228; @@ -1177,6 +1308,10 @@ void Play_Draw(PlayState* this) { R_PAUSE_BG_PRERENDER_STATE = PAUSE_BG_PRERENDER_OFF; } +#if ENABLE_MOTION_BLUR + Play_DrawMotionBlur(this); +#endif + if (R_PAUSE_BG_PRERENDER_STATE == PAUSE_BG_PRERENDER_READY) { Gfx* gfxP = POLY_OPA_DISP; @@ -1326,6 +1461,33 @@ void Play_Draw(PlayState* this) { } Play_Draw_skip: + if (ENABLE_MOTION_BLUR && ENABLE_MOTION_BLUR_DEBUG) { + // motion blur testing display + GfxPrint printer; + Gfx* gfxRef; + Gfx* gfx; + + gfxRef = POLY_OPA_DISP; + gfx = Gfx_Open(POLY_OPA_DISP); + gSPDisplayList(OVERLAY_DISP++, gfx); + + GfxPrint_Init(&printer); + GfxPrint_Open(&printer, gfx); + + GfxPrint_SetColor(&printer, 255, 255, 55, 32); + + GfxPrint_SetPos(&printer, 6, 18); + GfxPrint_Printf(&printer, "Motion Blur Enabled: %x", R_MOTION_BLUR_ENABLED); + GfxPrint_SetPos(&printer, 6, 20); + GfxPrint_Printf(&printer, "Motion Blur Alpha: %x", R_MOTION_BLUR_ALPHA); + + gfx = GfxPrint_Close(&printer); + GfxPrint_Destroy(&printer); + + gSPEndDisplayList(gfx++); + Gfx_Close(gfxRef, gfx); + POLY_OPA_DISP = gfx; + } if (this->view.unk_124 != 0) { Camera_Update(GET_ACTIVE_CAM(this));