Floor Scrolling Interpolation (#176)
Some checks are pending
GenerateBuilds / generate-port-o2r (push) Waiting to run
GenerateBuilds / build-windows (push) Blocked by required conditions
GenerateBuilds / build-macos (push) Blocked by required conditions
GenerateBuilds / build-linux (push) Blocked by required conditions
GenerateBuilds / build-switch (push) Blocked by required conditions

* Floor Scrolling Interpolation

* Scrolling Improvements

* Fix scroll stutter

* bump LUS & Torch

* missing prototype

---------

Co-authored-by: Sonic Dreamcaster <alejandro.asenjo88@gmail.com>
This commit is contained in:
Nicholas Estelami 2025-03-28 03:25:11 -04:00 committed by GitHub
parent ea42549842
commit 30ec4720c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 170 additions and 21 deletions

View file

@ -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) \
{ \

View file

@ -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;

View file

@ -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 {

View file

@ -77,6 +77,7 @@ f32 gSavedPathProgress;
UNK_TYPE F_80177CB8;
f32 gWaterLevel;
f32 gPathGroundScroll;
f32 gLastPathTexScroll;
f32 gPathTexScroll;
f32 gPathVelZ;
f32 gPathProgress;

View file

@ -612,6 +612,8 @@ void Game_Update(void) {
// @port: @event: Call GamePostUpdateEvent
CALL_EVENT(GamePostUpdateEvent);
gLastPathTexScroll = gPathTexScroll;
}
}

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

@ -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) {