summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmill <emil.lenngren@gmail.com>2022-05-14 00:43:55 +0200
committerGitHub <noreply@github.com>2022-05-13 18:43:55 -0400
commit45e5e5ca720100e22a1e32dba5c84c3f11cbadcd (patch)
treecf7555103db37224cbaba654b2ca4f030cd51c44
parentbcd57f45b2cf5e553bd5d8dbc0848e26304d9a0b (diff)
Experimental interpolation (#309)
* Experimental 60 fps * Fix compile error * Fix compile error * Fix compile error
-rw-r--r--libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp52
-rw-r--r--libultraship/libultraship/Lib/Fast3D/gfx_pc.h14
-rw-r--r--libultraship/libultraship/SohImGuiImpl.cpp1
-rw-r--r--libultraship/libultraship/Window.cpp8
-rw-r--r--libultraship/libultraship/Window.h2
-rw-r--r--soh/include/macros.h8
-rw-r--r--soh/include/z64effect.h1
-rw-r--r--soh/soh.vcxproj2
-rw-r--r--soh/soh.vcxproj.filters4
-rw-r--r--soh/soh/Enhancements/debugger/colViewer.cpp1
-rw-r--r--soh/soh/OTRGlobals.cpp13
-rw-r--r--soh/soh/frame_interpolation.cpp732
-rw-r--r--soh/soh/frame_interpolation.h61
-rw-r--r--soh/src/code/sys_matrix.c22
-rw-r--r--soh/src/code/z_actor.c8
-rw-r--r--soh/src/code/z_camera.c5
-rw-r--r--soh/src/code/z_eff_blure.c4
-rw-r--r--soh/src/code/z_eff_shield_particle.c4
-rw-r--r--soh/src/code/z_eff_spark.c4
-rw-r--r--soh/src/code/z_effect_soft_sprite.c5
-rw-r--r--soh/src/code/z_lights.c4
-rw-r--r--soh/src/code/z_play.c4
-rw-r--r--soh/src/code/z_skin_matrix.c3
-rw-r--r--soh/src/code/z_view.c107
-rw-r--r--soh/src/code/z_vr_box_draw.c5
-rw-r--r--soh/src/libultra/gu/guLookAt.c3
-rw-r--r--soh/src/libultra/gu/guPerspectiveF.c4
-rw-r--r--soh/src/libultra/gu/ortho.c7
-rw-r--r--soh/src/overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.c10
-rw-r--r--soh/src/overlays/actors/ovl_Bg_Hidan_Rsekizou/z_bg_hidan_rsekizou.c3
-rw-r--r--soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c16
-rw-r--r--soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.h1
-rw-r--r--soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c5
-rw-r--r--soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c29
-rw-r--r--soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h1
-rw-r--r--soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c17
-rw-r--r--soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c20
-rw-r--r--soh/src/overlays/actors/ovl_En_Zo/z_en_zo.c11
-rw-r--r--soh/src/overlays/actors/ovl_En_Zo/z_en_zo.h1
-rw-r--r--soh/src/overlays/actors/ovl_Fishing/z_fishing.c33
-rw-r--r--soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c11
-rw-r--r--soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.h1
-rw-r--r--soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c4
43 files changed, 1204 insertions, 47 deletions
diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp
index da39c0a4d..6f7e8c271 100644
--- a/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp
+++ b/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp
@@ -190,8 +190,12 @@ static int game_framebuffer_msaa_resolved;
uint32_t gfx_msaa_level = 1;
+static bool has_drawn_imgui_menu;
+
static bool dropped_frame;
+static const std::unordered_map<Mtx *, MtxF> *current_mtx_replacements;
+
static float buf_vbo[MAX_BUFFERED * (32 * 3)]; // 3 vertices in a triangle and 32 floats per vtx
static size_t buf_vbo_len;
static size_t buf_vbo_num_tris;
@@ -916,20 +920,31 @@ static void gfx_matrix_mul(float res[4][4], const float a[4][4], const float b[4
static void gfx_sp_matrix(uint8_t parameters, const int32_t *addr) {
float matrix[4][4];
+
+ if (auto it = current_mtx_replacements->find((Mtx *)addr); it != current_mtx_replacements->end()) {
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ float v = it->second.mf[i][j];
+ int as_int = (int)(v * 65536.0f);
+ matrix[i][j] = as_int * (1.0f / 65536.0f);
+ }
+ }
+ } else {
#ifndef GBI_FLOATS
- // Original GBI where fixed point matrices are used
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j += 2) {
- int32_t int_part = addr[i * 2 + j / 2];
- uint32_t frac_part = addr[8 + i * 2 + j / 2];
- matrix[i][j] = (int32_t)((int_part & 0xffff0000) | (frac_part >> 16)) / 65536.0f;
- matrix[i][j + 1] = (int32_t)((int_part << 16) | (frac_part & 0xffff)) / 65536.0f;
+ // Original GBI where fixed point matrices are used
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j += 2) {
+ int32_t int_part = addr[i * 2 + j / 2];
+ uint32_t frac_part = addr[8 + i * 2 + j / 2];
+ matrix[i][j] = (int32_t)((int_part & 0xffff0000) | (frac_part >> 16)) / 65536.0f;
+ matrix[i][j + 1] = (int32_t)((int_part << 16) | (frac_part & 0xffff)) / 65536.0f;
+ }
}
- }
#else
- // For a modified GBI where fixed point values are replaced with floats
- memcpy(matrix, addr, sizeof(matrix));
+ // For a modified GBI where fixed point values are replaced with floats
+ memcpy(matrix, addr, sizeof(matrix));
#endif
+ }
if (parameters & G_MTX_PROJECTION) {
if (parameters & G_MTX_LOAD) {
@@ -2708,6 +2723,7 @@ void gfx_start_frame(void) {
gfx_wapi->handle_events();
gfx_wapi->get_dimensions(&gfx_current_window_dimensions.width, &gfx_current_window_dimensions.height);
SohImGui::DrawMainMenuAndCalculateGameSize();
+ has_drawn_imgui_menu = true;
if (gfx_current_dimensions.height == 0) {
// Avoid division by zero
gfx_current_dimensions.height = 1;
@@ -2746,7 +2762,7 @@ void gfx_start_frame(void) {
fbActive = 0;
}
-void gfx_run(Gfx *commands) {
+void gfx_run(Gfx *commands, const std::unordered_map<Mtx *, MtxF>& mtx_replacements) {
gfx_sp_reset();
//puts("New frame");
@@ -2755,12 +2771,21 @@ void gfx_run(Gfx *commands) {
if (!gfx_wapi->start_frame()) {
dropped_frame = true;
- SohImGui::DrawFramebufferAndGameInput();
- SohImGui::CancelFrame();
+ if (has_drawn_imgui_menu) {
+ SohImGui::DrawFramebufferAndGameInput();
+ SohImGui::CancelFrame();
+ has_drawn_imgui_menu = false;
+ }
return;
}
dropped_frame = false;
+ if (!has_drawn_imgui_menu) {
+ SohImGui::DrawMainMenuAndCalculateGameSize();
+ }
+
+ current_mtx_replacements = &mtx_replacements;
+
double t0 = gfx_wapi->get_time();
gfx_rapi->update_framebuffer_parameters(0, gfx_current_window_dimensions.width, gfx_current_window_dimensions.height, 1, false, true, true, !game_renders_to_framebuffer);
gfx_rapi->start_frame();
@@ -2792,6 +2817,7 @@ void gfx_run(Gfx *commands) {
//printf("Process %f %f\n", t1, t1 - t0);
gfx_rapi->end_frame();
gfx_wapi->swap_buffers_begin();
+ has_drawn_imgui_menu = false;
}
void gfx_end_frame(void) {
diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_pc.h b/libultraship/libultraship/Lib/Fast3D/gfx_pc.h
index d9cd9be8d..62f80ab3c 100644
--- a/libultraship/libultraship/Lib/Fast3D/gfx_pc.h
+++ b/libultraship/libultraship/Lib/Fast3D/gfx_pc.h
@@ -6,6 +6,8 @@
#include <unordered_map>
#include <list>
+#include "U64/PR/ultra64/types.h"
+
struct GfxRenderingAPI;
struct GfxWindowManagerAPI;
@@ -52,26 +54,24 @@ struct TextureCacheValue {
#endif
};
-#ifdef __cplusplus
extern "C" {
-#endif
+
extern struct GfxDimensions gfx_current_window_dimensions; // The dimensions of the window
extern struct GfxDimensions gfx_current_dimensions; // The dimensions of the draw area the game draws to, before scaling (if applicable)
extern struct XYWidthHeight gfx_current_game_window_viewport; // The area of the window the game is drawn to, (0, 0) is top-left corner
extern uint32_t gfx_msaa_level;
+}
+
void gfx_init(struct GfxWindowManagerAPI* wapi, struct GfxRenderingAPI* rapi, const char* game_name, bool start_in_fullscreen);
struct GfxRenderingAPI* gfx_get_current_rendering_api(void);
void gfx_start_frame(void);
-void gfx_run(Gfx* commands);
+void gfx_run(Gfx* commands, const std::unordered_map<Mtx*, MtxF>& mtx_replacements);
void gfx_end_frame(void);
void gfx_set_framedivisor(int);
void gfx_texture_cache_clear();
-int gfx_create_framebuffer(uint32_t width, uint32_t height);
+extern "C" int gfx_create_framebuffer(uint32_t width, uint32_t height);
void gfx_get_pixel_depth_prepare(float x, float y);
uint16_t gfx_get_pixel_depth(float x, float y);
-#ifdef __cplusplus
-}
-#endif
#endif
diff --git a/libultraship/libultraship/SohImGuiImpl.cpp b/libultraship/libultraship/SohImGuiImpl.cpp
index 72d9cee1a..785a29dee 100644
--- a/libultraship/libultraship/SohImGuiImpl.cpp
+++ b/libultraship/libultraship/SohImGuiImpl.cpp
@@ -681,6 +681,7 @@ namespace SohImGui {
EXPERIMENTAL();
+ EnhancementCheckbox("60 fps interpolation", "g60FPS");
EnhancementCheckbox("Disable LOD", "gDisableLOD");
ImGui::EndMenu();
diff --git a/libultraship/libultraship/Window.cpp b/libultraship/libultraship/Window.cpp
index 35a95f0de..588e3af50 100644
--- a/libultraship/libultraship/Window.cpp
+++ b/libultraship/libultraship/Window.cpp
@@ -282,8 +282,12 @@ namespace Ship {
gfx_start_frame();
}
- void Window::RunCommands(Gfx* Commands) {
- gfx_run(Commands);
+ void Window::RunCommands(Gfx* Commands, const std::vector<std::unordered_map<Mtx*, MtxF>>& mtx_replacements) {
+ for (const auto& m : mtx_replacements) {
+ gfx_run(Commands, m);
+ gfx_end_frame();
+ }
+ gfx_run(Commands, {});
gfx_end_frame();
}
diff --git a/libultraship/libultraship/Window.h b/libultraship/libultraship/Window.h
index b075e496f..04886c53e 100644
--- a/libultraship/libultraship/Window.h
+++ b/libultraship/libultraship/Window.h
@@ -19,7 +19,7 @@ namespace Ship {
void MainLoop(void (*MainFunction)(void));
void Init();
void StartFrame();
- void RunCommands(Gfx* Commands);
+ void RunCommands(Gfx* Commands, const std::vector<std::unordered_map<Mtx*, MtxF>>& mtx_replacements);
void SetFrameDivisor(int divisor);
void GetPixelDepthPrepare(float x, float y);
uint16_t GetPixelDepth(float x, float y);
diff --git a/soh/include/macros.h b/soh/include/macros.h
index a621a65f8..7392274d9 100644
--- a/soh/include/macros.h
+++ b/soh/include/macros.h
@@ -138,6 +138,8 @@ extern GraphicsContext* __gfxCtx;
#ifndef NDEBUG
#define OPEN_DISPS(gfxCtx, file, line) \
{ \
+ void FrameInterpolation_RecordOpenChild(const void* a, int b); \
+ FrameInterpolation_RecordOpenChild(file, line); \
GraphicsContext* __gfxCtx; \
Gfx* dispRefs[4]; \
__gfxCtx = gfxCtx; \
@@ -146,6 +148,8 @@ extern GraphicsContext* __gfxCtx;
#else
#define OPEN_DISPS(gfxCtx, file, line) \
{ \
+ void FrameInterpolation_RecordOpenChild(const void* a, int b); \
+ FrameInterpolation_RecordOpenChild(file, line); \
GraphicsContext* __gfxCtx; \
__gfxCtx = gfxCtx; \
(void)__gfxCtx;
@@ -153,11 +157,15 @@ extern GraphicsContext* __gfxCtx;
#ifndef NDEBUG
#define CLOSE_DISPS(gfxCtx, file, line) \
+ {void FrameInterpolation_RecordCloseChild(void); \
+ FrameInterpolation_RecordCloseChild();} \
Graph_CloseDisps(dispRefs, gfxCtx, file, line); \
} \
(void)0
#else
#define CLOSE_DISPS(gfxCtx, file, line) \
+ {void FrameInterpolation_RecordCloseChild(void); \
+ FrameInterpolation_RecordCloseChild();} \
(void)0; \
} \
(void)0
diff --git a/soh/include/z64effect.h b/soh/include/z64effect.h
index c85c9d617..1b35d46b6 100644
--- a/soh/include/z64effect.h
+++ b/soh/include/z64effect.h
@@ -225,6 +225,7 @@ typedef struct EffectSs {
/* 0x5C */ s16 life; // -1 means this entry is free
/* 0x5E */ u8 priority; // Lower value means higher priority
/* 0x5F */ u8 type;
+ u32 epoch;
} EffectSs; // size = 0x60
typedef struct {
diff --git a/soh/soh.vcxproj b/soh/soh.vcxproj
index a395a22ce..0a2006122 100644
--- a/soh/soh.vcxproj
+++ b/soh/soh.vcxproj
@@ -173,6 +173,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
+ <ClCompile Include="soh\frame_interpolation.cpp" />
<ClCompile Include="soh\Enhancements\bootcommands.c" />
<ClCompile Include="soh\Enhancements\debugconsole.cpp" />
<ClCompile Include="soh\Enhancements\debugger\colViewer.cpp" />
@@ -878,6 +879,7 @@
<ClCompile Include="src\overlays\misc\ovl_map_mark_data\z_map_mark_data.c" />
</ItemGroup>
<ItemGroup>
+ <ClInclude Include="soh\frame_interpolation.h" />
<ClInclude Include="include\alloca.h" />
<ClInclude Include="include\bgm.h" />
<ClInclude Include="include\color.h" />
diff --git a/soh/soh.vcxproj.filters b/soh/soh.vcxproj.filters
index b9d68b704..04f6030a8 100644
--- a/soh/soh.vcxproj.filters
+++ b/soh/soh.vcxproj.filters
@@ -2186,6 +2186,8 @@
</ClCompile>
<ClCompile Include="soh\Enhancements\debugger\ImGuiHelpers.cpp">
<Filter>Source Files\soh\Enhancements\debugger</Filter>
+ <ClCompile Include="soh\frame_interpolation.cpp">
+ <Filter>Source Files\soh</Filter>
</ClCompile>
<ClCompile Include="src\code\z_cheap_proc.c">
<Filter>Source Files</Filter>
@@ -3748,6 +3750,8 @@
</ClInclude>
<ClInclude Include="soh\Enhancements\debugger\ImGuiHelpers.h">
<Filter>Header Files\soh\Enhancements\debugger</Filter>
+ <ClInclude Include="soh\frame_interpolation.h">
+ <Filter>Header Files\soh</Filter>
</ClInclude>
<ClInclude Include="soh\Enhancements\savestates.h">
<Filter>Source Files\soh\Enhancements</Filter>
diff --git a/soh/soh/Enhancements/debugger/colViewer.cpp b/soh/soh/Enhancements/debugger/colViewer.cpp
index ed0fe0b56..f5c283aac 100644
--- a/soh/soh/Enhancements/debugger/colViewer.cpp
+++ b/soh/soh/Enhancements/debugger/colViewer.cpp
@@ -1,6 +1,7 @@
#include "colViewer.h"
#include "../libultraship/SohImGuiImpl.h"
#include "ImGuiHelpers.h"
+#include "../../frame_interpolation.h"
#include <vector>
#include <string>
diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp
index 7456ebf7e..e8e1b31c1 100644
--- a/soh/soh/OTRGlobals.cpp
+++ b/soh/soh/OTRGlobals.cpp
@@ -29,6 +29,7 @@
#include "AudioPlayer.h"
#include "Enhancements/debugconsole.h"
#include "Enhancements/debugger/debugger.h"
+#include "soh/frame_interpolation.h"
#include "Utils/BitConverter.h"
#include "variables.h"
#include "macros.h"
@@ -174,7 +175,7 @@ extern "C" void Graph_StartFrame() {
// C->C++ Bridge
extern "C" void Graph_ProcessGfxCommands(Gfx* commands) {
- OTRGlobals::Instance->context->GetWindow()->SetFrameDivisor(R_UPDATE_RATE);
+ OTRGlobals::Instance->context->GetWindow()->SetFrameDivisor(CVar_GetS32("g60FPS", 0) == 0 ? R_UPDATE_RATE : 1);
if (!audio.initialized) {
audio.initialized = true;
@@ -224,7 +225,15 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) {
}
audio.cv_to_thread.notify_one();
- OTRGlobals::Instance->context->GetWindow()->RunCommands(commands);
+ std::vector<std::unordered_map<Mtx*, MtxF>> mtx_replacements;
+ if (CVar_GetS32("g60FPS", 0) != 0) {
+ int to = R_UPDATE_RATE;
+ for (int i = 1; i < to; i++) {
+ mtx_replacements.push_back(FrameInterpolation_Interpolate(i / (float)to));
+ }
+ }
+
+ OTRGlobals::Instance->context->GetWindow()->RunCommands(commands, mtx_replacements);
{
std::unique_lock<std::mutex> Lock(audio.mutex);
diff --git a/soh/soh/frame_interpolation.cpp b/soh/soh/frame_interpolation.cpp
new file mode 100644
index 000000000..bd85d41ce
--- /dev/null
+++ b/soh/soh/frame_interpolation.cpp
@@ -0,0 +1,732 @@
+#include "Cvar.h"
+
+#include <vector>
+#include <map>
+#include <unordered_map>
+#include <math.h>
+
+#include "frame_interpolation.h"
+
+/*
+Frame interpolation.
+
+The idea of this code is to interpolate all matrices.
+
+The code contains two approaches. The first is to interpolate
+all inputs in transformations, such as angles, scale and distances,
+and then perform the same transformations with the interpolated values.
+After evaluation for some reason some animations such rolling look strange.
+
+The second approach is to simply interpolate the final matrices. This will
+more or less simply interpolate the world coordinates for movements.
+This will however make rotations ~180 degrees get the "paper effect".
+The mitigation is to identify this case for actors and interpolate the
+matrix but in model coordinates instead, by "removing" the rotation-
+translation before interpolating, create a rotation matrix with the
+interpolated angle which is then applied to the matrix.
+
+Currently the code contains both methods but only the second one is currently
+used.
+
+Both approaches build a tree of instructions, containing matrices
+at leaves. Every node is built from OPEN_DISPS/CLOSE_DISPS and manually
+inserted FrameInterpolation_OpenChild/FrameInterpolation_Close child calls.
+These nodes contain information that should suffice to identify the matrix,
+so we can find it in an adjacent frame.
+
+We can interpolate an arbitrary amount of frames between two original frames,
+given a specific interpolation factor (0=old frame, 0.5=average of frames,
+1.0=new frame).
+*/
+
+extern "C" {
+
+void Matrix_Init(struct GameState* gameState);
+void Matrix_Push(void);
+void Matrix_Pop(void);
+void Matrix_Get(MtxF* dest);
+void Matrix_Put(MtxF* src);
+void Matrix_Mult(MtxF* mf, u8 mode);
+void Matrix_Translate(f32 x, f32 y, f32 z, u8 mode);
+void Matrix_Scale(f32 x, f32 y, f32 z, u8 mode);
+void Matrix_RotateX(f32 x, u8 mode);
+void Matrix_RotateY(f32 y, u8 mode);
+void Matrix_RotateZ(f32 z, u8 mode);
+void Matrix_RotateZYX(s16 x, s16 y, s16 z, u8 mode);
+void Matrix_TranslateRotateZYX(Vec3f* translation, Vec3s* rotation);
+void Matrix_SetTranslateRotateYXZ(f32 translateX, f32 translateY, f32 translateZ, Vec3s* rot);
+Mtx* Matrix_MtxFToMtx(MtxF* src, Mtx* dest);
+Mtx* Matrix_ToMtx(Mtx* dest, char* file, s32 line);
+Mtx* Matrix_NewMtx(struct GraphicsContext* gfxCtx, char* file, s32 line);
+Mtx* Matrix_MtxFToNewMtx(MtxF* src, struct GraphicsContext* gfxCtx);
+void Matrix_MultVec3f(Vec3f* src, Vec3f* dest);
+void Matrix_MtxFCopy(MtxF* dest, MtxF* src);
+void Matrix_MtxToMtxF(Mtx* src, MtxF* dest);
+void Matrix_MultVec3fExt(Vec3f* src, Vec3f* dest, MtxF* mf);
+void Matrix_Transpose(MtxF* mf);
+void Matrix_ReplaceRotation(MtxF* mf);
+void Matrix_MtxFToYXZRotS(MtxF* mf, Vec3s* rotDest, s32 flag);
+void Matrix_MtxFToZYXRotS(MtxF* mf, Vec3s* rotDest, s32 flag);
+void Matrix_RotateAxis(f32 angle, Vec3f* axis, u8 mode);
+MtxF* Matrix_CheckFloats(MtxF* mf, char* file, s32 line);
+void Matrix_SetTranslateScaleMtx2(Mtx* mtx, f32 scaleX, f32 scaleY, f32 scaleZ, f32 translateX, f32 translateY,
+ f32 translateZ);
+
+MtxF* Matrix_GetCurrent(void);
+
+void SkinMatrix_MtxFMtxFMult(MtxF* mfA, MtxF* mfB, MtxF* dest);
+
+}
+
+static bool invert_matrix(const float m[16], float invOut[16]);
+
+using namespace std;
+
+namespace {
+
+ enum class Op {
+ OpenChild,
+ CloseChild,
+
+ MatrixPush,
+ MatrixPop,
+ MatrixPut,
+ MatrixMult,
+ MatrixTranslate,
+ MatrixScale,
+ MatrixRotate1Coord,
+ MatrixRotateZYX,
+ MatrixTranslateRotateZYX,
+ MatrixSetTranslateRotateYXZ,
+ MatrixMtxFToMtx,
+ MatrixToMtx,
+ MatrixReplaceRotation,
+ MatrixRotateAxis,
+ SkinMatrixMtxFToMtx
+ };
+
+ typedef pair<const void*, int> label;
+
+ union Data {
+ Data() {
+ }
+
+ struct {
+ MtxF src;
+ } matrix_put;
+
+ struct {
+ MtxF mf;
+ u8 mode;
+ } matrix_mult;
+
+ struct {
+ f32 x, y, z;
+ u8 mode;
+ } matrix_translate, matrix_scale;
+
+ struct {
+ u32 coord;
+ f32 value;
+ u8 mode;
+ } matrix_rotate_1_coord;
+
+ struct {
+ s16 x, y, z;
+ u8 mode;
+ } matrix_rotate_zyx;
+
+ struct {
+ Vec3f translation;
+ Vec3s rotation;
+ } matrix_translate_rotate_zyx;
+
+ struct {
+ f32 translateX, translateY, translateZ;
+ Vec3s rot;
+ //MtxF mtx;
+ bool has_mtx;
+ } matrix_set_translate_rotate_yxz;
+
+ struct {
+ MtxF src;
+ Mtx* dest;
+ } matrix_mtxf_to_mtx;
+
+ struct {
+ Mtx* dest;
+ MtxF src;
+ bool has_adjusted;
+ } matrix_to_mtx;
+
+ struct {
+ MtxF mf;
+ } matrix_replace_rotation;
+
+ struct {
+ f32 angle;
+ Vec3f axis;
+ u8 mode;
+ } matrix_rotate_axis;
+
+ struct {
+ label key;
+ size_t idx;
+ } open_child;
+ };
+
+ struct Path {
+ map<label, vector<Path>> children;
+ map<Op, vector<Data>> ops;
+ vector<pair<Op, size_t>> items;
+ };
+
+ struct Recording {
+ Path root_path;
+ };
+
+ bool is_recording;
+ vector<Path*> current_path;
+ uint32_t camera_epoch;
+ uint32_t previous_camera_epoch;
+ Recording current_recording;
+ Recording previous_recording;
+
+ bool next_is_actor_pos_rot_matrix;
+ bool has_inv_actor_mtx;
+ MtxF inv_actor_mtx;
+ size_t inv_actor_mtx_path_index;
+
+ Data& append(Op op) {
+ auto& m = current_path.back()->ops[op];
+ current_path.back()->items.emplace_back(op, m.size());
+ return m.emplace_back();
+ }
+
+ struct InterpolateCtx {
+ float step;
+ float w;
+ unordered_map<Mtx*, MtxF> mtx_replacements;
+ MtxF tmp_mtxf, tmp_mtxf2;
+ Vec3f tmp_vec3f;
+ Vec3s tmp_vec3s;
+ MtxF actor_mtx;
+
+ MtxF* new_replacement(Mtx* addr) {
+ return &mtx_replacements[addr];
+ }
+
+ void interpolate_mtxf(MtxF* res, MtxF* o, MtxF* n) {
+ for (size_t i = 0; i < 4; i++) {
+ for (size_t j = 0; j < 4; j++) {
+ res->mf[i][j] = w * o->mf[i][j] + step * n->mf[i][j];
+ }
+ }
+ }
+
+ float lerp(f32 o, f32 n) {
+ return w * o + step * n;
+ }
+
+ void lerp_vec3f(Vec3f* res, Vec3f* o, Vec3f* n) {
+ res->x = lerp(o->x, n->x);
+ res->y = lerp(o->y, n->y);
+ res->z = lerp(o->z, n->z);
+ }
+
+ float interpolate_angle(f32 o, f32 n) {
+ if (o == n)
+ return n;
+ o = fmodf(o, 2 * M_PI);
+ if (o < 0.0f) {
+ o += 2 * M_PI;
+ }
+ n = fmodf(n, 2 * M_PI);
+ if (n < 0.0f) {
+ n += 2 * M_PI;
+ }
+ if (fabsf(o - n) > M_PI) {
+ if (o < n) {
+ o += 2 * M_PI;
+ } else {
+ n += 2 * M_PI;
+ }
+ }
+ if (fabsf(o - n) > M_PI / 2) {
+ //return n;
+ }
+ return lerp(o, n);
+ }
+
+ s16 interpolate_angle(s16 os, s16 ns) {
+ if (os == ns)
+ return ns;
+ int o = (u16)os;
+ int n = (u16)ns;
+ u16 res;
+ int diff = o - n;
+ if (-0x8000 <= diff && diff <= 0x8000) {
+ if (diff < -0x4000 || diff > 0x4000) {
+ return ns;
+ }
+ res = (u16)(w * o + step * n);
+ } else {
+ if (o < n) {
+ o += 0x10000;
+ } else {
+ n += 0x10000;
+ }
+ diff = o - n;
+ if (diff < -0x4000 || diff > 0x4000) {
+ return ns;
+ }
+ res = (u16)(w * o + step * n);
+ }
+ if (os / 327 == ns / 327 && (s16)res / 327 != os / 327) {
+ int bp = 0;
+ }
+ return res;
+ }
+
+ void interpolate_angles(Vec3s* res, Vec3s* o, Vec3s* n) {
+ res->x = interpolate_angle(o->x, n->x);
+ res->y = interpolate_angle(o->y, n->y);
+ res->z = interpolate_angle(o->z, n->z);
+ }
+
+ void interpolate_branch(Path* old_path, Path *new_path) {
+ for (auto& item : new_path->items) {
+ Data& new_op = new_path->ops[item.first][item.second];
+
+ if (item.first == Op::OpenChild) {
+ if (auto it = old_path->children.find(new_op.open_child.key);
+ it != old_path->children.end() && new_op.open_child.idx < it->second.size()) {
+ interpolate_branch(&it->second[new_op.open_child.idx],
+ &new_path->children.find(new_op.open_child.key)->second[new_op.open_child.idx]);
+ } else {
+ interpolate_branch(
+ &new_path->children.find(new_op.open_child.key)->second[new_op.open_child.idx],
+ &new_path->children.find(new_op.open_child.key)->second[new_op.open_child.idx]);
+ }
+ continue;
+ }
+
+ if (auto it = old_path->ops.find(item.first); it != old_path->ops.end()) {
+ if (item.second < it->second.size()) {
+ Data& old_op = it->second[item.second];
+ switch (item.first) {
+ case Op::OpenChild:
+ break;
+ case Op::CloseChild:
+ break;
+
+ case Op::MatrixPush:
+ Matrix_Push();
+ break;
+
+ case Op::MatrixPop:
+ Matrix_Pop();
+ break;
+
+ case Op::MatrixPut:
+ interpolate_mtxf(&tmp_mtxf, &old_op.matrix_put.src, &new_op.matrix_put.src);
+ Matrix_Put(&tmp_mtxf);
+ break;
+
+ case Op::MatrixMult:
+ interpolate_mtxf(&tmp_mtxf, &old_op.matrix_mult.mf, &new_op.matrix_mult.mf);
+ Matrix_Mult(&tmp_mtxf, new_op.matrix_mult.mode);
+ break;
+
+ case Op::MatrixTranslate:
+ Matrix_Translate(lerp(old_op.matrix_translate.x, new_op.matrix_translate.x),
+ lerp(old_op.matrix_translate.y, new_op.matrix_translate.y),
+ lerp(old_op.matrix_translate.z, new_op.matrix_translate.z),
+ new_op.matrix_translate.mode);
+ break;
+
+ case Op::MatrixScale:
+ Matrix_Scale(lerp(old_op.matrix_scale.x, new_op.matrix_scale.x),
+ lerp(old_op.matrix_scale.y, new_op.matrix_scale.y),
+ lerp(old_op.matrix_scale.z, new_op.matrix_scale.z),
+ new_op.matrix_scale.mode);
+ break;
+
+ case Op::MatrixRotate1Coord: {
+ float v = interpolate_angle(old_op.matrix_rotate_1_coord.value, new_op.matrix_rotate_1_coord.value);
+ u8 mode = new_op.matrix_rotate_1_coord.mode;
+ switch (new_op.matrix_rotate_1_coord.coord) {
+ case 0:
+ Matrix_RotateX(v, mode);
+ break;
+
+ case 1:
+ Matrix_RotateY(v, mode);
+ break;
+
+ case 2:
+ Matrix_RotateZ(v, mode);
+ break;
+ }
+ break;
+ }
+
+ case Op::MatrixRotateZYX:
+ Matrix_RotateZYX(interpolate_angle(old_op.matrix_rotate_zyx.x, new_op.matrix_rotate_zyx.x),
+ interpolate_angle(old_op.matrix_rotate_zyx.y, new_op.matrix_rotate_zyx.y),
+ interpolate_angle(old_op.matrix_rotate_zyx.z, new_op.matrix_rotate_zyx.z),
+ new_op.matrix_rotate_zyx.mode);
+ break;
+
+ case Op::MatrixTranslateRotateZYX:
+ lerp_vec3f(&tmp_vec3f, &old_op.matrix_translate_rotate_zyx.translation, &new_op.matrix_translate_rotate_zyx.translation);
+ interpolate_angles(&tmp_vec3s, &old_op.matrix_translate_rotate_zyx.rotation, &new_op.matrix_translate_rotate_zyx.rotation);
+ Matrix_TranslateRotateZYX(&tmp_vec3f, &tmp_vec3s);
+ break;
+
+ case Op::MatrixSetTranslateRotateYXZ:
+ interpolate_angles(&tmp_vec3s, &old_op.matrix_set_translate_rotate_yxz.rot,
+ &new_op.matrix_set_translate_rotate_yxz.rot);
+ Matrix_SetTranslateRotateYXZ(lerp(old_op.matrix_set_translate_rotate_yxz.translateX,
+ new_op.matrix_set_translate_rotate_yxz.translateX),
+ lerp(old_op.matrix_set_translate_rotate_yxz.translateY,
+ new_op.matrix_set_translate_rotate_yxz.translateY),
+ lerp(old_op.matrix_set_translate_rotate_yxz.translateZ,
+ new_op.matrix_set_translate_rotate_yxz.translateZ),
+ &tmp_vec3s);
+ if (new_op.matrix_set_translate_rotate_yxz.has_mtx && old_op.matrix_set_translate_rotate_yxz.has_mtx) {
+ actor_mtx = *Matrix_GetCurrent();
+ }
+ break;
+
+ case Op::MatrixMtxFToMtx:
+ interpolate_mtxf(new_replacement(new_op.matrix_mtxf_to_mtx.dest),
+ &old_op.matrix_mtxf_to_mtx.src, &new_op.matrix_mtxf_to_mtx.src);
+ break;
+
+ case Op::MatrixToMtx: {
+ //*new_replacement(new_op.matrix_to_mtx.dest) = *Matrix_GetCurrent();
+ if (old_op.matrix_to_mtx.has_adjusted && new_op.matrix_to_mtx.has_adjusted) {
+ interpolate_mtxf(&tmp_mtxf, &old_op.matrix_to_mtx.src, &new_op.matrix_to_mtx.src);
+ SkinMatrix_MtxFMtxFMult(&actor_mtx, &tmp_mtxf, new_replacement(new_op.matrix_to_mtx.dest));
+ } else {
+ interpolate_mtxf(new_replacement(new_op.matrix_to_mtx.dest),
+ &old_op.matrix_to_mtx.src, &new_op.matrix_to_mtx.src);
+ }
+ break;
+ }
+
+ case Op::MatrixReplaceRotation:
+ interpolate_mtxf(&tmp_mtxf, &old_op.matrix_replace_rotation.mf, &new_op.matrix_replace_rotation.mf);
+ Matrix_ReplaceRotation(&tmp_mtxf);
+ break;
+
+ case Op::MatrixRotateAxis:
+ lerp_vec3f(&tmp_vec3f, &old_op.matrix_rotate_axis.axis, &new_op.matrix_rotate_axis.axis);
+ Matrix_RotateAxis(interpolate_angle(old_op.matrix_rotate_axis.angle, new_op.matrix_rotate_axis.angle),
+ &tmp_vec3f, new_op.matrix_rotate_axis.mode);
+ break;
+
+ case Op::SkinMatrixMtxFToMtx:
+ break;
+ }
+ }
+ }
+ }
+ }
+ };
+
+} // anonymous namespace
+
+unordered_map<Mtx*, MtxF> FrameInterpolation_Interpolate(float step) {
+ InterpolateCtx ctx;
+ ctx.step = step;
+ ctx.w = 1.0f - step;
+ ctx.interpolate_branch(&previous_recording.root_path, &current_recording.root_path);
+ return ctx.mtx_replacements;
+}
+
+void FrameInterpolation_StartRecord(void) {
+ previous_recording = move(current_recording);
+ current_recording = {};
+ current_path.clear();
+ current_path.push_back(&current_recording.root_path);
+ if (CVar_GetS32("g60FPS", 0) != 0) {
+ is_recording = true;
+ }
+}
+
+void FrameInterpolation_StopRecord(void) {
+ previous_camera_epoch = camera_epoch;
+ is_recording = false;
+}
+
+void FrameInterpolation_RecordOpenChild(const void* a, int b) {
+ if (!is_recording)
+ return;
+ label key = { a, b };
+ auto& m = current_path.back()->children[key];
+ append(Op::OpenChild).open_child = { key, m.size() };
+ current_path.push_back(&m.emplace_back());
+}
+
+void FrameInterpolation_RecordCloseChild(void) {
+ if (!is_recording)
+ return;
+ //append(Op::CloseChild);
+ if (has_inv_actor_mtx && current_path.size() == inv_actor_mtx_path_index) {
+ has_inv_actor_mtx = false;
+ }
+ current_path.pop_back();
+}
+
+void FrameInterpolation_DontInterpolateCamera(void) {
+ camera_epoch = previous_camera_epoch + 1;
+}
+
+int FrameInterpolation_GetCameraEpoch(void) {
+ return (int)camera_epoch;
+}
+
+void FrameInterpolation_RecordActorPosRotMatrix(void) {
+ if (!is_recording)
+ return;
+ next_is_actor_pos_rot_matrix = true;
+}
+
+void FrameInterpolation_RecordMatrixPush(void) {
+ if (!is_recording)
+ return;
+ append(Op::MatrixPush);
+}
+
+void FrameInterpolation_RecordMatrixPop(void) {
+ if (!is_recording)
+ return;
+ append(Op::MatrixPop);
+}
+
+void FrameInterpolation_RecordMatrixPut(MtxF* src) {
+ if (!is_recording)
+ return;
+ append(Op::MatrixPut).matrix_put = { *src };
+}
+
+void FrameInterpolation_RecordMatrixMult(MtxF* mf, u8 mode) {
+ if (!is_recording)
+ return;
+ append(Op::MatrixMult).matrix_mult = { *mf, mode };
+}
+
+void FrameInterpolation_RecordMatrixTranslate(f32 x, f32 y, f32 z, u8 mode) {
+ if (!is_recording)
+ return;
+ append(Op::MatrixTranslate).matrix_translate = { x, y, z, mode };
+}
+
+void FrameInterpolation_RecordMatrixScale(f32 x, f32 y, f32 z, u8 mode) {
+ if (!is_recording)
+ return;
+ append(Op::MatrixScale).matrix_scale = { x, y, z, mode };
+}
+
+void FrameInterpolation_RecordMatrixRotate1Coord(u32 coord, f32 value, u8 mode) {
+ if (!is_recording)
+ return;
+ append(Op::MatrixRotate1Coord).matrix_rotate_1_coord = { coord, value, mode };
+}
+
+void FrameInterpolation_RecordMatrixRotateZYX(s16 x, s16 y, s16 z, u8 mode) {
+ if (!is_recording)
+ return;
+ append(Op::MatrixRotateZYX).matrix_rotate_zyx = { x, y, z, mode };
+}
+
+void FrameInterpolation_RecordMatrixTranslateRotateZYX(Vec3f* translation, Vec3s* rotation) {
+ if (!is_recording)
+ return;
+ append(Op::MatrixTranslateRotateZYX).matrix_translate_rotate_zyx = { *translation, *rotation };
+}
+
+void FrameInterpolation_RecordMatrixSetTranslateRotateYXZ(f32 translateX, f32 translateY, f32 translateZ, Vec3s* rot) {
+ if (!is_recording)
+ return;
+ auto& d = append(Op::MatrixSetTranslateRotateYXZ).matrix_set_translate_rotate_yxz = { translateX, translateY, translateZ,
+ *rot };
+ if (next_is_actor_pos_rot_matrix) {
+ d.has_mtx = true;
+ //d.mtx = *Matrix_GetCurrent();
+ invert_matrix((const float *)Matrix_GetCurrent()->mf, (float *)inv_actor_mtx.mf);
+ next_is_actor_pos_rot_matrix = false;
+ has_inv_actor_mtx = true;
+ inv_actor_mtx_path_index = current_path.size();
+ }
+}
+
+void FrameInterpolation_RecordMatrixMtxFToMtx(MtxF* src, Mtx* dest) {
+ if (!is_recording)
+ return;
+ append(Op::MatrixMtxFToMtx).matrix_mtxf_to_mtx = { *src, dest };
+}
+
+void FrameInterpolation_RecordMatrixToMtx(Mtx* dest, char* file, s32 line) {
+ if (!is_recording)
+ return;
+ auto& d = append(Op::MatrixToMtx).matrix_to_mtx = { dest };
+ if (has_inv_actor_mtx) {
+ d.has_adjusted = true;
+ SkinMatrix_MtxFMtxFMult(&inv_actor_mtx, Matrix_GetCurrent(), &d.src);
+ } else {
+ d.src = *Matrix_GetCurrent();
+ }
+}
+
+void FrameInterpolation_RecordMatrixReplaceRotation(MtxF* mf) {
+ if (!is_recording)
+ return;
+ append(Op::MatrixReplaceRotation).matrix_replace_rotation = { *mf };
+}
+
+void FrameInterpolation_RecordMatrixRotateAxis(f32 angle, Vec3f* axis, u8 mode) {
+ if (!is_recording)
+ return;
+ append(Op::MatrixRotateAxis).matrix_rotate_axis = { angle, *axis, mode };
+}
+
+void FrameInterpolation_RecordSkinMatrixMtxFToMtx(MtxF* src, Mtx* dest) {
+ if (!is_recording)
+ return;
+ FrameInterpolation_RecordMatrixMtxFToMtx(src, dest);
+}
+
+// https://stackoverflow.com/questions/1148309/inverting-a-4x4-matrix
+static bool invert_matrix(const float m[16], float invOut[16]) {
+ float inv[16], det;
+ int i;
+
+ inv[0] = m[5] * m[10] * m[15] -
+ m[5] * m[11] * m[14] -
+ m[9] * m[6] * m[15] +
+ m[9] * m[7] * m[14] +
+ m[13] * m[6] * m[11] -
+ m[13] * m[7] * m[10];
+
+ inv[4] = -m[4] * m[10] * m[15] +
+ m[4] * m[11] * m[14] +
+ m[8] * m[6] * m[15] -
+ m[8] * m[7] * m[14] -
+ m[12] * m[6] * m[11] +
+ m[12] * m[7] * m[10];
+
+ inv[8] = m[4] * m[9] * m[15] -
+ m[4] * m[11] * m[13] -
+ m[8] * m[5] * m[15] +
+ m[8] * m[7] * m[13] +
+ m[12] * m[5] * m[11] -
+ m[12] * m[7] * m[9];
+
+ inv[12] = -m[4] * m[9] * m[14] +
+ m[4] * m[10] * m[13] +
+ m[8] * m[5] * m[14] -
+ m[8] * m[6] * m[13] -
+ m[12] * m[5] * m[10] +
+ m[12] * m[6] * m[9];
+
+ inv[1] = -m[1] * m[10] * m[15] +
+ m[1] * m[11] * m[14] +
+ m[9] * m[2] * m[15] -
+ m[9] * m[3] * m[14] -
+ m[13] * m[2] * m[11] +
+ m[13] * m[3] * m[10];
+
+ inv[5] = m[0] * m[10] * m[15] -
+ m[0] * m[11] * m[14] -
+ m[8] * m[2] * m[15] +
+ m[8] * m[3] * m[14] +
+ m[12] * m[2] * m[11] -
+ m[12] * m[3] * m[10];
+
+ inv[9] = -m[0] * m[9] * m[15] +
+ m[0] * m[11] * m[13] +
+ m[8] * m[1] * m[15] -
+ m[8] * m[3] * m[13] -
+ m[12] * m[1] * m[11] +
+ m[12] * m[3] * m[9];
+
+ inv[13] = m[0] * m[9] * m[14] -
+ m[0] * m[10] * m[13] -
+ m[8] * m[1] * m[14] +
+ m[8] * m[2] * m[13] +
+ m[12] * m[1] * m[10] -
+ m[12] * m[2] * m[9];
+
+ inv[2] = m[1] * m[6] * m[15] -
+ m[1] * m[7] * m[14] -
+ m[5] * m[2] * m[15] +
+ m[5] * m[3] * m[14] +
+ m[13] * m[2] * m[7] -
+ m[13] * m[3] * m[6];
+
+ inv[6] = -m[0] * m[6] * m[15] +
+ m[0] * m[7] * m[14] +
+ m[4] * m[2] * m[15] -
+ m[4] * m[3] * m[14] -
+ m[12] * m[2] * m[7] +
+ m[12] * m[3] * m[6];
+
+ inv[10] = m[0] * m[5] * m[15] -
+ m[0] * m[7] * m[13] -
+ m[4] * m[1] * m[15] +
+ m[4] * m[3] * m[13] +
+ m[12] * m[1] * m[7] -
+ m[12] * m[3] * m[5];
+
+ inv[14] = -m[0] * m[5] * m[14] +
+ m[0] * m[6] * m[13] +
+ m[4] * m[1] * m[14] -
+ m[4] * m[2] * m[13] -
+ m[12] * m[1] * m[6] +
+ m[12] * m[2] * m[5];
+
+ inv[3] = -m[1] * m[6] * m[11] +
+ m[1] * m[7] * m[10] +
+ m[5] * m[2] * m[11] -
+ m[5] * m[3] * m[10] -
+ m[9] * m[2] * m[7] +
+ m[9] * m[3] * m[6];
+
+ inv[7] = m[0] * m[6] * m[11] -
+ m[0] * m[7] * m[10] -
+ m[4] * m[2] * m[11] +
+ m[4] * m[3] * m[10] +
+ m[8] * m[2] * m[7] -
+ m[8] * m[3] * m[6];
+
+ inv[11] = -m[0] * m[5] * m[11] +
+ m[0] * m[7] * m[9] +
+ m[4] * m[1] * m[11] -
+ m[4] * m[3] * m[9] -
+ m[8] * m[1] * m[7] +
+ m[8] * m[3] * m[5];
+
+ inv[15] = m[0] * m[5] * m[10] -
+ m[0] * m[6] * m[9] -
+ m[4] * m[1] * m[10] +
+ m[4] * m[2] * m[9] +
+ m[8] * m[1] * m[6] -
+ m[8] * m[2] * m[5];
+
+ det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
+
+ if (det == 0) {
+ return false;
+ }
+
+ det = 1.0 / det;
+
+ for (i = 0; i < 16; i++) {
+ invOut[i] = inv[i] * det;
+ }
+
+ return true;
+}
diff --git a/soh/soh/frame_interpolation.h b/soh/soh/frame_interpolation.h
new file mode 100644
index 000000000..64c7197cd
--- /dev/null
+++ b/soh/soh/frame_interpolation.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#include "include/z64math.h"
+
+#ifdef __cplusplus
+
+#include <unordered_map>
+
+std::unordered_map<Mtx*, MtxF> FrameInterpolation_Interpolate(float step);
+
+extern "C" {
+
+#endif
+
+void FrameInterpolation_StartRecord(void);
+
+void FrameInterpolation_StopRecord(void);
+
+void FrameInterpolation_RecordOpenChild(const void* a, int b);
+
+void FrameInterpolation_RecordCloseChild(void);
+
+void FrameInterpolation_DontInterpolateCamera(void);
+
+int FrameInterpolation_GetCameraEpoch(void);
+
+void FrameInterpolation_RecordActorPosRotMatrix(void);
+
+void FrameInterpolation_RecordMatrixPush(void);
+
+void FrameInterpolation_RecordMatrixPop(void);
+
+void FrameInterpolation_RecordMatrixPut(MtxF* src);
+
+void FrameInterpolation_RecordMatrixMult(MtxF* mf, u8 mode);
+
+void FrameInterpolation_RecordMatrixTranslate(f32 x, f32 y, f32 z, u8 mode);
+
+void FrameInterpolation_RecordMatrixScale(f32 x, f32 y, f32 z, u8 mode);
+
+void FrameInterpolation_RecordMatrixRotate1Coord(u32 coord, f32 value, u8 mode);
+
+void FrameInterpolation_RecordMatrixRotateZYX(s16 x, s16 y, s16 z, u8 mode);
+
+void FrameInterpolation_RecordMatrixTranslateRotateZYX(Vec3f* translation, Vec3s* rotation);
+
+void FrameInterpolation_RecordMatrixSetTranslateRotateYXZ(f32 translateX, f32 translateY, f32 translateZ, Vec3s* rot);
+
+void FrameInterpolation_RecordMatrixMtxFToMtx(MtxF* src, Mtx* dest);
+
+void FrameInterpolation_RecordMatrixToMtx(Mtx* dest, char* file, s32 line);
+
+void FrameInterpolation_RecordMatrixReplaceRotation(MtxF* mf);
+
+void FrameInterpolation_RecordMatrixRotateAxis(f32 angle, Vec3f* axis, u8 mode);
+
+void FrameInterpolation_RecordSkinMatrixMtxFToMtx(MtxF* src, Mtx* dest);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/soh/src/code/sys_matrix.c b/soh/src/code/sys_matrix.c
index 9d7c73967..bc90bf669 100644
--- a/soh/src/code/sys_matrix.c
+++ b/soh/src/code/sys_matrix.c
@@ -1,5 +1,7 @@
#include "global.h"
+#include "soh/frame_interpolation.h"
+
// clang-format off
Mtx gMtxClear = {
65536, 0, 1, 0,
@@ -25,11 +27,13 @@ void Matrix_Init(GameState* gameState) {
}
void Matrix_Push(void) {
+ FrameInterpolation_RecordMatrixPush();
Matrix_MtxFCopy(sCurrentMatrix + 1, sCurrentMatrix);
sCurrentMatrix++;
}
void Matrix_Pop(void) {
+ FrameInterpolation_RecordMatrixPop();
sCurrentMatrix--;
ASSERT(sCurrentMatrix >= sMatrixStack, "Matrix_now >= Matrix_stack", "../sys_matrix.c", 176);
}
@@ -39,6 +43,7 @@ void Matrix_Get(MtxF* dest) {
}
void Matrix_Put(MtxF* src) {
+ FrameInterpolation_RecordMatrixPut(src);
Matrix_MtxFCopy(sCurrentMatrix, src);
}
@@ -47,6 +52,7 @@ MtxF* Matrix_GetCurrent(void) {
}
void Matrix_Mult(MtxF* mf, u8 mode) {
+ FrameInterpolation_RecordMatrixMult(mf, mode);
MtxF* cmf = Matrix_GetCurrent();
if (mode == MTXMODE_APPLY) {
@@ -57,6 +63,7 @@ void Matrix_Mult(MtxF* mf, u8 mode) {
}
void Matrix_Translate(f32 x, f32 y, f32 z, u8 mode) {
+ FrameInterpolation_RecordMatrixTranslate(x, y, z, mode);
MtxF* cmf = sCurrentMatrix;
f32 tx;
f32 ty;
@@ -80,6 +87,7 @@ void Matrix_Translate(f32 x, f32 y, f32 z, u8 mode) {
}
void Matrix_Scale(f32 x, f32 y, f32 z, u8 mode) {
+ FrameInterpolation_RecordMatrixScale(x, y, z, mode);
MtxF* cmf = sCurrentMatrix;
if (mode == MTXMODE_APPLY) {
@@ -101,6 +109,7 @@ void Matrix_Scale(f32 x, f32 y, f32 z, u8 mode) {
}
void Matrix_RotateX(f32 x, u8 mode) {
+ FrameInterpolation_RecordMatrixRotate1Coord(0, x, mode);
MtxF* cmf;
f32 sin;
f32 cos;
@@ -165,6 +174,7 @@ void Matrix_RotateX(f32 x, u8 mode) {
}
void Matrix_RotateY(f32 y, u8 mode) {
+ FrameInterpolation_RecordMatrixRotate1Coord(1, y, mode);
MtxF* cmf;
f32 sin;
f32 cos;
@@ -229,6 +239,7 @@ void Matrix_RotateY(f32 y, u8 mode) {
}
void Matrix_RotateZ(f32 z, u8 mode) {
+ FrameInterpolation_RecordMatrixRotate1Coord(2, z, mode);
MtxF* cmf;
f32 sin;
f32 cos;
@@ -299,6 +310,7 @@ void Matrix_RotateZ(f32 z, u8 mode) {
* Original Name: Matrix_RotateXYZ, changed to reflect rotation order.
*/
void Matrix_RotateZYX(s16 x, s16 y, s16 z, u8 mode) {
+ FrameInterpolation_RecordMatrixRotateZYX(x, y, z, mode);
MtxF* cmf = sCurrentMatrix;
f32 temp1;
f32 temp2;
@@ -389,6 +401,7 @@ void Matrix_RotateZYX(s16 x, s16 y, s16 z, u8 mode) {
* transformed according to whatever the matrix was previously.
*/
void Matrix_TranslateRotateZYX(Vec3f* translation, Vec3s* rotation) {
+ FrameInterpolation_RecordMatrixTranslateRotateZYX(translation, rotation);
MtxF* cmf = sCurrentMatrix;
f32 sin = Math_SinS(rotation->z);
f32 cos = Math_CosS(rotation->z);
@@ -530,15 +543,20 @@ void Matrix_SetTranslateRotateYXZ(f32 translateX, f32 translateY, f32 translateZ
} else {
cmf->yx = 0.0f;
}
+ FrameInterpolation_RecordMatrixSetTranslateRotateYXZ(translateX, translateY, translateZ, rot);
}
Mtx* Matrix_MtxFToMtx(MtxF* src, Mtx* dest) {
+ FrameInterpolation_RecordMatrixMtxFToMtx(src, dest);
guMtxF2L(src, dest);
return dest;
}
Mtx* Matrix_ToMtx(Mtx* dest, char* file, s32 line) {
- return Matrix_MtxFToMtx(Matrix_CheckFloats(sCurrentMatrix, file, line), dest);
+ FrameInterpolation_RecordMatrixToMtx(dest, file, line);
+ guMtxF2L(Matrix_CheckFloats(sCurrentMatrix, file, line), dest);
+ return dest;
+ //return Matrix_MtxFToMtx(Matrix_CheckFloats(sCurrentMatrix, file, line), dest);
}
Mtx* Matrix_NewMtx(GraphicsContext* gfxCtx, char* file, s32 line) {
@@ -627,6 +645,7 @@ void Matrix_Transpose(MtxF* mf) {
* seen as replacing the R rotation with `mf`, hence the function name.
*/
void Matrix_ReplaceRotation(MtxF* mf) {
+ FrameInterpolation_RecordMatrixReplaceRotation(mf);
MtxF* cmf = sCurrentMatrix;
f32 acc;
f32 temp;
@@ -779,6 +798,7 @@ void Matrix_MtxFToZYXRotS(MtxF* mf, Vec3s* rotDest, s32 flag) {
* NB: `axis` is assumed to be a unit vector.
*/
void Matrix_RotateAxis(f32 angle, Vec3f* axis, u8 mode) {
+ FrameInterpolation_RecordMatrixRotateAxis(angle, axis, mode);
MtxF* cmf;
f32 sin;
f32 cos;
diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c
index a9f50f687..caa16a7ec 100644
--- a/soh/src/code/z_actor.c
+++ b/soh/src/code/z_actor.c
@@ -6,6 +6,7 @@
#include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h"
#include "objects/object_bdoor/object_bdoor.h"
+#include "soh/frame_interpolation.h"
#if defined(_MSC_VER) || defined(__GNUC__)
#include <string.h>
@@ -410,6 +411,7 @@ void func_8002C124(TargetContext* targetCtx, GlobalContext* globalCtx) {
f32 var2;
s32 i;
+ FrameInterpolation_RecordOpenChild(actor, 0);
player = GET_PLAYER(globalCtx);
spCE = 0xFF;
@@ -486,10 +488,12 @@ void func_8002C124(TargetContext* targetCtx, GlobalContext* globalCtx) {
}
}
}
+ FrameInterpolation_RecordCloseChild();
}
actor = targetCtx->unk_94;
if ((actor != NULL) && !(actor->flags & ACTOR_FLAG_27)) {
+ FrameInterpolation_RecordOpenChild(actor, 1);
NaviColor* naviColor = &sNaviColorList[actor->category];
POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x7);
@@ -503,6 +507,7 @@ void func_8002C124(TargetContext* targetCtx, GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_actor.c", 2153),
G_MTX_MODELVIEW | G_MTX_LOAD);
gSPDisplayList(POLY_XLU_DISP++, gZTargetArrowDL);
+ FrameInterpolation_RecordCloseChild();
}
CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 2158);
@@ -2490,6 +2495,7 @@ void Actor_Draw(GlobalContext* globalCtx, Actor* actor) {
Fault_AddClient(&faultClient, Actor_FaultPrint, actor, "Actor_draw");
+ FrameInterpolation_RecordOpenChild(actor, 0);
OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 6035);
lights = LightContext_NewLights(&globalCtx->lightCtx, globalCtx->state.gfxCtx);
@@ -2497,6 +2503,7 @@ void Actor_Draw(GlobalContext* globalCtx, Actor* actor) {
Lights_BindAll(lights, globalCtx->lightCtx.listHead, (actor->flags & ACTOR_FLAG_22) ? NULL : &actor->world.pos);
Lights_Draw(lights, globalCtx->state.gfxCtx);
+ FrameInterpolation_RecordActorPosRotMatrix();
if (actor->flags & ACTOR_FLAG_12) {
Matrix_SetTranslateRotateYXZ(
actor->world.pos.x + globalCtx->mainCamera.skyboxOffset.x,
@@ -2546,6 +2553,7 @@ void Actor_Draw(GlobalContext* globalCtx, Actor* actor) {
}
CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 6119);
+ FrameInterpolation_RecordCloseChild();
Fault_RemoveClient(&faultClient);
}
diff --git a/soh/src/code/z_camera.c b/soh/src/code/z_camera.c
index 41c16b5cc..00f3f70f0 100644
--- a/soh/src/code/z_camera.c
+++ b/soh/src/code/z_camera.c
@@ -6,6 +6,8 @@
#include "overlays/actors/ovl_En_Horse/z_en_horse.h"
+#include "soh/frame_interpolation.h"
+
s16 Camera_ChangeSettingFlags(Camera* camera, s16 setting, s16 flags);
s32 Camera_ChangeModeFlags(Camera* camera, s16 mode, u8 flags);
s32 Camera_QRegInit(void);
@@ -6675,6 +6677,7 @@ s32 Camera_Special9(Camera* camera) {
case 1:
spec9->doorParams.timer1--;
if (spec9->doorParams.timer1 <= 0) {
+ FrameInterpolation_DontInterpolateCamera();
camera->animState++;
if (params->interfaceFlags & 1) {
camPosData = Camera_GetCamBGData(camera);
@@ -7968,6 +7971,8 @@ s32 Camera_SetCSParams(Camera* camera, CutsceneCameraPoint* atPoints, CutsceneCa
camera->speedRatio = 0.0f;
}
+ FrameInterpolation_DontInterpolateCamera();
+
return 1;
}
diff --git a/soh/src/code/z_eff_blure.c b/soh/src/code/z_eff_blure.c
index e08f8f337..f3a1e6122 100644
--- a/soh/src/code/z_eff_blure.c
+++ b/soh/src/code/z_eff_blure.c
@@ -1,6 +1,8 @@
#include "global.h"
#include "objects/gameplay_keep/gameplay_keep.h"
+#include "soh/frame_interpolation.h"
+
void EffectBlure_AddVertex(EffectBlure* this, Vec3f* p1, Vec3f* p2) {
EffectBlureElement* elem;
s32 numElements;
@@ -946,6 +948,7 @@ void EffectBlure_Draw(void* thisx, GraphicsContext* gfxCtx) {
s32 j;
s32 phi_t2;
+ FrameInterpolation_RecordOpenChild(this, 0);
OPEN_DISPS(gfxCtx, "../z_eff_blure.c", 1596);
gSPMatrix(POLY_XLU_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
@@ -1059,4 +1062,5 @@ void EffectBlure_Draw(void* thisx, GraphicsContext* gfxCtx) {
}
CLOSE_DISPS(gfxCtx, "../z_eff_blure.c", 1823);
+ FrameInterpolation_RecordCloseChild();
}
diff --git a/soh/src/code/z_eff_shield_particle.c b/soh/src/code/z_eff_shield_particle.c
index fad499286..73134a654 100644
--- a/soh/src/code/z_eff_shield_particle.c
+++ b/soh/src/code/z_eff_shield_particle.c
@@ -2,6 +2,8 @@
#include "vt.h"
#include "objects/gameplay_keep/gameplay_keep.h"
+#include "soh/frame_interpolation.h"
+
static Vtx sVertices[5] = {
VTX(-32, -32, 0, 0, 1024, 0xFF, 0xFF, 0xFF, 0xFF),
VTX(32, 32, 0, 1024, 0, 0xFF, 0xFF, 0xFF, 0xFF),
@@ -154,6 +156,7 @@ void EffectShieldParticle_Draw(void* thisx, GraphicsContext* gfxCtx) {
Color_RGBA8 primColor;
Color_RGBA8 envColor;
+ FrameInterpolation_RecordOpenChild(this, 0);
OPEN_DISPS(gfxCtx, "../z_eff_shield_particle.c", 272);
if (this != NULL) {
@@ -213,4 +216,5 @@ void EffectShieldParticle_Draw(void* thisx, GraphicsContext* gfxCtx) {
}
CLOSE_DISPS(gfxCtx, "../z_eff_shield_particle.c", 359);
+ FrameInterpolation_RecordCloseChild();
}
diff --git a/soh/src/code/z_eff_spark.c b/soh/src/code/z_eff_spark.c
index cfe8f628f..b76968371 100644
--- a/soh/src/code/z_eff_spark.c
+++ b/soh/src/code/z_eff_spark.c
@@ -1,6 +1,8 @@
#include "global.h"
#include "objects/gameplay_keep/gameplay_keep.h"
+#include "soh/frame_interpolation.h"
+
// original name: "spark"
void EffectSpark_Init(void* thisx, void* initParamsx) {
EffectSpark* this = (EffectSpark*)thisx;
@@ -152,6 +154,7 @@ void EffectSpark_Draw(void* thisx, GraphicsContext* gfxCtx) {
u8 sp1C4;
f32 ratio;
+ FrameInterpolation_RecordOpenChild(this, 0);
OPEN_DISPS(gfxCtx, "../z_eff_spark.c", 293);
if (this != NULL) {
@@ -274,4 +277,5 @@ void EffectSpark_Draw(void* thisx, GraphicsContext* gfxCtx) {
end:
CLOSE_DISPS(gfxCtx, "../z_eff_spark.c", 498);
+ FrameInterpolation_RecordCloseChild();
}
diff --git a/soh/src/code/z_effect_soft_sprite.c b/soh/src/code/z_effect_soft_sprite.c
index aa0b922fe..2548966cc 100644
--- a/soh/src/code/z_effect_soft_sprite.c
+++ b/soh/src/code/z_effect_soft_sprite.c
@@ -1,6 +1,8 @@
#include "global.h"
#include "vt.h"
+#include "soh/frame_interpolation.h"
+
EffectSsInfo sEffectSsInfo = { 0 }; // "EffectSS2Info"
void EffectSs_InitInfo(GlobalContext* globalCtx, s32 tableSize) {
@@ -233,6 +235,7 @@ void EffectSs_Spawn(GlobalContext* globalCtx, s32 type, s32 priority, void* init
sEffectSsInfo.table[index].type = type;
sEffectSsInfo.table[index].priority = priority;
+ sEffectSsInfo.table[index].epoch++;
if (initInfo->init(globalCtx, index, &sEffectSsInfo.table[index], initParams) == 0) {
osSyncPrintf(VT_FGCOL(GREEN));
@@ -284,7 +287,9 @@ void EffectSs_Draw(GlobalContext* globalCtx, s32 index) {
EffectSs* effectSs = &sEffectSsInfo.table[index];
if (effectSs->draw != NULL) {
+ FrameInterpolation_RecordOpenChild(effectSs, effectSs->epoch);
effectSs->draw(globalCtx, index, effectSs);
+ FrameInterpolation_RecordCloseChild();
}
}
diff --git a/soh/src/code/z_lights.c b/soh/src/code/z_lights.c
index c9b60ceca..bee677276 100644
--- a/soh/src/code/z_lights.c
+++ b/soh/src/code/z_lights.c
@@ -4,6 +4,8 @@
#include "objects/gameplay_keep/gameplay_keep.h"
+#include "soh/frame_interpolation.h"
+
#define LIGHTS_BUFFER_SIZE 32
//#define LIGHTS_BUFFER_SIZE 1024 // Kill me
@@ -434,12 +436,14 @@ void Lights_DrawGlow(GlobalContext* globalCtx) {
if ((info->type == LIGHT_POINT_GLOW) && (params->drawGlow)) {
scale = SQ(params->radius) * 0.0000026f;
+ FrameInterpolation_RecordOpenChild(node, 0);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, params->color[0], params->color[1], params->color[2], 50);
Matrix_Translate(params->x, params->y, params->z, MTXMODE_NEW);
Matrix_Scale(scale, scale, scale, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_lights.c", 918),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gGlowCircleDL);
+ FrameInterpolation_RecordCloseChild();
}
node = node->next;
diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c
index 2934e5e1b..632ed41f6 100644
--- a/soh/src/code/z_play.c
+++ b/soh/src/code/z_play.c
@@ -5,6 +5,8 @@
#include "soh/Enhancements/gameconsole.h"
+#include "soh/frame_interpolation.h"
+
void* D_8012D1F0 = NULL;
//UNK_TYPE D_8012D1F4 = 0; // unused
Input* D_8012D1F8 = NULL;
@@ -1379,7 +1381,9 @@ void Gameplay_Main(GameState* thisx) {
LOG_NUM("1", 1, "../z_play.c", 4583);
}
+ FrameInterpolation_StartRecord();
Gameplay_Draw(globalCtx);
+ FrameInterpolation_StopRecord();
if (1 && HREG(63)) {
LOG_NUM("1", 1, "../z_play.c", 4587);
diff --git a/soh/src/code/z_skin_matrix.c b/soh/src/code/z_skin_matrix.c
index f7df8091f..d822d6c13 100644
--- a/soh/src/code/z_skin_matrix.c
+++ b/soh/src/code/z_skin_matrix.c
@@ -1,6 +1,8 @@
#include "global.h"
#include "vt.h"
+#include "soh/frame_interpolation.h"
+
// clang-format off
MtxF sMtxFClear = {
1.0f, 0.0f, 0.0f, 0.0f,
@@ -523,6 +525,7 @@ void SkinMatrix_Vec3sToVec3f(Vec3s* src, Vec3f* dest) {
}
void SkinMatrix_MtxFToMtx(MtxF* src, Mtx* dest) {
+ FrameInterpolation_RecordSkinMatrixMtxFToMtx(src, dest);
guMtxF2L(src, dest);
}
diff --git a/soh/src/code/z_view.c b/soh/src/code/z_view.c
index 97f1ad26e..e03daee83 100644
--- a/soh/src/code/z_view.c
+++ b/soh/src/code/z_view.c
@@ -2,6 +2,9 @@
#include "vt.h"
#include <string.h>
+#include <math.h>
+
+#include "soh/frame_interpolation.h"
vu32 D_8012ABF0 = true;
@@ -277,6 +280,10 @@ void func_800AAA50(View* view, s32 arg1) {
}
}
+static float sqr(float a) {
+ return a * a;
+}
+
s32 func_800AAA9C(View* view) {
f32 aspect;
s32 width;
@@ -307,6 +314,85 @@ s32 func_800AAA9C(View* view) {
height = view->viewport.bottomY - view->viewport.topY;
aspect = (f32)width / (f32)height;
+ viewing = Graph_Alloc(gfxCtx, sizeof(Mtx));
+ LogUtils_CheckNullPointer("viewing", viewing, "../z_view.c", 667);
+ view->viewingPtr = viewing;
+
+ if (view->eye.x == view->lookAt.x && view->eye.y == view->lookAt.y && view->eye.z == view->lookAt.z) {
+ view->eye.x += 1.0f;
+ view->eye.y += 1.0f;
+ view->eye.z += 1.0f;
+ }
+
+ func_800ABE74(view->eye.x, view->eye.y, view->eye.z);
+ MtxF viewingF;
+ guLookAtF(viewingF.mf, view->eye.x, view->eye.y, view->eye.z, view->lookAt.x, view->lookAt.y, view->lookAt.z, view->up.x,
+ view->up.y, view->up.z);
+
+ // Some heuristics to identify instant camera movements and skip interpolation in that case
+
+ static View old_view;
+
+ float dirx = view->eye.x - view->lookAt.x;
+ float diry = view->eye.y - view->lookAt.y;
+ float dirz = view->eye.z - view->lookAt.z;
+ float dir_dist = sqrtf(sqr(dirx) + sqr(diry) + sqr(dirz));
+ dirx /= dir_dist;
+ diry /= dir_dist;
+ dirz /= dir_dist;
+
+ float odirx = old_view.eye.x - old_view.lookAt.x;
+ float odiry = old_view.eye.y - old_view.lookAt.y;
+ float odirz = old_view.eye.z - old_view.lookAt.z;
+ float odir_dist = sqrtf(sqr(odirx) + sqr(odiry) + sqr(odirz));
+ odirx /= odir_dist;
+ odiry /= odir_dist;
+ odirz /= odir_dist;
+
+ float eye_dist = sqrtf(sqr(view->eye.x - old_view.eye.x) + sqr(view->eye.y - old_view.eye.y) + sqr(view->eye.z - old_view.eye.z));
+ float look_dist = sqrtf(sqr(view->lookAt.x - old_view.lookAt.x) + sqr(view->lookAt.y - old_view.lookAt.y) + sqr(view->lookAt.z - old_view.lookAt.z));
+ float up_dist = sqrtf(sqr(view->up.x - old_view.up.x) + sqr(view->up.y - old_view.up.y) + sqr(view->up.z - old_view.up.z));
+ float d_dist = sqrtf(sqr(dirx - odirx) + sqr(diry - odiry) + sqr(dirz - odirz));
+
+ bool dont_interpolate = false;
+
+ if (up_dist < 0.01 && d_dist < 0.01) {
+ if (eye_dist + look_dist > 300) {
+ dont_interpolate = true;
+ }
+ } else {
+ if (eye_dist >= 400) {
+ dont_interpolate = true;
+ }
+ if (look_dist >= 100) {
+ dont_interpolate = true;
+ }
+ if (up_dist >= 1.50f) {
+ dont_interpolate = true;
+ }
+ if (d_dist >= 1.414f && look_dist >= 15) {
+ dont_interpolate = true;
+ }
+ if (d_dist >= 1.414f && up_dist >= 0.31f && look_dist >= 1 && eye_dist >= 300) {
+ dont_interpolate = true;
+ }
+ if (d_dist >= 0.5f && up_dist >= 0.31f && look_dist >= 3 && eye_dist >= 170) {
+ dont_interpolate = true;
+ }
+ if (look_dist >= 52 && eye_dist >= 52) {
+ dont_interpolate = true;
+ }
+ if (look_dist >= 30 && eye_dist >= 90) {
+ dont_interpolate = true;
+ }
+ }
+
+ if (dont_interpolate) {
+ FrameInterpolation_DontInterpolateCamera();
+ }
+
+ FrameInterpolation_RecordOpenChild(NULL, FrameInterpolation_GetCameraEpoch());
+
if (HREG(80) == 11) {
if (HREG(94) != 11) {
HREG(94) = 11;
@@ -347,21 +433,16 @@ s32 func_800AAA9C(View* view) {
gSPPerspNormalize(POLY_KAL_DISP++, view->normal);
gSPMatrix(POLY_KAL_DISP++, projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION);
- viewing = Graph_Alloc(gfxCtx, sizeof(Mtx));
- LogUtils_CheckNullPointer("viewing", viewing, "../z_view.c", 667);
- view->viewingPtr = viewing;
+ Matrix_MtxFToMtx(viewingF.mf, viewing);
- if (view->eye.x == view->lookAt.x && view->eye.y == view->lookAt.y && view->eye.z == view->lookAt.z) {
- view->eye.x += 1.0f;
- view->eye.y += 1.0f;
- view->eye.z += 1.0f;
- }
+ view->viewing = *viewing;
- func_800ABE74(view->eye.x, view->eye.y, view->eye.z);
- guLookAt(viewing, view->eye.x, view->eye.y, view->eye.z, view->lookAt.x, view->lookAt.y, view->lookAt.z, view->up.x,
- view->up.y, view->up.z);
- view->viewing = *viewing;
+ /*if (eye_dist > 1 || look_dist > 1 || abs(up_dist) > 0.1 || abs(d_dist) > 0.1)
+ printf("%d %f %f %f, %f %f %f, %f %f %f, %f %f %f %f %d\n", (int)view->fovy, view->eye.x, view->eye.y, view->eye.z, view->lookAt.x, view->lookAt.y, view->lookAt.z,
+ view->up.x, view->up.y, view->up.z, eye_dist, look_dist, up_dist, d_dist, dont_interpolate);*/
+
+ old_view = *view;
if (QREG(88) & 2) {
s32 i;
@@ -374,10 +455,10 @@ s32 func_800AAA9C(View* view) {
}
osSyncPrintf("\n");
}
-
gSPMatrix(POLY_OPA_DISP++, viewing, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION);
gSPMatrix(POLY_XLU_DISP++, viewing, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION);
gSPMatrix(POLY_KAL_DISP++, viewing, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION);
+ FrameInterpolation_RecordCloseChild();
CLOSE_DISPS(gfxCtx, "../z_view.c", 711);
diff --git a/soh/src/code/z_vr_box_draw.c b/soh/src/code/z_vr_box_draw.c
index 292789333..13eb93bd0 100644
--- a/soh/src/code/z_vr_box_draw.c
+++ b/soh/src/code/z_vr_box_draw.c
@@ -1,5 +1,7 @@
#include "global.h"
+#include "soh/frame_interpolation.h"
+
Mtx* sSkyboxDrawMatrix;
Mtx* SkyboxDraw_UpdateMatrix(SkyboxContext* skyboxCtx, f32 x, f32 y, f32 z) {
@@ -13,6 +15,7 @@ Mtx* SkyboxDraw_UpdateMatrix(SkyboxContext* skyboxCtx, f32 x, f32 y, f32 z) {
void SkyboxDraw_Draw(SkyboxContext* skyboxCtx, GraphicsContext* gfxCtx, s16 skyboxId, s16 blend, f32 x, f32 y, f32 z) {
OPEN_DISPS(gfxCtx, "../z_vr_box_draw.c", 52);
+ FrameInterpolation_RecordOpenChild(NULL, FrameInterpolation_GetCameraEpoch());
func_800945A0(gfxCtx);
@@ -85,7 +88,7 @@ void SkyboxDraw_Draw(SkyboxContext* skyboxCtx, GraphicsContext* gfxCtx, s16 skyb
gDPPipeSync(POLY_OPA_DISP++);
//gsSPShaderTest2(POLY_OPA_DISP++);
-
+ FrameInterpolation_RecordCloseChild();
CLOSE_DISPS(gfxCtx, "../z_vr_box_draw.c", 125);
}
diff --git a/soh/src/libultra/gu/guLookAt.c b/soh/src/libultra/gu/guLookAt.c
index 58946f4dc..f94204bc4 100644
--- a/soh/src/libultra/gu/guLookAt.c
+++ b/soh/src/libultra/gu/guLookAt.c
@@ -62,5 +62,6 @@ void guLookAt(Mtx* m, f32 xEye, f32 yEye, f32 zEye, f32 xAt, f32 yAt, f32 zAt, f
guLookAtF(mf, xEye, yEye, zEye, xAt, yAt, zAt, xUp, yUp, zUp);
- guMtxF2L((MtxF*)mf, m);
+ //guMtxF2L((MtxF*)mf, m);
+ Matrix_MtxFToMtx((MtxF*)mf, m);
}
diff --git a/soh/src/libultra/gu/guPerspectiveF.c b/soh/src/libultra/gu/guPerspectiveF.c
index 731cec3e9..dd020f589 100644
--- a/soh/src/libultra/gu/guPerspectiveF.c
+++ b/soh/src/libultra/gu/guPerspectiveF.c
@@ -37,6 +37,6 @@ void guPerspective(Mtx* m, u16* perspNorm, f32 fovy, f32 aspect, f32 near, f32 f
f32 mf[4][4];
guPerspectiveF(mf, perspNorm, fovy, aspect, near, far, scale);
- guMtxF2L((MtxF*)mf, m);
-
+ //guMtxF2L((MtxF*)mf, m);
+ Matrix_MtxFToMtx((MtxF*)mf, m);
}
diff --git a/soh/src/libultra/gu/ortho.c b/soh/src/libultra/gu/ortho.c
index 517ba1dcc..77d43ce21 100644
--- a/soh/src/libultra/gu/ortho.c
+++ b/soh/src/libultra/gu/ortho.c
@@ -1,5 +1,7 @@
#include "global.h"
+#include "soh/frame_interpolation.h"
+
void guOrthoF(f32 mf[4][4], f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far, f32 scale) {
s32 i, j;
@@ -25,5 +27,8 @@ void guOrtho(Mtx* mtx, f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 f
guOrthoF(mf, left, right, bottom, top, near, far, scale);
- guMtxF2L((MtxF*)mf, mtx);
+ //guMtxF2L((MtxF*)mf, mtx);
+ FrameInterpolation_RecordOpenChild("ortho", 0);
+ Matrix_MtxFToMtx((MtxF*)mf, mtx);
+ FrameInterpolation_RecordCloseChild();
}
diff --git a/soh/src/overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.c b/soh/src/overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.c
index d1e940619..9616204cd 100644
--- a/soh/src/overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.c
+++ b/soh/src/overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.c
@@ -8,6 +8,8 @@
#include "overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h"
#include "vt.h"
+#include "soh/frame_interpolation.h"
+
#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5)
typedef enum {
@@ -283,6 +285,7 @@ void BgGanonOtyuka_Draw(Actor* thisx, GlobalContext* globalCtx) {
platform = (BgGanonOtyuka*)actor;
if (platform->dyna.actor.projectedPos.z > spBC) {
+ FrameInterpolation_RecordOpenChild(platform, 0);
if (camera->eye.y > platform->dyna.actor.world.pos.y) {
phi_s2 = sPlatformTopDL;
} else {
@@ -309,7 +312,7 @@ void BgGanonOtyuka_Draw(Actor* thisx, GlobalContext* globalCtx) {
}
for (i = 0; i < ARRAY_COUNT(sSides); i++) {
- if (platform->visibleSides & sSides[i]) {
+ if ((platform->visibleSides & sSides[i]) || 1) { // || 1 for frame interpolation
Matrix_Push();
Matrix_Translate(sSideCenters[i].x, 0.0f, sSideCenters[i].z, MTXMODE_APPLY);
Matrix_RotateY(sSideAngles[i], MTXMODE_APPLY);
@@ -320,6 +323,7 @@ void BgGanonOtyuka_Draw(Actor* thisx, GlobalContext* globalCtx) {
Matrix_Pop();
}
}
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -333,6 +337,7 @@ void BgGanonOtyuka_Draw(Actor* thisx, GlobalContext* globalCtx) {
platform = (BgGanonOtyuka*)actor;
if ((platform->dyna.actor.projectedPos.z > -30.0f) && (platform->flashState != FLASH_NONE)) {
+ FrameInterpolation_RecordOpenChild(platform, 0);
gSPSegment(POLY_XLU_DISP++, 0x08,
Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, platform->flashTimer * 4, 0, 32, 64, 1,
platform->flashTimer * 4, 0, 32, 64));
@@ -344,7 +349,7 @@ void BgGanonOtyuka_Draw(Actor* thisx, GlobalContext* globalCtx) {
Matrix_Translate(platform->dyna.actor.world.pos.x, 0.0f, platform->dyna.actor.world.pos.z, MTXMODE_NEW);
for (i = 0; i < ARRAY_COUNT(sSides); i++) {
- if (platform->unwalledSides & sSides[i]) {
+ if ((platform->unwalledSides & sSides[i]) || 1) { // || 1 for frame interpolation
Matrix_Push();
Matrix_Translate(sSideCenters[i].x, 0.0f, sSideCenters[i].z, MTXMODE_APPLY);
Matrix_RotateY(sSideAngles[i], MTXMODE_APPLY);
@@ -356,6 +361,7 @@ void BgGanonOtyuka_Draw(Actor* thisx, GlobalContext* globalCtx) {
Matrix_Pop();
}
}
+ FrameInterpolation_RecordCloseChild();
}
}
diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Rsekizou/z_bg_hidan_rsekizou.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Rsekizou/z_bg_hidan_rsekizou.c
index 73044e8b1..c2599e185 100644
--- a/soh/src/overlays/actors/ovl_Bg_Hidan_Rsekizou/z_bg_hidan_rsekizou.c
+++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Rsekizou/z_bg_hidan_rsekizou.c
@@ -240,7 +240,8 @@ void BgHidanRsekizou_Draw(Actor* thisx, GlobalContext* globalCtx) {
POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x14);
- if ((s16)((Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - this->dyna.actor.shape.rot.y) - 0x2E6C) >= 0) {
+ // Strange original code. Add || 1 for frame interpolation to get correct.
+ if ((s16)((Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - this->dyna.actor.shape.rot.y) - 0x2E6C) >= 0 || 1) {
for (i = 3; i >= 0; i--) {
POLY_XLU_DISP = BgHidanRsekizou_DrawFireball(globalCtx, this, i, &mf, 0, POLY_XLU_DISP);
}
diff --git a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c
index 649774346..5b468c7d3 100644
--- a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c
+++ b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c
@@ -12,6 +12,8 @@
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "objects/gameplay_keep/gameplay_keep.h"
+#include "soh/frame_interpolation.h"
+
#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5)
typedef enum {
@@ -78,6 +80,7 @@ void BossFd_SpawnEmber(BossFdEffect* effect, Vec3f* position, Vec3f* velocity, V
effect->scale = scale / 1000.0f;
effect->alpha = 255;
effect->timer1 = (s16)Rand_ZeroFloat(10.0f);
+ effect->epoch++;
break;
}
}
@@ -95,6 +98,7 @@ void BossFd_SpawnDebris(BossFdEffect* effect, Vec3f* position, Vec3f* velocity,
effect->scale = scale / 1000.0f;
effect->vFdFxRotX = Rand_ZeroFloat(100.0f);
effect->vFdFxRotY = Rand_ZeroFloat(100.0f);
+ effect->epoch++;
break;
}
}
@@ -111,6 +115,7 @@ void BossFd_SpawnDust(BossFdEffect* effect, Vec3f* position, Vec3f* velocity, Ve
effect->accel = *acceleration;
effect->timer2 = 0;
effect->scale = scale / 400.0f;
+ effect->epoch++;
break;
}
}
@@ -136,6 +141,7 @@ void BossFd_SpawnFireBreath(BossFdEffect* effect, Vec3f* position, Vec3f* veloci
effect->timer2 = 0;
effect->scale = scale / 400.0f;
effect->kbAngle = kbAngle;
+ effect->epoch++;
break;
}
}
@@ -1522,6 +1528,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) {
for (i = 0; i < 180; i++, effect++) {
if (effect->type == BFD_FX_EMBER) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!flag) {
func_80093D84(globalCtx->state.gfxCtx);
gSPDisplayList(POLY_XLU_DISP++, gVolvagiaEmberMaterialDL);
@@ -1536,6 +1543,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_fd.c", 4046),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gVolvagiaEmberModelDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -1543,6 +1551,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) {
flag = false;
for (i = 0; i < 180; i++, effect++) {
if (effect->type == BFD_FX_DEBRIS) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!flag) {
func_80093D18(globalCtx->state.gfxCtx);
gSPDisplayList(POLY_OPA_DISP++, gVolvagiaDebrisMaterialDL);
@@ -1557,6 +1566,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) {
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_fd.c", 4068),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gVolvagiaDebrisModelDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -1564,6 +1574,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) {
flag = false;
for (i = 0; i < 180; i++, effect++) {
if (effect->type == BFD_FX_DUST) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!flag) {
POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0);
gSPDisplayList(POLY_XLU_DISP++, gVolvagiaDustMaterialDL);
@@ -1580,6 +1591,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(dustTex[effect->timer2]));
gSPDisplayList(POLY_XLU_DISP++, gVolvagiaDustModelDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -1587,6 +1599,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) {
flag = false;
for (i = 0; i < 180; i++, effect++) {
if (effect->type == BFD_FX_FIRE_BREATH) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!flag) {
POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0);
gSPDisplayList(POLY_XLU_DISP++, gVolvagiaDustMaterialDL);
@@ -1603,6 +1616,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(dustTex[effect->timer2]));
gSPDisplayList(POLY_XLU_DISP++, gVolvagiaDustModelDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -1610,6 +1624,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) {
flag = false;
for (i = 0; i < 180; i++, effect++) {
if (effect->type == BFD_FX_SKULL_PIECE) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!flag) {
func_80093D84(globalCtx->state.gfxCtx);
gSPDisplayList(POLY_XLU_DISP++, gVolvagiaSkullPieceMaterialDL);
@@ -1624,6 +1639,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_fd.c", 4192),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gVolvagiaSkullPieceModelDL);
+ FrameInterpolation_RecordCloseChild();
}
}
diff --git a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.h b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.h
index ea24fd346..d9580fc28 100644
--- a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.h
+++ b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.h
@@ -49,6 +49,7 @@ typedef struct {
/* 0x30 */ f32 scale;
/* 0x34 */ f32 bFdFxFloat1;
/* 0x38 */ f32 bFdFxFloat2;
+ u32 epoch;
} BossFdEffect; // size = 0x3C
#define BOSSFD_EFFECT_COUNT 180
diff --git a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c
index 20db1fb55..779584e32 100644
--- a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c
+++ b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c
@@ -87,6 +87,7 @@ void BossFd2_SpawnDebris(GlobalContext* globalCtx, BossFdEffect* effect, Vec3f*
effect->scale = scale / 1000.0f;
effect->vFdFxRotX = Rand_ZeroFloat(100.0f);
effect->vFdFxRotY = Rand_ZeroFloat(100.0f);
+ effect->epoch++;
break;
}
}
@@ -112,6 +113,7 @@ void BossFd2_SpawnFireBreath(GlobalContext* globalCtx, BossFdEffect* effect, Vec
effect->timer2 = 0;
effect->scale = scale / 400.0f;
effect->kbAngle = kbAngle;
+ effect->epoch++;
break;
}
}
@@ -130,6 +132,7 @@ void BossFd2_SpawnEmber(GlobalContext* globalCtx, BossFdEffect* effect, Vec3f* p
effect->scale = scale / 1000.0f;
effect->alpha = 255;
effect->timer1 = (s16)Rand_ZeroFloat(10.0f);
+ effect->epoch++;
break;
}
}
@@ -148,6 +151,7 @@ void BossFd2_SpawnSkullPiece(GlobalContext* globalCtx, BossFdEffect* effect, Vec
effect->scale = scale / 1000.0f;
effect->vFdFxRotX = Rand_ZeroFloat(100.0f);
effect->vFdFxRotY = Rand_ZeroFloat(100.0f);
+ effect->epoch++;
break;
}
}
@@ -164,6 +168,7 @@ void BossFd2_SpawnDust(BossFdEffect* effect, Vec3f* position, Vec3f* velocity, V
effect->accel = *acceleration;
effect->timer2 = 0;
effect->scale = scale / 400.0f;
+ effect->epoch++;
break;
}
}
diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c
index f7132bcaf..21199d420 100644
--- a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c
+++ b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c
@@ -9,6 +9,8 @@
#include "assets/objects/object_ganon_anime2/object_ganon_anime2.h"
#include "assets/scenes/dungeons/ganon_boss/ganon_boss_scene.h"
+#include "soh/frame_interpolation.h"
+
#include <string.h>
#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5)
@@ -138,6 +140,7 @@ void BossGanonEff_SpawnWindowShard(GlobalContext* globalCtx, Vec3f* pos, Vec3f*
eff->color.g = color->g;
eff->color.b = color->b;
eff->timer = (s16)Rand_ZeroFloat(20.0f);
+ eff->epoch++;
break;
}
}
@@ -158,6 +161,7 @@ void BossGanonEff_SpawnSparkle(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velo
eff->unk_2E = (s16)Rand_ZeroFloat(100.0f) + 0xC8;
eff->unk_30 = arg6;
eff->timer = (s16)Rand_ZeroFloat(10.0f);
+ eff->epoch++;
break;
}
}
@@ -182,6 +186,7 @@ void BossGanonEff_SpawnLightRay(GlobalContext* globalCtx, Vec3f* pos, Vec3f* vel
eff->timer = (s16)Rand_ZeroFloat(10.0f);
eff->unk_48 = Math_Atan2F(eff->velocity.z, eff->velocity.x);
eff->unk_44 = -Math_Atan2F(sqrtf(SQXZ(eff->velocity)), eff->velocity.y);
+ eff->epoch++;
break;
}
}
@@ -201,6 +206,7 @@ void BossGanonEff_SpawnShock(GlobalContext* globalCtx, f32 scale, s16 shockType)
eff->scale = scale / 1000.0f;
eff->unk_2E = shockType;
eff->timer = 0;
+ eff->epoch++;
break;
}
}
@@ -220,6 +226,7 @@ void BossGanonEff_SpawnLightning(GlobalContext* globalCtx, f32 scale, f32 arg2,
eff->unk_48 = arg2;
eff->unk_3C = arg3;
eff->timer = 0;
+ eff->epoch++;
break;
}
}
@@ -240,6 +247,7 @@ void BossGanonEff_SpawnDustDark(GlobalContext* globalCtx, Vec3f* pos, f32 scale,
eff->unk_38 = arg3;
eff->unk_30 = (s16)Rand_ZeroFloat(100.0f);
eff->unk_2E = eff->timer = eff->alpha = 0;
+ eff->epoch++;
break;
}
}
@@ -257,6 +265,7 @@ void BossGanonEff_SpawnDustLight(GlobalContext* globalCtx, Vec3f* pos, f32 scale
effArr[bufIndex].unk_38 = arg3;
effArr[bufIndex].unk_30 = Rand_ZeroFloat(100.0f);
effArr[bufIndex].unk_2E = effArr[bufIndex].timer = effArr[bufIndex].alpha = 0;
+ effArr[bufIndex].epoch++;
}
void BossGanonEff_SpawnShockwave(GlobalContext* globalCtx, Vec3f* pos, f32 scale, f32 arg3) {
@@ -275,6 +284,7 @@ void BossGanonEff_SpawnShockwave(GlobalContext* globalCtx, Vec3f* pos, f32 scale
eff->unk_38 = arg3;
eff->unk_30 = (s16)Rand_ZeroFloat(100.0f);
eff->unk_2E = eff->timer = 0;
+ eff->epoch++;
break;
}
}
@@ -295,6 +305,7 @@ void BossGanonEff_SpawnBlackDot(GlobalContext* globalCtx, Vec3f* pos, f32 scale)
eff->timer = 0;
eff->alpha = 0;
eff->unk_2E = 0;
+ eff->epoch++;
break;
}
}
@@ -4820,6 +4831,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
for (i = 0; i < 200; i++, eff++) {
if (eff->type == GDF_EFF_WINDOW_SHARD) {
+ FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_OPA_DISP++);
if (flag == 0) {
gSPDisplayList(POLY_OPA_DISP++, gDorfWindowShardMaterialDL);
@@ -4837,6 +4849,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 10898),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gDorfWindowShardModelDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -4845,6 +4858,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_SPARKLE) {
+ FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_XLU_DISP++);
if (flag == 0) {
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0);
@@ -4859,6 +4873,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 10932),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -4867,6 +4882,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_LIGHT_RAY) {
+ FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_XLU_DISP++);
if (flag == 0) {
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0);
@@ -4883,6 +4899,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 10971),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -4891,6 +4908,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_SHOCK) {
+ FrameInterpolation_RecordOpenChild(eff, eff->epoch);
if (flag == 0) {
gDPPipeSync(POLY_XLU_DISP++);
if (eff->unk_2E == GDF_SHOCK_PLAYER_PURPLE) {
@@ -4909,6 +4927,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11023),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfShockDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -4916,6 +4935,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_LIGHTNING) {
+ FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_XLU_DISP++);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, sLightningPrimColors[(eff->timer * 3) + 0],
sLightningPrimColors[(eff->timer * 3) + 1], sLightningPrimColors[(eff->timer * 3) + 2],
@@ -4931,6 +4951,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sLightningTextures[eff->timer]));
gSPDisplayList(POLY_XLU_DISP++, gDorfLightningDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -4938,6 +4959,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_IMPACT_DUST_DARK) {
+ FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_XLU_DISP++);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, eff->alpha);
gDPSetEnvColor(POLY_XLU_DISP++, 100, 70, 0, 128);
@@ -4949,6 +4971,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11121),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfImpactDarkDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -4956,6 +4979,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_IMPACT_DUST_LIGHT) {
+ FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_XLU_DISP++);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, eff->alpha);
gDPSetEnvColor(POLY_XLU_DISP++, 200, 100, 0, 128);
@@ -4967,6 +4991,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11165),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfImpactLightDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -4974,6 +4999,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_SHOCKWAVE) {
+ FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_XLU_DISP++);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 170, eff->alpha);
gDPSetEnvColor(POLY_XLU_DISP++, 150, 255, 0, 128);
@@ -4986,6 +5012,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11209),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfShockwaveDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -4993,6 +5020,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
for (i = 0; i < 150; i++, eff++) {
if (eff->type == GDF_EFF_BLACK_DOT) {
+ FrameInterpolation_RecordOpenChild(eff, eff->epoch);
gDPPipeSync(POLY_XLU_DISP++);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 150, 170, 0, eff->alpha);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, 128);
@@ -5005,6 +5033,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11250),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gDorfDotDL);
+ FrameInterpolation_RecordCloseChild();
}
}
diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h
index 69a983717..7a797ad74 100644
--- a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h
+++ b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h
@@ -69,6 +69,7 @@ typedef struct {
/* 0x40 */ f32 unk_40;
/* 0x44 */ f32 unk_44; // mostly x rot
/* 0x48 */ f32 unk_48; // mostly y rot
+ u32 epoch;
} GanondorfEffect; // size = 0x4C
typedef struct BossGanon {
diff --git a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c
index ec4899a28..ae436ca2d 100644
--- a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c
+++ b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c
@@ -10,6 +10,8 @@
#include "objects/gameplay_keep/gameplay_keep.h"
#include "vt.h"
+#include "soh/frame_interpolation.h"
+
#include <string.h>
#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5)
@@ -34,6 +36,7 @@ typedef struct {
/* 0x30 */ f32 scale;
/* 0x30 */ f32 fwork[2];
/* 0x3C */ Vec3f* targetPos;
+ u32 epoch;
} BossMoEffect; // size = 0x40
#define MO_FX_MAX_SIZE 0
@@ -211,6 +214,7 @@ void BossMo_SpawnRipple(BossMoEffect* effect, Vec3f* pos, f32 scale, f32 maxScal
effect->rippleMode = 1;
effect->fwork[MO_FX_SPREAD_RATE] = (effect->fwork[MO_FX_MAX_SIZE] - effect->scale) * 0.1f;
}
+ effect->epoch++;
break;
}
}
@@ -232,6 +236,7 @@ void BossMo_SpawnDroplet(s16 type, BossMoEffect* effect, Vec3f* pos, Vec3f* vel,
effect->scale = scale;
effect->fwork[MO_FX_SPREAD_RATE] = 1.0f;
effect->stopTimer = 0;
+ effect->epoch++;
break;
}
}
@@ -250,6 +255,7 @@ void BossMo_SpawnStillDroplet(BossMoEffect* effect, Vec3f* pos, f32 scale) {
effect->accel = zeroVec;
effect->scale = scale;
effect->fwork[MO_FX_SPREAD_RATE] = 1.0f;
+ effect->epoch++;
break;
}
}
@@ -274,6 +280,7 @@ void BossMo_SpawnBubble(BossMoEffect* effect, Vec3f* pos, Vec3f* vel, Vec3f* acc
effect->alpha = 0;
}
effect->timer = 0;
+ effect->epoch++;
break;
}
}
@@ -2909,6 +2916,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) {
for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) {
if (effect->type == MO_FX_BIG_RIPPLE) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (flag == 0) {
func_80094BC4(gfxCtx);
@@ -2925,6 +2933,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gEffWaterRippleDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -2932,6 +2941,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) {
flag = 0;
for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) {
if (effect->type == MO_FX_SMALL_RIPPLE) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (flag == 0) {
func_80093D84(globalCtx->state.gfxCtx);
@@ -2948,6 +2958,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gEffShockwaveDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -2956,6 +2967,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) {
for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) {
if (((effect->type == MO_FX_DROPLET) || (effect->type == MO_FX_SPLASH)) ||
(effect->type == MO_FX_SPLASH_TRAIL)) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (flag == 0) {
POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0);
@@ -2977,6 +2989,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gMorphaDropletModelDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -2984,6 +2997,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) {
flag = 0;
for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) {
if (effect->type == MO_FX_WET_SPOT) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (flag == 0) {
func_80094044(gfxCtx);
@@ -3003,6 +3017,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gMorphaWetSpotModelDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -3010,6 +3025,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) {
flag = 0;
for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) {
if (effect->type == MO_FX_BUBBLE) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (flag == 0) {
func_80093D18(globalCtx->state.gfxCtx);
@@ -3027,6 +3043,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gMorphaBubbleDL);
+ FrameInterpolation_RecordCloseChild();
}
}
diff --git a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c
index c5b60a327..3d6865e24 100644
--- a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c
+++ b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c
@@ -12,6 +12,8 @@
#include "overlays/actors/ovl_En_Boom/z_en_boom.h"
#include "objects/gameplay_keep/gameplay_keep.h"
+#include "soh/frame_interpolation.h"
+
#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5)
#define GET_BODY(this) ((BossVa*)(this)->actor.parent)
@@ -40,6 +42,7 @@ typedef struct BossVaEffect {
/* 0x44 */ f32 scaleMod;
/* 0x48 */ Vec3f offset;
/* 0x54 */ struct BossVa* parent;
+ u32 epoch;
} BossVaEffect; // size = 0x58
typedef enum {
@@ -3519,6 +3522,7 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) {
for (i = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) {
if (effect->type == VA_LARGE_SPARK) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!flag) {
func_80093D84(globalCtx->state.gfxCtx);
gDPSetEnvColor(POLY_XLU_DISP++, 130, 130, 30, 0);
@@ -3534,12 +3538,14 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 4976),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_015710);
+ FrameInterpolation_RecordCloseChild();
}
}
effect = effectHead;
for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) {
if (effect->type == VA_SPARK_BALL) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!flag) {
func_80093D84(globalCtx->state.gfxCtx);
gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_011738);
@@ -3560,12 +3566,14 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) {
gDPSetEnvColor(POLY_XLU_DISP++, effect->envColor[0], effect->envColor[1], effect->envColor[2],
effect->envColor[3]);
gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_011768);
+ FrameInterpolation_RecordCloseChild();
}
}
effect = effectHead;
for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) {
if (effect->type == VA_BLOOD) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!flag) {
func_80093D84(globalCtx->state.gfxCtx);
gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_009430);
@@ -3590,6 +3598,7 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5052),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_009468);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -3598,6 +3607,7 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) {
if (effect->type == VA_TUMOR) {
BossVa* parent = effect->parent;
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!flag) {
func_80093D18(globalCtx->state.gfxCtx);
gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, effect->envColor[3]);
@@ -3614,12 +3624,14 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gBarinadeDL_012948);
}
+ FrameInterpolation_RecordCloseChild();
}
}
effect = effectHead;
for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) {
if (effect->type == VA_GORE) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!flag) {
func_80093D18(globalCtx->state.gfxCtx);
gSPDisplayList(POLY_OPA_DISP++, gBarinadeDL_012BA0);
@@ -3645,12 +3657,14 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) {
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5124),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gBarinadeDL_012C50);
+ FrameInterpolation_RecordCloseChild();
}
}
effect = effectHead;
for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) {
if (effect->type == VA_ZAP_CHARGE) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!flag) {
func_80093D84(globalCtx->state.gfxCtx);
gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_0135B0);
@@ -3668,12 +3682,14 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5152),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_013638);
+ FrameInterpolation_RecordCloseChild();
}
}
effect = effectHead;
for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) {
if (effect->type == VA_BLAST_SPARK) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!flag) {
func_80093C14(globalCtx->state.gfxCtx);
gDPSetEnvColor(POLY_XLU_DISP++, 130, 130, 30, 0);
@@ -3690,12 +3706,14 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5180),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_015710);
+ FrameInterpolation_RecordCloseChild();
}
}
effect = effectHead;
for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) {
if (effect->type == VA_SMALL_SPARK) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!flag) {
func_80093D84(globalCtx->state.gfxCtx);
gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 100, 0);
@@ -3712,6 +3730,7 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5208),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_008F70);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -3734,6 +3753,7 @@ void BossVa_SpawnSpark(GlobalContext* globalCtx, BossVaEffect* effect, BossVa* t
effect->timer = (s16)(Rand_ZeroOne() * 10.0f) + 111;
effect->velocity = effect->accel = sZeroVec;
effect->mode = mode;
+ effect->epoch++;
switch (mode) {
case SPARK_UNUSED:
diff --git a/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.c b/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.c
index b55777484..ac50f226a 100644
--- a/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.c
+++ b/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.c
@@ -7,6 +7,8 @@
#include "z_en_zo.h"
#include "objects/object_zo/object_zo.h"
+#include "soh/frame_interpolation.h"
+
#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3)
typedef enum {
@@ -41,6 +43,7 @@ void EnZo_Ripple(EnZo* this, Vec3f* pos, f32 scale, f32 targetScale, u8 alpha) {
effect->scale = scale;
effect->targetScale = targetScale;
effect->color.a = alpha;
+ effect->epoch++;
break;
}
effect++;
@@ -65,6 +68,7 @@ void EnZo_Bubble(EnZo* this, Vec3f* pos) {
effect->vec = *pos;
effect->vel = vel;
effect->scale = ((Rand_ZeroOne() - 0.5f) * 0.02f) + 0.12f;
+ effect->epoch++;
break;
}
}
@@ -87,6 +91,7 @@ void EnZo_Splash(EnZo* this, Vec3f* pos, Vec3f* vel, f32 scale) {
effect->vel = *vel;
effect->color.a = (Rand_ZeroOne() * 100.0f) + 100.0f;
effect->scale = scale;
+ effect->epoch++;
break;
}
effect++;
@@ -180,6 +185,7 @@ void EnZo_DrawRipples(EnZo* this, GlobalContext* globalCtx) {
func_80093D84(globalCtx->state.gfxCtx);
for (i = 0; i < ARRAY_COUNT(this->effects); i++) {
if (effect->type == ENZO_EFFECT_RIPPLE) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!setup) {
if (1) {}
gDPPipeSync(POLY_XLU_DISP++);
@@ -194,6 +200,7 @@ void EnZo_DrawRipples(EnZo* this, GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_zo_eff.c", 242),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gZoraRipplesModelDL);
+ FrameInterpolation_RecordCloseChild();
}
effect++;
}
@@ -210,6 +217,7 @@ void EnZo_DrawBubbles(EnZo* this, GlobalContext* globalCtx) {
func_80093D84(globalCtx->state.gfxCtx);
for (i = 0; i < ARRAY_COUNT(this->effects); i++) {
if (effect->type == ENZO_EFFECT_BUBBLE) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!setup) {
if (1) {}
gSPDisplayList(POLY_XLU_DISP++, gZoraBubblesMaterialDL);
@@ -227,6 +235,7 @@ void EnZo_DrawBubbles(EnZo* this, GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_zo_eff.c", 281),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gZoraBubblesModelDL);
+ FrameInterpolation_RecordCloseChild();
}
effect++;
}
@@ -244,6 +253,7 @@ void EnZo_DrawSplashes(EnZo* this, GlobalContext* globalCtx) {
func_80093D84(globalCtx->state.gfxCtx);
for (i = 0; i < ARRAY_COUNT(this->effects); i++) {
if (effect->type == ENZO_EFFECT_SPLASH) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (!setup) {
if (1) {}
gSPDisplayList(POLY_XLU_DISP++, gZoraSplashesMaterialDL);
@@ -260,6 +270,7 @@ void EnZo_DrawSplashes(EnZo* this, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gZoraSplashesModelDL);
+ FrameInterpolation_RecordCloseChild();
}
effect++;
}
diff --git a/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.h b/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.h
index ba6eebb30..8911ce399 100644
--- a/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.h
+++ b/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.h
@@ -15,6 +15,7 @@ typedef struct {
/* 0x14 */ Vec3f pos;
/* 0x20 */ Vec3f vel;
/* 0x2C */ Vec3f vec; // Usage specific
+ u32 epoch;
} EnZoEffect; // size = 0x38
typedef void (*EnZoActionFunc)(struct EnZo*, GlobalContext*);
diff --git a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c
index a103c4826..0798145db 100644
--- a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c
+++ b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c
@@ -10,6 +10,8 @@
#include "objects/object_fish/object_fish.h"
#include "vt.h"
+#include "soh/frame_interpolation.h"
+
#define FLAGS ACTOR_FLAG_4
#define WATER_SURFACE_Y(globalCtx) globalCtx->colCtx.colHeader->waterBoxes->ySurface
@@ -56,6 +58,7 @@ typedef struct {
/* 0x34 */ f32 unk_34;
/* 0x38 */ f32 unk_38;
/* 0x3C */ f32 unk_3C;
+ u32 epoch;
} FishingEffect; // size = 0x40
#define POND_PROP_COUNT 140
@@ -490,6 +493,8 @@ void Fishing_SpawnRipple(Vec3f* projectedPos, FishingEffect* effect, Vec3f* pos,
effect->unk_2C = 1;
effect->unk_38 = (effect->unk_34 - effect->unk_30) * 0.1f;
}
+
+ effect->epoch++;
break;
}
@@ -514,6 +519,7 @@ void Fishing_SpawnDustSplash(Vec3f* projectedPos, FishingEffect* effect, Vec3f*
effect->accel = accel;
effect->alpha = 100 + (s16)Rand_ZeroFloat(100.0f);
effect->unk_30 = scale;
+ effect->epoch++;
break;
}
@@ -539,6 +545,7 @@ void Fishing_SpawnWaterDust(Vec3f* projectedPos, FishingEffect* effect, Vec3f* p
effect->timer = (s16)Rand_ZeroFloat(100.0f);
effect->unk_30 = scale;
effect->unk_34 = 2.0f * scale;
+ effect->epoch++;
break;
}
@@ -563,6 +570,7 @@ void Fishing_SpawnBubble(Vec3f* projectedPos, FishingEffect* effect, Vec3f* pos,
effect->timer = (s16)Rand_ZeroFloat(100.0f);
effect->unk_30 = scale;
effect->unk_2C = arg4;
+ effect->epoch++;
break;
}
@@ -591,6 +599,7 @@ void Fishing_SpawnRainDrop(FishingEffect* effect, Vec3f* pos, Vec3f* rot) {
Matrix_RotateY(rot->y, MTXMODE_NEW);
Matrix_RotateX(rot->x, MTXMODE_APPLY);
Matrix_MultVec3f(&velSrc, &effect->vel);
+ effect->epoch++;
break;
}
@@ -1182,6 +1191,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) {
for (i = 0; i < 100; i++) {
if (effect->type == FS_EFF_RIPPLE) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (flag == 0) {
gSPDisplayList(POLY_XLU_DISP++, gFishingRippleMaterialDL);
gDPSetEnvColor(POLY_XLU_DISP++, 155, 155, 155, 0);
@@ -1197,6 +1207,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gFishingRippleModelDL);
+ FrameInterpolation_RecordCloseChild();
}
effect++;
}
@@ -1205,6 +1216,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) {
flag = 0;
for (i = 0; i < 100; i++) {
if (effect->type == FS_EFF_DUST_SPLASH) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (flag == 0) {
gSPDisplayList(POLY_XLU_DISP++, gFishingDustSplashMaterialDL);
gDPSetEnvColor(POLY_XLU_DISP++, 200, 200, 200, 0);
@@ -1221,6 +1233,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gFishingDustSplashModelDL);
+ FrameInterpolation_RecordCloseChild();
}
effect++;
}
@@ -1229,6 +1242,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) {
flag = 0;
for (i = 0; i < 100; i++) {
if (effect->type == FS_EFF_WATER_DUST) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (flag == 0) {
gSPDisplayList(POLY_OPA_DISP++, gFishingWaterDustMaterialDL);
gDPSetEnvColor(POLY_OPA_DISP++, 40, 90, 80, 128);
@@ -1249,6 +1263,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gFishingWaterDustModelDL);
+ FrameInterpolation_RecordCloseChild();
}
effect++;
}
@@ -1257,6 +1272,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) {
flag = 0;
for (i = 0; i < 100; i++) {
if (effect->type == FS_EFF_BUBBLE) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (flag == 0) {
gSPDisplayList(POLY_XLU_DISP++, gFishingBubbleMaterialDL);
gDPSetEnvColor(POLY_XLU_DISP++, 150, 150, 150, 0);
@@ -1272,6 +1288,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gFishingBubbleModelDL);
+ FrameInterpolation_RecordCloseChild();
}
effect++;
}
@@ -1280,6 +1297,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) {
flag = 0;
for (i = 30; i < EFFECT_COUNT; i++) {
if (effect->type == FS_EFF_RAIN_DROP) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (flag == 0) {
POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x14);
gDPSetCombineMode(POLY_XLU_DISP++, G_CC_PRIMITIVE, G_CC_PRIMITIVE);
@@ -1297,6 +1315,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gFishingRainDropModelDL);
+ FrameInterpolation_RecordCloseChild();
}
effect++;
}
@@ -1307,6 +1326,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) {
flag = 0;
for (i = 30; i < EFFECT_COUNT; i++) {
if (effect->type == FS_EFF_RAIN_RIPPLE) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (flag == 0) {
gSPDisplayList(POLY_XLU_DISP++, gFishingRippleMaterialDL);
gDPSetEnvColor(POLY_XLU_DISP++, 155, 155, 155, 0);
@@ -1321,6 +1341,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gFishingRippleModelDL);
+ FrameInterpolation_RecordCloseChild();
}
effect++;
}
@@ -1329,6 +1350,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) {
flag = 0;
for (i = 30; i < EFFECT_COUNT; i++) {
if (effect->type == FS_EFF_RAIN_SPLASH) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
if (flag == 0) {
gSPDisplayList(POLY_XLU_DISP++, gFishingRainSplashMaterialDL);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, KREG(19) + 80);
@@ -1350,12 +1372,14 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gFishingRainSplashModelDL);
+ FrameInterpolation_RecordCloseChild();
}
effect++;
}
effect = firstEffect;
if (effect->type == FS_EFF_OWNER_HAT) {
+ FrameInterpolation_RecordOpenChild(effect, effect->epoch);
Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW);
Matrix_RotateY((sEffOwnerHatRot.y * M_PI) / 32768, MTXMODE_APPLY);
Matrix_RotateX((sEffOwnerHatRot.x * M_PI) / 32768, MTXMODE_APPLY);
@@ -1367,6 +1391,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) {
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gFishingOwnerHatDL);
+ FrameInterpolation_RecordCloseChild();
}
Matrix_Pop();
@@ -4398,6 +4423,7 @@ void Fishing_DrawPondProps(GlobalContext* globalCtx) {
}
if (prop->shouldDraw) {
+ FrameInterpolation_RecordOpenChild(prop, 0);
Matrix_Translate(prop->pos.x, prop->pos.y, prop->pos.z, MTXMODE_NEW);
Matrix_Scale(prop->scale, prop->scale, prop->scale, MTXMODE_APPLY);
Matrix_RotateY(prop->rotY, MTXMODE_APPLY);
@@ -4407,6 +4433,7 @@ void Fishing_DrawPondProps(GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 7726),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gFishingReedModelDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -4423,12 +4450,14 @@ void Fishing_DrawPondProps(GlobalContext* globalCtx) {
}
if (prop->shouldDraw) {
+ FrameInterpolation_RecordOpenChild(prop, 0);
Matrix_Translate(prop->pos.x, prop->pos.y, prop->pos.z, MTXMODE_NEW);
Matrix_Scale(prop->scale, prop->scale, prop->scale, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 7748),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gFishingWoodPostModelDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -4445,6 +4474,7 @@ void Fishing_DrawPondProps(GlobalContext* globalCtx) {
}
if (prop->shouldDraw) {
+ FrameInterpolation_RecordOpenChild(prop, 0);
Matrix_Translate(prop->pos.x, prop->pos.y, prop->pos.z, MTXMODE_NEW);
Matrix_Scale(prop->scale, 1.0f, prop->scale, MTXMODE_APPLY);
Matrix_RotateY(prop->lilyPadAngle * (M_PI / 32768), MTXMODE_APPLY);
@@ -4454,6 +4484,7 @@ void Fishing_DrawPondProps(GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 7774),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gFishingLilyPadModelDL);
+ FrameInterpolation_RecordCloseChild();
}
}
@@ -4470,6 +4501,7 @@ void Fishing_DrawPondProps(GlobalContext* globalCtx) {
}
if (prop->shouldDraw) {
+ FrameInterpolation_RecordOpenChild(prop, 0);
Matrix_Translate(prop->pos.x, prop->pos.y, prop->pos.z, MTXMODE_NEW);
Matrix_Scale(prop->scale, prop->scale, prop->scale, MTXMODE_APPLY);
Matrix_RotateY(prop->rotY, MTXMODE_APPLY);
@@ -4477,6 +4509,7 @@ void Fishing_DrawPondProps(GlobalContext* globalCtx) {
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 7798),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gFishingRockModelDL);
+ FrameInterpolation_RecordCloseChild();
}
}
diff --git a/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c b/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c
index b01bcf1d9..d4282b91b 100644
--- a/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c
+++ b/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c
@@ -9,6 +9,8 @@
#include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/object_spot02_objects/object_spot02_objects.h"
+#include "soh/frame_interpolation.h"
+
#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_25)
void ObjectKankyo_Init(Actor* thisx, GlobalContext* globalCtx);
@@ -260,6 +262,7 @@ void ObjectKankyo_Fairies(ObjectKankyo* this, GlobalContext* globalCtx) {
this->effects[i].dirPhase.z = Rand_ZeroOne() * 360.0f;
this->effects[i].state++;
this->effects[i].timer = 0;
+ this->effects[i].epoch++;
break;
case 1: // blinking fairies / inactive fairy trails
@@ -417,26 +420,32 @@ void ObjectKankyo_Fairies(ObjectKankyo* this, GlobalContext* globalCtx) {
if (this->effects[i].base.x + this->effects[i].pos.x - baseX > maxDist) {
this->effects[i].base.x = baseX - maxDist;
this->effects[i].pos.x = 0.0f;
+ this->effects[i].epoch++;
}
if (this->effects[i].base.x + this->effects[i].pos.x - baseX < -maxDist) {
this->effects[i].base.x = baseX + maxDist;
this->effects[i].pos.x = 0.0f;
+ this->effects[i].epoch++;
}
if (this->effects[i].base.y + this->effects[i].pos.y - baseY > 50.0f) {
this->effects[i].base.y = baseY - 50.0f;
this->effects[i].pos.y = 0.0f;
+ this->effects[i].epoch++;
}
if (this->effects[i].base.y + this->effects[i].pos.y - baseY < -50.0f) {
this->effects[i].base.y = baseY + 50.0f;
this->effects[i].pos.y = 0.0f;
+ this->effects[i].epoch++;
}
if (this->effects[i].base.z + this->effects[i].pos.z - baseZ > maxDist) {
this->effects[i].base.z = baseZ - maxDist;
this->effects[i].pos.z = 0.0f;
+ this->effects[i].epoch++;
}
if (this->effects[i].base.z + this->effects[i].pos.z - baseZ < -maxDist) {
this->effects[i].base.z = baseZ + maxDist;
this->effects[i].pos.z = 0.0f;
+ this->effects[i].epoch++;
}
}
}
@@ -496,6 +505,7 @@ void ObjectKankyo_DrawFairies(ObjectKankyo* this2, GlobalContext* globalCtx2) {
gSPDisplayList(POLY_XLU_DISP++, gKokiriDustMoteTextureLoadDL);
for (i = 0; i < globalCtx->envCtx.unk_EE[3]; i++) {
+ FrameInterpolation_RecordOpenChild(&this->effects[i], this->effects[i].epoch);
Matrix_Translate(this->effects[i].base.x + this->effects[i].pos.x,
this->effects[i].base.y + this->effects[i].pos.y,
this->effects[i].base.z + this->effects[i].pos.z, MTXMODE_NEW);
@@ -561,6 +571,7 @@ void ObjectKankyo_DrawFairies(ObjectKankyo* this2, GlobalContext* globalCtx2) {
Matrix_RotateZ(DEG_TO_RAD(globalCtx->state.frames * 20.0f), MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 913), G_MTX_LOAD);
gSPDisplayList(POLY_XLU_DISP++, gKokiriDustMoteDL);
+ FrameInterpolation_RecordCloseChild();
}
CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 922);
}
diff --git a/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.h b/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.h
index 9b9a69975..fc45e156e 100644
--- a/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.h
+++ b/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.h
@@ -24,6 +24,7 @@ typedef struct ObjectKankyoEffect {
/* 0x4A */ u16 flightRadius;
/* 0x4C */ f32 amplitude;
/* 0x50 */ u16 timer;
+ u32 epoch;
} ObjectKankyoEffect; // size = 0x54
typedef struct ObjectKankyo {
diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c
index df6520ebf..4dbb1b20f 100644
--- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c
+++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c
@@ -14,6 +14,8 @@
#include "vt.h"
#include "SohHooks.h"
+#include "soh/frame_interpolation.h"
+
static void* sEquipmentFRATexs[] = {
gPauseEquipment00FRATex, gPauseEquipment01Tex, gPauseEquipment02Tex, gPauseEquipment03Tex, gPauseEquipment04Tex,
gPauseEquipment10FRATex, gPauseEquipment11Tex, gPauseEquipment12Tex, gPauseEquipment13Tex, gPauseEquipment14Tex,
@@ -1047,6 +1049,7 @@ void KaleidoScope_DrawPages(GlobalContext* globalCtx, GraphicsContext* gfxCtx) {
s16 stepG;
s16 stepB;
+ FrameInterpolation_RecordOpenChild(NULL, pauseCtx->state + pauseCtx->pageIndex * 100);
OPEN_DISPS(gfxCtx, "../z_kaleido_scope_PAL.c", 1100);
if ((pauseCtx->state < 8) || (pauseCtx->state > 0x11)) {
@@ -1418,6 +1421,7 @@ void KaleidoScope_DrawPages(GlobalContext* globalCtx, GraphicsContext* gfxCtx) {
}
CLOSE_DISPS(gfxCtx, "../z_kaleido_scope_PAL.c", 1577);
+ FrameInterpolation_RecordCloseChild();
}
void KaleidoScope_DrawInfoPanel(GlobalContext* globalCtx) {