From d88bd76041ff0f30412260a5662c57bdac46309f Mon Sep 17 00:00:00 2001 From: Victor Diego Alejandro Diaz Urbaneja <49420637+sodomon2@users.noreply.github.com> Date: Wed, 9 Sep 2020 01:25:51 -0400 Subject: [PATCH 01/20] Update README_es_ES.md --- README_es_ES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README_es_ES.md b/README_es_ES.md index 44a34a1c..1382d88b 100644 --- a/README_es_ES.md +++ b/README_es_ES.md @@ -155,6 +155,11 @@ sudo xbps-install -S base-devel python3 audiofile-devel SDL2-devel glew-devel sudo xbps-install -S base-devel python3 audiofile-devel-32bit SDL2-devel-32bit glew-devel-32bit ``` +##### Alpine Linux - (compilando para 32 bits y 64 bits) +``` +sudo apk add build-base python3 audiofile-dev sdl2-dev glew-dev +``` + #### Compila el ejecutable. Ejecuta `make` para compilar (por defecto `VERSION=us`) From 9ee16a3ef6337354e7b18f8a2bbf9552c874194e Mon Sep 17 00:00:00 2001 From: s4Ys369 <69868583+s4Ys369@users.noreply.github.com> Date: Sat, 3 Oct 2020 19:10:55 -0400 Subject: [PATCH 02/20] Bugfix: Discord Integration crashes Fix by PeachyPeach to stop crashes with Discord Integration when enter later Bowser courses and fights early --- src/pc/discord/discordrpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pc/discord/discordrpc.c b/src/pc/discord/discordrpc.c index f814f4d5..6ce0aebe 100644 --- a/src/pc/discord/discordrpc.c +++ b/src/pc/discord/discordrpc.c @@ -186,7 +186,7 @@ static void set_state(void) { // when exiting a stage the act doesn't get reset if (gCurrActNum && gCurrCourseNum) { // any stage over 19 is a special stage without acts - if (gCurrCourseNum < 19) { + if (gCurrCourseNum <= COURSE_STAGES_MAX) { void **actNameTbl; #ifndef VERSION_EU actNameTbl = segmented_to_virtual(seg2_act_name_table); From e6d2e7afea17d9d2b2d3fcc6e389488ba1146ced Mon Sep 17 00:00:00 2001 From: s4Ys369 <69868583+s4Ys369@users.noreply.github.com> Date: Fri, 9 Oct 2020 11:40:45 -0400 Subject: [PATCH 03/20] Revert "Bugfix: Discord Integration crashes" --- src/pc/discord/discordrpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pc/discord/discordrpc.c b/src/pc/discord/discordrpc.c index 6ce0aebe..f814f4d5 100644 --- a/src/pc/discord/discordrpc.c +++ b/src/pc/discord/discordrpc.c @@ -186,7 +186,7 @@ static void set_state(void) { // when exiting a stage the act doesn't get reset if (gCurrActNum && gCurrCourseNum) { // any stage over 19 is a special stage without acts - if (gCurrCourseNum <= COURSE_STAGES_MAX) { + if (gCurrCourseNum < 19) { void **actNameTbl; #ifndef VERSION_EU actNameTbl = segmented_to_virtual(seg2_act_name_table); From 2750d7aaf29d8d1d9ca0c3612c3d1f109038d4a5 Mon Sep 17 00:00:00 2001 From: TeeVenDick Date: Wed, 14 Oct 2020 23:17:02 +0800 Subject: [PATCH 04/20] Added Bahasa Melayu translation of readme --- README.md | 2 +- README_ms_MY.md | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 README_ms_MY.md diff --git a/README.md b/README.md index 86d7bd43..ce9742bc 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Run `./extract_assets.py --clean && make clean` or `make distclean` to remove RO Please contribute **first** to the [nightly branch](https://github.com/sm64pc/sm64ex/tree/nightly/). New functionality will be merged to master once they're considered to be well-tested. -*Read this in other languages: [Español](README_es_ES.md), [Português](README_pt_BR.md) or [简体中文](README_zh_CN.md).* +*Read this in other languages: [Español](README_es_ES.md), [Português](README_pt_BR.md), [简体中文](README_zh_CN.md) atau [Bahasa Melayu](README_ms_MY.md).* ## New features diff --git a/README_ms_MY.md b/README_ms_MY.md new file mode 100644 index 00000000..c8709bb6 --- /dev/null +++ b/README_ms_MY.md @@ -0,0 +1,30 @@ +# sm64ex +Fork dari [sm64-port/sm64-port](https://github.com/sm64-port/sm64-port) dengan ciri tambahan. + +Jangan ragu-ragu untuk melaporkan pepijat dan memberi sumbangan, tetapi ingat, tidak boleh **memuat naik aset berhak cipta**. +Jalankan `./extract_assets.py --clean && make clean` atau `make distclean` untuk membuang kandungan yang berasal dari ROM. + +Sila sumbangkan **dahulu** ke [nightly branch](https://github.com/sm64pc/sm64ex/tree/nightly/). Fungsi baru akan digabungkan ke master setelah siap diuji. + +*Baca ini dalam bahasa lain: [Español](README_es_ES.md), [Português](README_pt_BR.md), [简体中文](README_zh_CN.md) atau [Bahasa Melayu](README_ms_MY.md).* + +## Ciri-ciri baru + + * Menu pilihan dengan pelbagai tetapan, termasuk pemetaan semula butang. + * Pilihan untuk pemuatan data luaran (setakat ini hanya tekstur dan papan suara yang dipasang), memberikan sokongan untuk pek tekstur tersuai. + * Pilihan untuk rupa kamera analog dan tetikus (menggunakan [Puppycam](https://github.com/FazanaJ/puppycam)). + * Pilihan untuk perender berasaskan OpenGL1.3 untuk mesin yang lebih tua, serta perender asli GL2.1, D3D11 dan D3D12 dari Emill's [n64-fast3d-engine](https://github.com/Emill/n64-fast3d-engine/). + * Pilihan untuk menyahaktifkan jarak lukisan. + * Pilihan untuk pembaikan model dan tekstur (mis. Tekstur asap). + * Langkau cutscenes pengenalan Peach & Lakitu dengan pilihan CLI `--skip-intro`. + * Menu cheats di Options (aktifkan dengan `--cheats` atau dengan menekan L tiga kali di menu pause). + * Sokongan untuk kedua-dua fail simpanan little-endian dan big-endian (bermaksud anda boleh menggunakan fail simpan dari sm64-port dan kebanyakan emulator), serta format simpanan berasaskan teks pilihan. + +Perubahan terbaru di Nightly telah memindahkan laluan fail simpan dan konfigurasi ke `%HOMEPATH%\AppData\Roaming\sm64pc` pada Windows dan `$HOME/.local/share/sm64pc` di Linux. Tingkah laku ini dapat diubah dengan `--savepath` pilihan CLI. +For example `--savepath .` akan membaca simpanan dari direktori semasa (yang tidak selalu sepadan dengan direktori exe, tetapi selalunya ia berlaku); + `--savepath '!'` akan membaca simpanan dari direktori executable. + +## Pembangunan +Untuk arahan pembangunan, sila rujuk di [wiki](https://github.com/sm64pc/sm64ex/wiki). + +**Pastikan anda mempunyai MXE terlebih dahulu sebelum cuba compile untuk Windows di Linux dan WSL. Ikuti panduan di wiki.** From d67df4ce903c9dea17773916a3571cd1eb9eaf31 Mon Sep 17 00:00:00 2001 From: TeeVenDick Date: Fri, 16 Oct 2020 12:51:22 +0800 Subject: [PATCH 05/20] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ce9742bc..dbf736bb 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Run `./extract_assets.py --clean && make clean` or `make distclean` to remove RO Please contribute **first** to the [nightly branch](https://github.com/sm64pc/sm64ex/tree/nightly/). New functionality will be merged to master once they're considered to be well-tested. -*Read this in other languages: [Español](README_es_ES.md), [Português](README_pt_BR.md), [简体中文](README_zh_CN.md) atau [Bahasa Melayu](README_ms_MY.md).* +*Read this in other languages: [Español](README_es_ES.md), [Português](README_pt_BR.md), [简体中文](README_zh_CN.md) or [Bahasa Melayu](README_ms_MY.md).* ## New features From b9cdcb2d81d9e7a688d35e065d4e34b5eeed290e Mon Sep 17 00:00:00 2001 From: DanTheMan827 <790119+DanTheMan827@users.noreply.github.com> Date: Fri, 22 Jan 2021 11:15:23 -0600 Subject: [PATCH 06/20] Load gamecontrollerdb.txt before SDL_Init --- src/pc/controller/controller_sdl2.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pc/controller/controller_sdl2.c b/src/pc/controller/controller_sdl2.c index a71a82eb..c7e9c7c7 100644 --- a/src/pc/controller/controller_sdl2.c +++ b/src/pc/controller/controller_sdl2.c @@ -89,13 +89,6 @@ static void controller_sdl_bind(void) { } static void controller_sdl_init(void) { - if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS) != 0) { - fprintf(stderr, "SDL init error: %s\n", SDL_GetError()); - return; - } - - haptics_enabled = (SDL_InitSubSystem(SDL_INIT_HAPTIC) == 0); - // try loading an external gamecontroller mapping file uint64_t gcsize = 0; void *gcdata = fs_load_file("gamecontrollerdb.txt", &gcsize); @@ -109,6 +102,13 @@ static void controller_sdl_init(void) { free(gcdata); } + if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS) != 0) { + fprintf(stderr, "SDL init error: %s\n", SDL_GetError()); + return; + } + + haptics_enabled = (SDL_InitSubSystem(SDL_INIT_HAPTIC) == 0); + #ifdef BETTERCAMERA if (newcam_mouse == 1) SDL_SetRelativeMouseMode(SDL_TRUE); From f9a26149ebf4d18a9743c2abb8d0b72264b1ae1f Mon Sep 17 00:00:00 2001 From: DanTheMan827 <790119+DanTheMan827@users.noreply.github.com> Date: Sun, 24 Jan 2021 14:48:15 -0600 Subject: [PATCH 07/20] Add NO_PIE option --- Makefile | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 772f31a7..621aedd9 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,9 @@ TARGET_WEB ?= 0 # Makeflag to enable OSX fixes OSX_BUILD ?= 0 +# Enable -no-pie linker option +NO_PIE ?= 1 + # Specify the target you are building for, TARGET_BITS=0 means native TARGET_ARCH ?= native TARGET_BITS ?= 0 @@ -638,9 +641,13 @@ else ifeq ($(OSX_BUILD),1) LDFLAGS := -lm $(BACKEND_LDFLAGS) -no-pie -lpthread else - LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -lm $(BACKEND_LDFLAGS) -no-pie -lpthread + LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -lm $(BACKEND_LDFLAGS) -lpthread -ldl + ifeq ($(NO_PIE), 1) + LDFLAGS += -no-pie + endif + ifeq ($(DISCORDRPC),1) - LDFLAGS += -ldl -Wl,-rpath . + LDFLAGS += -Wl,-rpath . endif endif # End of LDFLAGS From 65690040f21827300a7e95e91adc64453b5ba309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Fri, 12 Nov 2021 16:15:27 +0100 Subject: [PATCH 08/20] Haiku port oneliner --- include/PR/os_libc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/PR/os_libc.h b/include/PR/os_libc.h index 9eb872e7..05ab6682 100644 --- a/include/PR/os_libc.h +++ b/include/PR/os_libc.h @@ -5,7 +5,7 @@ // old bstring functions that aren't present on some platforms -#if defined(__APPLE__) +#if defined(__APPLE__) || defined(__HAIKU__) // macOS libc has them #include From 24423cccc9eae09e5b1dd974a4031b8c23862c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Sun, 14 Nov 2021 16:07:54 +0100 Subject: [PATCH 09/20] No libdl on Haiku libm and pthreads are superfluous as well. --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 621aedd9..9ab49a3b 100644 --- a/Makefile +++ b/Makefile @@ -640,6 +640,9 @@ else ifeq ($(TARGET_RPI),1) else ifeq ($(OSX_BUILD),1) LDFLAGS := -lm $(BACKEND_LDFLAGS) -no-pie -lpthread +else ifeq ($(HOST_OS),Haiku) + LDFLAGS := $(BACKEND_LDFLAGS) -no-pie + else LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -lm $(BACKEND_LDFLAGS) -lpthread -ldl ifeq ($(NO_PIE), 1) From c4a55e351de1aea3405496a214804bdbae8dc709 Mon Sep 17 00:00:00 2001 From: blackops7799 Date: Wed, 24 Nov 2021 21:32:38 -0500 Subject: [PATCH 10/20] Always syncronize framerate with timer (Fixes #381) --- src/pc/gfx/gfx_sdl2.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c index f808db90..125e6f26 100644 --- a/src/pc/gfx/gfx_sdl2.c +++ b/src/pc/gfx/gfx_sdl2.c @@ -154,14 +154,12 @@ static inline void gfx_sdl_set_vsync(const bool enabled) { if (vblanks) { printf("determined swap interval: %d\n", vblanks); SDL_GL_SetSwapInterval(vblanks); - use_timer = false; return; } else { printf("could not determine swap interval, falling back to timer sync\n"); } } - use_timer = true; SDL_GL_SetSwapInterval(0); } @@ -233,6 +231,7 @@ static void gfx_sdl_init(const char *window_title) { gfx_sdl_set_fullscreen(); perf_freq = SDL_GetPerformanceFrequency(); + frame_time = perf_freq / FRAMERATE; for (size_t i = 0; i < sizeof(windows_scancode_table) / sizeof(SDL_Scancode); i++) { @@ -362,7 +361,7 @@ static inline void sync_framerate_with_timer(void) { } static void gfx_sdl_swap_buffers_begin(void) { - if (use_timer) sync_framerate_with_timer(); + sync_framerate_with_timer(); SDL_GL_SwapWindow(wnd); } From d1b787427cb19d8a236bac6f2fcde19890becd66 Mon Sep 17 00:00:00 2001 From: blackops7799 Date: Thu, 25 Nov 2021 03:05:58 -0500 Subject: [PATCH 11/20] Remove test_vsync Simplify sync_framerate_with_timer --- src/pc/gfx/gfx_sdl2.c | 95 ++++++++++--------------------------------- 1 file changed, 22 insertions(+), 73 deletions(-) diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c index 125e6f26..daff6864 100644 --- a/src/pc/gfx/gfx_sdl2.c +++ b/src/pc/gfx/gfx_sdl2.c @@ -54,7 +54,9 @@ static void (*kb_all_keys_up)(void) = NULL; // whether to use timer for frame control static bool use_timer = true; // time between consequtive game frames, in perf counter ticks -static double frame_time = 0.0; // set in init() +static double frame_rate = 0.0; // set in init() +// time in which a frame began, in perf counter ticks +static double frame_time = 0.0; // updated in start_frame() // GetPerformanceFrequency static double perf_freq = 0.0; @@ -115,54 +117,6 @@ static inline void sys_sleep(const uint64_t us) { usleep(us); } -static int test_vsync(void) { - // Even if SDL_GL_SetSwapInterval succeeds, it doesn't mean that VSync actually works. - // A 60 Hz monitor should have a swap interval of 16.67 milliseconds. - // Try to detect the length of a vsync by swapping buffers some times. - // Since the graphics card may enqueue a fixed number of frames, - // first send in four dummy frames to hopefully fill the queue. - // This method will fail if the refresh rate is changed, which, in - // combination with that we can't control the queue size (i.e. lag) - // is a reason this generic SDL2 backend should only be used as last resort. - - for (int i = 0; i < 8; ++i) - SDL_GL_SwapWindow(wnd); - - Uint32 start = SDL_GetTicks(); - SDL_GL_SwapWindow(wnd); - SDL_GL_SwapWindow(wnd); - SDL_GL_SwapWindow(wnd); - SDL_GL_SwapWindow(wnd); - Uint32 end = SDL_GetTicks(); - - const float average = 4.0 * 1000.0 / (end - start); - - if (average > 27.0f && average < 33.0f) return 1; - if (average > 57.0f && average < 63.0f) return 2; - if (average > 86.0f && average < 94.0f) return 3; - if (average > 115.0f && average < 125.0f) return 4; - if (average > 234.0f && average < 246.0f) return 8; - - return 0; -} - -static inline void gfx_sdl_set_vsync(const bool enabled) { - if (enabled) { - // try to detect refresh rate - SDL_GL_SetSwapInterval(1); - const int vblanks = gCLIOpts.SyncFrames ? (int)gCLIOpts.SyncFrames : test_vsync(); - if (vblanks) { - printf("determined swap interval: %d\n", vblanks); - SDL_GL_SetSwapInterval(vblanks); - return; - } else { - printf("could not determine swap interval, falling back to timer sync\n"); - } - } - - SDL_GL_SetSwapInterval(0); -} - static void gfx_sdl_set_fullscreen(void) { if (configWindow.reset) configWindow.fullscreen = false; @@ -198,7 +152,7 @@ static void gfx_sdl_reset_dimension_and_pos(void) { SDL_SetWindowSize(wnd, configWindow.w, configWindow.h); SDL_SetWindowPosition(wnd, xpos, ypos); // in case vsync changed - gfx_sdl_set_vsync(configWindow.vsync); + SDL_GL_SetSwapInterval(configWindow.vsync); } static void gfx_sdl_init(const char *window_title) { @@ -226,13 +180,13 @@ static void gfx_sdl_init(const char *window_title) { ); ctx = SDL_GL_CreateContext(wnd); - gfx_sdl_set_vsync(configWindow.vsync); + SDL_GL_SetSwapInterval(configWindow.vsync); gfx_sdl_set_fullscreen(); perf_freq = SDL_GetPerformanceFrequency(); - frame_time = perf_freq / FRAMERATE; + frame_rate = perf_freq / FRAMERATE; for (size_t i = 0; i < sizeof(windows_scancode_table) / sizeof(SDL_Scancode); i++) { inverted_scancode_table[windows_scancode_table[i]] = i; @@ -333,39 +287,34 @@ static void gfx_sdl_set_keyboard_callbacks(kb_callback_t on_key_down, kb_callbac } static bool gfx_sdl_start_frame(void) { + frame_time = SDL_GetPerformanceCounter(); return true; } static inline void sync_framerate_with_timer(void) { - static double last_time; - static double last_sec; - static int frames_since_last_sec; - const double now = SDL_GetPerformanceCounter(); - frames_since_last_sec += 1; - if (last_time) { - const double elapsed = last_sec ? (now - last_sec) : (now - last_time); - if ((elapsed < frame_time && !last_sec) || (elapsed < frames_since_last_sec * frame_time && last_sec)) { - const double delay = last_sec ? frames_since_last_sec * frame_time - elapsed : frame_time - elapsed; - sys_sleep(delay / perf_freq * 1000000.0); - last_time = now + delay; - } else { - last_time = now; - } - if ((int64_t)(now / perf_freq) > (int64_t)(last_sec / perf_freq)) { - last_sec = last_time; - frames_since_last_sec = 0; - } - } else { - last_time = now; + // calculate how long it took for the frame to render + const double frame_length = SDL_GetPerformanceCounter() - frame_time; + + if (frame_length < frame_rate) { + // Only sleep if we have time to spare + const double remain = frame_rate - frame_length; + // Sleep remaining time away + sys_sleep(remain / perf_freq * 1000000.0); } } static void gfx_sdl_swap_buffers_begin(void) { - sync_framerate_with_timer(); + // Swap after we finished rendering, only if this frame wasn't dropped. + // This will wait for vblank if vsync is enabled and then update our window with our render. SDL_GL_SwapWindow(wnd); } static void gfx_sdl_swap_buffers_end(void) { + // The game isn't always going to run at a consistent rate, + // with frame pacing going up and down depending on hardware performance. + // Sleep off any remaining time to make the main loop iteration be called at a consistent frane rate. + // We do this after our swap, because it actually will take the time to swap into account. + sync_framerate_with_timer(); } static double gfx_sdl_get_time(void) { From 48247471fe385a3113d197ba458fb546bf3fa7e8 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Thu, 25 Nov 2021 16:06:56 +0300 Subject: [PATCH 12/20] update 60fps patch --- enhancements/60fps_ex.patch | 1987 ++++++++++++++++++++++++++++++++++- 1 file changed, 1969 insertions(+), 18 deletions(-) diff --git a/enhancements/60fps_ex.patch b/enhancements/60fps_ex.patch index 5980d9bf..6fd89548 100644 --- a/enhancements/60fps_ex.patch +++ b/enhancements/60fps_ex.patch @@ -1,3 +1,1967 @@ +diff --git a/enhancements/60fps_ex.patch b/enhancements/60fps_ex.patch +index 5980d9b..e69de29 100644 +--- a/enhancements/60fps_ex.patch ++++ b/enhancements/60fps_ex.patch +@@ -1,1959 +0,0 @@ +-diff --git a/include/types.h b/include/types.h +-index b3dc27e..c46bdf0 100644 +---- a/include/types.h +-+++ b/include/types.h +-@@ -118,6 +118,10 @@ struct GraphNodeObject_sub +- /*0x0A 0x42*/ u16 animTimer; +- /*0x0C 0x44*/ s32 animFrameAccelAssist; +- /*0x10 0x48*/ s32 animAccel; +-+ s16 prevAnimFrame; +-+ s16 prevAnimID; +-+ u32 prevAnimFrameTimestamp; +-+ struct Animation *prevAnimPtr; +- }; +- +- struct GraphNodeObject +-@@ -128,11 +132,22 @@ struct GraphNodeObject +- /*0x19*/ s8 unk19; +- /*0x1A*/ Vec3s angle; +- /*0x20*/ Vec3f pos; +-+ Vec3s prevAngle; +-+ Vec3f prevPos; +-+ u32 prevTimestamp; +-+ Vec3f prevShadowPos; +-+ u32 prevShadowPosTimestamp; +- /*0x2C*/ Vec3f scale; +-+ Vec3f prevScale; +-+ u32 prevScaleTimestamp; +- /*0x38*/ struct GraphNodeObject_sub unk38; +- /*0x4C*/ struct SpawnInfo *unk4C; +- /*0x50*/ Mat4 *throwMatrix; // matrix ptr +-+ Mat4 prevThrowMatrix; +-+ u32 prevThrowMatrixTimestamp; +-+ Mat4 *throwMatrixInterpolated; +- /*0x54*/ Vec3f cameraToObject; +-+ u32 skipInterpolationTimestamp; +- }; +- +- struct ObjectNode +-@@ -243,6 +258,10 @@ struct Surface +- } normal; +- /*0x28*/ f32 originOffset; +- /*0x2C*/ struct Object *object; +-+ Vec3s prevVertex1; +-+ Vec3s prevVertex2; +-+ Vec3s prevVertex3; +-+ u32 modifiedTimestamp; +- }; +- +- struct MarioBodyState +-diff --git a/src/engine/graph_node.h b/src/engine/graph_node.h +-index 802d97a..1b0d677 100644 +---- a/src/engine/graph_node.h +-+++ b/src/engine/graph_node.h +-@@ -110,6 +110,8 @@ struct GraphNodePerspective +- /*0x1C*/ f32 fov; // horizontal field of view in degrees +- /*0x20*/ s16 near; // near clipping plane +- /*0x22*/ s16 far; // far clipping plane +-+ f32 prevFov; +-+ f32 prevTimestamp; +- }; +- +- /** An entry in the master list. It is a linked list of display lists +-@@ -118,7 +120,9 @@ struct GraphNodePerspective +- struct DisplayListNode +- { +- Mtx *transform; +-+ void *transformInterpolated; +- void *displayList; +-+ void *displayListInterpolated; +- struct DisplayListNode *next; +- }; +- +-@@ -185,7 +189,11 @@ struct GraphNodeCamera +- } config; +- /*0x1C*/ Vec3f pos; +- /*0x28*/ Vec3f focus; +-+ Vec3f prevPos; +-+ Vec3f prevFocus; +-+ u32 prevTimestamp; +- /*0x34*/ Mat4 *matrixPtr; // pointer to look-at matrix of this camera as a Mat4 +-+ Mat4 *matrixPtrInterpolated; +- /*0x38*/ s16 roll; // roll in look at matrix. Doesn't account for light direction unlike rollScreen. +- /*0x3A*/ s16 rollScreen; // rolls screen while keeping the light direction consistent +- }; +-@@ -226,7 +234,8 @@ struct GraphNodeRotation +- /*0x00*/ struct GraphNode node; +- /*0x14*/ void *displayList; +- /*0x18*/ Vec3s rotation; +-- u8 pad1E[2]; +-+ Vec3s prevRotation; +-+ u32 prevTimestamp; +- }; +- +- /** GraphNode part that transforms itself and its children based on animation +-@@ -323,6 +332,9 @@ struct GraphNodeBackground +- /*0x00*/ struct FnGraphNode fnNode; +- /*0x18*/ s32 unused; +- /*0x1C*/ s32 background; // background ID, or rgba5551 color if fnNode.func is null +-+ Vec3f prevCameraPos; +-+ Vec3f prevCameraFocus; +-+ u32 prevCameraTimestamp; +- }; +- +- /** Renders the object that Mario is holding. +-@@ -333,6 +345,8 @@ struct GraphNodeHeldObject +- /*0x18*/ s32 playerIndex; +- /*0x1C*/ struct Object *objNode; +- /*0x20*/ Vec3s translation; +-+ Vec3f prevShadowPos; +-+ u32 prevShadowPosTimestamp; +- }; +- +- /** A node that allows an object to specify a different culling radius than the +-diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c +-index 1da535b..49a5c03 100644 +---- a/src/engine/surface_collision.c +-+++ b/src/engine/surface_collision.c +-@@ -8,6 +8,7 @@ +- #include "surface_collision.h" +- #include "surface_load.h" +- #include "math_util.h" +-+#include "game/game_init.h" +- +- /************************************************** +- * WALLS * +-@@ -394,26 +395,44 @@ f32 find_floor_height_and_data(f32 xPos, f32 yPos, f32 zPos, struct FloorGeometr +- return floorHeight; +- } +- +-+u8 gInterpolatingSurfaces; +-+ +- /** +- * Iterate through the list of floors and find the first floor under a given point. +- */ +- static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 x, s32 y, s32 z, f32 *pheight) { +- register struct Surface *surf; +-- register s32 x1, z1, x2, z2, x3, z3; +-+ register f32 x1, z1, x2, z2, x3, z3; +- f32 nx, ny, nz; +- f32 oo; +- f32 height; +- struct Surface *floor = NULL; +-+ s32 interpolate; +- +- // Iterate through the list of floors until there are no more floors. +- while (surfaceNode != NULL) { +- surf = surfaceNode->surface; +- surfaceNode = surfaceNode->next; +-+ interpolate = gInterpolatingSurfaces && surf->modifiedTimestamp == gGlobalTimer; +- +- x1 = surf->vertex1[0]; +- z1 = surf->vertex1[2]; +- x2 = surf->vertex2[0]; +- z2 = surf->vertex2[2]; +-+ if (interpolate) { +-+ f32 diff = (surf->prevVertex1[0] - x1) * (surf->prevVertex1[0] - x1); +-+ diff += (surf->prevVertex1[1] - surf->vertex1[1]) * (surf->prevVertex1[1] - surf->vertex1[1]); +-+ diff += (surf->prevVertex1[2] - z1) * (surf->prevVertex1[2] - z1); +-+ //printf("%f\n", sqrtf(diff)); +-+ if (diff > 10000) { +-+ interpolate = FALSE; +-+ } else { +-+ x1 = (surf->prevVertex1[0] + x1) / 2; +-+ z1 = (surf->prevVertex1[2] + z1) / 2; +-+ x2 = (surf->prevVertex2[0] + x2) / 2; +-+ z2 = (surf->prevVertex2[2] + z2) / 2; +-+ } +-+ } +- +- // Check that the point is within the triangle bounds. +- if ((z1 - z) * (x2 - x1) - (x1 - x) * (z2 - z1) < 0) { +-@@ -423,6 +442,10 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 +- // To slightly save on computation time, set this later. +- x3 = surf->vertex3[0]; +- z3 = surf->vertex3[2]; +-+ if (interpolate) { +-+ x3 = (surf->prevVertex3[0] + x3) / 2; +-+ z3 = (surf->prevVertex3[2] + z3) / 2; +-+ } +- +- if ((z2 - z) * (x3 - x2) - (x2 - x) * (z3 - z2) < 0) { +- continue; +-@@ -442,10 +465,30 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 +- continue; +- } +- +-- nx = surf->normal.x; +-- ny = surf->normal.y; +-- nz = surf->normal.z; +-- oo = surf->originOffset; +-+ if (interpolate) { +-+ f32 y1, y2, y3; +-+ f32 mag; +-+ y1 = (surf->prevVertex1[1] + surf->vertex1[1]) / 2; +-+ y2 = (surf->prevVertex2[1] + surf->vertex2[1]) / 2; +-+ y3 = (surf->prevVertex3[1] + surf->vertex3[1]) / 2; +-+ nx = (y2 - y1) * (z3 - z2) - (z2 - z1) * (y3 - y2); +-+ ny = (z2 - z1) * (x3 - x2) - (x2 - x1) * (z3 - z2); +-+ nz = (x2 - x1) * (y3 - y2) - (y2 - y1) * (x3 - x2); +-+ mag = sqrtf(nx * nx + ny * ny + nz * nz); +-+ if (mag < 0.0001) { +-+ continue; +-+ } +-+ mag = (f32)(1.0 / mag); +-+ nx *= mag; +-+ ny *= mag; +-+ nz *= mag; +-+ oo = -(nx * x1 + ny * y1 + nz * z1); +-+ } else { +-+ nx = surf->normal.x; +-+ ny = surf->normal.y; +-+ nz = surf->normal.z; +-+ oo = surf->originOffset; +-+ } +- +- // If a wall, ignore it. Likely a remnant, should never occur. +- if (ny == 0.0f) { +-@@ -460,6 +503,15 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 +- } +- +- *pheight = height; +-+ if (interpolate) { +-+ static struct Surface s; +-+ s.type = surf->type; +-+ s.normal.x = nx; +-+ s.normal.y = ny; +-+ s.normal.z = nz; +-+ s.originOffset = oo; +-+ return &s; +-+ } +- floor = surf; +- break; +- } +-diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c +-index ac2ee50..323b7d0 100644 +---- a/src/engine/surface_load.c +-+++ b/src/engine/surface_load.c +-@@ -14,6 +14,7 @@ +- #include "game/mario.h" +- #include "game/object_list_processor.h" +- #include "surface_load.h" +-+#include "game/game_init.h" +- +- s32 unused8038BE90; +- +-@@ -359,6 +360,11 @@ static struct Surface *read_surface_data(s16 *vertexData, s16 **vertexIndices) { +- +- surface = alloc_surface(); +- +-+ vec3s_copy(surface->prevVertex1, surface->vertex1); +-+ vec3s_copy(surface->prevVertex2, surface->vertex2); +-+ vec3s_copy(surface->prevVertex3, surface->vertex3); +-+ surface->modifiedTimestamp = gGlobalTimer; +-+ +- surface->vertex1[0] = x1; +- surface->vertex2[0] = x2; +- surface->vertex3[0] = x3; +-diff --git a/src/game/camera.c b/src/game/camera.c +-index bde0662..9351dea 100644 +---- a/src/game/camera.c +-+++ b/src/game/camera.c +-@@ -484,6 +484,10 @@ CameraTransition sModeTransitions[] = { +- extern u8 sDanceCutsceneIndexTable[][4]; +- extern u8 sZoomOutAreaMasks[]; +- +-+static void skip_camera_interpolation(void) { +-+ gLakituState.skipCameraInterpolationTimestamp = gGlobalTimer; +-+} +-+ +- /** +- * Starts a camera shake triggered by an interaction +- */ +-@@ -5552,6 +5556,7 @@ s32 set_camera_mode_fixed(struct Camera *c, s16 x, s16 y, s16 z) { +- c->mode = CAMERA_MODE_FIXED; +- vec3f_set(c->pos, sFixedModeBasePosition[0], sMarioCamState->pos[1], +- sFixedModeBasePosition[2]); +-+ skip_camera_interpolation(); +- } +- return basePosSet; +- } +-@@ -5714,6 +5719,7 @@ BAD_RETURN(s32) cam_rr_enter_building_side(struct Camera *c) { +- if (c->mode != CAMERA_MODE_FIXED) { +- sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT; +- c->mode = CAMERA_MODE_FIXED; +-+ skip_camera_interpolation(); +- } +- } +- +-@@ -5909,6 +5915,7 @@ BAD_RETURN(s32) cam_castle_enter_lobby(struct Camera *c) { +- sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT; +- set_fixed_cam_axis_sa_lobby(c->mode); +- c->mode = CAMERA_MODE_FIXED; +-+ skip_camera_interpolation(); +- } +- } +- +-@@ -7279,6 +7286,7 @@ BAD_RETURN(s32) cutscene_unused_loop(UNUSED struct Camera *c) { +- BAD_RETURN(s32) cutscene_ending_mario_fall_start(struct Camera *c) { +- vec3f_set(c->focus, -26.f, 0.f, -137.f); +- vec3f_set(c->pos, 165.f, 4725.f, 324.f); +-+ skip_camera_interpolation(); +- } +- +- /** +-@@ -7311,6 +7319,7 @@ BAD_RETURN(s32) cutscene_ending_mario_fall(struct Camera *c) { +- BAD_RETURN(s32) cutscene_ending_mario_land_closeup(struct Camera *c) { +- vec3f_set(c->focus, 85.f, 826.f, 250.f); +- vec3f_set(c->pos, -51.f, 988.f, -202.f); +-+ skip_camera_interpolation(); +- player2_rotate_cam(c, -0x2000, 0x2000, -0x2000, 0x2000); +- } +- +-@@ -7320,6 +7329,7 @@ BAD_RETURN(s32) cutscene_ending_mario_land_closeup(struct Camera *c) { +- BAD_RETURN(s32) cutscene_ending_reset_spline(UNUSED struct Camera *c) { +- sCutsceneVars[9].point[0] = 0.f; +- cutscene_reset_spline(); +-+ skip_camera_interpolation(); +- } +- +- /** +-@@ -7355,6 +7365,7 @@ BAD_RETURN(s32) cutscene_ending_peach_appear_closeup(struct Camera *c) { +- vec3f_set(c->pos, 179.f, 2463.f, -1216.f); +- c->pos[1] = gCutsceneFocus->oPosY + 35.f; +- vec3f_set(c->focus, gCutsceneFocus->oPosX, gCutsceneFocus->oPosY + 125.f, gCutsceneFocus->oPosZ); +-+ skip_camera_interpolation(); +- } +- +- /** +-@@ -7373,6 +7384,7 @@ BAD_RETURN(s32) cutscene_ending_peach_appears(struct Camera *c) { +- BAD_RETURN(s32) cutscene_ending_peach_descends_start(UNUSED struct Camera *c) { +- cutscene_reset_spline(); +- sCutsceneVars[2].point[1] = 150.f; +-+ skip_camera_interpolation(); +- } +- +- /** +-@@ -7459,6 +7471,7 @@ BAD_RETURN(s32) cutscene_ending_peach_wakeup(struct Camera *c) { +- BAD_RETURN(s32) cutscene_ending_dialog(struct Camera *c) { +- vec3f_set(c->focus, 11.f, 983.f, -1273.f); +- vec3f_set(c->pos, -473.f, 970.f, -1152.f); +-+ skip_camera_interpolation(); +- player2_rotate_cam(c, -0x800, 0x2000, -0x2000, 0x2000); +- } +- +-@@ -7469,6 +7482,7 @@ BAD_RETURN(s32) cutscene_ending_kiss_closeup(struct Camera *c) { +- set_fov_function(CAM_FOV_SET_29); +- vec3f_set(c->focus, 350.f, 1034.f, -1216.f); +- vec3f_set(c->pos, -149.f, 1021.f, -1216.f); +-+ skip_camera_interpolation(); +- } +- +- /** +-@@ -7504,6 +7518,7 @@ BAD_RETURN(s32) cutscene_ending_kiss(struct Camera *c) { +- BAD_RETURN(s32) cutscene_ending_look_at_sky(struct Camera *c) { +- move_point_along_spline(c->focus, sEndingLookAtSkyFocus, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress); +- vec3f_set(c->pos, 699.f, 1680.f, -703.f); +-+ skip_camera_interpolation(); +- } +- +- /** +-@@ -10340,6 +10355,7 @@ BAD_RETURN(s32) cutscene_door_start(struct Camera *c) { +- BAD_RETURN(s32) cutscene_door_fix_cam(struct Camera *c) { +- vec3f_copy(c->pos, sCutsceneVars[0].point); +- vec3f_copy(c->focus, sCutsceneVars[1].point); +-+ skip_camera_interpolation(); +- } +- +- /** +-@@ -10373,6 +10389,7 @@ BAD_RETURN(s32) cutscene_door_move_behind_mario(struct Camera *c) { +- } +- +- offset_rotated(c->pos, sMarioCamState->pos, camOffset, sCutsceneVars[0].angle); +-+ skip_camera_interpolation(); +- } +- +- /** +-diff --git a/src/game/camera.h b/src/game/camera.h +-index 173ab8a..b1abdc4 100644 +---- a/src/game/camera.h +-+++ b/src/game/camera.h +-@@ -657,6 +657,8 @@ struct LakituState +- /// Mario's action from the previous frame. Only used to determine if Mario just finished a dive. +- /*0xB8*/ u32 lastFrameAction; +- /*0xBC*/ s16 unused; +-+ +-+ u32 skipCameraInterpolationTimestamp; +- }; +- +- // bss order hack to not affect BSS order. if possible, remove me, but it will be hard to match otherwise +-diff --git a/src/game/envfx_bubbles.c b/src/game/envfx_bubbles.c +-index 16a9272..ee1b029 100644 +---- a/src/game/envfx_bubbles.c +-+++ b/src/game/envfx_bubbles.c +-@@ -35,6 +35,20 @@ Vtx_t gBubbleTempVtx[3] = { +- { { 0, 0, 0 }, 0, { -498, 964 }, { 0xFF, 0xFF, 0xFF, 0xFF } }, +- }; +- +-+static Gfx sGfxSaved[60 / 5]; +-+static Gfx *sBubbleInterpolatedDisplayListPos[60 / 5]; +-+static Vec3s sPrevBubblePositions[60]; +-+ +-+void patch_interpolated_bubble_particles(void) { +-+ s32 i; +-+ for (i = 0; i < 60 / 5; i++) { +-+ if (sBubbleInterpolatedDisplayListPos[i] != NULL) { +-+ *sBubbleInterpolatedDisplayListPos[i] = sGfxSaved[i]; +-+ sBubbleInterpolatedDisplayListPos[i] = NULL; +-+ } +-+ } +-+} +-+ +- /** +- * Check whether the particle with the given index is +- * laterally within distance of point (x, z). Used to +-@@ -241,6 +255,7 @@ void envfx_update_whirlpool(void) { +- (gEnvFxBuffer + i)->yPos = (i + gEnvFxBuffer)->bubbleY; +- (gEnvFxBuffer + i)->unusedBubbleVar = 0; +- (gEnvFxBuffer + i)->isAlive = 1; +-+ (gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer; +- +- envfx_rotate_around_whirlpool(&(gEnvFxBuffer + i)->xPos, &(gEnvFxBuffer + i)->yPos, +- &(gEnvFxBuffer + i)->zPos); +-@@ -299,6 +314,7 @@ void envfx_update_jetstream(void) { +- + coss((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1]; +- (gEnvFxBuffer + i)->yPos = +- gEnvFxBubbleConfig[ENVFX_STATE_SRC_Y] + (random_float() * 400.0f - 200.0f); +-+ (gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer; +- } else { +- (gEnvFxBuffer + i)->angleAndDist[1] += 10; +- (gEnvFxBuffer + i)->xPos += sins((gEnvFxBuffer + i)->angleAndDist[0]) * 10.0f; +-@@ -506,6 +522,12 @@ Gfx *envfx_update_bubble_particles(s32 mode, UNUSED Vec3s marioPos, Vec3s camFro +- Vec3s vertex1; +- Vec3s vertex2; +- Vec3s vertex3; +-+ Vec3s interpolatedVertices[3]; +-+ +-+ static Vec3s prevVertex1; +-+ static Vec3s prevVertex2; +-+ static Vec3s prevVertex3; +-+ static u32 prevTimestamp; +- +- Gfx *gfxStart; +- +-@@ -521,18 +543,52 @@ Gfx *envfx_update_bubble_particles(s32 mode, UNUSED Vec3s marioPos, Vec3s camFro +- envfx_bubbles_update_switch(mode, camTo, vertex1, vertex2, vertex3); +- rotate_triangle_vertices(vertex1, vertex2, vertex3, pitch, yaw); +- +-+ if (gGlobalTimer == prevTimestamp + 1) { +-+ interpolate_vectors_s16(interpolatedVertices[0], prevVertex1, vertex1); +-+ interpolate_vectors_s16(interpolatedVertices[1], prevVertex2, vertex2); +-+ interpolate_vectors_s16(interpolatedVertices[2], prevVertex3, vertex3); +-+ } +-+ vec3s_copy(prevVertex1, vertex1); +-+ vec3s_copy(prevVertex2, vertex2); +-+ vec3s_copy(prevVertex3, vertex3); +-+ prevTimestamp = gGlobalTimer; +-+ +- gSPDisplayList(sGfxCursor++, &tiny_bubble_dl_0B006D38); +- +- for (i = 0; i < sBubbleParticleMaxCount; i += 5) { +-+ Vtx *interpolatedVertBuf = alloc_display_list(15 * sizeof(Vtx)); +-+ s32 j, k; +- gDPPipeSync(sGfxCursor++); +- envfx_set_bubble_texture(mode, i); +-- append_bubble_vertex_buffer(sGfxCursor++, i, vertex1, vertex2, vertex3, (Vtx *) gBubbleTempVtx); +-+ sBubbleInterpolatedDisplayListPos[i / 5] = sGfxCursor; +-+ for (j = 0; j < 5; j++) { +-+ for (k = 0; k < 3; k++) { +-+ Vtx *v = &interpolatedVertBuf[j * 3 + k]; +-+ v->v = gBubbleTempVtx[k]; +-+ if (gGlobalTimer != gEnvFxBuffer[i + j].spawnTimestamp && mode != ENVFX_LAVA_BUBBLES) { +-+ v->v.ob[0] = (sPrevBubblePositions[i + j][0] + gEnvFxBuffer[i + j].xPos) / 2.0f + interpolatedVertices[k][0]; +-+ v->v.ob[1] = (sPrevBubblePositions[i + j][1] + gEnvFxBuffer[i + j].yPos) / 2.0f + interpolatedVertices[k][1]; +-+ v->v.ob[2] = (sPrevBubblePositions[i + j][2] + gEnvFxBuffer[i + j].zPos) / 2.0f + interpolatedVertices[k][2]; +-+ } else { +-+ v->v.ob[0] = gEnvFxBuffer[i + j].xPos + interpolatedVertices[k][0]; +-+ v->v.ob[1] = gEnvFxBuffer[i + j].yPos + interpolatedVertices[k][1]; +-+ v->v.ob[2] = gEnvFxBuffer[i + j].zPos + interpolatedVertices[k][2]; +-+ } +-+ } +-+ } +-+ gSPVertex(sGfxCursor++, VIRTUAL_TO_PHYSICAL(interpolatedVertBuf), 15, 0); +-+ append_bubble_vertex_buffer(&sGfxSaved[i / 5], i, vertex1, vertex2, vertex3, (Vtx *) gBubbleTempVtx); +- gSP1Triangle(sGfxCursor++, 0, 1, 2, 0); +- gSP1Triangle(sGfxCursor++, 3, 4, 5, 0); +- gSP1Triangle(sGfxCursor++, 6, 7, 8, 0); +- gSP1Triangle(sGfxCursor++, 9, 10, 11, 0); +- gSP1Triangle(sGfxCursor++, 12, 13, 14, 0); +- } +-+ for (i = 0; i < sBubbleParticleMaxCount; i++) { +-+ sPrevBubblePositions[i][0] = gEnvFxBuffer[i].xPos; +-+ sPrevBubblePositions[i][1] = gEnvFxBuffer[i].yPos; +-+ sPrevBubblePositions[i][2] = gEnvFxBuffer[i].zPos; +-+ } +- +- gSPDisplayList(sGfxCursor++, &tiny_bubble_dl_0B006AB0); +- gSPEndDisplayList(sGfxCursor++); +-diff --git a/src/game/envfx_snow.c b/src/game/envfx_snow.c +-index c3c14a5..d2212ef 100644 +---- a/src/game/envfx_snow.c +-+++ b/src/game/envfx_snow.c +-@@ -54,6 +54,26 @@ extern void *tiny_bubble_dl_0B006AB0; +- extern void *tiny_bubble_dl_0B006A50; +- extern void *tiny_bubble_dl_0B006CD8; +- +-+static struct { +-+ Gfx *pos; +-+ Vtx vertices[15]; +-+} sPrevSnowVertices[140 / 5]; +-+static s16 sPrevSnowParticleCount; +-+static u32 sPrevSnowTimestamp; +-+ +-+void patch_interpolated_snow_particles(void) { +-+ int i; +-+ +-+ if (gGlobalTimer != sPrevSnowTimestamp + 1) { +-+ return; +-+ } +-+ +-+ for (i = 0; i < sPrevSnowParticleCount; i += 5) { +-+ gSPVertex(sPrevSnowVertices[i / 5].pos, +-+ VIRTUAL_TO_PHYSICAL(sPrevSnowVertices[i / 5].vertices), 15, 0); +-+ } +-+} +-+ +- /** +- * Initialize snow particles by allocating a buffer for storing their state +- * and setting a start amount. +-@@ -217,6 +237,7 @@ void envfx_update_snow_normal(s32 snowCylinderX, s32 snowCylinderY, s32 snowCyli +- 400.0f * random_float() - 200.0f + snowCylinderZ + (s16)(deltaZ * 2); +- (gEnvFxBuffer + i)->yPos = 200.0f * random_float() + snowCylinderY; +- (gEnvFxBuffer + i)->isAlive = 1; +-+ (gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer; +- } else { +- (gEnvFxBuffer + i)->xPos += random_float() * 2 - 1.0f + (s16)(deltaX / 1.2); +- (gEnvFxBuffer + i)->yPos -= 2 -(s16)(deltaY * 0.8); +-@@ -251,6 +272,7 @@ void envfx_update_snow_blizzard(s32 snowCylinderX, s32 snowCylinderY, s32 snowCy +- 400.0f * random_float() - 200.0f + snowCylinderZ + (s16)(deltaZ * 2); +- (gEnvFxBuffer + i)->yPos = 400.0f * random_float() - 200.0f + snowCylinderY; +- (gEnvFxBuffer + i)->isAlive = 1; +-+ (gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer; +- } else { +- (gEnvFxBuffer + i)->xPos += random_float() * 2 - 1.0f + (s16)(deltaX / 1.2) + 20.0f; +- (gEnvFxBuffer + i)->yPos -= 5 -(s16)(deltaY * 0.8); +-@@ -294,6 +316,7 @@ void envfx_update_snow_water(s32 snowCylinderX, s32 snowCylinderY, s32 snowCylin +- (gEnvFxBuffer + i)->zPos = 400.0f * random_float() - 200.0f + snowCylinderZ; +- (gEnvFxBuffer + i)->yPos = 400.0f * random_float() - 200.0f + snowCylinderY; +- (gEnvFxBuffer + i)->isAlive = 1; +-+ (gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer; +- } +- } +- } +-@@ -346,6 +369,8 @@ void rotate_triangle_vertices(Vec3s vertex1, Vec3s vertex2, Vec3s vertex3, s16 p +- void append_snowflake_vertex_buffer(Gfx *gfx, s32 index, Vec3s vertex1, Vec3s vertex2, Vec3s vertex3) { +- s32 i = 0; +- Vtx *vertBuf = (Vtx *) alloc_display_list(15 * sizeof(Vtx)); +-+ Vtx *vertBufInterpolated = (Vtx *) alloc_display_list(15 * sizeof(Vtx)); +-+ Vtx *v; +- #ifdef VERSION_EU +- Vtx *p; +- #endif +-@@ -395,7 +420,23 @@ void append_snowflake_vertex_buffer(Gfx *gfx, s32 index, Vec3s vertex1, Vec3s ve +- #endif +- } +- +-- gSPVertex(gfx, VIRTUAL_TO_PHYSICAL(vertBuf), 15, 0); +-+ for (i = 0; i < 15; i++) { +-+ v = &sPrevSnowVertices[index / 5].vertices[i]; +-+ vertBufInterpolated[i] = gSnowTempVtx[i % 3]; +-+ if (index < sPrevSnowParticleCount && gGlobalTimer == sPrevSnowTimestamp + 1 && +-+ gGlobalTimer != gEnvFxBuffer[index + i / 3].spawnTimestamp) { +-+ vertBufInterpolated[i].v.ob[0] = (v->v.ob[0] + vertBuf[i].v.ob[0]) / 2; +-+ vertBufInterpolated[i].v.ob[1] = (v->v.ob[1] + vertBuf[i].v.ob[1]) / 2; +-+ vertBufInterpolated[i].v.ob[2] = (v->v.ob[2] + vertBuf[i].v.ob[2]) / 2; +-+ } else { +-+ vertBufInterpolated[i].v.ob[0] = vertBuf[i].v.ob[0]; +-+ vertBufInterpolated[i].v.ob[1] = vertBuf[i].v.ob[1]; +-+ vertBufInterpolated[i].v.ob[2] = vertBuf[i].v.ob[2]; +-+ } +-+ *v = vertBuf[i]; +-+ } +-+ sPrevSnowVertices[index / 5].pos = gfx; +-+ gSPVertex(gfx, VIRTUAL_TO_PHYSICAL(vertBufInterpolated), 15, 0); +- } +- +- /** +-@@ -479,6 +520,8 @@ Gfx *envfx_update_snow(s32 snowMode, Vec3s marioPos, Vec3s camFrom, Vec3s camTo) +- gSP1Triangle(gfx++, 9, 10, 11, 0); +- gSP1Triangle(gfx++, 12, 13, 14, 0); +- } +-+ sPrevSnowParticleCount = gSnowParticleCount; +-+ sPrevSnowTimestamp = gGlobalTimer; +- +- gSPDisplayList(gfx++, &tiny_bubble_dl_0B006AB0) gSPEndDisplayList(gfx++); +- +-diff --git a/src/game/envfx_snow.h b/src/game/envfx_snow.h +-index 7a83b53..f4acc2d 100644 +---- a/src/game/envfx_snow.h +-+++ b/src/game/envfx_snow.h +-@@ -25,7 +25,8 @@ struct EnvFxParticle { +- s32 angleAndDist[2]; // for whirpools, [0] = angle from center, [1] = distance from center +- s32 unusedBubbleVar; // set to zero for bubbles when respawning, never used elsewhere +- s32 bubbleY; // for Bubbles, yPos is always set to this +-- s8 filler20[56 - 0x20]; +-+ //s8 filler20[56 - 0x20]; +-+ u32 spawnTimestamp; +- }; +- +- extern s8 gEnvFxMode; +-diff --git a/src/game/hud.c b/src/game/hud.c +-index 1540b67..0de6e0b 100644 +---- a/src/game/hud.c +-+++ b/src/game/hud.c +-@@ -59,6 +59,20 @@ static struct UnusedHUDStruct sUnusedHUDValues = { 0x00, 0x0A, 0x00 }; +- +- static struct CameraHUD sCameraHUD = { CAM_STATUS_NONE }; +- +-+static u32 sPowerMeterLastRenderTimestamp; +-+static s16 sPowerMeterLastY; +-+static Gfx *sPowerMeterDisplayListPos; +-+ +-+void patch_interpolated_hud(void) { +-+ if (sPowerMeterDisplayListPos != NULL) { +-+ Mtx *mtx = alloc_display_list(sizeof(Mtx)); +-+ guTranslate(mtx, (f32) sPowerMeterHUD.x, (f32) sPowerMeterHUD.y, 0); +-+ gSPMatrix(sPowerMeterDisplayListPos, VIRTUAL_TO_PHYSICAL(mtx), +-+ G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); +-+ sPowerMeterDisplayListPos = NULL; +-+ } +-+} +-+ +- /** +- * Renders a rgba16 16x16 glyph texture from a table list. +- */ +-@@ -111,6 +125,7 @@ void render_power_meter_health_segment(s16 numHealthWedges) { +- */ +- void render_dl_power_meter(s16 numHealthWedges) { +- Mtx *mtx; +-+ f32 interpolatedY; +- +- mtx = alloc_display_list(sizeof(Mtx)); +- +-@@ -118,7 +133,15 @@ void render_dl_power_meter(s16 numHealthWedges) { +- return; +- } +- +-- guTranslate(mtx, (f32) sPowerMeterHUD.x, (f32) sPowerMeterHUD.y, 0); +-+ if (gGlobalTimer == sPowerMeterLastRenderTimestamp + 1) { +-+ interpolatedY = (sPowerMeterLastY + sPowerMeterHUD.y) / 2.0f; +-+ } else { +-+ interpolatedY = sPowerMeterHUD.y; +-+ } +-+ guTranslate(mtx, (f32) sPowerMeterHUD.x, interpolatedY, 0); +-+ sPowerMeterLastY = sPowerMeterHUD.y; +-+ sPowerMeterLastRenderTimestamp = gGlobalTimer; +-+ sPowerMeterDisplayListPos = gDisplayListHead; +- +- gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx++), +- G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); +-diff --git a/src/game/ingame_menu.c b/src/game/ingame_menu.c +-index 7ae9f1e..1c23c96 100644 +---- a/src/game/ingame_menu.c +-+++ b/src/game/ingame_menu.c +-@@ -130,6 +130,42 @@ s32 gDialogResponse = 0; +- static struct CachedChar { u8 used; u8 data[CHCACHE_BUFLEN]; } charCache[256]; +- #endif // VERSION +- +-+static Gfx *sInterpolatedDialogOffsetPos; +-+static f32 sInterpolatedDialogOffset; +-+static Gfx *sInterpolatedDialogRotationPos; +-+static f32 sInterpolatedDialogScale; +-+static f32 sInterpolatedDialogRotation; +-+static Gfx *sInterpolatedDialogZoomPos; +-+ +-+void patch_interpolated_dialog(void) { +-+ Mtx *matrix; +-+ +-+ if (sInterpolatedDialogOffsetPos != NULL) { +-+ matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); +-+ guTranslate(matrix, 0, sInterpolatedDialogOffset, 0); +-+ gSPMatrix(sInterpolatedDialogOffsetPos, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); +-+ sInterpolatedDialogOffsetPos = NULL; +-+ } +-+ if (sInterpolatedDialogRotationPos != NULL) { +-+ matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); +-+ guScale(matrix, 1.0 / sInterpolatedDialogScale, 1.0 / sInterpolatedDialogScale, 1.0f); +-+ gSPMatrix(sInterpolatedDialogRotationPos++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); +-+ matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); +-+ guRotate(matrix, sInterpolatedDialogRotation * 4.0f, 0, 0, 1.0f); +-+ gSPMatrix(sInterpolatedDialogRotationPos, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); +-+ sInterpolatedDialogRotationPos = NULL; +-+ } +-+ if (sInterpolatedDialogZoomPos != NULL) { +-+ matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); +-+ guTranslate(matrix, 65.0 - (65.0 / sInterpolatedDialogScale), (40.0 / sInterpolatedDialogScale) - 40, 0); +-+ gSPMatrix(sInterpolatedDialogZoomPos++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); +-+ matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); +-+ guScale(matrix, 1.0 / sInterpolatedDialogScale, 1.0 / sInterpolatedDialogScale, 1.0f); +-+ gSPMatrix(sInterpolatedDialogZoomPos, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); +-+ sInterpolatedDialogZoomPos = NULL; +-+ } +-+} +-+ +- void create_dl_identity_matrix(void) { +- Mtx *matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); +- +-@@ -969,6 +1005,14 @@ void render_dialog_box_type(struct DialogEntry *dialog, s8 linesPerBox) { +- switch (gDialogBoxType) { +- case DIALOG_TYPE_ROTATE: // Renders a dialog black box with zoom and rotation +- if (gDialogBoxState == DIALOG_STATE_OPENING || gDialogBoxState == DIALOG_STATE_CLOSING) { +-+ sInterpolatedDialogRotationPos = gDisplayListHead; +-+ if (gDialogBoxState == DIALOG_STATE_OPENING) { +-+ sInterpolatedDialogScale = gDialogBoxScale - 2 / 2; +-+ sInterpolatedDialogRotation = gDialogBoxOpenTimer - 7.5f / 2; +-+ } else { +-+ sInterpolatedDialogScale = gDialogBoxScale + 2 / 2; +-+ sInterpolatedDialogRotation = gDialogBoxOpenTimer + 7.5f / 2; +-+ } +- create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.0 / gDialogBoxScale, 1.0 / gDialogBoxScale, 1.0f); +- // convert the speed into angle +- create_dl_rotation_matrix(MENU_MTX_NOPUSH, gDialogBoxOpenTimer * 4.0f, 0, 0, 1.0f); +-@@ -977,6 +1021,12 @@ void render_dialog_box_type(struct DialogEntry *dialog, s8 linesPerBox) { +- break; +- case DIALOG_TYPE_ZOOM: // Renders a dialog white box with zoom +- if (gDialogBoxState == DIALOG_STATE_OPENING || gDialogBoxState == DIALOG_STATE_CLOSING) { +-+ sInterpolatedDialogZoomPos = gDisplayListHead; +-+ if (gDialogBoxState == DIALOG_STATE_OPENING) { +-+ sInterpolatedDialogScale = gDialogBoxScale - 2 / 2; +-+ } else { +-+ sInterpolatedDialogScale = gDialogBoxScale + 2 / 2; +-+ } +- create_dl_translation_matrix(MENU_MTX_NOPUSH, 65.0 - (65.0 / gDialogBoxScale), +- (40.0 / gDialogBoxScale) - 40, 0); +- create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.0 / gDialogBoxScale, 1.0 / gDialogBoxScale, 1.0f); +-@@ -1259,6 +1309,8 @@ void handle_dialog_text_and_pages(s8 colorMode, struct DialogEntry *dialog, s8 l +- #ifdef VERSION_EU +- gDialogY -= gDialogScrollOffsetY; +- #else +-+ sInterpolatedDialogOffset = gDialogScrollOffsetY + dialog->linesPerBox; +-+ sInterpolatedDialogOffsetPos = gDisplayListHead; +- create_dl_translation_matrix(MENU_MTX_NOPUSH, 0, (f32) gDialogScrollOffsetY, 0); +- #endif +- } +-diff --git a/src/game/level_geo.c b/src/game/level_geo.c +-index 4c98e70..abc5121 100644 +---- a/src/game/level_geo.c +-+++ b/src/game/level_geo.c +-@@ -34,12 +34,16 @@ Gfx *geo_envfx_main(s32 callContext, struct GraphNode *node, Mat4 mtxf) { +- vec3f_to_vec3s(marioPos, gPlayerCameraState->pos); +- particleList = envfx_update_particles(snowMode, marioPos, camTo, camFrom); +- if (particleList != NULL) { +-+#if 0 +- Mtx *mtx = alloc_display_list(sizeof(*mtx)); +- +- gfx = alloc_display_list(2 * sizeof(*gfx)); +- mtxf_to_mtx(mtx, mtxf); +- gSPMatrix(&gfx[0], VIRTUAL_TO_PHYSICAL(mtx), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); +- gSPBranchList(&gfx[1], VIRTUAL_TO_PHYSICAL(particleList)); +-+#else +-+ gfx = particleList; +-+#endif +- execNode->fnNode.node.flags = (execNode->fnNode.node.flags & 0xFF) | 0x400; +- } +- SET_HIGH_U16_OF_32(*params, gAreaUpdateCounter); +-diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c +-index 22b45b3..109d7f7 100644 +---- a/src/game/object_helpers.c +-+++ b/src/game/object_helpers.c +-@@ -1554,6 +1554,7 @@ void cur_obj_set_pos_to_home(void) { +- o->oPosX = o->oHomeX; +- o->oPosY = o->oHomeY; +- o->oPosZ = o->oHomeZ; +-+ o->header.gfx.skipInterpolationTimestamp = gGlobalTimer; +- } +- +- void cur_obj_set_pos_to_home_and_stop(void) { +-diff --git a/src/game/paintings.c b/src/game/paintings.c +-index 6cae19c..a304d4a 100644 +---- a/src/game/paintings.c +-+++ b/src/game/paintings.c +-@@ -189,6 +189,32 @@ struct Painting **sPaintingGroups[] = { +- s16 gPaintingUpdateCounter = 1; +- s16 gLastPaintingUpdateCounter = 0; +- +-+static Vtx sLastVertices[2 * 264 * 3]; +-+static u32 sLastVerticesTimestamp; +-+static Vtx *sVerticesPtr[2]; +-+static s32 sVerticesCount; +-+ +-+void patch_interpolated_paintings(void) { +-+ if (sVerticesPtr[0] != NULL) { +-+ s32 i; +-+ if (sVerticesPtr[1] != NULL) { +-+ for (i = 0; i < sVerticesCount / 2; i++) { +-+ sVerticesPtr[0][i] = sLastVertices[i]; +-+ } +-+ for (; i < sVerticesCount; i++) { +-+ sVerticesPtr[1][i - sVerticesCount / 2] = sLastVertices[i]; +-+ } +-+ } else { +-+ for (i = 0; i < sVerticesCount; i++) { +-+ sVerticesPtr[0][i] = sLastVertices[i]; +-+ } +-+ } +-+ sVerticesPtr[0] = NULL; +-+ sVerticesPtr[1] = NULL; +-+ sVerticesCount = 0; +-+ } +-+} +-+ +- /** +- * Stop paintings in paintingGroup from rippling if their id is different from *idptr. +- */ +-@@ -890,6 +916,23 @@ Gfx *render_painting(u8 *img, s16 tWidth, s16 tHeight, s16 *textureMap, s16 mapV +- gSP1Triangle(gfx++, group * 3, group * 3 + 1, group * 3 + 2, 0); +- } +- +-+ if (sVerticesCount >= numVtx * 2) { +-+ sVerticesCount = 0; +-+ } +-+ for (map = 0; map < numVtx; map++) { +-+ Vtx v = verts[map]; +-+ if (gGlobalTimer == sLastVerticesTimestamp + 1) { +-+ s32 i; +-+ for (i = 0; i < 3; i++) { +-+ verts[map].n.ob[i] = (v.n.ob[i] + sLastVertices[sVerticesCount + map].n.ob[i]) / 2; +-+ verts[map].n.n[i] = (v.n.n[i] + sLastVertices[sVerticesCount + map].n.n[i]) / 2; +-+ } +-+ } +-+ sLastVertices[sVerticesCount + map] = v; +-+ } +-+ sVerticesPtr[sVerticesCount / numVtx] = verts; +-+ sVerticesCount += numVtx; +-+ +- gSPEndDisplayList(gfx); +- return dlist; +- } +-@@ -954,6 +997,7 @@ Gfx *painting_ripple_image(struct Painting *painting) { +- meshTris = textureMap[meshVerts * 3 + 1]; +- gSPDisplayList(gfx++, render_painting(textures[i], tWidth, tHeight, textureMap, meshVerts, meshTris, painting->alpha)); +- } +-+ sLastVerticesTimestamp = gGlobalTimer; +- +- // Update the ripple, may automatically reset the painting's state. +- painting_update_ripple_state(painting); +-@@ -991,6 +1035,7 @@ Gfx *painting_ripple_env_mapped(struct Painting *painting) { +- meshVerts = textureMap[0]; +- meshTris = textureMap[meshVerts * 3 + 1]; +- gSPDisplayList(gfx++, render_painting(tArray[0], tWidth, tHeight, textureMap, meshVerts, meshTris, painting->alpha)); +-+ sLastVerticesTimestamp = gGlobalTimer; +- +- // Update the ripple, may automatically reset the painting's state. +- painting_update_ripple_state(painting); +-diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c +-index d5bf577..71656b4 100644 +---- a/src/game/rendering_graph_node.c +-+++ b/src/game/rendering_graph_node.c +-@@ -39,6 +39,8 @@ +- s16 gMatStackIndex; +- Mat4 gMatStack[32]; +- Mtx *gMatStackFixed[32]; +-+Mat4 gMatStackInterpolated[32]; +-+Mtx *gMatStackInterpolatedFixed[32]; +- +- /** +- * Animation nodes have state in global variables, so this struct captures +-@@ -52,6 +54,7 @@ struct GeoAnimState { +- /*0x04*/ f32 translationMultiplier; +- /*0x08*/ u16 *attribute; +- /*0x0C*/ s16 *data; +-+ s16 prevFrame; +- }; +- +- // For some reason, this is a GeoAnimState struct, but the current state consists +-@@ -61,6 +64,7 @@ struct GeoAnimState gGeoTempState; +- u8 gCurAnimType; +- u8 gCurAnimEnabled; +- s16 gCurrAnimFrame; +-+s16 gPrevAnimFrame; +- f32 gCurAnimTranslationMultiplier; +- u16 *gCurrAnimAttribute; +- s16 *gCurAnimData; +-@@ -129,6 +133,46 @@ u16 gAreaUpdateCounter = 0; +- LookAt lookAt; +- #endif +- +-+static Gfx *sPerspectivePos; +-+static Mtx *sPerspectiveMtx; +-+ +-+struct { +-+ Gfx *pos; +-+ void *mtx; +-+ void *displayList; +-+} gMtxTbl[6400]; +-+s32 gMtxTblSize; +-+ +-+static Gfx *sViewportPos; +-+static Vp sPrevViewport; +-+ +-+void mtx_patch_interpolated(void) { +-+ s32 i; +-+ +-+ if (sPerspectivePos != NULL) { +-+ gSPMatrix(sPerspectivePos, VIRTUAL_TO_PHYSICAL(sPerspectiveMtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); +-+ } +-+ +-+ for (i = 0; i < gMtxTblSize; i++) { +-+ Gfx *pos = gMtxTbl[i].pos; +-+ gSPMatrix(pos++, VIRTUAL_TO_PHYSICAL(gMtxTbl[i].mtx), +-+ G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); +-+ gSPDisplayList(pos++, gMtxTbl[i].displayList); +-+ } +-+ +-+ if (sViewportPos != NULL) { +-+ Gfx *saved = gDisplayListHead; +-+ gDisplayListHead = sViewportPos; +-+ make_viewport_clip_rect(&sPrevViewport); +-+ gSPViewport(gDisplayListHead, VIRTUAL_TO_PHYSICAL(&sPrevViewport)); +-+ gDisplayListHead = saved; +-+ } +-+ +-+ gMtxTblSize = 0; +-+ sPerspectivePos = NULL; +-+ sViewportPos = NULL; +-+} +-+ +- /** +- * Process a master list node. +- */ +-@@ -156,9 +200,14 @@ static void geo_process_master_list_sub(struct GraphNodeMasterList *node) { +- if ((currList = node->listHeads[i]) != NULL) { +- gDPSetRenderMode(gDisplayListHead++, modeList->modes[i], mode2List->modes[i]); +- while (currList != NULL) { +-- gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(currList->transform), +-+ if ((u32) gMtxTblSize < sizeof(gMtxTbl) / sizeof(gMtxTbl[0])) { +-+ gMtxTbl[gMtxTblSize].pos = gDisplayListHead; +-+ gMtxTbl[gMtxTblSize].mtx = currList->transform; +-+ gMtxTbl[gMtxTblSize++].displayList = currList->displayList; +-+ } +-+ gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(currList->transformInterpolated), +- G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); +-- gSPDisplayList(gDisplayListHead++, currList->displayList); +-+ gSPDisplayList(gDisplayListHead++, currList->displayListInterpolated); +- currList = currList->next; +- } +- } +-@@ -174,7 +223,7 @@ static void geo_process_master_list_sub(struct GraphNodeMasterList *node) { +- * parameter. Look at the RenderModeContainer struct to see the corresponding +- * render modes of layers. +- */ +--static void geo_append_display_list(void *displayList, s16 layer) { +-+static void geo_append_display_list2(void *displayList, void *displayListInterpolated, s16 layer) { +- +- #ifdef F3DEX_GBI_2 +- gSPLookAt(gDisplayListHead++, &lookAt); +-@@ -184,7 +233,9 @@ static void geo_append_display_list(void *displayList, s16 layer) { +- alloc_only_pool_alloc(gDisplayListHeap, sizeof(struct DisplayListNode)); +- +- listNode->transform = gMatStackFixed[gMatStackIndex]; +-+ listNode->transformInterpolated = gMatStackInterpolatedFixed[gMatStackIndex]; +- listNode->displayList = displayList; +-+ listNode->displayListInterpolated = displayListInterpolated; +- listNode->next = 0; +- if (gCurGraphNodeMasterList->listHeads[layer] == 0) { +- gCurGraphNodeMasterList->listHeads[layer] = listNode; +-@@ -195,6 +246,10 @@ static void geo_append_display_list(void *displayList, s16 layer) { +- } +- } +- +-+static void geo_append_display_list(void *displayList, s16 layer) { +-+ geo_append_display_list2(displayList, displayList, layer); +-+} +-+ +- /** +- * Process the master list node. +- */ +-@@ -241,7 +296,9 @@ static void geo_process_perspective(struct GraphNodePerspective *node) { +- } +- if (node->fnNode.node.children != NULL) { +- u16 perspNorm; +-+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); +- Mtx *mtx = alloc_display_list(sizeof(*mtx)); +-+ f32 fovInterpolated; +- +- #ifdef VERSION_EU +- f32 aspect = ((f32) gCurGraphNodeRoot->width / (f32) gCurGraphNodeRoot->height) * 1.1f; +-@@ -250,9 +307,23 @@ static void geo_process_perspective(struct GraphNodePerspective *node) { +- #endif +- +- guPerspective(mtx, &perspNorm, node->fov, aspect, node->near, node->far, 1.0f); +-- gSPPerspNormalize(gDisplayListHead++, perspNorm); +- +-- gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); +-+ if (gGlobalTimer == node->prevTimestamp + 1 && gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) { +-+ +-+ fovInterpolated = (node->prevFov + node->fov) / 2.0f; +-+ guPerspective(mtxInterpolated, &perspNorm, fovInterpolated, aspect, node->near, node->far, 1.0f); +-+ gSPPerspNormalize(gDisplayListHead++, perspNorm); +-+ +-+ sPerspectivePos = gDisplayListHead; +-+ sPerspectiveMtx = mtx; +-+ gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtxInterpolated), +-+ G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); +-+ } else { +-+ gSPPerspNormalize(gDisplayListHead++, perspNorm); +-+ gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); +-+ } +-+ node->prevFov = node->fov; +-+ node->prevTimestamp = gGlobalTimer; +- +- gCurGraphNodeCamFrustum = node; +- geo_process_node_and_siblings(node->fnNode.node.children); +-@@ -297,6 +368,39 @@ static void geo_process_switch(struct GraphNodeSwitchCase *node) { +- } +- } +- +-+void interpolate_vectors(Vec3f res, Vec3f a, Vec3f b) { +-+ res[0] = (a[0] + b[0]) / 2.0f; +-+ res[1] = (a[1] + b[1]) / 2.0f; +-+ res[2] = (a[2] + b[2]) / 2.0f; +-+} +-+ +-+void interpolate_vectors_s16(Vec3s res, Vec3s a, Vec3s b) { +-+ res[0] = (a[0] + b[0]) / 2; +-+ res[1] = (a[1] + b[1]) / 2; +-+ res[2] = (a[2] + b[2]) / 2; +-+} +-+ +-+static s16 interpolate_angle(s16 a, s16 b) { +-+ s32 absDiff = b - a; +-+ if (absDiff < 0) { +-+ absDiff = -absDiff; +-+ } +-+ if (absDiff >= 0x4000 && absDiff <= 0xC000) { +-+ return b; +-+ } +-+ if (absDiff <= 0x8000) { +-+ return (a + b) / 2; +-+ } else { +-+ return (a + b) / 2 + 0x8000; +-+ } +-+} +-+ +-+static void interpolate_angles(Vec3s res, Vec3s a, Vec3s b) { +-+ res[0] = interpolate_angle(a[0], b[0]); +-+ res[1] = interpolate_angle(a[1], b[1]); +-+ res[2] = interpolate_angle(a[2], b[2]); +-+} +-+ +- /** +- * Process a camera node. +- */ +-@@ -304,6 +408,9 @@ static void geo_process_camera(struct GraphNodeCamera *node) { +- Mat4 cameraTransform; +- Mtx *rollMtx = alloc_display_list(sizeof(*rollMtx)); +- Mtx *mtx = alloc_display_list(sizeof(*mtx)); +-+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); +-+ Vec3f posInterpolated; +-+ Vec3f focusInterpolated; +- +- if (node->fnNode.func != NULL) { +- node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]); +-@@ -314,12 +421,40 @@ static void geo_process_camera(struct GraphNodeCamera *node) { +- +- mtxf_lookat(cameraTransform, node->pos, node->focus, node->roll); +- mtxf_mul(gMatStack[gMatStackIndex + 1], cameraTransform, gMatStack[gMatStackIndex]); +-+ +-+ if (gGlobalTimer == node->prevTimestamp + 1 && gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) { +-+ interpolate_vectors(posInterpolated, node->prevPos, node->pos); +-+ interpolate_vectors(focusInterpolated, node->prevFocus, node->focus); +-+ float magnitude = 0; +-+ for (int i = 0; i < 3; i++) { +-+ float diff = node->pos[i] - node->prevPos[i]; +-+ magnitude += diff * diff; +-+ } +-+ if (magnitude > 500000) { +-+ // Observed ~479000 in BBH when toggling R camera +-+ // Can get over 3 million in VCUTM though... +-+ vec3f_copy(posInterpolated, node->pos); +-+ vec3f_copy(focusInterpolated, node->focus); +-+ } +-+ } else { +-+ vec3f_copy(posInterpolated, node->pos); +-+ vec3f_copy(focusInterpolated, node->focus); +-+ } +-+ vec3f_copy(node->prevPos, node->pos); +-+ vec3f_copy(node->prevFocus, node->focus); +-+ node->prevTimestamp = gGlobalTimer; +-+ mtxf_lookat(cameraTransform, posInterpolated, focusInterpolated, node->roll); +-+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], cameraTransform, gMatStackInterpolated[gMatStackIndex]); +-+ +- gMatStackIndex++; +- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); +- gMatStackFixed[gMatStackIndex] = mtx; +-+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); +-+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; +- if (node->fnNode.node.children != 0) { +- gCurGraphNodeCamera = node; +- node->matrixPtr = &gMatStack[gMatStackIndex]; +-+ node->matrixPtrInterpolated = &gMatStackInterpolated[gMatStackIndex]; +- geo_process_node_and_siblings(node->fnNode.node.children); +- gCurGraphNodeCamera = NULL; +- } +-@@ -336,13 +471,17 @@ static void geo_process_translation_rotation(struct GraphNodeTranslationRotation +- Mat4 mtxf; +- Vec3f translation; +- Mtx *mtx = alloc_display_list(sizeof(*mtx)); +-+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); +- +- vec3s_to_vec3f(translation, node->translation); +- mtxf_rotate_zxy_and_translate(mtxf, translation, node->rotation); +- mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); +-+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]); +- gMatStackIndex++; +- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); +- gMatStackFixed[gMatStackIndex] = mtx; +-+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); +-+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; +- if (node->displayList != NULL) { +- geo_append_display_list(node->displayList, node->node.flags >> 8); +- } +-@@ -361,13 +500,17 @@ static void geo_process_translation(struct GraphNodeTranslation *node) { +- Mat4 mtxf; +- Vec3f translation; +- Mtx *mtx = alloc_display_list(sizeof(*mtx)); +-+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); +- +- vec3s_to_vec3f(translation, node->translation); +- mtxf_rotate_zxy_and_translate(mtxf, translation, gVec3sZero); +- mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); +-+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]); +- gMatStackIndex++; +- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); +- gMatStackFixed[gMatStackIndex] = mtx; +-+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); +-+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; +- if (node->displayList != NULL) { +- geo_append_display_list(node->displayList, node->node.flags >> 8); +- } +-@@ -385,12 +528,23 @@ static void geo_process_translation(struct GraphNodeTranslation *node) { +- static void geo_process_rotation(struct GraphNodeRotation *node) { +- Mat4 mtxf; +- Mtx *mtx = alloc_display_list(sizeof(*mtx)); +-+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); +-+ Vec3s rotationInterpolated; +- +- mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, node->rotation); +- mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); +-+ if (gGlobalTimer == node->prevTimestamp + 1) { +-+ interpolate_angles(rotationInterpolated, node->prevRotation, node->rotation); +-+ mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, rotationInterpolated); +-+ } +-+ vec3s_copy(node->prevRotation, node->rotation); +-+ node->prevTimestamp = gGlobalTimer; +-+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]); +- gMatStackIndex++; +- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); +- gMatStackFixed[gMatStackIndex] = mtx; +-+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); +-+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; +- if (node->displayList != NULL) { +- geo_append_display_list(node->displayList, node->node.flags >> 8); +- } +-@@ -409,12 +563,16 @@ static void geo_process_scale(struct GraphNodeScale *node) { +- UNUSED Mat4 transform; +- Vec3f scaleVec; +- Mtx *mtx = alloc_display_list(sizeof(*mtx)); +-+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); +- +- vec3f_set(scaleVec, node->scale, node->scale, node->scale); +- mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], scaleVec); +-+ mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex], scaleVec); +- gMatStackIndex++; +- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); +- gMatStackFixed[gMatStackIndex] = mtx; +-+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); +-+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; +- if (node->displayList != NULL) { +- geo_append_display_list(node->displayList, node->node.flags >> 8); +- } +-@@ -433,21 +591,30 @@ static void geo_process_scale(struct GraphNodeScale *node) { +- static void geo_process_billboard(struct GraphNodeBillboard *node) { +- Vec3f translation; +- Mtx *mtx = alloc_display_list(sizeof(*mtx)); +-+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); +- +- gMatStackIndex++; +- vec3s_to_vec3f(translation, node->translation); +- mtxf_billboard(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex - 1], translation, +- gCurGraphNodeCamera->roll); +-+ mtxf_billboard(gMatStackInterpolated[gMatStackIndex], gMatStackInterpolated[gMatStackIndex - 1], translation, +-+ gCurGraphNodeCamera->roll); +- if (gCurGraphNodeHeldObject != NULL) { +- mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex], +- gCurGraphNodeHeldObject->objNode->header.gfx.scale); +-+ mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex], gMatStackInterpolated[gMatStackIndex], +-+ gCurGraphNodeHeldObject->objNode->header.gfx.scale); +- } else if (gCurGraphNodeObject != NULL) { +- mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex], +- gCurGraphNodeObject->scale); +-+ mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex], gMatStackInterpolated[gMatStackIndex], +-+ gCurGraphNodeObject->scale); +- } +- +- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); +- gMatStackFixed[gMatStackIndex] = mtx; +-+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); +-+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; +- if (node->displayList != NULL) { +- geo_append_display_list(node->displayList, node->node.flags >> 8); +- } +-@@ -496,13 +663,39 @@ static void geo_process_generated_list(struct GraphNodeGenerated *node) { +- */ +- static void geo_process_background(struct GraphNodeBackground *node) { +- Gfx *list = NULL; +-+ Gfx *listInterpolated = NULL; +- +- if (node->fnNode.func != NULL) { +-+ Vec3f posCopy; +-+ Vec3f focusCopy; +-+ Vec3f posInterpolated; +-+ Vec3f focusInterpolated; +-+ +-+ if (gGlobalTimer == node->prevCameraTimestamp + 1 && +-+ gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) { +-+ interpolate_vectors(posInterpolated, node->prevCameraPos, gLakituState.pos); +-+ interpolate_vectors(focusInterpolated, node->prevCameraFocus, gLakituState.focus); +-+ } else { +-+ vec3f_copy(posInterpolated, gLakituState.pos); +-+ vec3f_copy(focusInterpolated, gLakituState.focus); +-+ } +-+ vec3f_copy(node->prevCameraPos, gLakituState.pos); +-+ vec3f_copy(node->prevCameraFocus, gLakituState.focus); +-+ node->prevCameraTimestamp = gGlobalTimer; +-+ +- list = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, +- (struct AllocOnlyPool *) gMatStack[gMatStackIndex]); +-+ vec3f_copy(posCopy, gLakituState.pos); +-+ vec3f_copy(focusCopy, gLakituState.focus); +-+ vec3f_copy(gLakituState.pos, posInterpolated); +-+ vec3f_copy(gLakituState.focus, focusInterpolated); +-+ listInterpolated = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, NULL); +-+ vec3f_copy(gLakituState.pos, posCopy); +-+ vec3f_copy(gLakituState.focus, focusCopy); +- } +- if (list != 0) { +-- geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(list), node->fnNode.node.flags >> 8); +-+ geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(list), +-+ (void *) VIRTUAL_TO_PHYSICAL(listInterpolated), node->fnNode.node.flags >> 8); +- } else if (gCurGraphNodeMasterList != NULL) { +- #ifndef F3DEX_GBI_2E +- Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 7); +-@@ -527,61 +720,81 @@ static void geo_process_background(struct GraphNodeBackground *node) { +- } +- } +- +--/** +-- * Render an animated part. The current animation state is not part of the node +-- * but set in global variables. If an animated part is skipped, everything afterwards desyncs. +-- */ +--static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) { +-- Mat4 matrix; +-- Vec3s rotation; +-- Vec3f translation; +-- Mtx *matrixPtr = alloc_display_list(sizeof(*matrixPtr)); +-- +-- vec3s_copy(rotation, gVec3sZero); +-- vec3f_set(translation, node->translation[0], node->translation[1], node->translation[2]); +-- if (gCurAnimType == ANIM_TYPE_TRANSLATION) { +-- translation[0] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] +-+static void anim_process(Vec3f translation, Vec3s rotation, u8 *animType, s16 animFrame, u16 **animAttribute) { +-+ if (*animType == ANIM_TYPE_TRANSLATION) { +-+ translation[0] += gCurAnimData[retrieve_animation_index(animFrame, animAttribute)] +- * gCurAnimTranslationMultiplier; +-- translation[1] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] +-+ translation[1] += gCurAnimData[retrieve_animation_index(animFrame, animAttribute)] +- * gCurAnimTranslationMultiplier; +-- translation[2] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] +-+ translation[2] += gCurAnimData[retrieve_animation_index(animFrame, animAttribute)] +- * gCurAnimTranslationMultiplier; +-- gCurAnimType = ANIM_TYPE_ROTATION; +-+ *animType = ANIM_TYPE_ROTATION; +- } else { +-- if (gCurAnimType == ANIM_TYPE_LATERAL_TRANSLATION) { +-+ if (*animType == ANIM_TYPE_LATERAL_TRANSLATION) { +- translation[0] += +-- gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] +-+ gCurAnimData[retrieve_animation_index(animFrame, animAttribute)] +- * gCurAnimTranslationMultiplier; +-- gCurrAnimAttribute += 2; +-+ *animAttribute += 2; +- translation[2] += +-- gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] +-+ gCurAnimData[retrieve_animation_index(animFrame, animAttribute)] +- * gCurAnimTranslationMultiplier; +-- gCurAnimType = ANIM_TYPE_ROTATION; +-+ *animType = ANIM_TYPE_ROTATION; +- } else { +-- if (gCurAnimType == ANIM_TYPE_VERTICAL_TRANSLATION) { +-- gCurrAnimAttribute += 2; +-+ if (*animType == ANIM_TYPE_VERTICAL_TRANSLATION) { +-+ *animAttribute += 2; +- translation[1] += +-- gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] +-+ gCurAnimData[retrieve_animation_index(animFrame, animAttribute)] +- * gCurAnimTranslationMultiplier; +-- gCurrAnimAttribute += 2; +-- gCurAnimType = ANIM_TYPE_ROTATION; +-- } else if (gCurAnimType == ANIM_TYPE_NO_TRANSLATION) { +-- gCurrAnimAttribute += 6; +-- gCurAnimType = ANIM_TYPE_ROTATION; +-+ *animAttribute += 2; +-+ *animType = ANIM_TYPE_ROTATION; +-+ } else if (*animType == ANIM_TYPE_NO_TRANSLATION) { +-+ *animAttribute += 6; +-+ *animType = ANIM_TYPE_ROTATION; +- } +- } +- } +- +-- if (gCurAnimType == ANIM_TYPE_ROTATION) { +-- rotation[0] = gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]; +-- rotation[1] = gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]; +-- rotation[2] = gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]; +-+ if (*animType == ANIM_TYPE_ROTATION) { +-+ rotation[0] = gCurAnimData[retrieve_animation_index(animFrame, animAttribute)]; +-+ rotation[1] = gCurAnimData[retrieve_animation_index(animFrame, animAttribute)]; +-+ rotation[2] = gCurAnimData[retrieve_animation_index(animFrame, animAttribute)]; +- } +-+} +-+ +-+/** +-+ * Render an animated part. The current animation state is not part of the node +-+ * but set in global variables. If an animated part is skipped, everything afterwards desyncs. +-+ */ +-+static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) { +-+ Mat4 matrix; +-+ Vec3s rotation; +-+ Vec3f translation; +-+ Vec3s rotationInterpolated; +-+ Vec3f translationInterpolated; +-+ Mtx *matrixPtr = alloc_display_list(sizeof(*matrixPtr)); +-+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); +-+ u16 *animAttribute = gCurrAnimAttribute; +-+ u8 animType = gCurAnimType; +-+ +-+ vec3s_copy(rotation, gVec3sZero); +-+ vec3f_set(translation, node->translation[0], node->translation[1], node->translation[2]); +-+ vec3s_copy(rotationInterpolated, rotation); +-+ vec3f_copy(translationInterpolated, translation); +-+ +-+ anim_process(translationInterpolated, rotationInterpolated, &animType, gPrevAnimFrame, &animAttribute); +-+ anim_process(translation, rotation, &gCurAnimType, gCurrAnimFrame, &gCurrAnimAttribute); +-+ interpolate_vectors(translationInterpolated, translationInterpolated, translation); +-+ interpolate_angles(rotationInterpolated, rotationInterpolated, rotation); +-+ +- mtxf_rotate_xyz_and_translate(matrix, translation, rotation); +- mtxf_mul(gMatStack[gMatStackIndex + 1], matrix, gMatStack[gMatStackIndex]); +-+ mtxf_rotate_xyz_and_translate(matrix, translationInterpolated, rotationInterpolated); +-+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], matrix, gMatStackInterpolated[gMatStackIndex]); +- gMatStackIndex++; +- mtxf_to_mtx(matrixPtr, gMatStack[gMatStackIndex]); +- gMatStackFixed[gMatStackIndex] = matrixPtr; +-+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); +-+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; +- if (node->displayList != NULL) { +- geo_append_display_list(node->displayList, node->node.flags >> 8); +- } +-@@ -613,6 +826,17 @@ void geo_set_animation_globals(struct GraphNodeObject_sub *node, s32 hasAnimatio +- } +- +- gCurrAnimFrame = node->animFrame; +-+ if (node->prevAnimPtr == anim && node->prevAnimID == node->animID && +-+ gGlobalTimer == node->prevAnimFrameTimestamp + 1) { +-+ gPrevAnimFrame = node->prevAnimFrame; +-+ } else { +-+ gPrevAnimFrame = node->animFrame; +-+ } +-+ node->prevAnimPtr = anim; +-+ node->prevAnimID = node->animID; +-+ node->prevAnimFrame = node->animFrame; +-+ node->prevAnimFrameTimestamp = gGlobalTimer; +-+ +- gCurAnimEnabled = (anim->flags & ANIM_FLAG_5) == 0; +- gCurrAnimAttribute = segmented_to_virtual((void *) anim->index); +- gCurAnimData = segmented_to_virtual((void *) anim->values); +-@@ -631,8 +855,10 @@ void geo_set_animation_globals(struct GraphNodeObject_sub *node, s32 hasAnimatio +- */ +- static void geo_process_shadow(struct GraphNodeShadow *node) { +- Gfx *shadowList; +-+ Gfx *shadowListInterpolated; +- Mat4 mtxf; +- Vec3f shadowPos; +-+ Vec3f shadowPosInterpolated; +- Vec3f animOffset; +- f32 objScale; +- f32 shadowScale; +-@@ -640,6 +866,7 @@ static void geo_process_shadow(struct GraphNodeShadow *node) { +- f32 cosAng; +- struct GraphNode *geo; +- Mtx *mtx; +-+ Mtx *mtxInterpolated; +- +- if (gCurGraphNodeCamera != NULL && gCurGraphNodeObject != NULL) { +- if (gCurGraphNodeHeldObject != NULL) { +-@@ -678,21 +905,57 @@ static void geo_process_shadow(struct GraphNodeShadow *node) { +- } +- } +- +-+ if (gCurGraphNodeHeldObject != NULL) { +-+ if (gGlobalTimer == gCurGraphNodeHeldObject->prevShadowPosTimestamp + 1) { +-+ interpolate_vectors(shadowPosInterpolated, gCurGraphNodeHeldObject->prevShadowPos, shadowPos); +-+ } else { +-+ vec3f_copy(shadowPosInterpolated, shadowPos); +-+ } +-+ vec3f_copy(gCurGraphNodeHeldObject->prevShadowPos, shadowPos); +-+ gCurGraphNodeHeldObject->prevShadowPosTimestamp = gGlobalTimer; +-+ } else { +-+ if (gGlobalTimer == gCurGraphNodeObject->prevShadowPosTimestamp + 1 && +-+ gGlobalTimer != gCurGraphNodeObject->skipInterpolationTimestamp) { +-+ interpolate_vectors(shadowPosInterpolated, gCurGraphNodeObject->prevShadowPos, shadowPos); +-+ } else { +-+ vec3f_copy(shadowPosInterpolated, shadowPos); +-+ } +-+ vec3f_copy(gCurGraphNodeObject->prevShadowPos, shadowPos); +-+ gCurGraphNodeObject->prevShadowPosTimestamp = gGlobalTimer; +-+ } +-+ +-+ extern u8 gInterpolatingSurfaces; +-+ gInterpolatingSurfaces = TRUE; +-+ shadowListInterpolated = create_shadow_below_xyz(shadowPosInterpolated[0], shadowPosInterpolated[1], +-+ shadowPosInterpolated[2], shadowScale, +-+ node->shadowSolidity, node->shadowType); +-+ gInterpolatingSurfaces = FALSE; +- shadowList = create_shadow_below_xyz(shadowPos[0], shadowPos[1], shadowPos[2], shadowScale, +- node->shadowSolidity, node->shadowType); +-- if (shadowList != NULL) { +-+ if (shadowListInterpolated != NULL && shadowList != NULL) { +- mtx = alloc_display_list(sizeof(*mtx)); +-+ mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); +- gMatStackIndex++; +-+ +- mtxf_translate(mtxf, shadowPos); +- mtxf_mul(gMatStack[gMatStackIndex], mtxf, *gCurGraphNodeCamera->matrixPtr); +- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); +- gMatStackFixed[gMatStackIndex] = mtx; +-+ +-+ mtxf_translate(mtxf, shadowPosInterpolated); +-+ mtxf_mul(gMatStackInterpolated[gMatStackIndex], mtxf, *gCurGraphNodeCamera->matrixPtrInterpolated); +-+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); +-+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; +-+ +- if (gShadowAboveWaterOrLava == 1) { +-- geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 4); +-+ geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowList), +-+ (void *) VIRTUAL_TO_PHYSICAL(shadowListInterpolated), 4); +- } else if (gMarioOnIceOrCarpet == 1) { +-- geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 5); +-+ geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowList), +-+ (void *) VIRTUAL_TO_PHYSICAL(shadowListInterpolated), 5); +- } else { +-- geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 6); +-+ geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowList), +-+ (void *) VIRTUAL_TO_PHYSICAL(shadowListInterpolated), 6); +- } +- gMatStackIndex--; +- } +-@@ -789,31 +1052,101 @@ static int obj_is_in_view(struct GraphNodeObject *node, Mat4 matrix) { +- return TRUE; +- } +- +-+static void interpolate_matrix(Mat4 result, Mat4 a, Mat4 b) { +-+ s32 i, j; +-+ for (i = 0; i < 4; i++) { +-+ for (j = 0; j < 4; j++) { +-+ result[i][j] = (a[i][j] + b[i][j]) / 2.0f; +-+ } +-+ } +-+} +-+ +- /** +- * Process an object node. +- */ +- static void geo_process_object(struct Object *node) { +- Mat4 mtxf; +- s32 hasAnimation = (node->header.gfx.node.flags & GRAPH_RENDER_HAS_ANIMATION) != 0; +-+ Vec3f scaleInterpolated; +- +- if (node->header.gfx.unk18 == gCurGraphNodeRoot->areaIndex) { +- if (node->header.gfx.throwMatrix != NULL) { +- mtxf_mul(gMatStack[gMatStackIndex + 1], *node->header.gfx.throwMatrix, +- gMatStack[gMatStackIndex]); +-+ if (gGlobalTimer == node->header.gfx.prevThrowMatrixTimestamp + 1 && +-+ gGlobalTimer != node->header.gfx.skipInterpolationTimestamp) { +-+ interpolate_matrix(mtxf, *node->header.gfx.throwMatrix, node->header.gfx.prevThrowMatrix); +-+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, +-+ gMatStackInterpolated[gMatStackIndex]); +-+ } else { +-+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], (void *) node->header.gfx.throwMatrix, +-+ gMatStackInterpolated[gMatStackIndex]); +-+ } +-+ mtxf_copy(node->header.gfx.prevThrowMatrix, *node->header.gfx.throwMatrix); +-+ node->header.gfx.prevThrowMatrixTimestamp = gGlobalTimer; +- } else if (node->header.gfx.node.flags & GRAPH_RENDER_CYLBOARD) { +-+ Vec3f posInterpolated; +-+ if (gGlobalTimer == node->header.gfx.prevTimestamp + 1 && +-+ gGlobalTimer != node->header.gfx.skipInterpolationTimestamp) { +-+ interpolate_vectors(posInterpolated, node->header.gfx.prevPos, node->header.gfx.pos); +-+ } else { +-+ vec3f_copy(posInterpolated, node->header.gfx.pos); +-+ } +-+ vec3f_copy(node->header.gfx.prevPos, node->header.gfx.pos); +-+ node->header.gfx.prevTimestamp = gGlobalTimer; +- mtxf_cylboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], +- node->header.gfx.pos, gCurGraphNodeCamera->roll); +-+ mtxf_cylboard(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex], +-+ posInterpolated, gCurGraphNodeCamera->roll); +- } else if (node->header.gfx.node.flags & GRAPH_RENDER_BILLBOARD) { +-+ Vec3f posInterpolated; +-+ if (gGlobalTimer == node->header.gfx.prevTimestamp + 1 && +-+ gGlobalTimer != node->header.gfx.skipInterpolationTimestamp) { +-+ interpolate_vectors(posInterpolated, node->header.gfx.prevPos, node->header.gfx.pos); +-+ } else { +-+ vec3f_copy(posInterpolated, node->header.gfx.pos); +-+ } +-+ vec3f_copy(node->header.gfx.prevPos, node->header.gfx.pos); +-+ node->header.gfx.prevTimestamp = gGlobalTimer; +- mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], +- node->header.gfx.pos, gCurGraphNodeCamera->roll); +-+ mtxf_billboard(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex], +-+ posInterpolated, gCurGraphNodeCamera->roll); +- } else { +-+ Vec3f posInterpolated; +-+ Vec3s angleInterpolated; +-+ if (gGlobalTimer == node->header.gfx.prevTimestamp + 1 && +-+ gGlobalTimer != node->header.gfx.skipInterpolationTimestamp) { +-+ interpolate_vectors(posInterpolated, node->header.gfx.prevPos, node->header.gfx.pos); +-+ interpolate_angles(angleInterpolated, node->header.gfx.prevAngle, node->header.gfx.angle); +-+ } else { +-+ vec3f_copy(posInterpolated, node->header.gfx.pos); +-+ vec3s_copy(angleInterpolated, node->header.gfx.angle); +-+ } +-+ vec3f_copy(node->header.gfx.prevPos, node->header.gfx.pos); +-+ vec3s_copy(node->header.gfx.prevAngle, node->header.gfx.angle); +-+ node->header.gfx.prevTimestamp = gGlobalTimer; +- mtxf_rotate_zxy_and_translate(mtxf, node->header.gfx.pos, node->header.gfx.angle); +- mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); +-+ mtxf_rotate_zxy_and_translate(mtxf, posInterpolated, angleInterpolated); +-+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]); +-+ } +-+ +-+ if (gGlobalTimer == node->header.gfx.prevScaleTimestamp + 1 && +-+ gGlobalTimer != node->header.gfx.skipInterpolationTimestamp) { +-+ interpolate_vectors(scaleInterpolated, node->header.gfx.prevScale, node->header.gfx.scale); +-+ } else { +-+ vec3f_copy(scaleInterpolated, node->header.gfx.scale); +- } +-+ vec3f_copy(node->header.gfx.prevScale, node->header.gfx.scale); +-+ node->header.gfx.prevScaleTimestamp = gGlobalTimer; +- +- mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1], +- node->header.gfx.scale); +-+ mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex + 1], +-+ scaleInterpolated); +- node->header.gfx.throwMatrix = &gMatStack[++gMatStackIndex]; +-+ node->header.gfx.throwMatrixInterpolated = &gMatStackInterpolated[gMatStackIndex]; +- node->header.gfx.cameraToObject[0] = gMatStack[gMatStackIndex][3][0]; +- node->header.gfx.cameraToObject[1] = gMatStack[gMatStackIndex][3][1]; +- node->header.gfx.cameraToObject[2] = gMatStack[gMatStackIndex][3][2]; +-@@ -824,9 +1157,12 @@ static void geo_process_object(struct Object *node) { +- } +- if (obj_is_in_view(&node->header.gfx, gMatStack[gMatStackIndex])) { +- Mtx *mtx = alloc_display_list(sizeof(*mtx)); +-+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); +- +- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); +- gMatStackFixed[gMatStackIndex] = mtx; +-+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); +-+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; +- if (node->header.gfx.sharedChild != NULL) { +- gCurGraphNodeObject = (struct GraphNodeObject *) node; +- node->header.gfx.sharedChild->parent = &node->header.gfx.node; +-@@ -837,11 +1173,16 @@ static void geo_process_object(struct Object *node) { +- if (node->header.gfx.node.children != NULL) { +- geo_process_node_and_siblings(node->header.gfx.node.children); +- } +-+ } else { +-+ node->header.gfx.prevThrowMatrixTimestamp = 0; +-+ node->header.gfx.prevTimestamp = 0; +-+ node->header.gfx.prevScaleTimestamp = 0; +- } +- +- gMatStackIndex--; +- gCurAnimType = ANIM_TYPE_NONE; +- node->header.gfx.throwMatrix = NULL; +-+ node->header.gfx.throwMatrixInterpolated = NULL; +- } +- } +- +-@@ -868,6 +1209,8 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { +- Mat4 mat; +- Vec3f translation; +- Mtx *mtx = alloc_display_list(sizeof(*mtx)); +-+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); +-+ Vec3f scaleInterpolated; +- +- #ifdef F3DEX_GBI_2 +- gSPLookAt(gDisplayListHead++, &lookAt); +-@@ -883,6 +1226,14 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { +- translation[1] = node->translation[1] / 4.0f; +- translation[2] = node->translation[2] / 4.0f; +- +-+ if (gGlobalTimer == node->objNode->header.gfx.prevScaleTimestamp + 1) { +-+ interpolate_vectors(scaleInterpolated, node->objNode->header.gfx.prevScale, node->objNode->header.gfx.scale); +-+ } else { +-+ vec3f_copy(scaleInterpolated, node->objNode->header.gfx.scale); +-+ } +-+ vec3f_copy(node->objNode->header.gfx.prevScale, node->objNode->header.gfx.scale); +-+ node->objNode->header.gfx.prevScaleTimestamp = gGlobalTimer; +-+ +- mtxf_translate(mat, translation); +- mtxf_copy(gMatStack[gMatStackIndex + 1], *gCurGraphNodeObject->throwMatrix); +- gMatStack[gMatStackIndex + 1][3][0] = gMatStack[gMatStackIndex][3][0]; +-@@ -891,6 +1242,13 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { +- mtxf_mul(gMatStack[gMatStackIndex + 1], mat, gMatStack[gMatStackIndex + 1]); +- mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1], +- node->objNode->header.gfx.scale); +-+ mtxf_copy(gMatStackInterpolated[gMatStackIndex + 1], (void *) gCurGraphNodeObject->throwMatrixInterpolated); +-+ gMatStackInterpolated[gMatStackIndex + 1][3][0] = gMatStackInterpolated[gMatStackIndex][3][0]; +-+ gMatStackInterpolated[gMatStackIndex + 1][3][1] = gMatStackInterpolated[gMatStackIndex][3][1]; +-+ gMatStackInterpolated[gMatStackIndex + 1][3][2] = gMatStackInterpolated[gMatStackIndex][3][2]; +-+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mat, gMatStackInterpolated[gMatStackIndex + 1]); +-+ mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex + 1], +-+ scaleInterpolated); +- if (node->fnNode.func != NULL) { +- node->fnNode.func(GEO_CONTEXT_HELD_OBJ, &node->fnNode.node, +- (struct AllocOnlyPool *) gMatStack[gMatStackIndex + 1]); +-@@ -898,12 +1256,15 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { +- gMatStackIndex++; +- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); +- gMatStackFixed[gMatStackIndex] = mtx; +-+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); +-+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; +- gGeoTempState.type = gCurAnimType; +- gGeoTempState.enabled = gCurAnimEnabled; +- gGeoTempState.frame = gCurrAnimFrame; +- gGeoTempState.translationMultiplier = gCurAnimTranslationMultiplier; +- gGeoTempState.attribute = gCurrAnimAttribute; +- gGeoTempState.data = gCurAnimData; +-+ gGeoTempState.prevFrame = gPrevAnimFrame; +- gCurAnimType = 0; +- gCurGraphNodeHeldObject = (void *) node; +- if (node->objNode->header.gfx.unk38.curAnim != NULL) { +-@@ -918,6 +1279,7 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { +- gCurAnimTranslationMultiplier = gGeoTempState.translationMultiplier; +- gCurrAnimAttribute = gGeoTempState.attribute; +- gCurAnimData = gGeoTempState.data; +-+ gPrevAnimFrame = gGeoTempState.prevFrame; +- gMatStackIndex--; +- } +- +-@@ -1039,6 +1401,7 @@ void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor) +- if (node->node.flags & GRAPH_RENDER_ACTIVE) { +- Mtx *initialMatrix; +- Vp *viewport = alloc_display_list(sizeof(*viewport)); +-+ Vp *viewportInterpolated = viewport; +- +- gDisplayListHeap = alloc_only_pool_init(main_pool_available() - sizeof(struct AllocOnlyPool), +- MEMORY_POOL_LEFT); +-@@ -1049,7 +1412,12 @@ void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor) +- vec3s_set(viewport->vp.vscale, node->width * 4, node->height * 4, 511); +- if (b != NULL) { +- clear_frame_buffer(clearColor); +-- make_viewport_clip_rect(b); +-+ viewportInterpolated = alloc_display_list(sizeof(*viewportInterpolated)); +-+ interpolate_vectors_s16(viewportInterpolated->vp.vtrans, sPrevViewport.vp.vtrans, b->vp.vtrans); +-+ interpolate_vectors_s16(viewportInterpolated->vp.vscale, sPrevViewport.vp.vscale, b->vp.vscale); +-+ +-+ sViewportPos = gDisplayListHead; +-+ make_viewport_clip_rect(viewportInterpolated); +- *viewport = *b; +- } +- +-@@ -1057,11 +1425,16 @@ void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor) +- clear_frame_buffer(clearColor); +- make_viewport_clip_rect(c); +- } +-+ sPrevViewport = *viewport; +- +- mtxf_identity(gMatStack[gMatStackIndex]); +- mtxf_to_mtx(initialMatrix, gMatStack[gMatStackIndex]); +- gMatStackFixed[gMatStackIndex] = initialMatrix; +-- gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(viewport)); +-+ +-+ mtxf_identity(gMatStackInterpolated[gMatStackIndex]); +-+ gMatStackInterpolatedFixed[gMatStackIndex] = initialMatrix; +-+ +-+ gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(viewportInterpolated)); +- gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(gMatStackFixed[gMatStackIndex]), +- G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); +- gCurGraphNodeRoot = node; +-diff --git a/src/game/screen_transition.c b/src/game/screen_transition.c +-index b49ddaf..d6656af 100644 +---- a/src/game/screen_transition.c +-+++ b/src/game/screen_transition.c +-@@ -16,6 +16,19 @@ +- u8 sTransitionColorFadeCount[4] = { 0 }; +- u16 sTransitionTextureFadeCount[2] = { 0 }; +- +-+static Gfx *sScreenTransitionVerticesPos[2]; +-+static Vtx *sScreenTransitionVertices; +-+ +-+void patch_screen_transition_interpolated(void) { +-+ if (sScreenTransitionVerticesPos[0] != NULL) { +-+ gSPVertex(sScreenTransitionVerticesPos[0], VIRTUAL_TO_PHYSICAL(sScreenTransitionVertices), 8, 0); +-+ gSPVertex(sScreenTransitionVerticesPos[1], VIRTUAL_TO_PHYSICAL(sScreenTransitionVertices), 4, 0); +-+ sScreenTransitionVerticesPos[0] = NULL; +-+ sScreenTransitionVerticesPos[1] = NULL; +-+ sScreenTransitionVertices = NULL; +-+ } +-+} +-+ +- s32 set_and_reset_transition_fade_timer(s8 fadeTimer, u8 transTime) { +- s32 reset = FALSE; +- +-@@ -85,14 +98,29 @@ s32 render_fade_transition_into_color(s8 fadeTimer, u8 transTime, struct WarpTra +- return dl_transition_color(fadeTimer, transTime, transData, alpha); +- } +- +-+#if 0 +-+ +- s16 calc_tex_transition_radius(s8 fadeTimer, s8 transTime, struct WarpTransitionData *transData) { +- f32 texRadius = transData->endTexRadius - transData->startTexRadius; +- f32 radiusTime = sTransitionColorFadeCount[fadeTimer] * texRadius / (f32)(transTime - 1); +- f32 result = transData->startTexRadius + radiusTime; +- +-- return (s16)(result + 0.5);; +-+ return (s16)(result + 0.5); +- } +- +-+#else +-+ +-+s16 calc_tex_transition_radius(s8 fadeTimer, f32 interpolationFraction, s8 transTime, struct WarpTransitionData *transData) { +-+ f32 texRadius = transData->endTexRadius - transData->startTexRadius; +-+ f32 radiusTime = (sTransitionColorFadeCount[fadeTimer] == 0 ? 0 : +-+ sTransitionColorFadeCount[fadeTimer] - 1 + interpolationFraction) * texRadius / (f32)(transTime - 1); +-+ f32 result = transData->startTexRadius + radiusTime; +-+ +-+ return (s16)(result + 0.5); +-+} +-+ +-+#endif +-+ +- f32 calc_tex_transition_time(s8 fadeTimer, s8 transTime, struct WarpTransitionData *transData) { +- f32 startX = transData->startTexX; +- f32 startY = transData->startTexY; +-@@ -166,6 +194,8 @@ void *sTextureTransitionID[] = { +- texture_transition_bowser_half, +- }; +- +-+#if 0 +-+ +- s32 render_textured_transition(s8 fadeTimer, s8 transTime, struct WarpTransitionData *transData, s8 texID, s8 transTexType) { +- f32 texTransTime = calc_tex_transition_time(fadeTimer, transTime, transData); +- u16 texTransPos = convert_tex_transition_angle_to_pos(transData); +-@@ -206,6 +236,56 @@ s32 render_textured_transition(s8 fadeTimer, s8 transTime, struct WarpTransition +- return set_and_reset_transition_fade_timer(fadeTimer, transTime); +- } +- +-+#else +-+ +-+s32 render_textured_transition(s8 fadeTimer, s8 transTime, struct WarpTransitionData *transData, s8 texID, s8 transTexType) { +-+ f32 texTransTime = calc_tex_transition_time(fadeTimer, transTime, transData); +-+ u16 texTransPos = convert_tex_transition_angle_to_pos(transData); +-+ s16 centerTransX = center_tex_transition_x(transData, texTransTime, texTransPos); +-+ s16 centerTransY = center_tex_transition_y(transData, texTransTime, texTransPos); +-+ s16 texTransRadius = calc_tex_transition_radius(fadeTimer, 1.0f, transTime, transData); +-+ s16 texTransRadiusInterpolated = calc_tex_transition_radius(fadeTimer, 0.5f, transTime, transData); +-+ Vtx *verts = alloc_display_list(8 * sizeof(*verts)); +-+ Vtx *vertsInterpolated = alloc_display_list(8 * sizeof(*vertsInterpolated)); +-+ +-+ if (verts != NULL && vertsInterpolated != NULL) { +-+ load_tex_transition_vertex(verts, fadeTimer, transData, centerTransX, centerTransY, texTransRadius, transTexType); +-+ load_tex_transition_vertex(vertsInterpolated, fadeTimer, transData, centerTransX, centerTransY, texTransRadiusInterpolated, transTexType); +-+ sScreenTransitionVertices = verts; +-+ gSPDisplayList(gDisplayListHead++, dl_proj_mtx_fullscreen) +-+ gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE); +-+ gDPSetRenderMode(gDisplayListHead++, G_RM_AA_OPA_SURF, G_RM_AA_OPA_SURF2); +-+ sScreenTransitionVerticesPos[0] = gDisplayListHead; +-+ gSPVertex(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(vertsInterpolated), 8, 0); +-+ gSPDisplayList(gDisplayListHead++, dl_transition_draw_filled_region); +-+ gDPPipeSync(gDisplayListHead++); +-+ gDPSetCombineMode(gDisplayListHead++, G_CC_MODULATEIDECALA, G_CC_MODULATEIDECALA); +-+ gDPSetRenderMode(gDisplayListHead++, G_RM_AA_XLU_SURF, G_RM_AA_XLU_SURF2); +-+ gDPSetTextureFilter(gDisplayListHead++, G_TF_BILERP); +-+ switch (transTexType) { +-+ case TRANS_TYPE_MIRROR: +-+ gDPLoadTextureBlock(gDisplayListHead++, sTextureTransitionID[texID], G_IM_FMT_IA, G_IM_SIZ_8b, 32, 64, 0, +-+ G_TX_WRAP | G_TX_MIRROR, G_TX_WRAP | G_TX_MIRROR, 5, 6, G_TX_NOLOD, G_TX_NOLOD); +-+ break; +-+ case TRANS_TYPE_CLAMP: +-+ gDPLoadTextureBlock(gDisplayListHead++, sTextureTransitionID[texID], G_IM_FMT_IA, G_IM_SIZ_8b, 64, 64, 0, +-+ G_TX_CLAMP, G_TX_CLAMP, 6, 6, G_TX_NOLOD, G_TX_NOLOD); +-+ break; +-+ } +-+ gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); +-+ sScreenTransitionVerticesPos[1] = gDisplayListHead; +-+ gSPVertex(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(vertsInterpolated), 4, 0); +-+ gSPDisplayList(gDisplayListHead++, dl_draw_quad_verts_0123); +-+ gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF); +-+ gSPDisplayList(gDisplayListHead++, dl_screen_transition_end); +-+ sTransitionTextureFadeCount[fadeTimer] += transData->texTimer; +-+ } else { +-+ } +-+ return set_and_reset_transition_fade_timer(fadeTimer, transTime); +-+} +-+ +-+#endif +-+ +- int render_screen_transition(s8 fadeTimer, s8 transType, u8 transTime, struct WarpTransitionData *transData) { +- switch (transType) { +- case WARP_TRANSITION_FADE_FROM_COLOR: +-diff --git a/src/menu/intro_geo.c b/src/menu/intro_geo.c +-index 37c6752..d823d40 100644 +---- a/src/menu/intro_geo.c +-+++ b/src/menu/intro_geo.c +-@@ -1,5 +1,6 @@ +- #include +- +-+#include "engine/math_util.h" +- #include "game/memory.h" +- #include "game/segment2.h" +- #include "game/segment7.h" +-@@ -70,6 +71,18 @@ s8 gameOverBackgroundTable[] = { +- s8 gameOverBackgroundFlipOrder[] = { 0x00, 0x01, 0x02, 0x03, 0x07, 0x0B, +- 0x0a, 0x09, 0x08, 0x04, 0x05, 0x06 }; +- +-+static Gfx *sIntroScalePos; +-+static Vec3f sIntroScale; +-+ +-+void patch_title_screen_scales(void) { +-+ if (sIntroScalePos != NULL) { +-+ Mtx *scaleMat = alloc_display_list(sizeof(*scaleMat)); +-+ guScale(scaleMat, sIntroScale[0], sIntroScale[1], sIntroScale[2]); +-+ gSPMatrix(sIntroScalePos, scaleMat, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); +-+ sIntroScalePos = NULL; +-+ } +-+} +-+ +- Gfx *geo_title_screen(s32 sp50, struct GraphNode *sp54, UNUSED void *context) { +- struct GraphNode *graphNode; // sp4c +- Gfx *displayList; // sp48 +-@@ -80,6 +93,8 @@ Gfx *geo_title_screen(s32 sp50, struct GraphNode *sp54, UNUSED void *context) { +- f32 scaleX; // sp34 +- f32 scaleY; // sp30 +- f32 scaleZ; // sp2c +-+ Vec3f scale; +-+ Vec3f scaleInterpolated; +- graphNode = sp54; +- displayList = NULL; +- displayListIter = NULL; +-@@ -110,7 +125,11 @@ Gfx *geo_title_screen(s32 sp50, struct GraphNode *sp54, UNUSED void *context) { +- scaleY = 0.0f; +- scaleZ = 0.0f; +- } +-- guScale(scaleMat, scaleX, scaleY, scaleZ); +-+ vec3f_set(scale, scaleX, scaleY, scaleZ); +-+ interpolate_vectors(scaleInterpolated, sIntroScale, scale); +-+ vec3f_set(sIntroScale, scaleX, scaleY, scaleZ); +-+ guScale(scaleMat, scaleInterpolated[0], scaleInterpolated[1], scaleInterpolated[2]); +-+ sIntroScalePos = displayListIter; +- gSPMatrix(displayListIter++, scaleMat, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); +- gSPDisplayList(displayListIter++, &intro_seg7_dl_0700B3A0); +- gSPPopMatrix(displayListIter++, G_MTX_MODELVIEW); +-diff --git a/src/pc/gfx/gfx_dxgi.cpp b/src/pc/gfx/gfx_dxgi.cpp +-index 0467495..fa4eb33 100644 +---- a/src/pc/gfx/gfx_dxgi.cpp +-+++ b/src/pc/gfx/gfx_dxgi.cpp +-@@ -36,10 +36,10 @@ +- +- #ifdef VERSION_EU +- #define FRAME_INTERVAL_US_NUMERATOR 40000 +--#define FRAME_INTERVAL_US_DENOMINATOR 1 +-+#define FRAME_INTERVAL_US_DENOMINATOR 2 +- #else +- #define FRAME_INTERVAL_US_NUMERATOR 100000 +--#define FRAME_INTERVAL_US_DENOMINATOR 3 +-+#define FRAME_INTERVAL_US_DENOMINATOR 6 +- #endif +- +- using namespace Microsoft::WRL; // For ComPtr +-diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c +-index 0108ca9..28d56ac 100644 +---- a/src/pc/gfx/gfx_sdl2.c +-+++ b/src/pc/gfx/gfx_sdl2.c +-@@ -150,7 +150,11 @@ static inline void gfx_sdl_set_vsync(const bool enabled) { +- if (enabled) { +- // try to detect refresh rate +- SDL_GL_SetSwapInterval(1); +-- const int vblanks = gCLIOpts.SyncFrames ? (int)gCLIOpts.SyncFrames : test_vsync(); +-+ int vblanks = test_vsync(); +-+ if (vblanks & 1) +-+ vblanks = 0; // not divisible by 60, fuck that +-+ else +-+ vblanks /= 2; +- if (vblanks) { +- printf("determined swap interval: %d\n", vblanks); +- SDL_GL_SetSwapInterval(vblanks); +-@@ -233,7 +237,7 @@ static void gfx_sdl_init(const char *window_title) { +- gfx_sdl_set_fullscreen(); +- +- perf_freq = SDL_GetPerformanceFrequency(); +-- frame_time = perf_freq / FRAMERATE; +-+ frame_time = perf_freq / (2 * FRAMERATE); +- +- for (size_t i = 0; i < sizeof(windows_scancode_table) / sizeof(SDL_Scancode); i++) { +- inverted_scancode_table[windows_scancode_table[i]] = i; +-diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c +-index 923e7ea..4fe6161 100644 +---- a/src/pc/pc_main.c +-+++ b/src/pc/pc_main.c +-@@ -83,6 +83,25 @@ void send_display_list(struct SPTask *spTask) { +- #define SAMPLES_LOW 528 +- #endif +- +-+static inline void patch_interpolations(void) { +-+ extern void mtx_patch_interpolated(void); +-+ extern void patch_screen_transition_interpolated(void); +-+ extern void patch_title_screen_scales(void); +-+ extern void patch_interpolated_dialog(void); +-+ extern void patch_interpolated_hud(void); +-+ extern void patch_interpolated_paintings(void); +-+ extern void patch_interpolated_bubble_particles(void); +-+ extern void patch_interpolated_snow_particles(void); +-+ mtx_patch_interpolated(); +-+ patch_screen_transition_interpolated(); +-+ patch_title_screen_scales(); +-+ patch_interpolated_dialog(); +-+ patch_interpolated_hud(); +-+ patch_interpolated_paintings(); +-+ patch_interpolated_bubble_particles(); +-+ patch_interpolated_snow_particles(); +-+} +-+ +- void produce_one_frame(void) { +- gfx_start_frame(); +- +-@@ -110,6 +129,11 @@ void produce_one_frame(void) { +- audio_api->play((u8 *)audio_buffer, 2 * num_audio_samples * 4); +- +- gfx_end_frame(); +-+ +-+ gfx_start_frame(); +-+ patch_interpolations(); +-+ send_display_list(gGfxSPTask); +-+ gfx_end_frame(); +- } +- +- void audio_shutdown(void) { diff --git a/include/types.h b/include/types.h index b3dc27e..c46bdf0 100644 --- a/include/types.h @@ -1890,28 +3854,15 @@ index 0467495..fa4eb33 100644 using namespace Microsoft::WRL; // For ComPtr diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c -index 0108ca9..28d56ac 100644 +index daff686..66fb346 100644 --- a/src/pc/gfx/gfx_sdl2.c +++ b/src/pc/gfx/gfx_sdl2.c -@@ -150,7 +150,11 @@ static inline void gfx_sdl_set_vsync(const bool enabled) { - if (enabled) { - // try to detect refresh rate - SDL_GL_SetSwapInterval(1); -- const int vblanks = gCLIOpts.SyncFrames ? (int)gCLIOpts.SyncFrames : test_vsync(); -+ int vblanks = test_vsync(); -+ if (vblanks & 1) -+ vblanks = 0; // not divisible by 60, fuck that -+ else -+ vblanks /= 2; - if (vblanks) { - printf("determined swap interval: %d\n", vblanks); - SDL_GL_SetSwapInterval(vblanks); -@@ -233,7 +237,7 @@ static void gfx_sdl_init(const char *window_title) { - gfx_sdl_set_fullscreen(); +@@ -186,7 +186,7 @@ static void gfx_sdl_init(const char *window_title) { perf_freq = SDL_GetPerformanceFrequency(); -- frame_time = perf_freq / FRAMERATE; -+ frame_time = perf_freq / (2 * FRAMERATE); + +- frame_rate = perf_freq / FRAMERATE; ++ frame_rate = perf_freq / (2 * FRAMERATE); for (size_t i = 0; i < sizeof(windows_scancode_table) / sizeof(SDL_Scancode); i++) { inverted_scancode_table[windows_scancode_table[i]] = i; From 5bb2c99ba1492a2f15f9b172c1d7b15be3845888 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Thu, 25 Nov 2021 16:11:06 +0300 Subject: [PATCH 13/20] Revert "update 60fps patch" This reverts commit 48247471fe385a3113d197ba458fb546bf3fa7e8. --- enhancements/60fps_ex.patch | 1987 +---------------------------------- 1 file changed, 18 insertions(+), 1969 deletions(-) diff --git a/enhancements/60fps_ex.patch b/enhancements/60fps_ex.patch index 6fd89548..5980d9bf 100644 --- a/enhancements/60fps_ex.patch +++ b/enhancements/60fps_ex.patch @@ -1,1967 +1,3 @@ -diff --git a/enhancements/60fps_ex.patch b/enhancements/60fps_ex.patch -index 5980d9b..e69de29 100644 ---- a/enhancements/60fps_ex.patch -+++ b/enhancements/60fps_ex.patch -@@ -1,1959 +0,0 @@ --diff --git a/include/types.h b/include/types.h --index b3dc27e..c46bdf0 100644 ----- a/include/types.h --+++ b/include/types.h --@@ -118,6 +118,10 @@ struct GraphNodeObject_sub -- /*0x0A 0x42*/ u16 animTimer; -- /*0x0C 0x44*/ s32 animFrameAccelAssist; -- /*0x10 0x48*/ s32 animAccel; --+ s16 prevAnimFrame; --+ s16 prevAnimID; --+ u32 prevAnimFrameTimestamp; --+ struct Animation *prevAnimPtr; -- }; -- -- struct GraphNodeObject --@@ -128,11 +132,22 @@ struct GraphNodeObject -- /*0x19*/ s8 unk19; -- /*0x1A*/ Vec3s angle; -- /*0x20*/ Vec3f pos; --+ Vec3s prevAngle; --+ Vec3f prevPos; --+ u32 prevTimestamp; --+ Vec3f prevShadowPos; --+ u32 prevShadowPosTimestamp; -- /*0x2C*/ Vec3f scale; --+ Vec3f prevScale; --+ u32 prevScaleTimestamp; -- /*0x38*/ struct GraphNodeObject_sub unk38; -- /*0x4C*/ struct SpawnInfo *unk4C; -- /*0x50*/ Mat4 *throwMatrix; // matrix ptr --+ Mat4 prevThrowMatrix; --+ u32 prevThrowMatrixTimestamp; --+ Mat4 *throwMatrixInterpolated; -- /*0x54*/ Vec3f cameraToObject; --+ u32 skipInterpolationTimestamp; -- }; -- -- struct ObjectNode --@@ -243,6 +258,10 @@ struct Surface -- } normal; -- /*0x28*/ f32 originOffset; -- /*0x2C*/ struct Object *object; --+ Vec3s prevVertex1; --+ Vec3s prevVertex2; --+ Vec3s prevVertex3; --+ u32 modifiedTimestamp; -- }; -- -- struct MarioBodyState --diff --git a/src/engine/graph_node.h b/src/engine/graph_node.h --index 802d97a..1b0d677 100644 ----- a/src/engine/graph_node.h --+++ b/src/engine/graph_node.h --@@ -110,6 +110,8 @@ struct GraphNodePerspective -- /*0x1C*/ f32 fov; // horizontal field of view in degrees -- /*0x20*/ s16 near; // near clipping plane -- /*0x22*/ s16 far; // far clipping plane --+ f32 prevFov; --+ f32 prevTimestamp; -- }; -- -- /** An entry in the master list. It is a linked list of display lists --@@ -118,7 +120,9 @@ struct GraphNodePerspective -- struct DisplayListNode -- { -- Mtx *transform; --+ void *transformInterpolated; -- void *displayList; --+ void *displayListInterpolated; -- struct DisplayListNode *next; -- }; -- --@@ -185,7 +189,11 @@ struct GraphNodeCamera -- } config; -- /*0x1C*/ Vec3f pos; -- /*0x28*/ Vec3f focus; --+ Vec3f prevPos; --+ Vec3f prevFocus; --+ u32 prevTimestamp; -- /*0x34*/ Mat4 *matrixPtr; // pointer to look-at matrix of this camera as a Mat4 --+ Mat4 *matrixPtrInterpolated; -- /*0x38*/ s16 roll; // roll in look at matrix. Doesn't account for light direction unlike rollScreen. -- /*0x3A*/ s16 rollScreen; // rolls screen while keeping the light direction consistent -- }; --@@ -226,7 +234,8 @@ struct GraphNodeRotation -- /*0x00*/ struct GraphNode node; -- /*0x14*/ void *displayList; -- /*0x18*/ Vec3s rotation; --- u8 pad1E[2]; --+ Vec3s prevRotation; --+ u32 prevTimestamp; -- }; -- -- /** GraphNode part that transforms itself and its children based on animation --@@ -323,6 +332,9 @@ struct GraphNodeBackground -- /*0x00*/ struct FnGraphNode fnNode; -- /*0x18*/ s32 unused; -- /*0x1C*/ s32 background; // background ID, or rgba5551 color if fnNode.func is null --+ Vec3f prevCameraPos; --+ Vec3f prevCameraFocus; --+ u32 prevCameraTimestamp; -- }; -- -- /** Renders the object that Mario is holding. --@@ -333,6 +345,8 @@ struct GraphNodeHeldObject -- /*0x18*/ s32 playerIndex; -- /*0x1C*/ struct Object *objNode; -- /*0x20*/ Vec3s translation; --+ Vec3f prevShadowPos; --+ u32 prevShadowPosTimestamp; -- }; -- -- /** A node that allows an object to specify a different culling radius than the --diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c --index 1da535b..49a5c03 100644 ----- a/src/engine/surface_collision.c --+++ b/src/engine/surface_collision.c --@@ -8,6 +8,7 @@ -- #include "surface_collision.h" -- #include "surface_load.h" -- #include "math_util.h" --+#include "game/game_init.h" -- -- /************************************************** -- * WALLS * --@@ -394,26 +395,44 @@ f32 find_floor_height_and_data(f32 xPos, f32 yPos, f32 zPos, struct FloorGeometr -- return floorHeight; -- } -- --+u8 gInterpolatingSurfaces; --+ -- /** -- * Iterate through the list of floors and find the first floor under a given point. -- */ -- static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 x, s32 y, s32 z, f32 *pheight) { -- register struct Surface *surf; --- register s32 x1, z1, x2, z2, x3, z3; --+ register f32 x1, z1, x2, z2, x3, z3; -- f32 nx, ny, nz; -- f32 oo; -- f32 height; -- struct Surface *floor = NULL; --+ s32 interpolate; -- -- // Iterate through the list of floors until there are no more floors. -- while (surfaceNode != NULL) { -- surf = surfaceNode->surface; -- surfaceNode = surfaceNode->next; --+ interpolate = gInterpolatingSurfaces && surf->modifiedTimestamp == gGlobalTimer; -- -- x1 = surf->vertex1[0]; -- z1 = surf->vertex1[2]; -- x2 = surf->vertex2[0]; -- z2 = surf->vertex2[2]; --+ if (interpolate) { --+ f32 diff = (surf->prevVertex1[0] - x1) * (surf->prevVertex1[0] - x1); --+ diff += (surf->prevVertex1[1] - surf->vertex1[1]) * (surf->prevVertex1[1] - surf->vertex1[1]); --+ diff += (surf->prevVertex1[2] - z1) * (surf->prevVertex1[2] - z1); --+ //printf("%f\n", sqrtf(diff)); --+ if (diff > 10000) { --+ interpolate = FALSE; --+ } else { --+ x1 = (surf->prevVertex1[0] + x1) / 2; --+ z1 = (surf->prevVertex1[2] + z1) / 2; --+ x2 = (surf->prevVertex2[0] + x2) / 2; --+ z2 = (surf->prevVertex2[2] + z2) / 2; --+ } --+ } -- -- // Check that the point is within the triangle bounds. -- if ((z1 - z) * (x2 - x1) - (x1 - x) * (z2 - z1) < 0) { --@@ -423,6 +442,10 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 -- // To slightly save on computation time, set this later. -- x3 = surf->vertex3[0]; -- z3 = surf->vertex3[2]; --+ if (interpolate) { --+ x3 = (surf->prevVertex3[0] + x3) / 2; --+ z3 = (surf->prevVertex3[2] + z3) / 2; --+ } -- -- if ((z2 - z) * (x3 - x2) - (x2 - x) * (z3 - z2) < 0) { -- continue; --@@ -442,10 +465,30 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 -- continue; -- } -- --- nx = surf->normal.x; --- ny = surf->normal.y; --- nz = surf->normal.z; --- oo = surf->originOffset; --+ if (interpolate) { --+ f32 y1, y2, y3; --+ f32 mag; --+ y1 = (surf->prevVertex1[1] + surf->vertex1[1]) / 2; --+ y2 = (surf->prevVertex2[1] + surf->vertex2[1]) / 2; --+ y3 = (surf->prevVertex3[1] + surf->vertex3[1]) / 2; --+ nx = (y2 - y1) * (z3 - z2) - (z2 - z1) * (y3 - y2); --+ ny = (z2 - z1) * (x3 - x2) - (x2 - x1) * (z3 - z2); --+ nz = (x2 - x1) * (y3 - y2) - (y2 - y1) * (x3 - x2); --+ mag = sqrtf(nx * nx + ny * ny + nz * nz); --+ if (mag < 0.0001) { --+ continue; --+ } --+ mag = (f32)(1.0 / mag); --+ nx *= mag; --+ ny *= mag; --+ nz *= mag; --+ oo = -(nx * x1 + ny * y1 + nz * z1); --+ } else { --+ nx = surf->normal.x; --+ ny = surf->normal.y; --+ nz = surf->normal.z; --+ oo = surf->originOffset; --+ } -- -- // If a wall, ignore it. Likely a remnant, should never occur. -- if (ny == 0.0f) { --@@ -460,6 +503,15 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 -- } -- -- *pheight = height; --+ if (interpolate) { --+ static struct Surface s; --+ s.type = surf->type; --+ s.normal.x = nx; --+ s.normal.y = ny; --+ s.normal.z = nz; --+ s.originOffset = oo; --+ return &s; --+ } -- floor = surf; -- break; -- } --diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c --index ac2ee50..323b7d0 100644 ----- a/src/engine/surface_load.c --+++ b/src/engine/surface_load.c --@@ -14,6 +14,7 @@ -- #include "game/mario.h" -- #include "game/object_list_processor.h" -- #include "surface_load.h" --+#include "game/game_init.h" -- -- s32 unused8038BE90; -- --@@ -359,6 +360,11 @@ static struct Surface *read_surface_data(s16 *vertexData, s16 **vertexIndices) { -- -- surface = alloc_surface(); -- --+ vec3s_copy(surface->prevVertex1, surface->vertex1); --+ vec3s_copy(surface->prevVertex2, surface->vertex2); --+ vec3s_copy(surface->prevVertex3, surface->vertex3); --+ surface->modifiedTimestamp = gGlobalTimer; --+ -- surface->vertex1[0] = x1; -- surface->vertex2[0] = x2; -- surface->vertex3[0] = x3; --diff --git a/src/game/camera.c b/src/game/camera.c --index bde0662..9351dea 100644 ----- a/src/game/camera.c --+++ b/src/game/camera.c --@@ -484,6 +484,10 @@ CameraTransition sModeTransitions[] = { -- extern u8 sDanceCutsceneIndexTable[][4]; -- extern u8 sZoomOutAreaMasks[]; -- --+static void skip_camera_interpolation(void) { --+ gLakituState.skipCameraInterpolationTimestamp = gGlobalTimer; --+} --+ -- /** -- * Starts a camera shake triggered by an interaction -- */ --@@ -5552,6 +5556,7 @@ s32 set_camera_mode_fixed(struct Camera *c, s16 x, s16 y, s16 z) { -- c->mode = CAMERA_MODE_FIXED; -- vec3f_set(c->pos, sFixedModeBasePosition[0], sMarioCamState->pos[1], -- sFixedModeBasePosition[2]); --+ skip_camera_interpolation(); -- } -- return basePosSet; -- } --@@ -5714,6 +5719,7 @@ BAD_RETURN(s32) cam_rr_enter_building_side(struct Camera *c) { -- if (c->mode != CAMERA_MODE_FIXED) { -- sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT; -- c->mode = CAMERA_MODE_FIXED; --+ skip_camera_interpolation(); -- } -- } -- --@@ -5909,6 +5915,7 @@ BAD_RETURN(s32) cam_castle_enter_lobby(struct Camera *c) { -- sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT; -- set_fixed_cam_axis_sa_lobby(c->mode); -- c->mode = CAMERA_MODE_FIXED; --+ skip_camera_interpolation(); -- } -- } -- --@@ -7279,6 +7286,7 @@ BAD_RETURN(s32) cutscene_unused_loop(UNUSED struct Camera *c) { -- BAD_RETURN(s32) cutscene_ending_mario_fall_start(struct Camera *c) { -- vec3f_set(c->focus, -26.f, 0.f, -137.f); -- vec3f_set(c->pos, 165.f, 4725.f, 324.f); --+ skip_camera_interpolation(); -- } -- -- /** --@@ -7311,6 +7319,7 @@ BAD_RETURN(s32) cutscene_ending_mario_fall(struct Camera *c) { -- BAD_RETURN(s32) cutscene_ending_mario_land_closeup(struct Camera *c) { -- vec3f_set(c->focus, 85.f, 826.f, 250.f); -- vec3f_set(c->pos, -51.f, 988.f, -202.f); --+ skip_camera_interpolation(); -- player2_rotate_cam(c, -0x2000, 0x2000, -0x2000, 0x2000); -- } -- --@@ -7320,6 +7329,7 @@ BAD_RETURN(s32) cutscene_ending_mario_land_closeup(struct Camera *c) { -- BAD_RETURN(s32) cutscene_ending_reset_spline(UNUSED struct Camera *c) { -- sCutsceneVars[9].point[0] = 0.f; -- cutscene_reset_spline(); --+ skip_camera_interpolation(); -- } -- -- /** --@@ -7355,6 +7365,7 @@ BAD_RETURN(s32) cutscene_ending_peach_appear_closeup(struct Camera *c) { -- vec3f_set(c->pos, 179.f, 2463.f, -1216.f); -- c->pos[1] = gCutsceneFocus->oPosY + 35.f; -- vec3f_set(c->focus, gCutsceneFocus->oPosX, gCutsceneFocus->oPosY + 125.f, gCutsceneFocus->oPosZ); --+ skip_camera_interpolation(); -- } -- -- /** --@@ -7373,6 +7384,7 @@ BAD_RETURN(s32) cutscene_ending_peach_appears(struct Camera *c) { -- BAD_RETURN(s32) cutscene_ending_peach_descends_start(UNUSED struct Camera *c) { -- cutscene_reset_spline(); -- sCutsceneVars[2].point[1] = 150.f; --+ skip_camera_interpolation(); -- } -- -- /** --@@ -7459,6 +7471,7 @@ BAD_RETURN(s32) cutscene_ending_peach_wakeup(struct Camera *c) { -- BAD_RETURN(s32) cutscene_ending_dialog(struct Camera *c) { -- vec3f_set(c->focus, 11.f, 983.f, -1273.f); -- vec3f_set(c->pos, -473.f, 970.f, -1152.f); --+ skip_camera_interpolation(); -- player2_rotate_cam(c, -0x800, 0x2000, -0x2000, 0x2000); -- } -- --@@ -7469,6 +7482,7 @@ BAD_RETURN(s32) cutscene_ending_kiss_closeup(struct Camera *c) { -- set_fov_function(CAM_FOV_SET_29); -- vec3f_set(c->focus, 350.f, 1034.f, -1216.f); -- vec3f_set(c->pos, -149.f, 1021.f, -1216.f); --+ skip_camera_interpolation(); -- } -- -- /** --@@ -7504,6 +7518,7 @@ BAD_RETURN(s32) cutscene_ending_kiss(struct Camera *c) { -- BAD_RETURN(s32) cutscene_ending_look_at_sky(struct Camera *c) { -- move_point_along_spline(c->focus, sEndingLookAtSkyFocus, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress); -- vec3f_set(c->pos, 699.f, 1680.f, -703.f); --+ skip_camera_interpolation(); -- } -- -- /** --@@ -10340,6 +10355,7 @@ BAD_RETURN(s32) cutscene_door_start(struct Camera *c) { -- BAD_RETURN(s32) cutscene_door_fix_cam(struct Camera *c) { -- vec3f_copy(c->pos, sCutsceneVars[0].point); -- vec3f_copy(c->focus, sCutsceneVars[1].point); --+ skip_camera_interpolation(); -- } -- -- /** --@@ -10373,6 +10389,7 @@ BAD_RETURN(s32) cutscene_door_move_behind_mario(struct Camera *c) { -- } -- -- offset_rotated(c->pos, sMarioCamState->pos, camOffset, sCutsceneVars[0].angle); --+ skip_camera_interpolation(); -- } -- -- /** --diff --git a/src/game/camera.h b/src/game/camera.h --index 173ab8a..b1abdc4 100644 ----- a/src/game/camera.h --+++ b/src/game/camera.h --@@ -657,6 +657,8 @@ struct LakituState -- /// Mario's action from the previous frame. Only used to determine if Mario just finished a dive. -- /*0xB8*/ u32 lastFrameAction; -- /*0xBC*/ s16 unused; --+ --+ u32 skipCameraInterpolationTimestamp; -- }; -- -- // bss order hack to not affect BSS order. if possible, remove me, but it will be hard to match otherwise --diff --git a/src/game/envfx_bubbles.c b/src/game/envfx_bubbles.c --index 16a9272..ee1b029 100644 ----- a/src/game/envfx_bubbles.c --+++ b/src/game/envfx_bubbles.c --@@ -35,6 +35,20 @@ Vtx_t gBubbleTempVtx[3] = { -- { { 0, 0, 0 }, 0, { -498, 964 }, { 0xFF, 0xFF, 0xFF, 0xFF } }, -- }; -- --+static Gfx sGfxSaved[60 / 5]; --+static Gfx *sBubbleInterpolatedDisplayListPos[60 / 5]; --+static Vec3s sPrevBubblePositions[60]; --+ --+void patch_interpolated_bubble_particles(void) { --+ s32 i; --+ for (i = 0; i < 60 / 5; i++) { --+ if (sBubbleInterpolatedDisplayListPos[i] != NULL) { --+ *sBubbleInterpolatedDisplayListPos[i] = sGfxSaved[i]; --+ sBubbleInterpolatedDisplayListPos[i] = NULL; --+ } --+ } --+} --+ -- /** -- * Check whether the particle with the given index is -- * laterally within distance of point (x, z). Used to --@@ -241,6 +255,7 @@ void envfx_update_whirlpool(void) { -- (gEnvFxBuffer + i)->yPos = (i + gEnvFxBuffer)->bubbleY; -- (gEnvFxBuffer + i)->unusedBubbleVar = 0; -- (gEnvFxBuffer + i)->isAlive = 1; --+ (gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer; -- -- envfx_rotate_around_whirlpool(&(gEnvFxBuffer + i)->xPos, &(gEnvFxBuffer + i)->yPos, -- &(gEnvFxBuffer + i)->zPos); --@@ -299,6 +314,7 @@ void envfx_update_jetstream(void) { -- + coss((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1]; -- (gEnvFxBuffer + i)->yPos = -- gEnvFxBubbleConfig[ENVFX_STATE_SRC_Y] + (random_float() * 400.0f - 200.0f); --+ (gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer; -- } else { -- (gEnvFxBuffer + i)->angleAndDist[1] += 10; -- (gEnvFxBuffer + i)->xPos += sins((gEnvFxBuffer + i)->angleAndDist[0]) * 10.0f; --@@ -506,6 +522,12 @@ Gfx *envfx_update_bubble_particles(s32 mode, UNUSED Vec3s marioPos, Vec3s camFro -- Vec3s vertex1; -- Vec3s vertex2; -- Vec3s vertex3; --+ Vec3s interpolatedVertices[3]; --+ --+ static Vec3s prevVertex1; --+ static Vec3s prevVertex2; --+ static Vec3s prevVertex3; --+ static u32 prevTimestamp; -- -- Gfx *gfxStart; -- --@@ -521,18 +543,52 @@ Gfx *envfx_update_bubble_particles(s32 mode, UNUSED Vec3s marioPos, Vec3s camFro -- envfx_bubbles_update_switch(mode, camTo, vertex1, vertex2, vertex3); -- rotate_triangle_vertices(vertex1, vertex2, vertex3, pitch, yaw); -- --+ if (gGlobalTimer == prevTimestamp + 1) { --+ interpolate_vectors_s16(interpolatedVertices[0], prevVertex1, vertex1); --+ interpolate_vectors_s16(interpolatedVertices[1], prevVertex2, vertex2); --+ interpolate_vectors_s16(interpolatedVertices[2], prevVertex3, vertex3); --+ } --+ vec3s_copy(prevVertex1, vertex1); --+ vec3s_copy(prevVertex2, vertex2); --+ vec3s_copy(prevVertex3, vertex3); --+ prevTimestamp = gGlobalTimer; --+ -- gSPDisplayList(sGfxCursor++, &tiny_bubble_dl_0B006D38); -- -- for (i = 0; i < sBubbleParticleMaxCount; i += 5) { --+ Vtx *interpolatedVertBuf = alloc_display_list(15 * sizeof(Vtx)); --+ s32 j, k; -- gDPPipeSync(sGfxCursor++); -- envfx_set_bubble_texture(mode, i); --- append_bubble_vertex_buffer(sGfxCursor++, i, vertex1, vertex2, vertex3, (Vtx *) gBubbleTempVtx); --+ sBubbleInterpolatedDisplayListPos[i / 5] = sGfxCursor; --+ for (j = 0; j < 5; j++) { --+ for (k = 0; k < 3; k++) { --+ Vtx *v = &interpolatedVertBuf[j * 3 + k]; --+ v->v = gBubbleTempVtx[k]; --+ if (gGlobalTimer != gEnvFxBuffer[i + j].spawnTimestamp && mode != ENVFX_LAVA_BUBBLES) { --+ v->v.ob[0] = (sPrevBubblePositions[i + j][0] + gEnvFxBuffer[i + j].xPos) / 2.0f + interpolatedVertices[k][0]; --+ v->v.ob[1] = (sPrevBubblePositions[i + j][1] + gEnvFxBuffer[i + j].yPos) / 2.0f + interpolatedVertices[k][1]; --+ v->v.ob[2] = (sPrevBubblePositions[i + j][2] + gEnvFxBuffer[i + j].zPos) / 2.0f + interpolatedVertices[k][2]; --+ } else { --+ v->v.ob[0] = gEnvFxBuffer[i + j].xPos + interpolatedVertices[k][0]; --+ v->v.ob[1] = gEnvFxBuffer[i + j].yPos + interpolatedVertices[k][1]; --+ v->v.ob[2] = gEnvFxBuffer[i + j].zPos + interpolatedVertices[k][2]; --+ } --+ } --+ } --+ gSPVertex(sGfxCursor++, VIRTUAL_TO_PHYSICAL(interpolatedVertBuf), 15, 0); --+ append_bubble_vertex_buffer(&sGfxSaved[i / 5], i, vertex1, vertex2, vertex3, (Vtx *) gBubbleTempVtx); -- gSP1Triangle(sGfxCursor++, 0, 1, 2, 0); -- gSP1Triangle(sGfxCursor++, 3, 4, 5, 0); -- gSP1Triangle(sGfxCursor++, 6, 7, 8, 0); -- gSP1Triangle(sGfxCursor++, 9, 10, 11, 0); -- gSP1Triangle(sGfxCursor++, 12, 13, 14, 0); -- } --+ for (i = 0; i < sBubbleParticleMaxCount; i++) { --+ sPrevBubblePositions[i][0] = gEnvFxBuffer[i].xPos; --+ sPrevBubblePositions[i][1] = gEnvFxBuffer[i].yPos; --+ sPrevBubblePositions[i][2] = gEnvFxBuffer[i].zPos; --+ } -- -- gSPDisplayList(sGfxCursor++, &tiny_bubble_dl_0B006AB0); -- gSPEndDisplayList(sGfxCursor++); --diff --git a/src/game/envfx_snow.c b/src/game/envfx_snow.c --index c3c14a5..d2212ef 100644 ----- a/src/game/envfx_snow.c --+++ b/src/game/envfx_snow.c --@@ -54,6 +54,26 @@ extern void *tiny_bubble_dl_0B006AB0; -- extern void *tiny_bubble_dl_0B006A50; -- extern void *tiny_bubble_dl_0B006CD8; -- --+static struct { --+ Gfx *pos; --+ Vtx vertices[15]; --+} sPrevSnowVertices[140 / 5]; --+static s16 sPrevSnowParticleCount; --+static u32 sPrevSnowTimestamp; --+ --+void patch_interpolated_snow_particles(void) { --+ int i; --+ --+ if (gGlobalTimer != sPrevSnowTimestamp + 1) { --+ return; --+ } --+ --+ for (i = 0; i < sPrevSnowParticleCount; i += 5) { --+ gSPVertex(sPrevSnowVertices[i / 5].pos, --+ VIRTUAL_TO_PHYSICAL(sPrevSnowVertices[i / 5].vertices), 15, 0); --+ } --+} --+ -- /** -- * Initialize snow particles by allocating a buffer for storing their state -- * and setting a start amount. --@@ -217,6 +237,7 @@ void envfx_update_snow_normal(s32 snowCylinderX, s32 snowCylinderY, s32 snowCyli -- 400.0f * random_float() - 200.0f + snowCylinderZ + (s16)(deltaZ * 2); -- (gEnvFxBuffer + i)->yPos = 200.0f * random_float() + snowCylinderY; -- (gEnvFxBuffer + i)->isAlive = 1; --+ (gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer; -- } else { -- (gEnvFxBuffer + i)->xPos += random_float() * 2 - 1.0f + (s16)(deltaX / 1.2); -- (gEnvFxBuffer + i)->yPos -= 2 -(s16)(deltaY * 0.8); --@@ -251,6 +272,7 @@ void envfx_update_snow_blizzard(s32 snowCylinderX, s32 snowCylinderY, s32 snowCy -- 400.0f * random_float() - 200.0f + snowCylinderZ + (s16)(deltaZ * 2); -- (gEnvFxBuffer + i)->yPos = 400.0f * random_float() - 200.0f + snowCylinderY; -- (gEnvFxBuffer + i)->isAlive = 1; --+ (gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer; -- } else { -- (gEnvFxBuffer + i)->xPos += random_float() * 2 - 1.0f + (s16)(deltaX / 1.2) + 20.0f; -- (gEnvFxBuffer + i)->yPos -= 5 -(s16)(deltaY * 0.8); --@@ -294,6 +316,7 @@ void envfx_update_snow_water(s32 snowCylinderX, s32 snowCylinderY, s32 snowCylin -- (gEnvFxBuffer + i)->zPos = 400.0f * random_float() - 200.0f + snowCylinderZ; -- (gEnvFxBuffer + i)->yPos = 400.0f * random_float() - 200.0f + snowCylinderY; -- (gEnvFxBuffer + i)->isAlive = 1; --+ (gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer; -- } -- } -- } --@@ -346,6 +369,8 @@ void rotate_triangle_vertices(Vec3s vertex1, Vec3s vertex2, Vec3s vertex3, s16 p -- void append_snowflake_vertex_buffer(Gfx *gfx, s32 index, Vec3s vertex1, Vec3s vertex2, Vec3s vertex3) { -- s32 i = 0; -- Vtx *vertBuf = (Vtx *) alloc_display_list(15 * sizeof(Vtx)); --+ Vtx *vertBufInterpolated = (Vtx *) alloc_display_list(15 * sizeof(Vtx)); --+ Vtx *v; -- #ifdef VERSION_EU -- Vtx *p; -- #endif --@@ -395,7 +420,23 @@ void append_snowflake_vertex_buffer(Gfx *gfx, s32 index, Vec3s vertex1, Vec3s ve -- #endif -- } -- --- gSPVertex(gfx, VIRTUAL_TO_PHYSICAL(vertBuf), 15, 0); --+ for (i = 0; i < 15; i++) { --+ v = &sPrevSnowVertices[index / 5].vertices[i]; --+ vertBufInterpolated[i] = gSnowTempVtx[i % 3]; --+ if (index < sPrevSnowParticleCount && gGlobalTimer == sPrevSnowTimestamp + 1 && --+ gGlobalTimer != gEnvFxBuffer[index + i / 3].spawnTimestamp) { --+ vertBufInterpolated[i].v.ob[0] = (v->v.ob[0] + vertBuf[i].v.ob[0]) / 2; --+ vertBufInterpolated[i].v.ob[1] = (v->v.ob[1] + vertBuf[i].v.ob[1]) / 2; --+ vertBufInterpolated[i].v.ob[2] = (v->v.ob[2] + vertBuf[i].v.ob[2]) / 2; --+ } else { --+ vertBufInterpolated[i].v.ob[0] = vertBuf[i].v.ob[0]; --+ vertBufInterpolated[i].v.ob[1] = vertBuf[i].v.ob[1]; --+ vertBufInterpolated[i].v.ob[2] = vertBuf[i].v.ob[2]; --+ } --+ *v = vertBuf[i]; --+ } --+ sPrevSnowVertices[index / 5].pos = gfx; --+ gSPVertex(gfx, VIRTUAL_TO_PHYSICAL(vertBufInterpolated), 15, 0); -- } -- -- /** --@@ -479,6 +520,8 @@ Gfx *envfx_update_snow(s32 snowMode, Vec3s marioPos, Vec3s camFrom, Vec3s camTo) -- gSP1Triangle(gfx++, 9, 10, 11, 0); -- gSP1Triangle(gfx++, 12, 13, 14, 0); -- } --+ sPrevSnowParticleCount = gSnowParticleCount; --+ sPrevSnowTimestamp = gGlobalTimer; -- -- gSPDisplayList(gfx++, &tiny_bubble_dl_0B006AB0) gSPEndDisplayList(gfx++); -- --diff --git a/src/game/envfx_snow.h b/src/game/envfx_snow.h --index 7a83b53..f4acc2d 100644 ----- a/src/game/envfx_snow.h --+++ b/src/game/envfx_snow.h --@@ -25,7 +25,8 @@ struct EnvFxParticle { -- s32 angleAndDist[2]; // for whirpools, [0] = angle from center, [1] = distance from center -- s32 unusedBubbleVar; // set to zero for bubbles when respawning, never used elsewhere -- s32 bubbleY; // for Bubbles, yPos is always set to this --- s8 filler20[56 - 0x20]; --+ //s8 filler20[56 - 0x20]; --+ u32 spawnTimestamp; -- }; -- -- extern s8 gEnvFxMode; --diff --git a/src/game/hud.c b/src/game/hud.c --index 1540b67..0de6e0b 100644 ----- a/src/game/hud.c --+++ b/src/game/hud.c --@@ -59,6 +59,20 @@ static struct UnusedHUDStruct sUnusedHUDValues = { 0x00, 0x0A, 0x00 }; -- -- static struct CameraHUD sCameraHUD = { CAM_STATUS_NONE }; -- --+static u32 sPowerMeterLastRenderTimestamp; --+static s16 sPowerMeterLastY; --+static Gfx *sPowerMeterDisplayListPos; --+ --+void patch_interpolated_hud(void) { --+ if (sPowerMeterDisplayListPos != NULL) { --+ Mtx *mtx = alloc_display_list(sizeof(Mtx)); --+ guTranslate(mtx, (f32) sPowerMeterHUD.x, (f32) sPowerMeterHUD.y, 0); --+ gSPMatrix(sPowerMeterDisplayListPos, VIRTUAL_TO_PHYSICAL(mtx), --+ G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); --+ sPowerMeterDisplayListPos = NULL; --+ } --+} --+ -- /** -- * Renders a rgba16 16x16 glyph texture from a table list. -- */ --@@ -111,6 +125,7 @@ void render_power_meter_health_segment(s16 numHealthWedges) { -- */ -- void render_dl_power_meter(s16 numHealthWedges) { -- Mtx *mtx; --+ f32 interpolatedY; -- -- mtx = alloc_display_list(sizeof(Mtx)); -- --@@ -118,7 +133,15 @@ void render_dl_power_meter(s16 numHealthWedges) { -- return; -- } -- --- guTranslate(mtx, (f32) sPowerMeterHUD.x, (f32) sPowerMeterHUD.y, 0); --+ if (gGlobalTimer == sPowerMeterLastRenderTimestamp + 1) { --+ interpolatedY = (sPowerMeterLastY + sPowerMeterHUD.y) / 2.0f; --+ } else { --+ interpolatedY = sPowerMeterHUD.y; --+ } --+ guTranslate(mtx, (f32) sPowerMeterHUD.x, interpolatedY, 0); --+ sPowerMeterLastY = sPowerMeterHUD.y; --+ sPowerMeterLastRenderTimestamp = gGlobalTimer; --+ sPowerMeterDisplayListPos = gDisplayListHead; -- -- gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx++), -- G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); --diff --git a/src/game/ingame_menu.c b/src/game/ingame_menu.c --index 7ae9f1e..1c23c96 100644 ----- a/src/game/ingame_menu.c --+++ b/src/game/ingame_menu.c --@@ -130,6 +130,42 @@ s32 gDialogResponse = 0; -- static struct CachedChar { u8 used; u8 data[CHCACHE_BUFLEN]; } charCache[256]; -- #endif // VERSION -- --+static Gfx *sInterpolatedDialogOffsetPos; --+static f32 sInterpolatedDialogOffset; --+static Gfx *sInterpolatedDialogRotationPos; --+static f32 sInterpolatedDialogScale; --+static f32 sInterpolatedDialogRotation; --+static Gfx *sInterpolatedDialogZoomPos; --+ --+void patch_interpolated_dialog(void) { --+ Mtx *matrix; --+ --+ if (sInterpolatedDialogOffsetPos != NULL) { --+ matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); --+ guTranslate(matrix, 0, sInterpolatedDialogOffset, 0); --+ gSPMatrix(sInterpolatedDialogOffsetPos, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); --+ sInterpolatedDialogOffsetPos = NULL; --+ } --+ if (sInterpolatedDialogRotationPos != NULL) { --+ matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); --+ guScale(matrix, 1.0 / sInterpolatedDialogScale, 1.0 / sInterpolatedDialogScale, 1.0f); --+ gSPMatrix(sInterpolatedDialogRotationPos++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); --+ matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); --+ guRotate(matrix, sInterpolatedDialogRotation * 4.0f, 0, 0, 1.0f); --+ gSPMatrix(sInterpolatedDialogRotationPos, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); --+ sInterpolatedDialogRotationPos = NULL; --+ } --+ if (sInterpolatedDialogZoomPos != NULL) { --+ matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); --+ guTranslate(matrix, 65.0 - (65.0 / sInterpolatedDialogScale), (40.0 / sInterpolatedDialogScale) - 40, 0); --+ gSPMatrix(sInterpolatedDialogZoomPos++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); --+ matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); --+ guScale(matrix, 1.0 / sInterpolatedDialogScale, 1.0 / sInterpolatedDialogScale, 1.0f); --+ gSPMatrix(sInterpolatedDialogZoomPos, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); --+ sInterpolatedDialogZoomPos = NULL; --+ } --+} --+ -- void create_dl_identity_matrix(void) { -- Mtx *matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); -- --@@ -969,6 +1005,14 @@ void render_dialog_box_type(struct DialogEntry *dialog, s8 linesPerBox) { -- switch (gDialogBoxType) { -- case DIALOG_TYPE_ROTATE: // Renders a dialog black box with zoom and rotation -- if (gDialogBoxState == DIALOG_STATE_OPENING || gDialogBoxState == DIALOG_STATE_CLOSING) { --+ sInterpolatedDialogRotationPos = gDisplayListHead; --+ if (gDialogBoxState == DIALOG_STATE_OPENING) { --+ sInterpolatedDialogScale = gDialogBoxScale - 2 / 2; --+ sInterpolatedDialogRotation = gDialogBoxOpenTimer - 7.5f / 2; --+ } else { --+ sInterpolatedDialogScale = gDialogBoxScale + 2 / 2; --+ sInterpolatedDialogRotation = gDialogBoxOpenTimer + 7.5f / 2; --+ } -- create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.0 / gDialogBoxScale, 1.0 / gDialogBoxScale, 1.0f); -- // convert the speed into angle -- create_dl_rotation_matrix(MENU_MTX_NOPUSH, gDialogBoxOpenTimer * 4.0f, 0, 0, 1.0f); --@@ -977,6 +1021,12 @@ void render_dialog_box_type(struct DialogEntry *dialog, s8 linesPerBox) { -- break; -- case DIALOG_TYPE_ZOOM: // Renders a dialog white box with zoom -- if (gDialogBoxState == DIALOG_STATE_OPENING || gDialogBoxState == DIALOG_STATE_CLOSING) { --+ sInterpolatedDialogZoomPos = gDisplayListHead; --+ if (gDialogBoxState == DIALOG_STATE_OPENING) { --+ sInterpolatedDialogScale = gDialogBoxScale - 2 / 2; --+ } else { --+ sInterpolatedDialogScale = gDialogBoxScale + 2 / 2; --+ } -- create_dl_translation_matrix(MENU_MTX_NOPUSH, 65.0 - (65.0 / gDialogBoxScale), -- (40.0 / gDialogBoxScale) - 40, 0); -- create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.0 / gDialogBoxScale, 1.0 / gDialogBoxScale, 1.0f); --@@ -1259,6 +1309,8 @@ void handle_dialog_text_and_pages(s8 colorMode, struct DialogEntry *dialog, s8 l -- #ifdef VERSION_EU -- gDialogY -= gDialogScrollOffsetY; -- #else --+ sInterpolatedDialogOffset = gDialogScrollOffsetY + dialog->linesPerBox; --+ sInterpolatedDialogOffsetPos = gDisplayListHead; -- create_dl_translation_matrix(MENU_MTX_NOPUSH, 0, (f32) gDialogScrollOffsetY, 0); -- #endif -- } --diff --git a/src/game/level_geo.c b/src/game/level_geo.c --index 4c98e70..abc5121 100644 ----- a/src/game/level_geo.c --+++ b/src/game/level_geo.c --@@ -34,12 +34,16 @@ Gfx *geo_envfx_main(s32 callContext, struct GraphNode *node, Mat4 mtxf) { -- vec3f_to_vec3s(marioPos, gPlayerCameraState->pos); -- particleList = envfx_update_particles(snowMode, marioPos, camTo, camFrom); -- if (particleList != NULL) { --+#if 0 -- Mtx *mtx = alloc_display_list(sizeof(*mtx)); -- -- gfx = alloc_display_list(2 * sizeof(*gfx)); -- mtxf_to_mtx(mtx, mtxf); -- gSPMatrix(&gfx[0], VIRTUAL_TO_PHYSICAL(mtx), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); -- gSPBranchList(&gfx[1], VIRTUAL_TO_PHYSICAL(particleList)); --+#else --+ gfx = particleList; --+#endif -- execNode->fnNode.node.flags = (execNode->fnNode.node.flags & 0xFF) | 0x400; -- } -- SET_HIGH_U16_OF_32(*params, gAreaUpdateCounter); --diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c --index 22b45b3..109d7f7 100644 ----- a/src/game/object_helpers.c --+++ b/src/game/object_helpers.c --@@ -1554,6 +1554,7 @@ void cur_obj_set_pos_to_home(void) { -- o->oPosX = o->oHomeX; -- o->oPosY = o->oHomeY; -- o->oPosZ = o->oHomeZ; --+ o->header.gfx.skipInterpolationTimestamp = gGlobalTimer; -- } -- -- void cur_obj_set_pos_to_home_and_stop(void) { --diff --git a/src/game/paintings.c b/src/game/paintings.c --index 6cae19c..a304d4a 100644 ----- a/src/game/paintings.c --+++ b/src/game/paintings.c --@@ -189,6 +189,32 @@ struct Painting **sPaintingGroups[] = { -- s16 gPaintingUpdateCounter = 1; -- s16 gLastPaintingUpdateCounter = 0; -- --+static Vtx sLastVertices[2 * 264 * 3]; --+static u32 sLastVerticesTimestamp; --+static Vtx *sVerticesPtr[2]; --+static s32 sVerticesCount; --+ --+void patch_interpolated_paintings(void) { --+ if (sVerticesPtr[0] != NULL) { --+ s32 i; --+ if (sVerticesPtr[1] != NULL) { --+ for (i = 0; i < sVerticesCount / 2; i++) { --+ sVerticesPtr[0][i] = sLastVertices[i]; --+ } --+ for (; i < sVerticesCount; i++) { --+ sVerticesPtr[1][i - sVerticesCount / 2] = sLastVertices[i]; --+ } --+ } else { --+ for (i = 0; i < sVerticesCount; i++) { --+ sVerticesPtr[0][i] = sLastVertices[i]; --+ } --+ } --+ sVerticesPtr[0] = NULL; --+ sVerticesPtr[1] = NULL; --+ sVerticesCount = 0; --+ } --+} --+ -- /** -- * Stop paintings in paintingGroup from rippling if their id is different from *idptr. -- */ --@@ -890,6 +916,23 @@ Gfx *render_painting(u8 *img, s16 tWidth, s16 tHeight, s16 *textureMap, s16 mapV -- gSP1Triangle(gfx++, group * 3, group * 3 + 1, group * 3 + 2, 0); -- } -- --+ if (sVerticesCount >= numVtx * 2) { --+ sVerticesCount = 0; --+ } --+ for (map = 0; map < numVtx; map++) { --+ Vtx v = verts[map]; --+ if (gGlobalTimer == sLastVerticesTimestamp + 1) { --+ s32 i; --+ for (i = 0; i < 3; i++) { --+ verts[map].n.ob[i] = (v.n.ob[i] + sLastVertices[sVerticesCount + map].n.ob[i]) / 2; --+ verts[map].n.n[i] = (v.n.n[i] + sLastVertices[sVerticesCount + map].n.n[i]) / 2; --+ } --+ } --+ sLastVertices[sVerticesCount + map] = v; --+ } --+ sVerticesPtr[sVerticesCount / numVtx] = verts; --+ sVerticesCount += numVtx; --+ -- gSPEndDisplayList(gfx); -- return dlist; -- } --@@ -954,6 +997,7 @@ Gfx *painting_ripple_image(struct Painting *painting) { -- meshTris = textureMap[meshVerts * 3 + 1]; -- gSPDisplayList(gfx++, render_painting(textures[i], tWidth, tHeight, textureMap, meshVerts, meshTris, painting->alpha)); -- } --+ sLastVerticesTimestamp = gGlobalTimer; -- -- // Update the ripple, may automatically reset the painting's state. -- painting_update_ripple_state(painting); --@@ -991,6 +1035,7 @@ Gfx *painting_ripple_env_mapped(struct Painting *painting) { -- meshVerts = textureMap[0]; -- meshTris = textureMap[meshVerts * 3 + 1]; -- gSPDisplayList(gfx++, render_painting(tArray[0], tWidth, tHeight, textureMap, meshVerts, meshTris, painting->alpha)); --+ sLastVerticesTimestamp = gGlobalTimer; -- -- // Update the ripple, may automatically reset the painting's state. -- painting_update_ripple_state(painting); --diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c --index d5bf577..71656b4 100644 ----- a/src/game/rendering_graph_node.c --+++ b/src/game/rendering_graph_node.c --@@ -39,6 +39,8 @@ -- s16 gMatStackIndex; -- Mat4 gMatStack[32]; -- Mtx *gMatStackFixed[32]; --+Mat4 gMatStackInterpolated[32]; --+Mtx *gMatStackInterpolatedFixed[32]; -- -- /** -- * Animation nodes have state in global variables, so this struct captures --@@ -52,6 +54,7 @@ struct GeoAnimState { -- /*0x04*/ f32 translationMultiplier; -- /*0x08*/ u16 *attribute; -- /*0x0C*/ s16 *data; --+ s16 prevFrame; -- }; -- -- // For some reason, this is a GeoAnimState struct, but the current state consists --@@ -61,6 +64,7 @@ struct GeoAnimState gGeoTempState; -- u8 gCurAnimType; -- u8 gCurAnimEnabled; -- s16 gCurrAnimFrame; --+s16 gPrevAnimFrame; -- f32 gCurAnimTranslationMultiplier; -- u16 *gCurrAnimAttribute; -- s16 *gCurAnimData; --@@ -129,6 +133,46 @@ u16 gAreaUpdateCounter = 0; -- LookAt lookAt; -- #endif -- --+static Gfx *sPerspectivePos; --+static Mtx *sPerspectiveMtx; --+ --+struct { --+ Gfx *pos; --+ void *mtx; --+ void *displayList; --+} gMtxTbl[6400]; --+s32 gMtxTblSize; --+ --+static Gfx *sViewportPos; --+static Vp sPrevViewport; --+ --+void mtx_patch_interpolated(void) { --+ s32 i; --+ --+ if (sPerspectivePos != NULL) { --+ gSPMatrix(sPerspectivePos, VIRTUAL_TO_PHYSICAL(sPerspectiveMtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); --+ } --+ --+ for (i = 0; i < gMtxTblSize; i++) { --+ Gfx *pos = gMtxTbl[i].pos; --+ gSPMatrix(pos++, VIRTUAL_TO_PHYSICAL(gMtxTbl[i].mtx), --+ G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); --+ gSPDisplayList(pos++, gMtxTbl[i].displayList); --+ } --+ --+ if (sViewportPos != NULL) { --+ Gfx *saved = gDisplayListHead; --+ gDisplayListHead = sViewportPos; --+ make_viewport_clip_rect(&sPrevViewport); --+ gSPViewport(gDisplayListHead, VIRTUAL_TO_PHYSICAL(&sPrevViewport)); --+ gDisplayListHead = saved; --+ } --+ --+ gMtxTblSize = 0; --+ sPerspectivePos = NULL; --+ sViewportPos = NULL; --+} --+ -- /** -- * Process a master list node. -- */ --@@ -156,9 +200,14 @@ static void geo_process_master_list_sub(struct GraphNodeMasterList *node) { -- if ((currList = node->listHeads[i]) != NULL) { -- gDPSetRenderMode(gDisplayListHead++, modeList->modes[i], mode2List->modes[i]); -- while (currList != NULL) { --- gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(currList->transform), --+ if ((u32) gMtxTblSize < sizeof(gMtxTbl) / sizeof(gMtxTbl[0])) { --+ gMtxTbl[gMtxTblSize].pos = gDisplayListHead; --+ gMtxTbl[gMtxTblSize].mtx = currList->transform; --+ gMtxTbl[gMtxTblSize++].displayList = currList->displayList; --+ } --+ gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(currList->transformInterpolated), -- G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); --- gSPDisplayList(gDisplayListHead++, currList->displayList); --+ gSPDisplayList(gDisplayListHead++, currList->displayListInterpolated); -- currList = currList->next; -- } -- } --@@ -174,7 +223,7 @@ static void geo_process_master_list_sub(struct GraphNodeMasterList *node) { -- * parameter. Look at the RenderModeContainer struct to see the corresponding -- * render modes of layers. -- */ ---static void geo_append_display_list(void *displayList, s16 layer) { --+static void geo_append_display_list2(void *displayList, void *displayListInterpolated, s16 layer) { -- -- #ifdef F3DEX_GBI_2 -- gSPLookAt(gDisplayListHead++, &lookAt); --@@ -184,7 +233,9 @@ static void geo_append_display_list(void *displayList, s16 layer) { -- alloc_only_pool_alloc(gDisplayListHeap, sizeof(struct DisplayListNode)); -- -- listNode->transform = gMatStackFixed[gMatStackIndex]; --+ listNode->transformInterpolated = gMatStackInterpolatedFixed[gMatStackIndex]; -- listNode->displayList = displayList; --+ listNode->displayListInterpolated = displayListInterpolated; -- listNode->next = 0; -- if (gCurGraphNodeMasterList->listHeads[layer] == 0) { -- gCurGraphNodeMasterList->listHeads[layer] = listNode; --@@ -195,6 +246,10 @@ static void geo_append_display_list(void *displayList, s16 layer) { -- } -- } -- --+static void geo_append_display_list(void *displayList, s16 layer) { --+ geo_append_display_list2(displayList, displayList, layer); --+} --+ -- /** -- * Process the master list node. -- */ --@@ -241,7 +296,9 @@ static void geo_process_perspective(struct GraphNodePerspective *node) { -- } -- if (node->fnNode.node.children != NULL) { -- u16 perspNorm; --+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); -- Mtx *mtx = alloc_display_list(sizeof(*mtx)); --+ f32 fovInterpolated; -- -- #ifdef VERSION_EU -- f32 aspect = ((f32) gCurGraphNodeRoot->width / (f32) gCurGraphNodeRoot->height) * 1.1f; --@@ -250,9 +307,23 @@ static void geo_process_perspective(struct GraphNodePerspective *node) { -- #endif -- -- guPerspective(mtx, &perspNorm, node->fov, aspect, node->near, node->far, 1.0f); --- gSPPerspNormalize(gDisplayListHead++, perspNorm); -- --- gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); --+ if (gGlobalTimer == node->prevTimestamp + 1 && gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) { --+ --+ fovInterpolated = (node->prevFov + node->fov) / 2.0f; --+ guPerspective(mtxInterpolated, &perspNorm, fovInterpolated, aspect, node->near, node->far, 1.0f); --+ gSPPerspNormalize(gDisplayListHead++, perspNorm); --+ --+ sPerspectivePos = gDisplayListHead; --+ sPerspectiveMtx = mtx; --+ gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtxInterpolated), --+ G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); --+ } else { --+ gSPPerspNormalize(gDisplayListHead++, perspNorm); --+ gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); --+ } --+ node->prevFov = node->fov; --+ node->prevTimestamp = gGlobalTimer; -- -- gCurGraphNodeCamFrustum = node; -- geo_process_node_and_siblings(node->fnNode.node.children); --@@ -297,6 +368,39 @@ static void geo_process_switch(struct GraphNodeSwitchCase *node) { -- } -- } -- --+void interpolate_vectors(Vec3f res, Vec3f a, Vec3f b) { --+ res[0] = (a[0] + b[0]) / 2.0f; --+ res[1] = (a[1] + b[1]) / 2.0f; --+ res[2] = (a[2] + b[2]) / 2.0f; --+} --+ --+void interpolate_vectors_s16(Vec3s res, Vec3s a, Vec3s b) { --+ res[0] = (a[0] + b[0]) / 2; --+ res[1] = (a[1] + b[1]) / 2; --+ res[2] = (a[2] + b[2]) / 2; --+} --+ --+static s16 interpolate_angle(s16 a, s16 b) { --+ s32 absDiff = b - a; --+ if (absDiff < 0) { --+ absDiff = -absDiff; --+ } --+ if (absDiff >= 0x4000 && absDiff <= 0xC000) { --+ return b; --+ } --+ if (absDiff <= 0x8000) { --+ return (a + b) / 2; --+ } else { --+ return (a + b) / 2 + 0x8000; --+ } --+} --+ --+static void interpolate_angles(Vec3s res, Vec3s a, Vec3s b) { --+ res[0] = interpolate_angle(a[0], b[0]); --+ res[1] = interpolate_angle(a[1], b[1]); --+ res[2] = interpolate_angle(a[2], b[2]); --+} --+ -- /** -- * Process a camera node. -- */ --@@ -304,6 +408,9 @@ static void geo_process_camera(struct GraphNodeCamera *node) { -- Mat4 cameraTransform; -- Mtx *rollMtx = alloc_display_list(sizeof(*rollMtx)); -- Mtx *mtx = alloc_display_list(sizeof(*mtx)); --+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); --+ Vec3f posInterpolated; --+ Vec3f focusInterpolated; -- -- if (node->fnNode.func != NULL) { -- node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]); --@@ -314,12 +421,40 @@ static void geo_process_camera(struct GraphNodeCamera *node) { -- -- mtxf_lookat(cameraTransform, node->pos, node->focus, node->roll); -- mtxf_mul(gMatStack[gMatStackIndex + 1], cameraTransform, gMatStack[gMatStackIndex]); --+ --+ if (gGlobalTimer == node->prevTimestamp + 1 && gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) { --+ interpolate_vectors(posInterpolated, node->prevPos, node->pos); --+ interpolate_vectors(focusInterpolated, node->prevFocus, node->focus); --+ float magnitude = 0; --+ for (int i = 0; i < 3; i++) { --+ float diff = node->pos[i] - node->prevPos[i]; --+ magnitude += diff * diff; --+ } --+ if (magnitude > 500000) { --+ // Observed ~479000 in BBH when toggling R camera --+ // Can get over 3 million in VCUTM though... --+ vec3f_copy(posInterpolated, node->pos); --+ vec3f_copy(focusInterpolated, node->focus); --+ } --+ } else { --+ vec3f_copy(posInterpolated, node->pos); --+ vec3f_copy(focusInterpolated, node->focus); --+ } --+ vec3f_copy(node->prevPos, node->pos); --+ vec3f_copy(node->prevFocus, node->focus); --+ node->prevTimestamp = gGlobalTimer; --+ mtxf_lookat(cameraTransform, posInterpolated, focusInterpolated, node->roll); --+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], cameraTransform, gMatStackInterpolated[gMatStackIndex]); --+ -- gMatStackIndex++; -- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); -- gMatStackFixed[gMatStackIndex] = mtx; --+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); --+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; -- if (node->fnNode.node.children != 0) { -- gCurGraphNodeCamera = node; -- node->matrixPtr = &gMatStack[gMatStackIndex]; --+ node->matrixPtrInterpolated = &gMatStackInterpolated[gMatStackIndex]; -- geo_process_node_and_siblings(node->fnNode.node.children); -- gCurGraphNodeCamera = NULL; -- } --@@ -336,13 +471,17 @@ static void geo_process_translation_rotation(struct GraphNodeTranslationRotation -- Mat4 mtxf; -- Vec3f translation; -- Mtx *mtx = alloc_display_list(sizeof(*mtx)); --+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); -- -- vec3s_to_vec3f(translation, node->translation); -- mtxf_rotate_zxy_and_translate(mtxf, translation, node->rotation); -- mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); --+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]); -- gMatStackIndex++; -- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); -- gMatStackFixed[gMatStackIndex] = mtx; --+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); --+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; -- if (node->displayList != NULL) { -- geo_append_display_list(node->displayList, node->node.flags >> 8); -- } --@@ -361,13 +500,17 @@ static void geo_process_translation(struct GraphNodeTranslation *node) { -- Mat4 mtxf; -- Vec3f translation; -- Mtx *mtx = alloc_display_list(sizeof(*mtx)); --+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); -- -- vec3s_to_vec3f(translation, node->translation); -- mtxf_rotate_zxy_and_translate(mtxf, translation, gVec3sZero); -- mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); --+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]); -- gMatStackIndex++; -- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); -- gMatStackFixed[gMatStackIndex] = mtx; --+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); --+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; -- if (node->displayList != NULL) { -- geo_append_display_list(node->displayList, node->node.flags >> 8); -- } --@@ -385,12 +528,23 @@ static void geo_process_translation(struct GraphNodeTranslation *node) { -- static void geo_process_rotation(struct GraphNodeRotation *node) { -- Mat4 mtxf; -- Mtx *mtx = alloc_display_list(sizeof(*mtx)); --+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); --+ Vec3s rotationInterpolated; -- -- mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, node->rotation); -- mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); --+ if (gGlobalTimer == node->prevTimestamp + 1) { --+ interpolate_angles(rotationInterpolated, node->prevRotation, node->rotation); --+ mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, rotationInterpolated); --+ } --+ vec3s_copy(node->prevRotation, node->rotation); --+ node->prevTimestamp = gGlobalTimer; --+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]); -- gMatStackIndex++; -- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); -- gMatStackFixed[gMatStackIndex] = mtx; --+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); --+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; -- if (node->displayList != NULL) { -- geo_append_display_list(node->displayList, node->node.flags >> 8); -- } --@@ -409,12 +563,16 @@ static void geo_process_scale(struct GraphNodeScale *node) { -- UNUSED Mat4 transform; -- Vec3f scaleVec; -- Mtx *mtx = alloc_display_list(sizeof(*mtx)); --+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); -- -- vec3f_set(scaleVec, node->scale, node->scale, node->scale); -- mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], scaleVec); --+ mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex], scaleVec); -- gMatStackIndex++; -- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); -- gMatStackFixed[gMatStackIndex] = mtx; --+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); --+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; -- if (node->displayList != NULL) { -- geo_append_display_list(node->displayList, node->node.flags >> 8); -- } --@@ -433,21 +591,30 @@ static void geo_process_scale(struct GraphNodeScale *node) { -- static void geo_process_billboard(struct GraphNodeBillboard *node) { -- Vec3f translation; -- Mtx *mtx = alloc_display_list(sizeof(*mtx)); --+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); -- -- gMatStackIndex++; -- vec3s_to_vec3f(translation, node->translation); -- mtxf_billboard(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex - 1], translation, -- gCurGraphNodeCamera->roll); --+ mtxf_billboard(gMatStackInterpolated[gMatStackIndex], gMatStackInterpolated[gMatStackIndex - 1], translation, --+ gCurGraphNodeCamera->roll); -- if (gCurGraphNodeHeldObject != NULL) { -- mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex], -- gCurGraphNodeHeldObject->objNode->header.gfx.scale); --+ mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex], gMatStackInterpolated[gMatStackIndex], --+ gCurGraphNodeHeldObject->objNode->header.gfx.scale); -- } else if (gCurGraphNodeObject != NULL) { -- mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex], -- gCurGraphNodeObject->scale); --+ mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex], gMatStackInterpolated[gMatStackIndex], --+ gCurGraphNodeObject->scale); -- } -- -- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); -- gMatStackFixed[gMatStackIndex] = mtx; --+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); --+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; -- if (node->displayList != NULL) { -- geo_append_display_list(node->displayList, node->node.flags >> 8); -- } --@@ -496,13 +663,39 @@ static void geo_process_generated_list(struct GraphNodeGenerated *node) { -- */ -- static void geo_process_background(struct GraphNodeBackground *node) { -- Gfx *list = NULL; --+ Gfx *listInterpolated = NULL; -- -- if (node->fnNode.func != NULL) { --+ Vec3f posCopy; --+ Vec3f focusCopy; --+ Vec3f posInterpolated; --+ Vec3f focusInterpolated; --+ --+ if (gGlobalTimer == node->prevCameraTimestamp + 1 && --+ gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) { --+ interpolate_vectors(posInterpolated, node->prevCameraPos, gLakituState.pos); --+ interpolate_vectors(focusInterpolated, node->prevCameraFocus, gLakituState.focus); --+ } else { --+ vec3f_copy(posInterpolated, gLakituState.pos); --+ vec3f_copy(focusInterpolated, gLakituState.focus); --+ } --+ vec3f_copy(node->prevCameraPos, gLakituState.pos); --+ vec3f_copy(node->prevCameraFocus, gLakituState.focus); --+ node->prevCameraTimestamp = gGlobalTimer; --+ -- list = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, -- (struct AllocOnlyPool *) gMatStack[gMatStackIndex]); --+ vec3f_copy(posCopy, gLakituState.pos); --+ vec3f_copy(focusCopy, gLakituState.focus); --+ vec3f_copy(gLakituState.pos, posInterpolated); --+ vec3f_copy(gLakituState.focus, focusInterpolated); --+ listInterpolated = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, NULL); --+ vec3f_copy(gLakituState.pos, posCopy); --+ vec3f_copy(gLakituState.focus, focusCopy); -- } -- if (list != 0) { --- geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(list), node->fnNode.node.flags >> 8); --+ geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(list), --+ (void *) VIRTUAL_TO_PHYSICAL(listInterpolated), node->fnNode.node.flags >> 8); -- } else if (gCurGraphNodeMasterList != NULL) { -- #ifndef F3DEX_GBI_2E -- Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 7); --@@ -527,61 +720,81 @@ static void geo_process_background(struct GraphNodeBackground *node) { -- } -- } -- ---/** --- * Render an animated part. The current animation state is not part of the node --- * but set in global variables. If an animated part is skipped, everything afterwards desyncs. --- */ ---static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) { --- Mat4 matrix; --- Vec3s rotation; --- Vec3f translation; --- Mtx *matrixPtr = alloc_display_list(sizeof(*matrixPtr)); --- --- vec3s_copy(rotation, gVec3sZero); --- vec3f_set(translation, node->translation[0], node->translation[1], node->translation[2]); --- if (gCurAnimType == ANIM_TYPE_TRANSLATION) { --- translation[0] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] --+static void anim_process(Vec3f translation, Vec3s rotation, u8 *animType, s16 animFrame, u16 **animAttribute) { --+ if (*animType == ANIM_TYPE_TRANSLATION) { --+ translation[0] += gCurAnimData[retrieve_animation_index(animFrame, animAttribute)] -- * gCurAnimTranslationMultiplier; --- translation[1] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] --+ translation[1] += gCurAnimData[retrieve_animation_index(animFrame, animAttribute)] -- * gCurAnimTranslationMultiplier; --- translation[2] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] --+ translation[2] += gCurAnimData[retrieve_animation_index(animFrame, animAttribute)] -- * gCurAnimTranslationMultiplier; --- gCurAnimType = ANIM_TYPE_ROTATION; --+ *animType = ANIM_TYPE_ROTATION; -- } else { --- if (gCurAnimType == ANIM_TYPE_LATERAL_TRANSLATION) { --+ if (*animType == ANIM_TYPE_LATERAL_TRANSLATION) { -- translation[0] += --- gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] --+ gCurAnimData[retrieve_animation_index(animFrame, animAttribute)] -- * gCurAnimTranslationMultiplier; --- gCurrAnimAttribute += 2; --+ *animAttribute += 2; -- translation[2] += --- gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] --+ gCurAnimData[retrieve_animation_index(animFrame, animAttribute)] -- * gCurAnimTranslationMultiplier; --- gCurAnimType = ANIM_TYPE_ROTATION; --+ *animType = ANIM_TYPE_ROTATION; -- } else { --- if (gCurAnimType == ANIM_TYPE_VERTICAL_TRANSLATION) { --- gCurrAnimAttribute += 2; --+ if (*animType == ANIM_TYPE_VERTICAL_TRANSLATION) { --+ *animAttribute += 2; -- translation[1] += --- gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] --+ gCurAnimData[retrieve_animation_index(animFrame, animAttribute)] -- * gCurAnimTranslationMultiplier; --- gCurrAnimAttribute += 2; --- gCurAnimType = ANIM_TYPE_ROTATION; --- } else if (gCurAnimType == ANIM_TYPE_NO_TRANSLATION) { --- gCurrAnimAttribute += 6; --- gCurAnimType = ANIM_TYPE_ROTATION; --+ *animAttribute += 2; --+ *animType = ANIM_TYPE_ROTATION; --+ } else if (*animType == ANIM_TYPE_NO_TRANSLATION) { --+ *animAttribute += 6; --+ *animType = ANIM_TYPE_ROTATION; -- } -- } -- } -- --- if (gCurAnimType == ANIM_TYPE_ROTATION) { --- rotation[0] = gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]; --- rotation[1] = gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]; --- rotation[2] = gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]; --+ if (*animType == ANIM_TYPE_ROTATION) { --+ rotation[0] = gCurAnimData[retrieve_animation_index(animFrame, animAttribute)]; --+ rotation[1] = gCurAnimData[retrieve_animation_index(animFrame, animAttribute)]; --+ rotation[2] = gCurAnimData[retrieve_animation_index(animFrame, animAttribute)]; -- } --+} --+ --+/** --+ * Render an animated part. The current animation state is not part of the node --+ * but set in global variables. If an animated part is skipped, everything afterwards desyncs. --+ */ --+static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) { --+ Mat4 matrix; --+ Vec3s rotation; --+ Vec3f translation; --+ Vec3s rotationInterpolated; --+ Vec3f translationInterpolated; --+ Mtx *matrixPtr = alloc_display_list(sizeof(*matrixPtr)); --+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); --+ u16 *animAttribute = gCurrAnimAttribute; --+ u8 animType = gCurAnimType; --+ --+ vec3s_copy(rotation, gVec3sZero); --+ vec3f_set(translation, node->translation[0], node->translation[1], node->translation[2]); --+ vec3s_copy(rotationInterpolated, rotation); --+ vec3f_copy(translationInterpolated, translation); --+ --+ anim_process(translationInterpolated, rotationInterpolated, &animType, gPrevAnimFrame, &animAttribute); --+ anim_process(translation, rotation, &gCurAnimType, gCurrAnimFrame, &gCurrAnimAttribute); --+ interpolate_vectors(translationInterpolated, translationInterpolated, translation); --+ interpolate_angles(rotationInterpolated, rotationInterpolated, rotation); --+ -- mtxf_rotate_xyz_and_translate(matrix, translation, rotation); -- mtxf_mul(gMatStack[gMatStackIndex + 1], matrix, gMatStack[gMatStackIndex]); --+ mtxf_rotate_xyz_and_translate(matrix, translationInterpolated, rotationInterpolated); --+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], matrix, gMatStackInterpolated[gMatStackIndex]); -- gMatStackIndex++; -- mtxf_to_mtx(matrixPtr, gMatStack[gMatStackIndex]); -- gMatStackFixed[gMatStackIndex] = matrixPtr; --+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); --+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; -- if (node->displayList != NULL) { -- geo_append_display_list(node->displayList, node->node.flags >> 8); -- } --@@ -613,6 +826,17 @@ void geo_set_animation_globals(struct GraphNodeObject_sub *node, s32 hasAnimatio -- } -- -- gCurrAnimFrame = node->animFrame; --+ if (node->prevAnimPtr == anim && node->prevAnimID == node->animID && --+ gGlobalTimer == node->prevAnimFrameTimestamp + 1) { --+ gPrevAnimFrame = node->prevAnimFrame; --+ } else { --+ gPrevAnimFrame = node->animFrame; --+ } --+ node->prevAnimPtr = anim; --+ node->prevAnimID = node->animID; --+ node->prevAnimFrame = node->animFrame; --+ node->prevAnimFrameTimestamp = gGlobalTimer; --+ -- gCurAnimEnabled = (anim->flags & ANIM_FLAG_5) == 0; -- gCurrAnimAttribute = segmented_to_virtual((void *) anim->index); -- gCurAnimData = segmented_to_virtual((void *) anim->values); --@@ -631,8 +855,10 @@ void geo_set_animation_globals(struct GraphNodeObject_sub *node, s32 hasAnimatio -- */ -- static void geo_process_shadow(struct GraphNodeShadow *node) { -- Gfx *shadowList; --+ Gfx *shadowListInterpolated; -- Mat4 mtxf; -- Vec3f shadowPos; --+ Vec3f shadowPosInterpolated; -- Vec3f animOffset; -- f32 objScale; -- f32 shadowScale; --@@ -640,6 +866,7 @@ static void geo_process_shadow(struct GraphNodeShadow *node) { -- f32 cosAng; -- struct GraphNode *geo; -- Mtx *mtx; --+ Mtx *mtxInterpolated; -- -- if (gCurGraphNodeCamera != NULL && gCurGraphNodeObject != NULL) { -- if (gCurGraphNodeHeldObject != NULL) { --@@ -678,21 +905,57 @@ static void geo_process_shadow(struct GraphNodeShadow *node) { -- } -- } -- --+ if (gCurGraphNodeHeldObject != NULL) { --+ if (gGlobalTimer == gCurGraphNodeHeldObject->prevShadowPosTimestamp + 1) { --+ interpolate_vectors(shadowPosInterpolated, gCurGraphNodeHeldObject->prevShadowPos, shadowPos); --+ } else { --+ vec3f_copy(shadowPosInterpolated, shadowPos); --+ } --+ vec3f_copy(gCurGraphNodeHeldObject->prevShadowPos, shadowPos); --+ gCurGraphNodeHeldObject->prevShadowPosTimestamp = gGlobalTimer; --+ } else { --+ if (gGlobalTimer == gCurGraphNodeObject->prevShadowPosTimestamp + 1 && --+ gGlobalTimer != gCurGraphNodeObject->skipInterpolationTimestamp) { --+ interpolate_vectors(shadowPosInterpolated, gCurGraphNodeObject->prevShadowPos, shadowPos); --+ } else { --+ vec3f_copy(shadowPosInterpolated, shadowPos); --+ } --+ vec3f_copy(gCurGraphNodeObject->prevShadowPos, shadowPos); --+ gCurGraphNodeObject->prevShadowPosTimestamp = gGlobalTimer; --+ } --+ --+ extern u8 gInterpolatingSurfaces; --+ gInterpolatingSurfaces = TRUE; --+ shadowListInterpolated = create_shadow_below_xyz(shadowPosInterpolated[0], shadowPosInterpolated[1], --+ shadowPosInterpolated[2], shadowScale, --+ node->shadowSolidity, node->shadowType); --+ gInterpolatingSurfaces = FALSE; -- shadowList = create_shadow_below_xyz(shadowPos[0], shadowPos[1], shadowPos[2], shadowScale, -- node->shadowSolidity, node->shadowType); --- if (shadowList != NULL) { --+ if (shadowListInterpolated != NULL && shadowList != NULL) { -- mtx = alloc_display_list(sizeof(*mtx)); --+ mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); -- gMatStackIndex++; --+ -- mtxf_translate(mtxf, shadowPos); -- mtxf_mul(gMatStack[gMatStackIndex], mtxf, *gCurGraphNodeCamera->matrixPtr); -- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); -- gMatStackFixed[gMatStackIndex] = mtx; --+ --+ mtxf_translate(mtxf, shadowPosInterpolated); --+ mtxf_mul(gMatStackInterpolated[gMatStackIndex], mtxf, *gCurGraphNodeCamera->matrixPtrInterpolated); --+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); --+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; --+ -- if (gShadowAboveWaterOrLava == 1) { --- geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 4); --+ geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowList), --+ (void *) VIRTUAL_TO_PHYSICAL(shadowListInterpolated), 4); -- } else if (gMarioOnIceOrCarpet == 1) { --- geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 5); --+ geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowList), --+ (void *) VIRTUAL_TO_PHYSICAL(shadowListInterpolated), 5); -- } else { --- geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 6); --+ geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowList), --+ (void *) VIRTUAL_TO_PHYSICAL(shadowListInterpolated), 6); -- } -- gMatStackIndex--; -- } --@@ -789,31 +1052,101 @@ static int obj_is_in_view(struct GraphNodeObject *node, Mat4 matrix) { -- return TRUE; -- } -- --+static void interpolate_matrix(Mat4 result, Mat4 a, Mat4 b) { --+ s32 i, j; --+ for (i = 0; i < 4; i++) { --+ for (j = 0; j < 4; j++) { --+ result[i][j] = (a[i][j] + b[i][j]) / 2.0f; --+ } --+ } --+} --+ -- /** -- * Process an object node. -- */ -- static void geo_process_object(struct Object *node) { -- Mat4 mtxf; -- s32 hasAnimation = (node->header.gfx.node.flags & GRAPH_RENDER_HAS_ANIMATION) != 0; --+ Vec3f scaleInterpolated; -- -- if (node->header.gfx.unk18 == gCurGraphNodeRoot->areaIndex) { -- if (node->header.gfx.throwMatrix != NULL) { -- mtxf_mul(gMatStack[gMatStackIndex + 1], *node->header.gfx.throwMatrix, -- gMatStack[gMatStackIndex]); --+ if (gGlobalTimer == node->header.gfx.prevThrowMatrixTimestamp + 1 && --+ gGlobalTimer != node->header.gfx.skipInterpolationTimestamp) { --+ interpolate_matrix(mtxf, *node->header.gfx.throwMatrix, node->header.gfx.prevThrowMatrix); --+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, --+ gMatStackInterpolated[gMatStackIndex]); --+ } else { --+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], (void *) node->header.gfx.throwMatrix, --+ gMatStackInterpolated[gMatStackIndex]); --+ } --+ mtxf_copy(node->header.gfx.prevThrowMatrix, *node->header.gfx.throwMatrix); --+ node->header.gfx.prevThrowMatrixTimestamp = gGlobalTimer; -- } else if (node->header.gfx.node.flags & GRAPH_RENDER_CYLBOARD) { --+ Vec3f posInterpolated; --+ if (gGlobalTimer == node->header.gfx.prevTimestamp + 1 && --+ gGlobalTimer != node->header.gfx.skipInterpolationTimestamp) { --+ interpolate_vectors(posInterpolated, node->header.gfx.prevPos, node->header.gfx.pos); --+ } else { --+ vec3f_copy(posInterpolated, node->header.gfx.pos); --+ } --+ vec3f_copy(node->header.gfx.prevPos, node->header.gfx.pos); --+ node->header.gfx.prevTimestamp = gGlobalTimer; -- mtxf_cylboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], -- node->header.gfx.pos, gCurGraphNodeCamera->roll); --+ mtxf_cylboard(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex], --+ posInterpolated, gCurGraphNodeCamera->roll); -- } else if (node->header.gfx.node.flags & GRAPH_RENDER_BILLBOARD) { --+ Vec3f posInterpolated; --+ if (gGlobalTimer == node->header.gfx.prevTimestamp + 1 && --+ gGlobalTimer != node->header.gfx.skipInterpolationTimestamp) { --+ interpolate_vectors(posInterpolated, node->header.gfx.prevPos, node->header.gfx.pos); --+ } else { --+ vec3f_copy(posInterpolated, node->header.gfx.pos); --+ } --+ vec3f_copy(node->header.gfx.prevPos, node->header.gfx.pos); --+ node->header.gfx.prevTimestamp = gGlobalTimer; -- mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], -- node->header.gfx.pos, gCurGraphNodeCamera->roll); --+ mtxf_billboard(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex], --+ posInterpolated, gCurGraphNodeCamera->roll); -- } else { --+ Vec3f posInterpolated; --+ Vec3s angleInterpolated; --+ if (gGlobalTimer == node->header.gfx.prevTimestamp + 1 && --+ gGlobalTimer != node->header.gfx.skipInterpolationTimestamp) { --+ interpolate_vectors(posInterpolated, node->header.gfx.prevPos, node->header.gfx.pos); --+ interpolate_angles(angleInterpolated, node->header.gfx.prevAngle, node->header.gfx.angle); --+ } else { --+ vec3f_copy(posInterpolated, node->header.gfx.pos); --+ vec3s_copy(angleInterpolated, node->header.gfx.angle); --+ } --+ vec3f_copy(node->header.gfx.prevPos, node->header.gfx.pos); --+ vec3s_copy(node->header.gfx.prevAngle, node->header.gfx.angle); --+ node->header.gfx.prevTimestamp = gGlobalTimer; -- mtxf_rotate_zxy_and_translate(mtxf, node->header.gfx.pos, node->header.gfx.angle); -- mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); --+ mtxf_rotate_zxy_and_translate(mtxf, posInterpolated, angleInterpolated); --+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]); --+ } --+ --+ if (gGlobalTimer == node->header.gfx.prevScaleTimestamp + 1 && --+ gGlobalTimer != node->header.gfx.skipInterpolationTimestamp) { --+ interpolate_vectors(scaleInterpolated, node->header.gfx.prevScale, node->header.gfx.scale); --+ } else { --+ vec3f_copy(scaleInterpolated, node->header.gfx.scale); -- } --+ vec3f_copy(node->header.gfx.prevScale, node->header.gfx.scale); --+ node->header.gfx.prevScaleTimestamp = gGlobalTimer; -- -- mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1], -- node->header.gfx.scale); --+ mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex + 1], --+ scaleInterpolated); -- node->header.gfx.throwMatrix = &gMatStack[++gMatStackIndex]; --+ node->header.gfx.throwMatrixInterpolated = &gMatStackInterpolated[gMatStackIndex]; -- node->header.gfx.cameraToObject[0] = gMatStack[gMatStackIndex][3][0]; -- node->header.gfx.cameraToObject[1] = gMatStack[gMatStackIndex][3][1]; -- node->header.gfx.cameraToObject[2] = gMatStack[gMatStackIndex][3][2]; --@@ -824,9 +1157,12 @@ static void geo_process_object(struct Object *node) { -- } -- if (obj_is_in_view(&node->header.gfx, gMatStack[gMatStackIndex])) { -- Mtx *mtx = alloc_display_list(sizeof(*mtx)); --+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); -- -- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); -- gMatStackFixed[gMatStackIndex] = mtx; --+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); --+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; -- if (node->header.gfx.sharedChild != NULL) { -- gCurGraphNodeObject = (struct GraphNodeObject *) node; -- node->header.gfx.sharedChild->parent = &node->header.gfx.node; --@@ -837,11 +1173,16 @@ static void geo_process_object(struct Object *node) { -- if (node->header.gfx.node.children != NULL) { -- geo_process_node_and_siblings(node->header.gfx.node.children); -- } --+ } else { --+ node->header.gfx.prevThrowMatrixTimestamp = 0; --+ node->header.gfx.prevTimestamp = 0; --+ node->header.gfx.prevScaleTimestamp = 0; -- } -- -- gMatStackIndex--; -- gCurAnimType = ANIM_TYPE_NONE; -- node->header.gfx.throwMatrix = NULL; --+ node->header.gfx.throwMatrixInterpolated = NULL; -- } -- } -- --@@ -868,6 +1209,8 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { -- Mat4 mat; -- Vec3f translation; -- Mtx *mtx = alloc_display_list(sizeof(*mtx)); --+ Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); --+ Vec3f scaleInterpolated; -- -- #ifdef F3DEX_GBI_2 -- gSPLookAt(gDisplayListHead++, &lookAt); --@@ -883,6 +1226,14 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { -- translation[1] = node->translation[1] / 4.0f; -- translation[2] = node->translation[2] / 4.0f; -- --+ if (gGlobalTimer == node->objNode->header.gfx.prevScaleTimestamp + 1) { --+ interpolate_vectors(scaleInterpolated, node->objNode->header.gfx.prevScale, node->objNode->header.gfx.scale); --+ } else { --+ vec3f_copy(scaleInterpolated, node->objNode->header.gfx.scale); --+ } --+ vec3f_copy(node->objNode->header.gfx.prevScale, node->objNode->header.gfx.scale); --+ node->objNode->header.gfx.prevScaleTimestamp = gGlobalTimer; --+ -- mtxf_translate(mat, translation); -- mtxf_copy(gMatStack[gMatStackIndex + 1], *gCurGraphNodeObject->throwMatrix); -- gMatStack[gMatStackIndex + 1][3][0] = gMatStack[gMatStackIndex][3][0]; --@@ -891,6 +1242,13 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { -- mtxf_mul(gMatStack[gMatStackIndex + 1], mat, gMatStack[gMatStackIndex + 1]); -- mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1], -- node->objNode->header.gfx.scale); --+ mtxf_copy(gMatStackInterpolated[gMatStackIndex + 1], (void *) gCurGraphNodeObject->throwMatrixInterpolated); --+ gMatStackInterpolated[gMatStackIndex + 1][3][0] = gMatStackInterpolated[gMatStackIndex][3][0]; --+ gMatStackInterpolated[gMatStackIndex + 1][3][1] = gMatStackInterpolated[gMatStackIndex][3][1]; --+ gMatStackInterpolated[gMatStackIndex + 1][3][2] = gMatStackInterpolated[gMatStackIndex][3][2]; --+ mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mat, gMatStackInterpolated[gMatStackIndex + 1]); --+ mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex + 1], --+ scaleInterpolated); -- if (node->fnNode.func != NULL) { -- node->fnNode.func(GEO_CONTEXT_HELD_OBJ, &node->fnNode.node, -- (struct AllocOnlyPool *) gMatStack[gMatStackIndex + 1]); --@@ -898,12 +1256,15 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { -- gMatStackIndex++; -- mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); -- gMatStackFixed[gMatStackIndex] = mtx; --+ mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); --+ gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; -- gGeoTempState.type = gCurAnimType; -- gGeoTempState.enabled = gCurAnimEnabled; -- gGeoTempState.frame = gCurrAnimFrame; -- gGeoTempState.translationMultiplier = gCurAnimTranslationMultiplier; -- gGeoTempState.attribute = gCurrAnimAttribute; -- gGeoTempState.data = gCurAnimData; --+ gGeoTempState.prevFrame = gPrevAnimFrame; -- gCurAnimType = 0; -- gCurGraphNodeHeldObject = (void *) node; -- if (node->objNode->header.gfx.unk38.curAnim != NULL) { --@@ -918,6 +1279,7 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { -- gCurAnimTranslationMultiplier = gGeoTempState.translationMultiplier; -- gCurrAnimAttribute = gGeoTempState.attribute; -- gCurAnimData = gGeoTempState.data; --+ gPrevAnimFrame = gGeoTempState.prevFrame; -- gMatStackIndex--; -- } -- --@@ -1039,6 +1401,7 @@ void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor) -- if (node->node.flags & GRAPH_RENDER_ACTIVE) { -- Mtx *initialMatrix; -- Vp *viewport = alloc_display_list(sizeof(*viewport)); --+ Vp *viewportInterpolated = viewport; -- -- gDisplayListHeap = alloc_only_pool_init(main_pool_available() - sizeof(struct AllocOnlyPool), -- MEMORY_POOL_LEFT); --@@ -1049,7 +1412,12 @@ void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor) -- vec3s_set(viewport->vp.vscale, node->width * 4, node->height * 4, 511); -- if (b != NULL) { -- clear_frame_buffer(clearColor); --- make_viewport_clip_rect(b); --+ viewportInterpolated = alloc_display_list(sizeof(*viewportInterpolated)); --+ interpolate_vectors_s16(viewportInterpolated->vp.vtrans, sPrevViewport.vp.vtrans, b->vp.vtrans); --+ interpolate_vectors_s16(viewportInterpolated->vp.vscale, sPrevViewport.vp.vscale, b->vp.vscale); --+ --+ sViewportPos = gDisplayListHead; --+ make_viewport_clip_rect(viewportInterpolated); -- *viewport = *b; -- } -- --@@ -1057,11 +1425,16 @@ void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor) -- clear_frame_buffer(clearColor); -- make_viewport_clip_rect(c); -- } --+ sPrevViewport = *viewport; -- -- mtxf_identity(gMatStack[gMatStackIndex]); -- mtxf_to_mtx(initialMatrix, gMatStack[gMatStackIndex]); -- gMatStackFixed[gMatStackIndex] = initialMatrix; --- gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(viewport)); --+ --+ mtxf_identity(gMatStackInterpolated[gMatStackIndex]); --+ gMatStackInterpolatedFixed[gMatStackIndex] = initialMatrix; --+ --+ gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(viewportInterpolated)); -- gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(gMatStackFixed[gMatStackIndex]), -- G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); -- gCurGraphNodeRoot = node; --diff --git a/src/game/screen_transition.c b/src/game/screen_transition.c --index b49ddaf..d6656af 100644 ----- a/src/game/screen_transition.c --+++ b/src/game/screen_transition.c --@@ -16,6 +16,19 @@ -- u8 sTransitionColorFadeCount[4] = { 0 }; -- u16 sTransitionTextureFadeCount[2] = { 0 }; -- --+static Gfx *sScreenTransitionVerticesPos[2]; --+static Vtx *sScreenTransitionVertices; --+ --+void patch_screen_transition_interpolated(void) { --+ if (sScreenTransitionVerticesPos[0] != NULL) { --+ gSPVertex(sScreenTransitionVerticesPos[0], VIRTUAL_TO_PHYSICAL(sScreenTransitionVertices), 8, 0); --+ gSPVertex(sScreenTransitionVerticesPos[1], VIRTUAL_TO_PHYSICAL(sScreenTransitionVertices), 4, 0); --+ sScreenTransitionVerticesPos[0] = NULL; --+ sScreenTransitionVerticesPos[1] = NULL; --+ sScreenTransitionVertices = NULL; --+ } --+} --+ -- s32 set_and_reset_transition_fade_timer(s8 fadeTimer, u8 transTime) { -- s32 reset = FALSE; -- --@@ -85,14 +98,29 @@ s32 render_fade_transition_into_color(s8 fadeTimer, u8 transTime, struct WarpTra -- return dl_transition_color(fadeTimer, transTime, transData, alpha); -- } -- --+#if 0 --+ -- s16 calc_tex_transition_radius(s8 fadeTimer, s8 transTime, struct WarpTransitionData *transData) { -- f32 texRadius = transData->endTexRadius - transData->startTexRadius; -- f32 radiusTime = sTransitionColorFadeCount[fadeTimer] * texRadius / (f32)(transTime - 1); -- f32 result = transData->startTexRadius + radiusTime; -- --- return (s16)(result + 0.5);; --+ return (s16)(result + 0.5); -- } -- --+#else --+ --+s16 calc_tex_transition_radius(s8 fadeTimer, f32 interpolationFraction, s8 transTime, struct WarpTransitionData *transData) { --+ f32 texRadius = transData->endTexRadius - transData->startTexRadius; --+ f32 radiusTime = (sTransitionColorFadeCount[fadeTimer] == 0 ? 0 : --+ sTransitionColorFadeCount[fadeTimer] - 1 + interpolationFraction) * texRadius / (f32)(transTime - 1); --+ f32 result = transData->startTexRadius + radiusTime; --+ --+ return (s16)(result + 0.5); --+} --+ --+#endif --+ -- f32 calc_tex_transition_time(s8 fadeTimer, s8 transTime, struct WarpTransitionData *transData) { -- f32 startX = transData->startTexX; -- f32 startY = transData->startTexY; --@@ -166,6 +194,8 @@ void *sTextureTransitionID[] = { -- texture_transition_bowser_half, -- }; -- --+#if 0 --+ -- s32 render_textured_transition(s8 fadeTimer, s8 transTime, struct WarpTransitionData *transData, s8 texID, s8 transTexType) { -- f32 texTransTime = calc_tex_transition_time(fadeTimer, transTime, transData); -- u16 texTransPos = convert_tex_transition_angle_to_pos(transData); --@@ -206,6 +236,56 @@ s32 render_textured_transition(s8 fadeTimer, s8 transTime, struct WarpTransition -- return set_and_reset_transition_fade_timer(fadeTimer, transTime); -- } -- --+#else --+ --+s32 render_textured_transition(s8 fadeTimer, s8 transTime, struct WarpTransitionData *transData, s8 texID, s8 transTexType) { --+ f32 texTransTime = calc_tex_transition_time(fadeTimer, transTime, transData); --+ u16 texTransPos = convert_tex_transition_angle_to_pos(transData); --+ s16 centerTransX = center_tex_transition_x(transData, texTransTime, texTransPos); --+ s16 centerTransY = center_tex_transition_y(transData, texTransTime, texTransPos); --+ s16 texTransRadius = calc_tex_transition_radius(fadeTimer, 1.0f, transTime, transData); --+ s16 texTransRadiusInterpolated = calc_tex_transition_radius(fadeTimer, 0.5f, transTime, transData); --+ Vtx *verts = alloc_display_list(8 * sizeof(*verts)); --+ Vtx *vertsInterpolated = alloc_display_list(8 * sizeof(*vertsInterpolated)); --+ --+ if (verts != NULL && vertsInterpolated != NULL) { --+ load_tex_transition_vertex(verts, fadeTimer, transData, centerTransX, centerTransY, texTransRadius, transTexType); --+ load_tex_transition_vertex(vertsInterpolated, fadeTimer, transData, centerTransX, centerTransY, texTransRadiusInterpolated, transTexType); --+ sScreenTransitionVertices = verts; --+ gSPDisplayList(gDisplayListHead++, dl_proj_mtx_fullscreen) --+ gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE); --+ gDPSetRenderMode(gDisplayListHead++, G_RM_AA_OPA_SURF, G_RM_AA_OPA_SURF2); --+ sScreenTransitionVerticesPos[0] = gDisplayListHead; --+ gSPVertex(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(vertsInterpolated), 8, 0); --+ gSPDisplayList(gDisplayListHead++, dl_transition_draw_filled_region); --+ gDPPipeSync(gDisplayListHead++); --+ gDPSetCombineMode(gDisplayListHead++, G_CC_MODULATEIDECALA, G_CC_MODULATEIDECALA); --+ gDPSetRenderMode(gDisplayListHead++, G_RM_AA_XLU_SURF, G_RM_AA_XLU_SURF2); --+ gDPSetTextureFilter(gDisplayListHead++, G_TF_BILERP); --+ switch (transTexType) { --+ case TRANS_TYPE_MIRROR: --+ gDPLoadTextureBlock(gDisplayListHead++, sTextureTransitionID[texID], G_IM_FMT_IA, G_IM_SIZ_8b, 32, 64, 0, --+ G_TX_WRAP | G_TX_MIRROR, G_TX_WRAP | G_TX_MIRROR, 5, 6, G_TX_NOLOD, G_TX_NOLOD); --+ break; --+ case TRANS_TYPE_CLAMP: --+ gDPLoadTextureBlock(gDisplayListHead++, sTextureTransitionID[texID], G_IM_FMT_IA, G_IM_SIZ_8b, 64, 64, 0, --+ G_TX_CLAMP, G_TX_CLAMP, 6, 6, G_TX_NOLOD, G_TX_NOLOD); --+ break; --+ } --+ gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); --+ sScreenTransitionVerticesPos[1] = gDisplayListHead; --+ gSPVertex(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(vertsInterpolated), 4, 0); --+ gSPDisplayList(gDisplayListHead++, dl_draw_quad_verts_0123); --+ gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF); --+ gSPDisplayList(gDisplayListHead++, dl_screen_transition_end); --+ sTransitionTextureFadeCount[fadeTimer] += transData->texTimer; --+ } else { --+ } --+ return set_and_reset_transition_fade_timer(fadeTimer, transTime); --+} --+ --+#endif --+ -- int render_screen_transition(s8 fadeTimer, s8 transType, u8 transTime, struct WarpTransitionData *transData) { -- switch (transType) { -- case WARP_TRANSITION_FADE_FROM_COLOR: --diff --git a/src/menu/intro_geo.c b/src/menu/intro_geo.c --index 37c6752..d823d40 100644 ----- a/src/menu/intro_geo.c --+++ b/src/menu/intro_geo.c --@@ -1,5 +1,6 @@ -- #include -- --+#include "engine/math_util.h" -- #include "game/memory.h" -- #include "game/segment2.h" -- #include "game/segment7.h" --@@ -70,6 +71,18 @@ s8 gameOverBackgroundTable[] = { -- s8 gameOverBackgroundFlipOrder[] = { 0x00, 0x01, 0x02, 0x03, 0x07, 0x0B, -- 0x0a, 0x09, 0x08, 0x04, 0x05, 0x06 }; -- --+static Gfx *sIntroScalePos; --+static Vec3f sIntroScale; --+ --+void patch_title_screen_scales(void) { --+ if (sIntroScalePos != NULL) { --+ Mtx *scaleMat = alloc_display_list(sizeof(*scaleMat)); --+ guScale(scaleMat, sIntroScale[0], sIntroScale[1], sIntroScale[2]); --+ gSPMatrix(sIntroScalePos, scaleMat, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); --+ sIntroScalePos = NULL; --+ } --+} --+ -- Gfx *geo_title_screen(s32 sp50, struct GraphNode *sp54, UNUSED void *context) { -- struct GraphNode *graphNode; // sp4c -- Gfx *displayList; // sp48 --@@ -80,6 +93,8 @@ Gfx *geo_title_screen(s32 sp50, struct GraphNode *sp54, UNUSED void *context) { -- f32 scaleX; // sp34 -- f32 scaleY; // sp30 -- f32 scaleZ; // sp2c --+ Vec3f scale; --+ Vec3f scaleInterpolated; -- graphNode = sp54; -- displayList = NULL; -- displayListIter = NULL; --@@ -110,7 +125,11 @@ Gfx *geo_title_screen(s32 sp50, struct GraphNode *sp54, UNUSED void *context) { -- scaleY = 0.0f; -- scaleZ = 0.0f; -- } --- guScale(scaleMat, scaleX, scaleY, scaleZ); --+ vec3f_set(scale, scaleX, scaleY, scaleZ); --+ interpolate_vectors(scaleInterpolated, sIntroScale, scale); --+ vec3f_set(sIntroScale, scaleX, scaleY, scaleZ); --+ guScale(scaleMat, scaleInterpolated[0], scaleInterpolated[1], scaleInterpolated[2]); --+ sIntroScalePos = displayListIter; -- gSPMatrix(displayListIter++, scaleMat, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); -- gSPDisplayList(displayListIter++, &intro_seg7_dl_0700B3A0); -- gSPPopMatrix(displayListIter++, G_MTX_MODELVIEW); --diff --git a/src/pc/gfx/gfx_dxgi.cpp b/src/pc/gfx/gfx_dxgi.cpp --index 0467495..fa4eb33 100644 ----- a/src/pc/gfx/gfx_dxgi.cpp --+++ b/src/pc/gfx/gfx_dxgi.cpp --@@ -36,10 +36,10 @@ -- -- #ifdef VERSION_EU -- #define FRAME_INTERVAL_US_NUMERATOR 40000 ---#define FRAME_INTERVAL_US_DENOMINATOR 1 --+#define FRAME_INTERVAL_US_DENOMINATOR 2 -- #else -- #define FRAME_INTERVAL_US_NUMERATOR 100000 ---#define FRAME_INTERVAL_US_DENOMINATOR 3 --+#define FRAME_INTERVAL_US_DENOMINATOR 6 -- #endif -- -- using namespace Microsoft::WRL; // For ComPtr --diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c --index 0108ca9..28d56ac 100644 ----- a/src/pc/gfx/gfx_sdl2.c --+++ b/src/pc/gfx/gfx_sdl2.c --@@ -150,7 +150,11 @@ static inline void gfx_sdl_set_vsync(const bool enabled) { -- if (enabled) { -- // try to detect refresh rate -- SDL_GL_SetSwapInterval(1); --- const int vblanks = gCLIOpts.SyncFrames ? (int)gCLIOpts.SyncFrames : test_vsync(); --+ int vblanks = test_vsync(); --+ if (vblanks & 1) --+ vblanks = 0; // not divisible by 60, fuck that --+ else --+ vblanks /= 2; -- if (vblanks) { -- printf("determined swap interval: %d\n", vblanks); -- SDL_GL_SetSwapInterval(vblanks); --@@ -233,7 +237,7 @@ static void gfx_sdl_init(const char *window_title) { -- gfx_sdl_set_fullscreen(); -- -- perf_freq = SDL_GetPerformanceFrequency(); --- frame_time = perf_freq / FRAMERATE; --+ frame_time = perf_freq / (2 * FRAMERATE); -- -- for (size_t i = 0; i < sizeof(windows_scancode_table) / sizeof(SDL_Scancode); i++) { -- inverted_scancode_table[windows_scancode_table[i]] = i; --diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c --index 923e7ea..4fe6161 100644 ----- a/src/pc/pc_main.c --+++ b/src/pc/pc_main.c --@@ -83,6 +83,25 @@ void send_display_list(struct SPTask *spTask) { -- #define SAMPLES_LOW 528 -- #endif -- --+static inline void patch_interpolations(void) { --+ extern void mtx_patch_interpolated(void); --+ extern void patch_screen_transition_interpolated(void); --+ extern void patch_title_screen_scales(void); --+ extern void patch_interpolated_dialog(void); --+ extern void patch_interpolated_hud(void); --+ extern void patch_interpolated_paintings(void); --+ extern void patch_interpolated_bubble_particles(void); --+ extern void patch_interpolated_snow_particles(void); --+ mtx_patch_interpolated(); --+ patch_screen_transition_interpolated(); --+ patch_title_screen_scales(); --+ patch_interpolated_dialog(); --+ patch_interpolated_hud(); --+ patch_interpolated_paintings(); --+ patch_interpolated_bubble_particles(); --+ patch_interpolated_snow_particles(); --+} --+ -- void produce_one_frame(void) { -- gfx_start_frame(); -- --@@ -110,6 +129,11 @@ void produce_one_frame(void) { -- audio_api->play((u8 *)audio_buffer, 2 * num_audio_samples * 4); -- -- gfx_end_frame(); --+ --+ gfx_start_frame(); --+ patch_interpolations(); --+ send_display_list(gGfxSPTask); --+ gfx_end_frame(); -- } -- -- void audio_shutdown(void) { diff --git a/include/types.h b/include/types.h index b3dc27e..c46bdf0 100644 --- a/include/types.h @@ -3854,15 +1890,28 @@ index 0467495..fa4eb33 100644 using namespace Microsoft::WRL; // For ComPtr diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c -index daff686..66fb346 100644 +index 0108ca9..28d56ac 100644 --- a/src/pc/gfx/gfx_sdl2.c +++ b/src/pc/gfx/gfx_sdl2.c -@@ -186,7 +186,7 @@ static void gfx_sdl_init(const char *window_title) { +@@ -150,7 +150,11 @@ static inline void gfx_sdl_set_vsync(const bool enabled) { + if (enabled) { + // try to detect refresh rate + SDL_GL_SetSwapInterval(1); +- const int vblanks = gCLIOpts.SyncFrames ? (int)gCLIOpts.SyncFrames : test_vsync(); ++ int vblanks = test_vsync(); ++ if (vblanks & 1) ++ vblanks = 0; // not divisible by 60, fuck that ++ else ++ vblanks /= 2; + if (vblanks) { + printf("determined swap interval: %d\n", vblanks); + SDL_GL_SetSwapInterval(vblanks); +@@ -233,7 +237,7 @@ static void gfx_sdl_init(const char *window_title) { + gfx_sdl_set_fullscreen(); perf_freq = SDL_GetPerformanceFrequency(); - -- frame_rate = perf_freq / FRAMERATE; -+ frame_rate = perf_freq / (2 * FRAMERATE); +- frame_time = perf_freq / FRAMERATE; ++ frame_time = perf_freq / (2 * FRAMERATE); for (size_t i = 0; i < sizeof(windows_scancode_table) / sizeof(SDL_Scancode); i++) { inverted_scancode_table[windows_scancode_table[i]] = i; From 6a904853bd695740a470c9b1b980a1ca60b302af Mon Sep 17 00:00:00 2001 From: fgsfds Date: Thu, 25 Nov 2021 16:20:11 +0300 Subject: [PATCH 14/20] remove --syncframes --- src/pc/cliopts.c | 3 --- src/pc/cliopts.h | 1 - 2 files changed, 4 deletions(-) diff --git a/src/pc/cliopts.c b/src/pc/cliopts.c index 74a42e86..b01ef685 100644 --- a/src/pc/cliopts.c +++ b/src/pc/cliopts.c @@ -61,9 +61,6 @@ void parse_cli_opts(int argc, char* argv[]) { else if (strcmp(argv[i], "--poolsize") == 0) // Main pool size arg_uint("--poolsize", argv[++i], &gCLIOpts.PoolSize); - else if (strcmp(argv[i], "--syncframes") == 0) // VBlanks to wait - arg_uint("--syncframes", argv[++i], &gCLIOpts.SyncFrames); - else if (strcmp(argv[i], "--configfile") == 0 && (i + 1) < argc) arg_string("--configfile", argv[++i], gCLIOpts.ConfigFile); diff --git a/src/pc/cliopts.h b/src/pc/cliopts.h index 413c2457..a0281e22 100644 --- a/src/pc/cliopts.h +++ b/src/pc/cliopts.h @@ -7,7 +7,6 @@ struct PCCLIOptions { unsigned int SkipIntro; unsigned int FullScreen; unsigned int PoolSize; - unsigned int SyncFrames; char ConfigFile[SYS_MAX_PATH]; char SavePath[SYS_MAX_PATH]; char GameDir[SYS_MAX_PATH]; From 8152239732219467593fdbd139e615b9d8a0d41e Mon Sep 17 00:00:00 2001 From: fgsfds Date: Thu, 25 Nov 2021 16:24:08 +0300 Subject: [PATCH 15/20] actually update 60fps patch for real --- enhancements/60fps_ex.patch | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/enhancements/60fps_ex.patch b/enhancements/60fps_ex.patch index 5980d9bf..c236bab6 100644 --- a/enhancements/60fps_ex.patch +++ b/enhancements/60fps_ex.patch @@ -1890,28 +1890,15 @@ index 0467495..fa4eb33 100644 using namespace Microsoft::WRL; // For ComPtr diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c -index 0108ca9..28d56ac 100644 +index daff686..66fb346 100644 --- a/src/pc/gfx/gfx_sdl2.c +++ b/src/pc/gfx/gfx_sdl2.c -@@ -150,7 +150,11 @@ static inline void gfx_sdl_set_vsync(const bool enabled) { - if (enabled) { - // try to detect refresh rate - SDL_GL_SetSwapInterval(1); -- const int vblanks = gCLIOpts.SyncFrames ? (int)gCLIOpts.SyncFrames : test_vsync(); -+ int vblanks = test_vsync(); -+ if (vblanks & 1) -+ vblanks = 0; // not divisible by 60, fuck that -+ else -+ vblanks /= 2; - if (vblanks) { - printf("determined swap interval: %d\n", vblanks); - SDL_GL_SetSwapInterval(vblanks); -@@ -233,7 +237,7 @@ static void gfx_sdl_init(const char *window_title) { - gfx_sdl_set_fullscreen(); +@@ -186,7 +186,7 @@ static void gfx_sdl_init(const char *window_title) { perf_freq = SDL_GetPerformanceFrequency(); -- frame_time = perf_freq / FRAMERATE; -+ frame_time = perf_freq / (2 * FRAMERATE); + +- frame_rate = perf_freq / FRAMERATE; ++ frame_rate = perf_freq / (2 * FRAMERATE); for (size_t i = 0; i < sizeof(windows_scancode_table) / sizeof(SDL_Scancode); i++) { inverted_scancode_table[windows_scancode_table[i]] = i; From b5f50dd975dd0327d8ef6c6ea95648fc5837a21e Mon Sep 17 00:00:00 2001 From: fgsfds Date: Fri, 26 Nov 2021 03:12:32 +0300 Subject: [PATCH 16/20] time whole frame instead of just the rendering part thanks to opmox for the fix --- src/pc/gfx/gfx_sdl2.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c index daff6864..601622d2 100644 --- a/src/pc/gfx/gfx_sdl2.c +++ b/src/pc/gfx/gfx_sdl2.c @@ -187,6 +187,7 @@ static void gfx_sdl_init(const char *window_title) { perf_freq = SDL_GetPerformanceFrequency(); frame_rate = perf_freq / FRAMERATE; + frame_time = SDL_GetPerformanceCounter(); for (size_t i = 0; i < sizeof(windows_scancode_table) / sizeof(SDL_Scancode); i++) { inverted_scancode_table[windows_scancode_table[i]] = i; @@ -287,19 +288,23 @@ static void gfx_sdl_set_keyboard_callbacks(kb_callback_t on_key_down, kb_callbac } static bool gfx_sdl_start_frame(void) { - frame_time = SDL_GetPerformanceCounter(); return true; } static inline void sync_framerate_with_timer(void) { // calculate how long it took for the frame to render - const double frame_length = SDL_GetPerformanceCounter() - frame_time; + const double now = SDL_GetPerformanceCounter(); + const double frame_length = now - frame_time; if (frame_length < frame_rate) { // Only sleep if we have time to spare const double remain = frame_rate - frame_length; // Sleep remaining time away sys_sleep(remain / perf_freq * 1000000.0); + // Assume we slept the required amount of time to keep the timer stable + frame_time = now + remain; + } else { + frame_time = now; } } From 2a312a8f686ecd45282d493abcffc55f191039a7 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Fri, 26 Nov 2021 03:29:37 +0300 Subject: [PATCH 17/20] move sys_sleep to platform.c --- src/pc/gfx/gfx_sdl2.c | 5 ----- src/pc/platform.c | 7 +++++++ src/pc/platform.h | 2 ++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c index 601622d2..243a7043 100644 --- a/src/pc/gfx/gfx_sdl2.c +++ b/src/pc/gfx/gfx_sdl2.c @@ -112,11 +112,6 @@ const SDL_Scancode scancode_rmapping_nonextended[][2] = { #define IS_FULLSCREEN() ((SDL_GetWindowFlags(wnd) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0) -static inline void sys_sleep(const uint64_t us) { - // TODO: not everything has usleep() - usleep(us); -} - static void gfx_sdl_set_fullscreen(void) { if (configWindow.reset) configWindow.fullscreen = false; diff --git a/src/pc/platform.c b/src/pc/platform.c index e68370a4..d984ca48 100644 --- a/src/pc/platform.c +++ b/src/pc/platform.c @@ -4,10 +4,12 @@ #include #include #include +#include #include "cliopts.h" #include "fs/fs.h" #include "configfile.h" +#include "platform.h" /* NULL terminated list of platform specific read-only data paths */ /* priority is top first */ @@ -66,6 +68,11 @@ const char *sys_file_name(const char *fpath) { return sep + 1; } +void sys_sleep(const uint64_t us) { + // TODO: figure out which of the platforms we want to support DOESN'T have usleep() + usleep(us); +} + /* this calls a platform-specific impl function after forming the error message */ static void sys_fatal_impl(const char *msg) __attribute__ ((noreturn)); diff --git a/src/pc/platform.h b/src/pc/platform.h index 168333d1..0a26b461 100644 --- a/src/pc/platform.h +++ b/src/pc/platform.h @@ -2,6 +2,7 @@ #define _SM64_PLATFORM_H_ #include +#include #include #include @@ -16,6 +17,7 @@ extern const char *sys_ropaths[]; char *sys_strdup(const char *src); char *sys_strlwr(char *src); int sys_strcasecmp(const char *s1, const char *s2); +void sys_sleep(const uint64_t us); // path stuff const char *sys_user_path(void); From 59adb5c634fdab016abe4ce6cbea91843ae48aa0 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Fri, 26 Nov 2021 03:33:03 +0300 Subject: [PATCH 18/20] fix 60fps patch --- enhancements/60fps_ex.patch | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/enhancements/60fps_ex.patch b/enhancements/60fps_ex.patch index c236bab6..9b0afc72 100644 --- a/enhancements/60fps_ex.patch +++ b/enhancements/60fps_ex.patch @@ -1890,18 +1890,18 @@ index 0467495..fa4eb33 100644 using namespace Microsoft::WRL; // For ComPtr diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c -index daff686..66fb346 100644 +index 243a704..193a245 100644 --- a/src/pc/gfx/gfx_sdl2.c +++ b/src/pc/gfx/gfx_sdl2.c -@@ -186,7 +186,7 @@ static void gfx_sdl_init(const char *window_title) { +@@ -181,7 +181,7 @@ static void gfx_sdl_init(const char *window_title) { perf_freq = SDL_GetPerformanceFrequency(); - frame_rate = perf_freq / FRAMERATE; + frame_rate = perf_freq / (2 * FRAMERATE); + frame_time = SDL_GetPerformanceCounter(); for (size_t i = 0; i < sizeof(windows_scancode_table) / sizeof(SDL_Scancode); i++) { - inverted_scancode_table[windows_scancode_table[i]] = i; diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index 923e7ea..4fe6161 100644 --- a/src/pc/pc_main.c From fa49c7bc59715ebae922cd1e86be3d7af63b8ac7 Mon Sep 17 00:00:00 2001 From: Xenthio Date: Tue, 30 Nov 2021 12:24:54 +1100 Subject: [PATCH 19/20] Fix macOS builds being broken (#489) also add .DS_Store to .gitignore --- .gitignore | 3 +++ Makefile | 7 ++++++- tools/aiff_extract_codebook.c | 5 +++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0caa3e08..6f09a7e9 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,6 @@ sm64config.txt !/sound/**/*custom*/**/*.aiff !/assets/**/*custom*.bin !/assets/**/*custom*/**/*.bin + +# macOS bullcrap directory settings file thats autocreated +.DS_Store diff --git a/Makefile b/Makefile index 9ab49a3b..3ea2541a 100644 --- a/Makefile +++ b/Makefile @@ -532,7 +532,12 @@ else ifeq ($(SDL1_USED),1) endif ifneq ($(SDL1_USED)$(SDL2_USED),00) - BACKEND_CFLAGS += `$(SDLCONFIG) --cflags` + ifeq ($(OSX_BUILD),1) + MAC_PREFIX := `$(SDLCONFIG) --prefix` + BACKEND_CFLAGS +=-I$(MAC_PREFIX)/include `$(SDLCONFIG) --cflags` # macOS homebrew SDL2 has the config laid out differently so this makes it point to the correct folder + a failsafe for if it ever changes for some reason in the future. + else + BACKEND_CFLAGS += `$(SDLCONFIG) --cflags` + endif ifeq ($(WINDOWS_BUILD),1) BACKEND_LDFLAGS += `$(SDLCONFIG) --static-libs` -lsetupapi -luser32 -limm32 -lole32 -loleaut32 -lshell32 -lwinmm -lversion else diff --git a/tools/aiff_extract_codebook.c b/tools/aiff_extract_codebook.c index 481e3133..6570fa59 100644 --- a/tools/aiff_extract_codebook.c +++ b/tools/aiff_extract_codebook.c @@ -26,6 +26,11 @@ typedef unsigned int u32; #define NORETURN __attribute__((noreturn)) #define UNUSED __attribute__((unused)) +#ifdef __APPLE__ +// even with -std=gnu99 vsnprintf seems to not be defined in stdio.h, why? +extern int vsnprintf(char * __restrict, size_t, const char * __restrict, va_list); +#endif + typedef struct { u32 start; From fc582d5faf7d2db22321d44dea818d24e69707dd Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 30 Nov 2021 15:51:11 +0300 Subject: [PATCH 20/20] fix undeclared functions in 60fps patch --- enhancements/60fps_ex.patch | 45 ++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/enhancements/60fps_ex.patch b/enhancements/60fps_ex.patch index 9b0afc72..f2f79f22 100644 --- a/enhancements/60fps_ex.patch +++ b/enhancements/60fps_ex.patch @@ -232,18 +232,19 @@ index 1da535b..49a5c03 100644 break; } diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c -index ac2ee50..323b7d0 100644 +index ac2ee50..ce5fd37 100644 --- a/src/engine/surface_load.c +++ b/src/engine/surface_load.c -@@ -14,6 +14,7 @@ +@@ -14,6 +14,8 @@ #include "game/mario.h" #include "game/object_list_processor.h" #include "surface_load.h" +#include "game/game_init.h" ++#include "math_util.h" s32 unused8038BE90; -@@ -359,6 +360,11 @@ static struct Surface *read_surface_data(s16 *vertexData, s16 **vertexIndices) { +@@ -359,6 +361,11 @@ static struct Surface *read_surface_data(s16 *vertexData, s16 **vertexIndices) { surface = alloc_surface(); @@ -388,10 +389,19 @@ index 173ab8a..b1abdc4 100644 // bss order hack to not affect BSS order. if possible, remove me, but it will be hard to match otherwise diff --git a/src/game/envfx_bubbles.c b/src/game/envfx_bubbles.c -index 16a9272..ee1b029 100644 +index 16a9272..500e796 100644 --- a/src/game/envfx_bubbles.c +++ b/src/game/envfx_bubbles.c -@@ -35,6 +35,20 @@ Vtx_t gBubbleTempVtx[3] = { +@@ -11,6 +11,8 @@ + #include "audio/external.h" + #include "textures.h" + ++extern void interpolate_vectors_s16(Vec3s res, Vec3s a, Vec3s b); ++ + /** + * This file implements environment effects that are not snow: + * Flowers (unused), lava bubbles and jet stream/whirlpool bubbles. +@@ -35,6 +37,20 @@ Vtx_t gBubbleTempVtx[3] = { { { 0, 0, 0 }, 0, { -498, 964 }, { 0xFF, 0xFF, 0xFF, 0xFF } }, }; @@ -412,7 +422,7 @@ index 16a9272..ee1b029 100644 /** * Check whether the particle with the given index is * laterally within distance of point (x, z). Used to -@@ -241,6 +255,7 @@ void envfx_update_whirlpool(void) { +@@ -241,6 +257,7 @@ void envfx_update_whirlpool(void) { (gEnvFxBuffer + i)->yPos = (i + gEnvFxBuffer)->bubbleY; (gEnvFxBuffer + i)->unusedBubbleVar = 0; (gEnvFxBuffer + i)->isAlive = 1; @@ -420,7 +430,7 @@ index 16a9272..ee1b029 100644 envfx_rotate_around_whirlpool(&(gEnvFxBuffer + i)->xPos, &(gEnvFxBuffer + i)->yPos, &(gEnvFxBuffer + i)->zPos); -@@ -299,6 +314,7 @@ void envfx_update_jetstream(void) { +@@ -299,6 +316,7 @@ void envfx_update_jetstream(void) { + coss((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1]; (gEnvFxBuffer + i)->yPos = gEnvFxBubbleConfig[ENVFX_STATE_SRC_Y] + (random_float() * 400.0f - 200.0f); @@ -428,7 +438,7 @@ index 16a9272..ee1b029 100644 } else { (gEnvFxBuffer + i)->angleAndDist[1] += 10; (gEnvFxBuffer + i)->xPos += sins((gEnvFxBuffer + i)->angleAndDist[0]) * 10.0f; -@@ -506,6 +522,12 @@ Gfx *envfx_update_bubble_particles(s32 mode, UNUSED Vec3s marioPos, Vec3s camFro +@@ -506,6 +524,12 @@ Gfx *envfx_update_bubble_particles(s32 mode, UNUSED Vec3s marioPos, Vec3s camFro Vec3s vertex1; Vec3s vertex2; Vec3s vertex3; @@ -441,7 +451,7 @@ index 16a9272..ee1b029 100644 Gfx *gfxStart; -@@ -521,18 +543,52 @@ Gfx *envfx_update_bubble_particles(s32 mode, UNUSED Vec3s marioPos, Vec3s camFro +@@ -521,18 +545,52 @@ Gfx *envfx_update_bubble_particles(s32 mode, UNUSED Vec3s marioPos, Vec3s camFro envfx_bubbles_update_switch(mode, camTo, vertex1, vertex2, vertex3); rotate_triangle_vertices(vertex1, vertex2, vertex3, pitch, yaw); @@ -1821,7 +1831,7 @@ index b49ddaf..d6656af 100644 switch (transType) { case WARP_TRANSITION_FADE_FROM_COLOR: diff --git a/src/menu/intro_geo.c b/src/menu/intro_geo.c -index 37c6752..d823d40 100644 +index 37c6752..7499b1f 100644 --- a/src/menu/intro_geo.c +++ b/src/menu/intro_geo.c @@ -1,5 +1,6 @@ @@ -1831,7 +1841,16 @@ index 37c6752..d823d40 100644 #include "game/memory.h" #include "game/segment2.h" #include "game/segment7.h" -@@ -70,6 +71,18 @@ s8 gameOverBackgroundTable[] = { +@@ -26,6 +27,8 @@ struct GraphNodeMore { + /*0x18*/ u32 unk18; + }; + ++extern void interpolate_vectors(Vec3f res, Vec3f a, Vec3f b); ++ + // intro geo bss + s32 gGameOverFrameCounter; + s32 gGameOverTableIndex; +@@ -70,6 +73,18 @@ s8 gameOverBackgroundTable[] = { s8 gameOverBackgroundFlipOrder[] = { 0x00, 0x01, 0x02, 0x03, 0x07, 0x0B, 0x0a, 0x09, 0x08, 0x04, 0x05, 0x06 }; @@ -1850,7 +1869,7 @@ index 37c6752..d823d40 100644 Gfx *geo_title_screen(s32 sp50, struct GraphNode *sp54, UNUSED void *context) { struct GraphNode *graphNode; // sp4c Gfx *displayList; // sp48 -@@ -80,6 +93,8 @@ Gfx *geo_title_screen(s32 sp50, struct GraphNode *sp54, UNUSED void *context) { +@@ -80,6 +95,8 @@ Gfx *geo_title_screen(s32 sp50, struct GraphNode *sp54, UNUSED void *context) { f32 scaleX; // sp34 f32 scaleY; // sp30 f32 scaleZ; // sp2c @@ -1859,7 +1878,7 @@ index 37c6752..d823d40 100644 graphNode = sp54; displayList = NULL; displayListIter = NULL; -@@ -110,7 +125,11 @@ Gfx *geo_title_screen(s32 sp50, struct GraphNode *sp54, UNUSED void *context) { +@@ -110,7 +127,11 @@ Gfx *geo_title_screen(s32 sp50, struct GraphNode *sp54, UNUSED void *context) { scaleY = 0.0f; scaleZ = 0.0f; }