summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Estelami <NEstelami@users.noreply.github.com>2025-03-28 03:25:11 -0400
committerGitHub <noreply@github.com>2025-03-28 04:25:11 -0300
commit30ec4720c06dd3418641226a6d320067f2706700 (patch)
tree39a6a83b02325b4a027d260854a84f50b5abcfcc
parentea4254984225884476c66da8563166505bfd4aee (diff)
Floor Scrolling Interpolation (#176)
* Floor Scrolling Interpolation * Scrolling Improvements * Fix scroll stutter * bump LUS & Torch * missing prototype --------- Co-authored-by: Sonic Dreamcaster <alejandro.asenjo88@gmail.com>
-rw-r--r--include/gfx.h8
-rw-r--r--include/sf64context.h1
-rw-r--r--src/engine/fox_bg.c152
-rw-r--r--src/engine/fox_context.c1
-rw-r--r--src/engine/fox_game.c2
-rw-r--r--src/engine/fox_hud.c1
-rw-r--r--src/port/Engine.cpp12
-rw-r--r--src/port/Engine.h5
-rw-r--r--src/port/GBIMiddleware.cpp9
9 files changed, 170 insertions, 21 deletions
diff --git a/include/gfx.h b/include/gfx.h
index 3e22a976..99eb5252 100644
--- a/include/gfx.h
+++ b/include/gfx.h
@@ -26,6 +26,14 @@
((height)-1) << G_TEXTURE_IMAGE_FRAC); \
} while (0)
+#define gDPSetupTile2(pkt, fmt, siz, width, height, dw, dh, \
+ cms, cmt, masks, maskt, shifts, shiftt) \
+{ \
+ gDPTileSync(pkt); \
+ gDPSetTile(pkt, fmt, siz, (((width) * siz##_LINE_BYTES)+7)>>3, 0,\
+ G_TX_RENDERTILE, 0, cmt, maskt, shiftt, cms, masks, shifts); \
+}
+
#define gDPSetupTile(pkt, fmt, siz, width, height, dw, dh, \
cms, cmt, masks, maskt, shifts, shiftt) \
{ \
diff --git a/include/sf64context.h b/include/sf64context.h
index 90eb3079..e4bfaf61 100644
--- a/include/sf64context.h
+++ b/include/sf64context.h
@@ -65,6 +65,7 @@ extern f32 gSavedPathProgress;
extern UNK_TYPE F_80177CB8;
extern f32 gWaterLevel;
extern f32 gPathGroundScroll;
+extern f32 gLastPathTexScroll;
extern f32 gPathTexScroll;
extern f32 gPathVelZ;
extern f32 gPathProgress;
diff --git a/src/engine/fox_bg.c b/src/engine/fox_bg.c
index ac9683d6..31c6c59f 100644
--- a/src/engine/fox_bg.c
+++ b/src/engine/fox_bg.c
@@ -27,6 +27,8 @@
#include "water_effect.inc"
+#include <libultra/gbi.h>
+
f32 gWarpZoneBgAlpha;
u8 D_bg_8015F964; // related to water surfaces
f32 D_bg_8015F968; // heat shimmer effect for SO and TI?
@@ -1386,10 +1388,32 @@ void Background_DrawGround(void) {
if (gLevelMode == LEVELMODE_ON_RAILS) {
gDPSetTextureImage(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, SEGMENTED_TO_VIRTUAL(D_CO_601B6C0));
- temp_s0 = fabsf(Math_ModF(2.0f * (gPathTexScroll * 0.2133333f), 128.0f)); // 0.64f / 3.0f
- temp_fv0 = Math_ModF((10000.0f - gPlayer[gPlayerNum].xPath) * 0.32f, 128.0f);
- gDPSetupTile(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, temp_fv0, temp_s0,
- G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD);
+
+ int interpolatedFrames = GameEngine_GetInterpolationFrameCount();
+
+ temp_s0 = fabsf(Math_ModF(2.0f * (gPathTexScroll * 0.2133333f), 128.0f)); // 0.64f / 3.0f
+ temp_fv0 = Math_ModF((10000.0f - gPlayer[gPlayerNum].xPath) * 0.32f, 128.0f);
+
+ float xScroll = temp_fv0;
+ float yScroll = temp_s0;
+
+ float inc = (2.0f * (gPathTexScroll - gLastPathTexScroll) * 0.2133333f) / (float)interpolatedFrames;
+
+ for (int i = 0; i < interpolatedFrames; i++)
+ {
+ gDPSetInterpolation(gMasterDisp++, i);
+
+ gDPSetupTile2(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, temp_fv0, temp_s0,
+ G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD);
+
+ gDPSetTileSizeInterp(gMasterDisp, G_TX_RENDERTILE, xScroll, yScroll, 32 << 2, 32 << 2);
+
+ gMasterDisp += 3;
+
+ yScroll += inc >= 0 ? inc : -inc;
+ yScroll = fabs(Math_ModF(yScroll, 128.0f));
+ }
+
switch (gGroundSurface) {
case SURFACE_GRASS:
gDPLoadTileTexture(gMasterDisp++, D_CO_601B6C0, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32);
@@ -1482,10 +1506,34 @@ void Background_DrawGround(void) {
break;
}
gDPSetTextureImage(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, sp1C4);
- temp_s0 = fabsf(Math_ModF(2.0f * (gPathTexScroll * 0.2133333f), 128.0f));
- temp_fv0 = Math_ModF((10000.0f - gPlayer[gPlayerNum].xPath) * 0.32f, 128.0f);
- gDPSetupTile(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, temp_fv0, temp_s0,
- G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD);
+
+ //gDPSetupTile(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, temp_fv0, temp_s0,
+ //G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD);
+
+ int interpolatedFrames = GameEngine_GetInterpolationFrameCount();
+
+ temp_s0 = fabsf(Math_ModF(2.0f * (gPathTexScroll * 0.2133333f), 128.0f));
+ temp_fv0 = Math_ModF((10000.0f - gPlayer[gPlayerNum].xPath) * 0.32f, 128.0f);
+
+ float xScroll = temp_fv0;
+ float yScroll = temp_s0;
+
+ float inc = (2.0f * (gPathTexScroll - gLastPathTexScroll) * 0.2133333f) / (float)interpolatedFrames;
+
+ for (int i = 0; i < interpolatedFrames; i++)
+ {
+ gDPSetInterpolation(gMasterDisp++, i);
+
+ gDPSetupTile2(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, temp_fv0, temp_s0,
+ G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD);
+
+ gDPSetTileSizeInterp(gMasterDisp, G_TX_RENDERTILE, xScroll, yScroll, 32 << 2, 32 << 2);
+
+ gMasterDisp += 3;
+
+ yScroll += inc >= 0 ? inc : -inc;
+ yScroll = fabs(Math_ModF(yScroll, 128.0f));
+ }
// CENTER FAR
Matrix_Push(&gGfxMatrix);
@@ -1569,10 +1617,31 @@ void Background_DrawGround(void) {
// gPathTexScroll -= (32.0f * 36.7f) / 2.0f;
// }
gDPSetTextureImage(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, sp1C4);
- temp_s0 = fabsf(Math_ModF(2.0f * (gPathTexScroll * 0.2133333f), 128.0f)); // 0.64f / 3.0f
- temp_fv0 = Math_ModF((10000.0f - gPlayer[gPlayerNum].xPath) * 0.32f, 128.0f);
- gDPSetupTile(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, temp_fv0, temp_s0,
- G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD);
+
+ int interpolatedFrames = GameEngine_GetInterpolationFrameCount();
+
+ temp_s0 = fabsf(Math_ModF(2.0f * (gPathTexScroll * 0.2133333f), 128.0f)); // 0.64f / 3.0f
+ temp_fv0 = Math_ModF((10000.0f - gPlayer[gPlayerNum].xPath) * 0.32f, 128.0f);
+
+ float xScroll = temp_fv0;
+ float yScroll = temp_s0;
+
+ float inc = (2.0f * (gPathTexScroll - gLastPathTexScroll) * 0.2133333f) / (float)interpolatedFrames;
+
+ for (int i = 0; i < interpolatedFrames; i++)
+ {
+ gDPSetInterpolation(gMasterDisp++, i);
+
+ gDPSetupTile2(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, temp_fv0, temp_s0,
+ G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD);
+
+ gDPSetTileSizeInterp(gMasterDisp, G_TX_RENDERTILE, xScroll, yScroll, 32 << 2, 32 << 2);
+
+ gMasterDisp += 3;
+
+ yScroll += inc >= 0 ? inc : -inc;
+ yScroll = fabs(Math_ModF(yScroll, 128.0f));
+ }
// Original Display (Center)
Matrix_Push(&gGfxMatrix);
@@ -1641,11 +1710,32 @@ void Background_DrawGround(void) {
gDPLoadTileTexture(gMasterDisp++, SEGMENTED_TO_VIRTUAL(D_AQ_600AB68), G_IM_FMT_RGBA, G_IM_SIZ_16b, 32,
32);
gDPSetTextureImage(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, SEGMENTED_TO_VIRTUAL(D_AQ_600AB68));
- temp_s0 = fabsf(Math_ModF(2.0f * (gPathTexScroll * 0.2133333f), 128.0f));
- temp_fv0 = Math_ModF((10000.0f - gPlayer[gPlayerNum].xPath) * 0.32f, 128.0f);
- gDPSetupTile(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, temp_fv0, temp_s0,
- G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD);
+ int interpolatedFrames = GameEngine_GetInterpolationFrameCount();
+
+ temp_s0 = fabsf(Math_ModF(2.0f * (gPathTexScroll * 0.2133333f), 128.0f));
+ temp_fv0 = Math_ModF((10000.0f - gPlayer[gPlayerNum].xPath) * 0.32f, 128.0f);
+
+ float xScroll = temp_fv0;
+ float yScroll = temp_s0;
+
+ float inc = (2.0f * (gPathTexScroll - gLastPathTexScroll) * 0.2133333f) / (float)interpolatedFrames;
+
+ for (int i = 0; i < interpolatedFrames; i++)
+ {
+ gDPSetInterpolation(gMasterDisp++, i);
+
+ gDPSetupTile2(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, temp_fv0, temp_s0,
+ G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD);
+
+ gDPSetTileSizeInterp(gMasterDisp, G_TX_RENDERTILE, xScroll, yScroll, 32 << 2, 32 << 2);
+
+ gMasterDisp += 3;
+
+ yScroll += inc >= 0 ? inc : -inc;
+ yScroll = fabs(Math_ModF(yScroll, 128.0f));
+ }
+
// CENTER FAR
Matrix_Push(&gGfxMatrix);
Matrix_Translate(gGfxMatrix, 0.0f, 0.0f, -3000.0f, MTXF_APPLY);
@@ -1695,10 +1785,32 @@ void Background_DrawGround(void) {
gDPLoadTileTexture(gMasterDisp++, SEGMENTED_TO_VIRTUAL(D_AQ_602ACC0), G_IM_FMT_RGBA, G_IM_SIZ_16b, 32,
32);
gDPSetTextureImage(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, SEGMENTED_TO_VIRTUAL(D_AQ_602ACC0));
- temp_s0 = fabsf(Math_ModF(2.0f * (gPathTexScroll * 0.2133333f), 128.0f));
- temp_fv0 = Math_ModF((10000.0f - gPlayer[gPlayerNum].xPath) * 0.32f, 128.0f);
- gDPSetupTile(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, temp_fv0, temp_s0,
- G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD);
+
+ int interpolatedFrames = GameEngine_GetInterpolationFrameCount();
+
+ temp_s0 = fabsf(Math_ModF(2.0f * (gPathTexScroll * 0.2133333f), 128.0f));
+ temp_fv0 = Math_ModF((10000.0f - gPlayer[gPlayerNum].xPath) * 0.32f, 128.0f);
+
+ float xScroll = temp_fv0;
+ float yScroll = temp_s0;
+
+ float inc = (2.0f * (gPathTexScroll - gLastPathTexScroll) * 0.2133333f) / (float)interpolatedFrames;
+
+ for (int i = 0; i < interpolatedFrames; i++)
+ {
+ gDPSetInterpolation(gMasterDisp++, i);
+
+ gDPSetupTile2(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, temp_fv0, temp_s0,
+ G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD);
+
+ gDPSetTileSizeInterp(gMasterDisp, G_TX_RENDERTILE, xScroll, yScroll, 32 << 2, 32 << 2);
+
+ gMasterDisp += 3;
+
+ yScroll += inc >= 0 ? inc : -inc;
+ yScroll = fabs(Math_ModF(yScroll, 128.0f));
+ }
+
if (gAqDrawMode != 0) {
RCP_SetupDL(&gMasterDisp, SETUPDL_47);
} else {
diff --git a/src/engine/fox_context.c b/src/engine/fox_context.c
index b5fa2454..25b1ec72 100644
--- a/src/engine/fox_context.c
+++ b/src/engine/fox_context.c
@@ -77,6 +77,7 @@ f32 gSavedPathProgress;
UNK_TYPE F_80177CB8;
f32 gWaterLevel;
f32 gPathGroundScroll;
+f32 gLastPathTexScroll;
f32 gPathTexScroll;
f32 gPathVelZ;
f32 gPathProgress;
diff --git a/src/engine/fox_game.c b/src/engine/fox_game.c
index 25c2e770..a0266dc6 100644
--- a/src/engine/fox_game.c
+++ b/src/engine/fox_game.c
@@ -612,6 +612,8 @@ void Game_Update(void) {
// @port: @event: Call GamePostUpdateEvent
CALL_EVENT(GamePostUpdateEvent);
+
+ gLastPathTexScroll = gPathTexScroll;
}
}
diff --git a/src/engine/fox_hud.c b/src/engine/fox_hud.c
index 377a5ae4..2b30ab7b 100644
--- a/src/engine/fox_hud.c
+++ b/src/engine/fox_hud.c
@@ -6260,6 +6260,7 @@ void Venom1_LevelStart2(Player* player) {
player->pos.x += player->vel.x;
player->pos.y += player->vel.y;
+ gLastPathTexScroll = gPathTexScroll;
gPathTexScroll += player->vel.z;
player->bankAngle = player->rot.z + player->zRotBank + player->zRotBarrelRoll;
diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp
index 4da6fd77..e0307752 100644
--- a/src/port/Engine.cpp
+++ b/src/port/Engine.cpp
@@ -452,8 +452,11 @@ void GameEngine::RunCommands(Gfx* Commands, const std::vector<std::unordered_map
// Process window events for resize, mouse, keyboard events
wnd->HandleEvents();
+ gInterpolationIndex = 0;
+
for (const auto& m : mtx_replacements) {
wnd->DrawAndRunGraphicsCommands(Commands, m);
+ gInterpolationIndex++;
}
bool curAltAssets = CVarGetInteger("gEnhancements.Mods.AlternateAssets", 0);
@@ -538,6 +541,15 @@ uint32_t GameEngine::GetInterpolationFPS() {
CVarGetInteger("gInterpolationFPS", 60));
}
+uint32_t GameEngine::GetInterpolationFrameCount()
+{
+ return ceil((float)GetInterpolationFPS() / 30.0f);
+}
+
+extern "C" uint32_t GameEngine_GetInterpolationFrameCount() {
+ return GameEngine::GetInterpolationFrameCount();
+}
+
void GameEngine::ShowMessage(const char* title, const char* message, SDL_MessageBoxFlags type) {
#if defined(__SWITCH__)
SPDLOG_ERROR(message);
diff --git a/src/port/Engine.h b/src/port/Engine.h
index 26f9109f..43dbf066 100644
--- a/src/port/Engine.h
+++ b/src/port/Engine.h
@@ -39,7 +39,8 @@ class GameEngine {
static void AudioExit();
static void RunCommands(Gfx* Commands, const std::vector<std::unordered_map<Mtx*, MtxF>>& mtx_replacements);
static void Destroy();
- static uint32_t GetInterpolationFPS();
+ static uint32_t GetInterpolationFPS();
+ static uint32_t GetInterpolationFrameCount();
static void ProcessGfxCommands(Gfx* commands);
static int ShowYesNoBox(const char* title, const char* box);
@@ -80,5 +81,7 @@ uint32_t OTRGetGameRenderWidth();
uint32_t OTRGetGameRenderHeight();
void* GameEngine_Malloc(size_t size);
void GameEngine_GetTextureInfo(const char* path, int32_t* width, int32_t* height, float* scale, bool* custom);
+void gDPSetTileSizeInterp(Gfx* pkt, int t, float uls, float ult, float lrs, float lrt);
+uint32_t GameEngine_GetInterpolationFrameCount();
#define memalloc(size) GameEngine_Malloc(size)
#endif \ No newline at end of file
diff --git a/src/port/GBIMiddleware.cpp b/src/port/GBIMiddleware.cpp
index 5e35e312..e0355a6d 100644
--- a/src/port/GBIMiddleware.cpp
+++ b/src/port/GBIMiddleware.cpp
@@ -20,6 +20,15 @@ extern "C" void gSPDisplayList(Gfx* pkt, Gfx* dl) {
__gSPDisplayList(pkt, dl);
}
+extern "C" void gDPSetTileSizeInterp(Gfx* pkt, int t, float uls, float ult, float lrs, float lrt)
+{
+ __gDPSetTileSizeInterp(pkt++, t, 0, 0, 0, 0);
+ memcpy(&pkt[0].words.w0, &uls, sizeof(float));
+ memcpy(&pkt[0].words.w1, &ult, sizeof(float));
+ memcpy(&pkt[1].words.w0, &lrs, sizeof(float));
+ memcpy(&pkt[1].words.w1, &lrt, sizeof(float));
+}
+
extern "C" void gSPVertex(Gfx* pkt, uintptr_t v, int n, int v0) {
if (GameEngine_OTRSigCheck((char*)v) == 1) {