diff --git a/mm/2s2h/BenPort.cpp b/mm/2s2h/BenPort.cpp index 88b471f16..9526ebac0 100644 --- a/mm/2s2h/BenPort.cpp +++ b/mm/2s2h/BenPort.cpp @@ -43,7 +43,7 @@ #include "Extractor/Extract.h" // OTRTODO //#include - +#include "2s2h/enhancements/interpolation/frame_interpolation.h" #ifdef ENABLE_CROWD_CONTROL #include "Enhancements/crowd-control/CrowdControl.h" @@ -490,23 +490,67 @@ extern "C" void Graph_StartFrame() { } void RunCommands(Gfx* Commands, const std::vector>& mtx_replacements) { - //for (const auto& m : mtx_replacements) { - gfx_run(Commands, {}); + for (const auto& m : mtx_replacements) { + gfx_run(Commands, m); gfx_end_frame(); - // } + } } // C->C++ Bridge extern "C" void Graph_ProcessGfxCommands(Gfx* commands) { - { - //std::unique_lock Lock(audio.mutex); - //audio.processing = true; - } + #if 0 + if (!audio.initialized) { + audio.initialized = true; + std::thread([]() { + for (;;) { + { + std::unique_lock Lock(audio.mutex); + while (!audio.processing) { + audio.cv_to_thread.wait(Lock); + } + } + std::unique_lock Lock(audio.mutex); +// AudioMgr_ThreadEntry(&gAudioMgr); +// 528 and 544 relate to 60 fps at 32 kHz 32000/60 = 533.333.. +// in an ideal world, one third of the calls should use num_samples=544 and two thirds num_samples=528 +#define SAMPLES_HIGH 560 +#define SAMPLES_LOW 528 +// PAL values +//#define SAMPLES_HIGH 656 +//#define SAMPLES_LOW 624 +#define AUDIO_FRAMES_PER_UPDATE (R_UPDATE_RATE > 0 ? R_UPDATE_RATE : 1) +#define NUM_AUDIO_CHANNELS 2 + int samples_left = AudioPlayer_Buffered(); + u32 num_audio_samples = samples_left < AudioPlayer_GetDesiredBuffered() ? SAMPLES_HIGH : SAMPLES_LOW; + // printf("Audio samples: %d %u\n", samples_left, num_audio_samples); + + // 3 is the maximum authentic frame divisor. + s16 audio_buffer[SAMPLES_HIGH * NUM_AUDIO_CHANNELS * 3]; + for (int i = 0; i < AUDIO_FRAMES_PER_UPDATE; i++) { + AudioMgr_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * NUM_AUDIO_CHANNELS), + num_audio_samples); + } + // for (uint32_t i = 0; i < 2 * num_audio_samples; i++) { + // audio_buffer[i] = Rand_Next() & 0xFF; + //} + // printf("Audio samples before submitting: %d\n", audio_api->buffered()); + AudioPlayer_Play((u8*)audio_buffer, + num_audio_samples * (sizeof(int16_t) * NUM_AUDIO_CHANNELS * AUDIO_FRAMES_PER_UPDATE)); + + audio.processing = false; + audio.cv_from_thread.notify_one(); + } + }).detach(); + } + { + std::unique_lock Lock(audio.mutex); + audio.processing = true; + } + audio.cv_to_thread.notify_one(); + #endif - //audio.cv_to_thread.notify_one(); std::vector> mtx_replacements; - int target_fps = 20; - //OTRGlobals::Instance->GetInterpolationFPS(); + int target_fps = CVarGetInteger("gInterpolationFPS", 20); static int last_fps; static int last_update_rate; static int time; @@ -522,31 +566,29 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) { } // time_base = fps * original_fps (one second) - // int next_original_frame = fps; + int next_original_frame = fps; - //while (time + original_fps <= next_original_frame) { - // time += original_fps; - // if (time != next_original_frame) { - // mtx_replacements.push_back(FrameInterpolation_Interpolate((float)time / next_original_frame)); - // } else { - // mtx_replacements.emplace_back(); - // } - //} + while (time + original_fps <= next_original_frame) { + time += original_fps; + if (time != next_original_frame) { + mtx_replacements.push_back(FrameInterpolation_Interpolate((float)time / next_original_frame)); + } else { + mtx_replacements.emplace_back(); + } + } - //time -= fps; + time -= fps; - OTRGlobals::Instance->context->GetWindow()->SetTargetFps(original_fps); - // OTRGlobals::Instance->context->GetWindow()->SetTargetFps(20); - //OTRGlobals::Instance->context->GetWindow()->SetTargetFps(60); + OTRGlobals::Instance->context->GetWindow()->SetTargetFps(fps); - //int threshold = CVarGetInteger("gExtraLatencyThreshold", 80); - //OTRGlobals::Instance->context->GetWindow()->SetMaximumFrameLatency(threshold > 0 && target_fps >= threshold ? 2 - //: 1); + int threshold = CVarGetInteger("gExtraLatencyThreshold", 80); + OTRGlobals::Instance->context->GetWindow()->SetMaximumFrameLatency(threshold > 0 && target_fps >= threshold ? 2 + : 1); RunCommands(commands, mtx_replacements); - //last_fps = fps; - //last_update_rate = R_UPDATE_RATE; + last_fps = fps; + last_update_rate = R_UPDATE_RATE; //{ // std::unique_lock Lock(audio.mutex); @@ -554,16 +596,10 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) { // audio.cv_from_thread.wait(Lock); // } //} - // - //if (ShouldClearTextureCacheAtEndOfFrame) { - // gfx_texture_cache_clear(); - // LUS::SkeletonPatcher::UpdateSkeletons(); - // ShouldClearTextureCacheAtEndOfFrame = false; - //} // OTRTODO: FIGURE OUT END FRAME POINT - /* if (OTRGlobals::Instance->context->lastScancode != -1) - OTRGlobals::Instance->context->lastScancode = -1;*/ + /* if (OTRGlobals::Instance->context->GetWindow()->lastScancode != -1) + OTRGlobals::Instance->context->GetWindow()->lastScancode = -1;*/ } float divisor_num = 0.0f; diff --git a/mm/2s2h/Enhancements/interpolation/frame_interpolation.cpp b/mm/2s2h/Enhancements/interpolation/frame_interpolation.cpp new file mode 100644 index 000000000..024cbac59 --- /dev/null +++ b/mm/2s2h/Enhancements/interpolation/frame_interpolation.cpp @@ -0,0 +1,733 @@ +#include + +#include +#include +#include +#include + +#include "frame_interpolation.h" +#include "2s2h/BenPort.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_RotateXF(f32 x, u8 mode); +void Matrix_RotateYF(f32 y, u8 mode); +void Matrix_RotateZF(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_RotateAxisF(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 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> children; + map> ops; + vector> items; + }; + + struct Recording { + Path root_path; + }; + + bool is_recording; + vector 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_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_RotateXF(v, mode); + break; + + case 1: + Matrix_RotateYF(v, mode); + break; + + case 2: + Matrix_RotateZF(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_RotateAxisF(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 FrameInterpolation_Interpolate(float step) { + InterpolateCtx ctx; + ctx.step = step; + ctx.w = 1.0f - step; + ctx.interpolate_branch(&previous_recording.root_path, ¤t_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(¤t_recording.root_path); + if (OTRGlobals::Instance->GetInterpolationFPS() != 20) { + 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/mm/2s2h/Enhancements/interpolation/frame_interpolation.h b/mm/2s2h/Enhancements/interpolation/frame_interpolation.h new file mode 100644 index 000000000..64c7197cd --- /dev/null +++ b/mm/2s2h/Enhancements/interpolation/frame_interpolation.h @@ -0,0 +1,61 @@ +#pragma once + +#include "include/z64math.h" + +#ifdef __cplusplus + +#include + +std::unordered_map 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/mm/CMakeLists.txt b/mm/CMakeLists.txt index 9559f4510..6a43dc0e0 100644 --- a/mm/CMakeLists.txt +++ b/mm/CMakeLists.txt @@ -182,6 +182,7 @@ file(GLOB_RECURSE soh__Enhancements RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} #source_group("soh\\Enhancements" REGULAR_EXPRESSION "soh/Enhancements/*") #source_group("soh\\Enhancements\\audio" REGULAR_EXPRESSION "soh/Enhancements/audio/*") source_group("2s2h\\Enhancements\\controls" REGULAR_EXPRESSION "2s2h/Enhancements/controls/*") +source_group("2s2h\\Enhancements\\interpolation" REGULAR_EXPRESSION "2s2h/Enhancements/interpolation/*") #source_group("soh\\Enhancements\\cosmetics" REGULAR_EXPRESSION "soh/Enhancements/cosmetics/*") #source_group("soh\\Enhancements\\crowd-control" REGULAR_EXPRESSION "soh/Enhancements/crowd-control/*") #source_group("soh\\Enhancements\\custom-message" REGULAR_EXPRESSION "soh/Enhancements/custom-message/*") diff --git a/mm/include/gfx.h b/mm/include/gfx.h index 879ea3eae..13ab495f2 100644 --- a/mm/include/gfx.h +++ b/mm/include/gfx.h @@ -265,6 +265,8 @@ extern Gfx gEmptyDL[]; // Use the DISP macros defined above when writing to display buffers. #define OPEN_DISPS(gfxCtx) \ { \ + void FrameInterpolation_RecordOpenChild(const void* a, int b); \ + FrameInterpolation_RecordOpenChild(__FILE__, __LINE__); \ GraphicsContext* __gfxCtx = gfxCtx; \ gDPNoOpOpenDisp(gfxCtx->polyOpa.p++, __FILE__, __LINE__); \ gDPNoOpOpenDisp(gfxCtx->polyXlu.p++, __FILE__, __LINE__); \ @@ -272,6 +274,8 @@ extern Gfx gEmptyDL[]; #define CLOSE_DISPS(gfxCtx) \ (void)0; \ + void FrameInterpolation_RecordCloseChild(void); \ + FrameInterpolation_RecordCloseChild(); \ gDPNoOpCloseDisp(gfxCtx->polyOpa.p++, __FILE__, __LINE__); \ gDPNoOpCloseDisp(gfxCtx->polyXlu.p++, __FILE__, __LINE__); \ gDPNoOpCloseDisp(gfxCtx->overlay.p++, __FILE__, __LINE__); \ diff --git a/mm/include/segment_symbols.h b/mm/include/segment_symbols.h index 19ead8081..f68cdcfaf 100644 --- a/mm/include/segment_symbols.h +++ b/mm/include/segment_symbols.h @@ -26,14 +26,14 @@ #define SEGMENT_END(segment) 0 //(_ ## segment ## SegmentEnd) -#define SEGMENT_SIZE(segment) (uintptr_t) NULL +#define SEGMENT_SIZE(segment) (uintptr_t) 0 //((uintptr_t)SEGMENT_END(segment) - (uintptr_t)SEGMENT_START(segment)) #define SEGMENT_ROM_START(segment) NULL //(_ ## segment ## SegmentRomStart) #define SEGMENT_ROM_END(segment) NULL //(_ ## segment ## SegmentRomEnd) -#define SEGMENT_ROM_SIZE(segment) NULL +#define SEGMENT_ROM_SIZE(segment) 0 //((uintptr_t)SEGMENT_ROM_END(segment) - (uintptr_t)SEGMENT_ROM_START(segment)) #define SEGMENT_BSS_START(segment) 0 diff --git a/mm/include/z64.h b/mm/include/z64.h index 5547f2aa9..64a8888c1 100644 --- a/mm/include/z64.h +++ b/mm/include/z64.h @@ -297,15 +297,15 @@ typedef enum { /* 3 */ PICTO_PHOTO_STATE_READY } PictoPhotoState; -#define ROM_FILE(name) \ - { 0, 0, #name } - -#define ROM_FILE_EMPTY \ - { 0, 0, "" } - -#define ROM_FILE_UNSET \ - { 0 } - +//#define ROM_FILE(name) \ +// { 0, 0, #name } +// +//#define ROM_FILE_EMPTY \ +// { 0, 0, "" } +// +//#define ROM_FILE_UNSET \ +// { 0 } +// #ifdef __cplusplus } #undef this diff --git a/mm/include/z64effect.h b/mm/include/z64effect.h index 8950958cb..607deaaaa 100644 --- a/mm/include/z64effect.h +++ b/mm/include/z64effect.h @@ -274,6 +274,7 @@ typedef struct EffectSs { /* 0x5C */ s16 life; // -1 means this entry is free /* 0x5E */ u8 priority; // Lower number mean higher priority /* 0x5F */ u8 type; + u32 epoch; } EffectSs; // size = 0x60 typedef struct { diff --git a/mm/src/code/stubs.c b/mm/src/code/stubs.c index 7f3733603..4ffb99c8b 100644 --- a/mm/src/code/stubs.c +++ b/mm/src/code/stubs.c @@ -3,6 +3,7 @@ #include "z64.h" #include #include "BenPort.h" +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" //#include #define SCREEN_WIDTH 320 @@ -713,7 +714,10 @@ void guOrthoF(float m[4][4], float l, float r, float b, float t, float n, float void guOrtho(Mtx* m, float l, float r, float b, float t, float n, float f, float scale) { float mf[4][4]; guOrthoF(mf, l, r, b, t, n, f, scale); - guMtxF2L(mf, m); + FrameInterpolation_RecordOpenChild("ortho", 0); + Matrix_MtxFToMtx((MtxF*)mf, m); + FrameInterpolation_RecordCloseChild(); + //guMtxF2L(mf, m); } #define GU_PI 3.1415926 @@ -823,7 +827,8 @@ 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(mf, m); + Matrix_MtxFToMtx((MtxF*)mf, m); + //guMtxF2L(mf, m); } void guRotateF(float m[4][4], float a, float x, float y, float z) { static float D_80097F90 = M_PI / 180.0f; diff --git a/mm/src/code/sys_matrix.c b/mm/src/code/sys_matrix.c index 6981dfc3c..66e725684 100644 --- a/mm/src/code/sys_matrix.c +++ b/mm/src/code/sys_matrix.c @@ -41,6 +41,7 @@ #include "prevent_bss_reordering.h" #include "global.h" +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" /* data */ #define qs1616(e) ((s32)((e)*0x00010000)) @@ -102,6 +103,7 @@ void Matrix_Init(GameState* gameState) { void Matrix_Push(void) { MtxF* prev = sCurrentMatrix; + FrameInterpolation_RecordMatrixPush(); sCurrentMatrix++; Matrix_MtxFCopy(sCurrentMatrix, prev); } @@ -112,6 +114,7 @@ void Matrix_Push(void) { * @remark original name: "Matrix_pull" */ void Matrix_Pop(void) { + FrameInterpolation_RecordMatrixPop(); sCurrentMatrix--; } @@ -134,6 +137,7 @@ void Matrix_Get(MtxF* dest) { * @remark original name: "Matrix_put" */ void Matrix_Put(MtxF* src) { + FrameInterpolation_RecordMatrixPut(src); Matrix_MtxFCopy(sCurrentMatrix, src); } @@ -161,6 +165,7 @@ MtxF* Matrix_GetCurrent(void) { * @remark original name: "Matrix_mult" */ void Matrix_Mult(MtxF* mf, MatrixMode mode) { + FrameInterpolation_RecordMatrixMult(mf, mode); MtxF* cmf = Matrix_GetCurrent(); if (mode == MTXMODE_APPLY) { @@ -194,6 +199,7 @@ void Matrix_Mult(MtxF* mf, MatrixMode mode) { * @remark original name: "Matrix_translate" */ void Matrix_Translate(f32 x, f32 y, f32 z, MatrixMode mode) { + FrameInterpolation_RecordMatrixTranslate(x, y, z, mode); MtxF* cmf = sCurrentMatrix; f32 tempX; f32 tempY; @@ -240,6 +246,7 @@ void Matrix_Translate(f32 x, f32 y, f32 z, MatrixMode mode) { * @remark original name: "Matrix_scale" */ void Matrix_Scale(f32 x, f32 y, f32 z, MatrixMode mode) { + FrameInterpolation_RecordMatrixScale(x, y, z, mode); MtxF* cmf = sCurrentMatrix; if (mode == MTXMODE_APPLY) { @@ -376,6 +383,7 @@ void Matrix_RotateXS(s16 x, MatrixMode mode) { * @remark original name may have been "Matrix_RotateX", but clashed with the previous function. */ void Matrix_RotateXF(f32 x, MatrixMode mode) { + FrameInterpolation_RecordMatrixRotate1Coord(0, x, mode); MtxF* cmf; f32 sin; f32 cos; @@ -643,6 +651,7 @@ void Matrix_RotateYS(s16 y, MatrixMode mode) { * @remark original name may have been "Matrix_RotateY", but clashed with the previous function. */ void Matrix_RotateYF(f32 y, MatrixMode mode) { + FrameInterpolation_RecordMatrixRotate1Coord(1, y, mode); MtxF* cmf; f32 sin; f32 cos; @@ -825,6 +834,7 @@ void Matrix_RotateZS(s16 z, MatrixMode mode) { * @remark original name may have been "Matrix_RotateZ", but clashed with the previous function. */ void Matrix_RotateZF(f32 z, MatrixMode mode) { + FrameInterpolation_RecordMatrixRotate1Coord(2, z, mode); MtxF* cmf; f32 sin; f32 cos; @@ -906,6 +916,7 @@ void Matrix_RotateZF(f32 z, MatrixMode mode) { * @remark original name: "Matrix_RotateXYZ", changed to reflect rotation order. */ void Matrix_RotateZYX(s16 x, s16 y, s16 z, MatrixMode mode) { + FrameInterpolation_RecordMatrixRotateZYX(x, y, z, mode); MtxF* cmf = sCurrentMatrix; f32 temp1; f32 temp2; @@ -1005,6 +1016,7 @@ void Matrix_RotateZYX(s16 x, s16 y, s16 z, MatrixMode mode) { * @remark original name appears to be "Matrix_softcv3_mult" */ void Matrix_TranslateRotateZYX(Vec3f* translation, Vec3s* rot) { + FrameInterpolation_RecordMatrixTranslateRotateZYX(translation, rot); MtxF* cmf = sCurrentMatrix; f32 sin = Math_SinS(rot->z); f32 cos = Math_CosS(rot->z); @@ -1156,6 +1168,7 @@ void Matrix_SetTranslateRotateYXZ(f32 x, f32 y, f32 z, Vec3s* rot) { } else { cmf->yx = 0.0f; } + FrameInterpolation_RecordMatrixSetTranslateRotateYXZ(x, y, z, rot); } /** @@ -1169,6 +1182,7 @@ void Matrix_SetTranslateRotateYXZ(f32 x, f32 y, f32 z, Vec3s* rot) { * @remark original name: "_MtxF_to_Mtx" */ Mtx* Matrix_MtxFToMtx(MtxF* src, Mtx* dest) { + FrameInterpolation_RecordMatrixMtxFToMtx(src, dest); // #Region 2S2H [Port] For compatibility with modern systems this has been changed to use guMtxF2L guMtxF2L(src, dest); return dest; @@ -1186,7 +1200,10 @@ Mtx* Matrix_MtxFToMtx(MtxF* src, Mtx* dest) { * @remark original name: "_Matrix_to_Mtx" */ Mtx* Matrix_ToMtx(Mtx* dest) { - return Matrix_MtxFToMtx(sCurrentMatrix, dest); + FrameInterpolation_RecordMatrixToMtx(dest, __FILE__, __LINE__); + guMtxF2L(sCurrentMatrix, dest); + return dest; + //return Matrix_MtxFToMtx(sCurrentMatrix, dest); } /** @@ -1480,6 +1497,7 @@ void Matrix_Transpose(MtxF* mf) { * @param[in] mf matrix whose linear part will replace the normalised part of A. */ void Matrix_ReplaceRotation(MtxF* mf) { + FrameInterpolation_RecordMatrixReplaceRotation(mf); MtxF* cmf = sCurrentMatrix; f32 acc; f32 component; @@ -1667,6 +1685,7 @@ void Matrix_MtxFToZYXRot(MtxF* src, Vec3s* dest, s32 nonUniformScale) { * @remark original name may have been "Matrix_RotateVector", but clashed with the next function. */ void Matrix_RotateAxisF(f32 angle, Vec3f* axis, MatrixMode mode) { + FrameInterpolation_RecordMatrixRotateAxis(angle, axis, mode); MtxF* cmf; f32 sin; f32 cos; diff --git a/mm/src/code/z_actor.c b/mm/src/code/z_actor.c index 8de6aa71a..d99b1d82a 100644 --- a/mm/src/code/z_actor.c +++ b/mm/src/code/z_actor.c @@ -21,6 +21,7 @@ #include "objects/object_bdoor/object_bdoor.h" #include +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" // bss // FaultClient sActorFaultClient; // 2 funcs @@ -527,6 +528,8 @@ void Target_Draw(TargetContext* targetCtx, PlayState* play) { s32 index; f32 lockOnScaleX; + FrameInterpolation_RecordOpenChild(actor, 0); + if (targetCtx->rotZTick != 0) { totalEntries = 1; } else { @@ -603,11 +606,14 @@ void Target_Draw(TargetContext* targetCtx, PlayState* play) { } } } + FrameInterpolation_RecordCloseChild(); } actor = targetCtx->arrowPointedActor; if ((actor != NULL) && !(actor->flags & ACTOR_FLAG_CANT_LOCK_ON)) { TatlColor* color = &sTatlColorList[actor->category]; + FrameInterpolation_RecordOpenChild(actor, 0); + POLY_XLU_DISP = Gfx_SetupDL(POLY_XLU_DISP, SETUPDL_7); @@ -619,6 +625,7 @@ void Target_Draw(TargetContext* targetCtx, PlayState* play) { gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, color->inner.r, color->inner.g, color->inner.b, 255); gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_MODELVIEW | G_MTX_LOAD); gSPDisplayList(POLY_XLU_DISP++, gZTargetArrowDL); + FrameInterpolation_RecordCloseChild(); } CLOSE_DISPS(play->state.gfxCtx); @@ -2699,6 +2706,7 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) { void Actor_Draw(PlayState* play, Actor* actor) { Lights* light; + FrameInterpolation_RecordOpenChild(actor, 0); OPEN_DISPS(play->state.gfxCtx); light = LightContext_NewLights(&play->lightCtx, play->state.gfxCtx); @@ -2710,6 +2718,7 @@ void Actor_Draw(PlayState* play, Actor* actor) { (actor->flags & (ACTOR_FLAG_10000000 | ACTOR_FLAG_400000)) ? NULL : &actor->world.pos, play); Lights_Draw(light, play->state.gfxCtx); + FrameInterpolation_RecordActorPosRotMatrix(); if (actor->flags & ACTOR_FLAG_IGNORE_QUAKE) { Matrix_SetTranslateRotateYXZ(actor->world.pos.x + play->mainCamera.quakeOffset.x, actor->world.pos.y + @@ -2766,6 +2775,7 @@ void Actor_Draw(PlayState* play, Actor* actor) { actor->isDrawn = true; CLOSE_DISPS(play->state.gfxCtx); + FrameInterpolation_RecordCloseChild(); } void Actor_UpdateFlaggedAudio(Actor* actor) { diff --git a/mm/src/code/z_camera.c b/mm/src/code/z_camera.c index ada5b6201..5ef292e9e 100644 --- a/mm/src/code/z_camera.c +++ b/mm/src/code/z_camera.c @@ -62,6 +62,7 @@ SwingAnimation D_801EDC30[4]; Vec3f D_801EDDD0; Vec3f D_801EDDE0; Vec3f D_801EDDF0; +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" // Camera will reload its paramData. Usually that means setting the read-only data from what is stored in // CameraModeValue arrays. Although sometimes some read-write data is reset as well @@ -6870,7 +6871,7 @@ s32 Camera_Special9(Camera* camera) { if (doorParams->timer1 > 0) { break; } - + FrameInterpolation_DontInterpolateCamera(); camera->animState++; // Setup for the camera moving behind the door diff --git a/mm/src/code/z_eff_blure.c b/mm/src/code/z_eff_blure.c index 3226e9ef2..01f7bb1a6 100644 --- a/mm/src/code/z_eff_blure.c +++ b/mm/src/code/z_eff_blure.c @@ -1,5 +1,6 @@ #include "global.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" void EffectBlure_AddVertex(EffectBlure* this, Vec3f* p1, Vec3f* p2) { EffectBlureElement* elem; @@ -918,6 +919,7 @@ void EffectBlure_Draw(void* thisx, GraphicsContext* gfxCtx) { s32 j; s32 phi_t2; + FrameInterpolation_RecordOpenChild(this, 0); OPEN_DISPS(gfxCtx); gSPMatrix(POLY_XLU_DISP++, &gIdentityMtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); @@ -1027,4 +1029,5 @@ void EffectBlure_Draw(void* thisx, GraphicsContext* gfxCtx) { } CLOSE_DISPS(gfxCtx); + FrameInterpolation_RecordCloseChild(); } diff --git a/mm/src/code/z_eff_shield_particle.c b/mm/src/code/z_eff_shield_particle.c index ddde771d9..178b289ea 100644 --- a/mm/src/code/z_eff_shield_particle.c +++ b/mm/src/code/z_eff_shield_particle.c @@ -3,6 +3,7 @@ #include "assets/code/eff_shield_particle/eff_shield_particle.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" void EffectShieldParticle_Init(void* thisx, void* initParamsx) { EffectShieldParticle* this = (EffectShieldParticle*)thisx; @@ -146,6 +147,8 @@ void EffectShieldParticle_Draw(void* thisx, GraphicsContext* gfxCtx) { Color_RGBA8 primColor; Color_RGBA8 envColor; + FrameInterpolation_RecordOpenChild(this, 0); + OPEN_DISPS(gfxCtx); if (this != NULL) { @@ -205,4 +208,5 @@ void EffectShieldParticle_Draw(void* thisx, GraphicsContext* gfxCtx) { } CLOSE_DISPS(gfxCtx); + FrameInterpolation_RecordCloseChild(); } diff --git a/mm/src/code/z_eff_spark.c b/mm/src/code/z_eff_spark.c index 77b40e3ed..19bb283f4 100644 --- a/mm/src/code/z_eff_spark.c +++ b/mm/src/code/z_eff_spark.c @@ -1,5 +1,6 @@ #include "global.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" void EffectSpark_Init(void* thisx, void* initParamsx) { EffectSpark* this = (EffectSpark*)thisx; @@ -143,6 +144,7 @@ void EffectSpark_Draw(void* thisx, GraphicsContext* gfxCtx) { u8 sp1C4; f32 ratio; + FrameInterpolation_RecordOpenChild(this, 0); OPEN_DISPS(gfxCtx); if (this != NULL) { @@ -263,4 +265,5 @@ void EffectSpark_Draw(void* thisx, GraphicsContext* gfxCtx) { end: CLOSE_DISPS(gfxCtx); + FrameInterpolation_RecordCloseChild(); } diff --git a/mm/src/code/z_effect_soft_sprite.c b/mm/src/code/z_effect_soft_sprite.c index 08aa845ab..7cf7eb0cc 100644 --- a/mm/src/code/z_effect_soft_sprite.c +++ b/mm/src/code/z_effect_soft_sprite.c @@ -1,7 +1,8 @@ #include "global.h" #include "loadfragment.h" +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" -EffectSsInfo sEffectSsInfo = { NULL, 0, 0 }; +EffectSsInfo sEffectSsInfo = { 0 }; void EffectSS_Init(PlayState* play, s32 numEntries) { u32 i; @@ -202,6 +203,7 @@ void EffectSs_Spawn(PlayState* play, s32 type, s32 priority, void* initData) { sEffectSsInfo.data_table[index].type = type; sEffectSsInfo.data_table[index].priority = priority; + sEffectSsInfo.data_table[index].epoch++; if (initInfo->init(play, index, &sEffectSsInfo.data_table[index], initData) == 0) { EffectSS_ResetEntry(&sEffectSsInfo.data_table[index]); @@ -247,7 +249,9 @@ void EffectSS_DrawParticle(PlayState* play, s32 index) { EffectSs* entry = &sEffectSsInfo.data_table[index]; if (entry->draw != NULL) { + FrameInterpolation_RecordOpenChild(entry, entry->epoch); entry->draw(play, index, entry); + FrameInterpolation_RecordCloseChild(); } } diff --git a/mm/src/code/z_lights.c b/mm/src/code/z_lights.c index 19955ab75..4cbba7811 100644 --- a/mm/src/code/z_lights.c +++ b/mm/src/code/z_lights.c @@ -2,6 +2,7 @@ #include "sys_cfb.h" #include "objects/gameplay_keep/gameplay_keep.h" #include +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" LightsBuffer sLightsBuffer; @@ -428,6 +429,7 @@ void Lights_DrawGlow(PlayState* play) { if (params->drawGlow) { f32 scale = SQ((f32)params->radius) * 2e-6f; + FrameInterpolation_RecordOpenChild(light, 0); gDPSetPrimColor(dl++, 0, 0, params->color[0], params->color[1], params->color[2], 50); Matrix_Translate(params->x, params->y, params->z, MTXMODE_NEW); @@ -436,6 +438,7 @@ void Lights_DrawGlow(PlayState* play) { gSPMatrix(dl++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(dl++, gameplay_keep_DL_029CF0); + FrameInterpolation_RecordCloseChild(); } } diff --git a/mm/src/code/z_play.c b/mm/src/code/z_play.c index 78b71795c..52ba810bb 100644 --- a/mm/src/code/z_play.c +++ b/mm/src/code/z_play.c @@ -34,6 +34,7 @@ u8 sMotionBlurStatus; #include "overlays/gamestates/ovl_file_choose/z_file_select.h" #include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" #include "debug.h" +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" #include s32 gDbgCamEnabled = false; @@ -1509,7 +1510,9 @@ void Play_Main(GameState* thisx) { if (1) { *CONTROLLER1(&this->state) = D_801F6C18; } + FrameInterpolation_StartRecord(); Play_Draw(this); + FrameInterpolation_StopRecord(); *CONTROLLER1(&this->state) = input; } diff --git a/mm/src/code/z_skin_matrix.c b/mm/src/code/z_skin_matrix.c index 20a3d5363..500b91c54 100644 --- a/mm/src/code/z_skin_matrix.c +++ b/mm/src/code/z_skin_matrix.c @@ -1,4 +1,5 @@ #include "global.h" +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" MtxF sMtxFClear = { { { 1.0f, 0.0f, 0.0f, 0.0f }, @@ -507,6 +508,7 @@ void SkinMatrix_Vec3sToVec3f(Vec3s* src, Vec3f* dest) { void SkinMatrix_MtxFToMtx(MtxF* src, Mtx* dest) { // #Region 2S2H [Port] For compatibility with modern systems this has been changed to use guMtxF2L + FrameInterpolation_RecordSkinMatrixMtxFToMtx(src, dest); guMtxF2L(src, dest); } diff --git a/mm/src/code/z_view.c b/mm/src/code/z_view.c index 4adc00b8d..34eddbfc8 100644 --- a/mm/src/code/z_view.c +++ b/mm/src/code/z_view.c @@ -1,6 +1,7 @@ #include "global.h" #include "z64shrink_window.h" #include "z64view.h" +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" s32 View_ApplyPerspective(View* view); s32 View_ApplyOrtho(View* view); @@ -309,6 +310,10 @@ void View_Apply(View* view, s32 mask) { } } +static float sqr(float a) { + return a * a; +} + s32 View_ApplyPerspective(View* view) { f32 aspect; s32 width; @@ -359,10 +364,78 @@ s32 View_ApplyPerspective(View* view) { guLookAt(viewing, view->eye.x, view->eye.y, view->eye.z, view->at.x, view->at.y, view->at.z, view->up.x, view->up.y, view->up.z); - view->viewing = *viewing; + // Some heuristics to identify instant camera movements and skip interpolation in that case + static View old_view; + + float dirx = view->eye.x - view->at.x; + float diry = view->eye.y - view->at.y; + float dirz = view->eye.z - view->at.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.at.x; + float odiry = old_view.eye.y - old_view.at.y; + float odirz = old_view.eye.z - old_view.at.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->at.x - old_view.at.x) + sqr(view->at.y - old_view.at.y) + + sqr(view->at.z - old_view.at.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()); + + view->viewing = *viewing; + old_view = *view; 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); + FrameInterpolation_RecordCloseChild(); CLOSE_DISPS(gfxCtx); diff --git a/mm/src/code/z_vr_box_draw.c b/mm/src/code/z_vr_box_draw.c index 656f6cad3..4ff75e667 100644 --- a/mm/src/code/z_vr_box_draw.c +++ b/mm/src/code/z_vr_box_draw.c @@ -1,4 +1,5 @@ #include "global.h" +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" Mtx* sSkyboxDrawMatrix; @@ -22,6 +23,7 @@ void Skybox_SetColors(SkyboxContext* skyboxCtx, u8 primR, u8 primG, u8 primB, u8 void Skybox_Draw(SkyboxContext* skyboxCtx, GraphicsContext* gfxCtx, s16 skyboxId, s16 blend, f32 x, f32 y, f32 z) { OPEN_DISPS(gfxCtx); + FrameInterpolation_RecordOpenChild(NULL, FrameInterpolation_GetCameraEpoch()); Gfx_SetupDL40_Opa(gfxCtx); @@ -60,6 +62,7 @@ void Skybox_Draw(SkyboxContext* skyboxCtx, GraphicsContext* gfxCtx, s16 skyboxId gDPPipeSync(POLY_OPA_DISP++); + FrameInterpolation_RecordCloseChild(); CLOSE_DISPS(gfxCtx); } diff --git a/mm/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c b/mm/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c index 9899926c8..4546fafc7 100644 --- a/mm/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c +++ b/mm/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c @@ -7,6 +7,7 @@ #include "z_demo_kankyo.h" #include "objects/gameplay_keep/gameplay_keep.h" #include "objects/object_bubble/object_bubble.h" +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" #define FLAGS (ACTOR_FLAG_10 | ACTOR_FLAG_20) @@ -512,6 +513,7 @@ void DemoKakyo_DrawLostWoodsSparkle(Actor* thisx, PlayState* play2) { gSPDisplayList(POLY_XLU_DISP++, gSunSparkleMaterialDL); for (i = 0; i < play->envCtx.precipitation[PRECIP_SNOW_MAX]; i++) { + FrameInterpolation_RecordOpenChild(this, i); worldPos.x = this->effects[i].posBase.x + this->effects[i].posOffset.x; worldPos.y = this->effects[i].posBase.y + this->effects[i].posOffset.y; worldPos.z = this->effects[i].posBase.z + this->effects[i].posOffset.z; @@ -578,6 +580,7 @@ void DemoKakyo_DrawLostWoodsSparkle(Actor* thisx, PlayState* play2) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gSunSparkleModelDL); } + FrameInterpolation_RecordCloseChild(); } CLOSE_DISPS(play->state.gfxCtx); @@ -602,6 +605,7 @@ void DemoKankyo_DrawMoonAndGiant(Actor* thisx, PlayState* play2) { Gfx_SetupDL25_Xlu(gfxCtx); for (i = 0; i < play->envCtx.precipitation[PRECIP_SNOW_MAX]; i++) { + FrameInterpolation_RecordOpenChild(this, i); worldPos.x = this->effects[i].posBase.x + this->effects[i].posOffset.x; worldPos.y = this->effects[i].posBase.y + this->effects[i].posOffset.y; worldPos.z = this->effects[i].posBase.z + this->effects[i].posOffset.z; @@ -656,6 +660,7 @@ void DemoKankyo_DrawMoonAndGiant(Actor* thisx, PlayState* play2) { gSPDisplayList(POLY_XLU_DISP++, gLightOrbModelDL); } } + FrameInterpolation_RecordCloseChild(); } CLOSE_DISPS(gfxCtx); diff --git a/mm/src/overlays/actors/ovl_En_Thiefbird/z_en_thiefbird.c b/mm/src/overlays/actors/ovl_En_Thiefbird/z_en_thiefbird.c index efcd9e08e..d0c0eab77 100644 --- a/mm/src/overlays/actors/ovl_En_Thiefbird/z_en_thiefbird.c +++ b/mm/src/overlays/actors/ovl_En_Thiefbird/z_en_thiefbird.c @@ -8,6 +8,7 @@ #include "z_en_thiefbird.h" #include "overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h" #include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" #define FLAGS \ (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UNFRIENDLY | ACTOR_FLAG_200 | ACTOR_FLAG_IGNORE_QUAKE | ACTOR_FLAG_80000000) @@ -1155,6 +1156,7 @@ void func_80C13354(EnThiefbird* this, PlayState* play2) { for (i = 0; i < ARRAY_COUNT(this->unk_3F0); i++, ptr++) { if (ptr->unk_22 != 0) { + FrameInterpolation_RecordOpenChild(ptr, i); Matrix_Translate(ptr->unk_00.x, ptr->unk_00.y, ptr->unk_00.z, MTXMODE_NEW); Matrix_ReplaceRotation(&play->billboardMtxF); Matrix_RotateYS(ptr->unk_1E, MTXMODE_APPLY); @@ -1165,6 +1167,7 @@ void func_80C13354(EnThiefbird* this, PlayState* play2) { gSPMatrix(&gfx[0], Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(&gfx[1], gTakkuriFeatherModelDL); gfx = &gfx[2]; + FrameInterpolation_RecordCloseChild(); } } POLY_OPA_DISP = gfx; diff --git a/mm/src/overlays/actors/ovl_Obj_Grass/z_obj_grass.c b/mm/src/overlays/actors/ovl_Obj_Grass/z_obj_grass.c index 5b354607e..f1711c1da 100644 --- a/mm/src/overlays/actors/ovl_Obj_Grass/z_obj_grass.c +++ b/mm/src/overlays/actors/ovl_Obj_Grass/z_obj_grass.c @@ -10,6 +10,7 @@ #include "z_obj_grass.h" #include "overlays/actors/ovl_Obj_Grass_Carry/z_obj_grass_carry.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" #define FLAGS (ACTOR_FLAG_10 | ACTOR_FLAG_20) @@ -466,6 +467,8 @@ void ObjGrass_DrawOpa(Actor* thisx, PlayState* play2) { for (j = 0; j < grassGroup->count; j++) { grassElem = &grassGroup->elements[j]; + FrameInterpolation_RecordOpenChild(this, i + j); + if ((grassElem->flags & OBJ_GRASS_ELEM_DRAW) && (grassElem->alpha == 255)) { rot.y = grassElem->rotY; Matrix_SetTranslateRotateYXZ(grassElem->pos.x, grassElem->pos.y, grassElem->pos.z, &rot); @@ -478,6 +481,8 @@ void ObjGrass_DrawOpa(Actor* thisx, PlayState* play2) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_OPA_DISP++, gObjGrass_D_809AAAE0); } + + FrameInterpolation_RecordCloseChild(); } } } @@ -506,6 +511,8 @@ void ObjGrass_DrawXlu(Actor* thisx, PlayState* play) { for (j = 0; j < grassGroup->count; j++) { grassElem = &grassGroup->elements[j]; + FrameInterpolation_RecordOpenChild(this, i + j); + if ((grassElem->flags & OBJ_GRASS_ELEM_DRAW) && (grassElem->alpha > 0) && (grassElem->alpha < 255)) { rot.y = grassElem->rotY; Matrix_SetTranslateRotateYXZ(grassElem->pos.x, grassElem->pos.y, grassElem->pos.z, &rot); @@ -516,6 +523,8 @@ void ObjGrass_DrawXlu(Actor* thisx, PlayState* play) { gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, grassElem->alpha); gSPDisplayList(POLY_XLU_DISP++, gObjGrass_D_809AAAE0); } + + FrameInterpolation_RecordCloseChild(); } } } diff --git a/mm/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c b/mm/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c index 978828a45..eb2866e6c 100644 --- a/mm/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c +++ b/mm/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c @@ -6,6 +6,7 @@ #include "z_object_kankyo.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" #define FLAGS (ACTOR_FLAG_10 | ACTOR_FLAG_20 | ACTOR_FLAG_2000000) @@ -539,9 +540,10 @@ void func_808DD3C8(Actor* thisx, PlayState* play2) { worldPos.x = this->unk_14C[i].unk_00 + this->unk_14C[i].unk_0C; worldPos.y = this->unk_14C[i].unk_04 + this->unk_14C[i].unk_10; worldPos.z = this->unk_14C[i].unk_08 + this->unk_14C[i].unk_14; - + FrameInterpolation_RecordOpenChild(this, i); Play_GetScreenPos(play, &worldPos, &screenPos); + if ((screenPos.x >= 0.0f) && (screenPos.x < SCREEN_WIDTH) && (screenPos.y >= 0.0f) && (screenPos.y < SCREEN_HEIGHT)) { if (!spB4) { @@ -572,6 +574,7 @@ void func_808DD3C8(Actor* thisx, PlayState* play2) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gEffDustDL); } + FrameInterpolation_RecordCloseChild(); } CLOSE_DISPS(play->state.gfxCtx); @@ -613,7 +616,7 @@ void func_808DD970(Actor* thisx, PlayState* play2) { worldPos.x = this->unk_14C[i].unk_00 + this->unk_14C[i].unk_0C; worldPos.y = this->unk_14C[i].unk_04 + this->unk_14C[i].unk_10; worldPos.z = this->unk_14C[i].unk_08 + this->unk_14C[i].unk_14; - + FrameInterpolation_RecordOpenChild(this, i); Play_GetScreenPos(play, &worldPos, &screenPos); if ((screenPos.x >= 0.0f) && (screenPos.x < SCREEN_WIDTH) && (screenPos.y >= 0.0f) && @@ -639,6 +642,7 @@ void func_808DD970(Actor* thisx, PlayState* play2) { gSPSetGeometryMode(POLY_XLU_DISP++, G_FOG); gSPDisplayList(POLY_XLU_DISP++, gEffDustDL); } + FrameInterpolation_RecordCloseChild(); } CLOSE_DISPS(play->state.gfxCtx); @@ -673,6 +677,7 @@ void func_808DDE9C(Actor* thisx, PlayState* play2) { temp_f22 = this->unk_14C[0].unk_04 + ((Rand_ZeroOne() - 0.7f) * this->unk_144); temp_f2 = this->unk_14C[0].unk_08 + ((Rand_ZeroOne() - 0.7f) * this->unk_144); + FrameInterpolation_RecordOpenChild(this, i); if (!((temp_f20 < -252.0f) && (temp_f20 > -500.0f) && (temp_f2 > 3820.0f) && (temp_f2 < 4150.0f))) { Matrix_Translate(temp_f20, temp_f22, temp_f2, MTXMODE_NEW); @@ -690,11 +695,13 @@ void func_808DDE9C(Actor* thisx, PlayState* play2) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gFallingRainDropDL); } + FrameInterpolation_RecordCloseChild(); } phi_s5 = false; if (player->actor.floorHeight < play->view.eye.y) { for (i = 0; i < end; i++) { + FrameInterpolation_RecordOpenChild(this, i + end); if (!phi_s5) { Gfx_SetupDL25_Xlu(play->state.gfxCtx); @@ -716,6 +723,7 @@ void func_808DDE9C(Actor* thisx, PlayState* play2) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gEffShockwaveDL); } + FrameInterpolation_RecordCloseChild(); } } diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c index 53b168825..bfb02f8c1 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c @@ -17,6 +17,8 @@ #include "gfxdebuggerbridge.h" #include "archives/item_name_static/item_name_static.h" #include "archives/map_name_static/map_name_static.h" +#include "2s2h/Enhancements/interpolation/frame_interpolation.h" + // Page Textures (Background of Page): // Broken up into multiple textures. // Numbered by column/row. @@ -584,6 +586,7 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { s16 stepG; s16 stepB; + FrameInterpolation_RecordOpenChild(NULL, pauseCtx->state + pauseCtx->pageIndex * 100); OPEN_DISPS(gfxCtx); if (!IS_PAUSE_STATE_GAMEOVER) { @@ -868,6 +871,7 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { } CLOSE_DISPS(gfxCtx); + FrameInterpolation_RecordCloseChild(); } TexturePtr D_8082B998[] = {