Frame interpolation (#78)

* Initial. Only enough to get it running. Missing changes to effects and actors.

* Add frame interp calls to most files in `code`

* Fix bubbles (The ones that look like bubbles)

* Fix kankyo and takkuri.

* Fix obj_grass interpolation

---------

Co-authored-by: Garrett Cox <garrettjcox@gmail.com>
This commit is contained in:
louist103 2024-01-21 11:19:23 -05:00 committed by Garrett Cox
parent 7573a9378c
commit ce17d97ac4
26 changed files with 1054 additions and 56 deletions

View File

@ -43,7 +43,7 @@
#include "Extractor/Extract.h"
// OTRTODO
//#include <functions.h>
#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<std::unordered_map<Mtx*, MtxF>>& 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<std::mutex> Lock(audio.mutex);
//audio.processing = true;
}
#if 0
if (!audio.initialized) {
audio.initialized = true;
std::thread([]() {
for (;;) {
{
std::unique_lock<std::mutex> Lock(audio.mutex);
while (!audio.processing) {
audio.cv_to_thread.wait(Lock);
}
}
std::unique_lock<std::mutex> 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<std::mutex> Lock(audio.mutex);
audio.processing = true;
}
audio.cv_to_thread.notify_one();
#endif
//audio.cv_to_thread.notify_one();
std::vector<std::unordered_map<Mtx*, MtxF>> 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<std::mutex> 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;

View File

@ -0,0 +1,733 @@
#include <libultraship/bridge.h>
#include <vector>
#include <map>
#include <unordered_map>
#include <math.h>
#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<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_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<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 (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;
}

View File

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

View File

@ -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/*")

View File

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

View File

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

View File

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

View File

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

View File

@ -3,6 +3,7 @@
#include "z64.h"
#include <assert.h>
#include "BenPort.h"
#include "2s2h/Enhancements/interpolation/frame_interpolation.h"
//#include <math.h>
#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;

View File

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

View File

@ -21,6 +21,7 @@
#include "objects/object_bdoor/object_bdoor.h"
#include <string.h>
#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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,7 @@
#include "sys_cfb.h"
#include "objects/gameplay_keep/gameplay_keep.h"
#include <string.h>
#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();
}
}

View File

@ -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 <string.h>
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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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[] = {