mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-07-07 20:11:46 -04:00
Frame interp: Revised camera system
FOV is now smooth, and the individual hacks for stars and Epona's reins are removed
This commit is contained in:
@@ -6,7 +6,6 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct cXyz;
|
||||
class camera_process_class;
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -15,7 +14,6 @@ namespace frame_interp {
|
||||
|
||||
void ensure_initialized();
|
||||
|
||||
void begin_record_camera();
|
||||
void begin_record();
|
||||
void end_record();
|
||||
void interpolate(float step);
|
||||
@@ -39,8 +37,17 @@ void record_final_mtx_raw_tagged(const Mtx* dest, const Mtx src, uint64_t stable
|
||||
bool lookup_replacement(const void* source, Mtx out);
|
||||
bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx out);
|
||||
|
||||
void camera_eye_from_view_mtx(MtxP view_mtx, cXyz* o_eye);
|
||||
bool build_star_view(Mtx o_view, Mtx o_cam_billboard_base, cXyz* o_anchor_eye, float* o_fovy);
|
||||
void begin_presentation_camera();
|
||||
void end_presentation_camera();
|
||||
|
||||
struct PresentationCameraScope {
|
||||
PresentationCameraScope() { begin_presentation_camera(); }
|
||||
~PresentationCameraScope() { end_presentation_camera(); }
|
||||
PresentationCameraScope(const PresentationCameraScope&) = delete;
|
||||
PresentationCameraScope& operator=(const PresentationCameraScope&) = delete;
|
||||
PresentationCameraScope(PresentationCameraScope&&) = delete;
|
||||
PresentationCameraScope& operator=(PresentationCameraScope&&) = delete;
|
||||
};
|
||||
|
||||
uint64_t alloc_simple_shadow_pair_base();
|
||||
} // namespace frame_interp
|
||||
|
||||
@@ -11079,7 +11079,6 @@ static int camera_draw(camera_process_class* i_this) {
|
||||
&process->view.lookat.up, process->view.bank);
|
||||
#ifdef TARGET_PC
|
||||
dusk::frame_interp::record_camera(process, camera_id);
|
||||
dusk::frame_interp::record_final_mtx_raw(reinterpret_cast<const Mtx*>(process->view.viewMtx), process->view.viewMtx);
|
||||
#endif
|
||||
|
||||
#if WIDESCREEN_SUPPORT
|
||||
|
||||
@@ -4110,54 +4110,20 @@ void dKyr_drawStar(Mtx drawMtx, u8** tex) {
|
||||
color_reg0.b = 0xFF;
|
||||
color_reg0.a = 0xFF;
|
||||
|
||||
#if TARGET_PC
|
||||
Mtx star_gx_view;
|
||||
cXyz anchor_eye;
|
||||
f32 star_fovy = 45.0f;
|
||||
MtxP gx_load_view = drawMtx;
|
||||
bool star_use_present_view = false;
|
||||
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
star_use_present_view = dusk::frame_interp::build_star_view(star_gx_view, camMtx, &anchor_eye, &star_fovy);
|
||||
}
|
||||
|
||||
if (star_use_present_view) {
|
||||
gx_load_view = star_gx_view;
|
||||
} else {
|
||||
if (dComIfGd_getView() != NULL) {
|
||||
MTXInverse(dComIfGd_getView()->viewMtxNoTrans, camMtx);
|
||||
anchor_eye = camera->view.lookat.eye;
|
||||
star_fovy = dComIfGd_getView()->fovy;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (dComIfGd_getView() != NULL) {
|
||||
MTXInverse(dComIfGd_getView()->viewMtxNoTrans, camMtx);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (strcmp(dComIfGp_getStartStageName(), "F_SP200") == 0 && dComIfG_play_c::getLayerNo(0) == 0) {
|
||||
moon_pos = envlight->moon_pos;
|
||||
} else {
|
||||
#if TARGET_PC
|
||||
moon_pos = anchor_eye + envlight->moon_pos;
|
||||
#else
|
||||
moon_pos = camera->view.lookat.eye + envlight->moon_pos;
|
||||
#endif
|
||||
if (sp38) {
|
||||
#if TARGET_PC
|
||||
moon_pos.x = 3900.0f + anchor_eye.x;
|
||||
moon_pos.y = 8052.0f + anchor_eye.y;
|
||||
moon_pos.z = -9072.0f + anchor_eye.z;
|
||||
#else
|
||||
moon_pos.x = 3900.0f + camera->view.lookat.eye.x;
|
||||
moon_pos.y = 8052.0f + camera->view.lookat.eye.y;
|
||||
moon_pos.z = -9072.0f + camera->view.lookat.eye.z;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4193,11 +4159,7 @@ void dKyr_drawStar(Mtx drawMtx, u8** tex) {
|
||||
MTXRotRad(rotMtx, 'Z', DEG_TO_RAD(rot));
|
||||
MTXConcat(camMtx, rotMtx, camMtx);
|
||||
|
||||
#if TARGET_PC
|
||||
GXLoadPosMtxImm(gx_load_view, GX_PNMTX0);
|
||||
#else
|
||||
GXLoadPosMtxImm(drawMtx, GX_PNMTX0);
|
||||
#endif
|
||||
GXSetCurrentMtx(GX_PNMTX0);
|
||||
|
||||
rot += 0.65f;
|
||||
@@ -4205,23 +4167,12 @@ void dKyr_drawStar(Mtx drawMtx, u8** tex) {
|
||||
rot = 0.0f;
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
spBC = anchor_eye;
|
||||
#else
|
||||
spBC.x = camera->view.lookat.eye.x;
|
||||
spBC.y = camera->view.lookat.eye.y;
|
||||
spBC.z = camera->view.lookat.eye.z;
|
||||
#endif
|
||||
|
||||
f32 sp34 = -1.0f;
|
||||
int sp30 = 0;
|
||||
#if TARGET_PC
|
||||
f32 var_f30 = star_fovy / 45.0f;
|
||||
if (var_f30 >= 1.0f) {
|
||||
var_f30 = 1.0f;
|
||||
}
|
||||
var_f30 = 1.0f - var_f30;
|
||||
#else
|
||||
f32 var_f30 = 0.0f;
|
||||
|
||||
if (dComIfGd_getView() != NULL) {
|
||||
@@ -4231,7 +4182,6 @@ void dKyr_drawStar(Mtx drawMtx, u8** tex) {
|
||||
}
|
||||
var_f30 = 1.0f - var_f30;
|
||||
}
|
||||
#endif
|
||||
f32 temp_f27 = 0.28f * (1.0f - var_f30);
|
||||
|
||||
sp98.x = 0.0f;
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
#include "f_op/f_op_camera_mng.h"
|
||||
#include "dusk/frame_interpolation.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "f_op/f_op_camera_mng.h"
|
||||
|
||||
namespace {
|
||||
enum class Op : uint8_t {
|
||||
@@ -80,6 +75,36 @@ std::vector<Path*> g_current_path;
|
||||
|
||||
std::unordered_map<const Mtx*, MatrixValue> g_replacements;
|
||||
|
||||
struct CameraSnapshot {
|
||||
cXyz eye{};
|
||||
cXyz center{};
|
||||
cXyz up{};
|
||||
s16 bank{};
|
||||
f32 fovy{};
|
||||
f32 aspect{};
|
||||
f32 near_{};
|
||||
f32 far_{};
|
||||
bool valid{};
|
||||
};
|
||||
|
||||
CameraSnapshot s_cam_prev{};
|
||||
CameraSnapshot s_cam_curr{};
|
||||
|
||||
view_class s_presentation_view_backup{};
|
||||
int s_presentation_depth = 0;
|
||||
|
||||
void copy_view_to_snap(CameraSnapshot* dst, const view_class& v) {
|
||||
dst->eye = v.lookat.eye;
|
||||
dst->center = v.lookat.center;
|
||||
dst->up = v.lookat.up;
|
||||
dst->bank = v.bank;
|
||||
dst->fovy = v.fovy;
|
||||
dst->aspect = v.aspect;
|
||||
dst->near_ = v.near_;
|
||||
dst->far_ = v.far_;
|
||||
dst->valid = true;
|
||||
}
|
||||
|
||||
inline void copy_matrix(const Mtx src, Mtx dst) {
|
||||
MTXCopy(src, dst);
|
||||
}
|
||||
@@ -104,6 +129,12 @@ inline void lerp_xyz(cXyz* out, const cXyz& lhs, const cXyz& rhs, float step) {
|
||||
out->z = lhs.z * old_weight + rhs.z * step;
|
||||
}
|
||||
|
||||
static s16 lerp_bank(s16 a, s16 b, f32 t) {
|
||||
const f32 ra = S2RAD(a);
|
||||
const f32 d = remainderf(S2RAD(b) - ra, 2.0f * static_cast<f32>(M_PI));
|
||||
return cAngle::Radian_to_SAngle(ra + d * t);
|
||||
}
|
||||
|
||||
inline bool matrix_differs(const Mtx lhs, const Mtx rhs, float epsilon = 0.0001f) {
|
||||
for (size_t row = 0; row < 3; ++row) {
|
||||
for (size_t col = 0; col < 4; ++col) {
|
||||
@@ -287,6 +318,7 @@ void ensure_initialized() {
|
||||
|
||||
void begin_record() {
|
||||
ensure_initialized();
|
||||
|
||||
if (!g_enabled) {
|
||||
g_interpolating = false;
|
||||
g_sync_presentation = false;
|
||||
@@ -294,6 +326,8 @@ void begin_record() {
|
||||
g_current_recording = {};
|
||||
g_current_path.clear();
|
||||
clear_replacements();
|
||||
s_cam_prev.valid = false;
|
||||
s_cam_curr.valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -305,6 +339,15 @@ void begin_record() {
|
||||
g_recording = true;
|
||||
g_interpolating = false;
|
||||
clear_replacements();
|
||||
|
||||
::camera_process_class* cam = dComIfGp_getCamera(0);
|
||||
if (cam == nullptr) {
|
||||
s_cam_prev.valid = false;
|
||||
s_cam_curr.valid = false;
|
||||
return;
|
||||
} else {
|
||||
copy_view_to_snap(&s_cam_prev, cam->view);
|
||||
}
|
||||
}
|
||||
|
||||
void end_record() {
|
||||
@@ -454,84 +497,126 @@ bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Is there already a built-in function for this?
|
||||
void camera_eye_from_view_mtx(MtxP view_mtx, cXyz* o_eye) {
|
||||
o_eye->x = -(view_mtx[0][0] * view_mtx[0][3] + view_mtx[1][0] * view_mtx[1][3] + view_mtx[2][0] * view_mtx[2][3]);
|
||||
o_eye->y = -(view_mtx[0][1] * view_mtx[0][3] + view_mtx[1][1] * view_mtx[1][3] + view_mtx[2][1] * view_mtx[2][3]);
|
||||
o_eye->z = -(view_mtx[0][2] * view_mtx[0][3] + view_mtx[1][2] * view_mtx[1][3] + view_mtx[2][2] * view_mtx[2][3]);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct CamSnap {
|
||||
cXyz eye{};
|
||||
cXyz center{};
|
||||
cXyz up{};
|
||||
s16 bank{};
|
||||
f32 fovy{};
|
||||
bool valid{};
|
||||
};
|
||||
|
||||
CamSnap s_star_prev{};
|
||||
CamSnap s_star_curr{};
|
||||
|
||||
static void copy_view_to_snap(CamSnap* dst, const view_class& v) {
|
||||
dst->eye = v.lookat.eye;
|
||||
dst->center = v.lookat.center;
|
||||
dst->up = v.lookat.up;
|
||||
dst->bank = v.bank;
|
||||
dst->fovy = v.fovy;
|
||||
dst->valid = true;
|
||||
}
|
||||
|
||||
static void billboard_base_from_view(MtxP view_mtx, MtxP o_cam_billboard_base) {
|
||||
Mtx rot;
|
||||
MTXCopy(view_mtx, rot);
|
||||
rot[0][3] = rot[1][3] = rot[2][3] = 0.0f;
|
||||
MTXInverse(rot, o_cam_billboard_base);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void begin_record_camera() {
|
||||
::camera_process_class* cam = dComIfGp_getCamera(0);
|
||||
if (cam == nullptr) {
|
||||
return;
|
||||
}
|
||||
copy_view_to_snap(&s_star_prev, cam->view);
|
||||
}
|
||||
|
||||
void record_camera(::camera_process_class* cam, int camera_id) {
|
||||
if (!getSettings().game.enableFrameInterpolation || camera_id != 0 || cam == nullptr) {
|
||||
if (!g_enabled || camera_id != 0 || cam == nullptr) {
|
||||
return;
|
||||
}
|
||||
copy_view_to_snap(&s_star_curr, cam->view);
|
||||
copy_view_to_snap(&s_cam_curr, cam->view);
|
||||
}
|
||||
|
||||
bool build_star_view(Mtx o_view, Mtx o_cam_billboard_base, cXyz* o_anchor_eye, float* o_fovy) {
|
||||
if (!getSettings().game.enableFrameInterpolation || !s_star_prev.valid || !s_star_curr.valid) {
|
||||
return false;
|
||||
void begin_presentation_camera() {
|
||||
ensure_initialized();
|
||||
if (!g_enabled) {
|
||||
return;
|
||||
}
|
||||
if (s_presentation_depth > 0) {
|
||||
s_presentation_depth++;
|
||||
return;
|
||||
}
|
||||
if (!s_cam_prev.valid || !s_cam_curr.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
view_class* const view = dComIfGd_getView();
|
||||
if (view == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::memcpy(&s_presentation_view_backup, view, sizeof(view_class));
|
||||
|
||||
const f32 step = get_interpolation_step();
|
||||
cXyz eye;
|
||||
cXyz center;
|
||||
cXyz up;
|
||||
lerp_xyz(&eye, s_star_prev.eye, s_star_curr.eye, step);
|
||||
lerp_xyz(¢er, s_star_prev.center, s_star_curr.center, step);
|
||||
lerp_xyz(&up, s_star_prev.up, s_star_curr.up, step);
|
||||
lerp_xyz(&eye, s_cam_prev.eye, s_cam_curr.eye, step);
|
||||
lerp_xyz(¢er, s_cam_prev.center, s_cam_curr.center, step);
|
||||
lerp_xyz(&up, s_cam_prev.up, s_cam_curr.up, step);
|
||||
if (!up.normalizeRS()) {
|
||||
up = s_star_curr.up;
|
||||
up = s_cam_curr.up;
|
||||
up.normalizeRS();
|
||||
}
|
||||
|
||||
const f32 bank_rad = S2RAD(s_star_prev.bank) * (1.0f - step) + S2RAD(s_star_curr.bank) * step;
|
||||
const s16 bank = cAngle::Radian_to_SAngle(bank_rad);
|
||||
view->lookat.eye = eye;
|
||||
view->lookat.center = center;
|
||||
view->lookat.up = up;
|
||||
view->bank = lerp_bank(s_cam_prev.bank, s_cam_curr.bank, step);
|
||||
view->fovy = s_cam_prev.fovy + (s_cam_curr.fovy - s_cam_prev.fovy) * step;
|
||||
view->aspect = s_cam_prev.aspect + (s_cam_curr.aspect - s_cam_prev.aspect) * step;
|
||||
view->near_ = s_cam_prev.near_ + (s_cam_curr.near_ - s_cam_prev.near_) * step;
|
||||
view->far_ = s_cam_prev.far_ + (s_cam_curr.far_ - s_cam_prev.far_) * step;
|
||||
|
||||
mDoMtx_lookAt(o_view, &eye, ¢er, &up, bank);
|
||||
billboard_base_from_view(o_view, o_cam_billboard_base);
|
||||
// FRAME INTERP TODO: Largely copied from d_camera's camera_draw function from this point, got any better ideas?
|
||||
C_MTXPerspective(view->projMtx, view->fovy, view->aspect, view->near_, view->far_);
|
||||
mDoMtx_lookAt(view->viewMtx, &view->lookat.eye, &view->lookat.center, &view->lookat.up, view->bank);
|
||||
#if WIDESCREEN_SUPPORT
|
||||
mDoGph_gInf_c::setWideZoomProjection(view->projMtx);
|
||||
#endif
|
||||
j3dSys.setViewMtx(view->viewMtx);
|
||||
cMtx_inverse(view->viewMtx, view->invViewMtx);
|
||||
|
||||
*o_anchor_eye = eye;
|
||||
*o_fovy = s_star_prev.fovy + (s_star_curr.fovy - s_star_prev.fovy) * step;
|
||||
return true;
|
||||
bool camera_attention_status = dComIfGp_getCameraAttentionStatus(0) & 0x80;
|
||||
Z2GetAudience()->setAudioCamera(view->viewMtx, view->lookat.eye, view->lookat.center, view->fovy, view->aspect, camera_attention_status, 0, false);
|
||||
|
||||
dBgS_GndChk gndchk;
|
||||
gndchk.OnWaterGrp();
|
||||
gndchk.SetPos(&view->lookat.eye);
|
||||
f32 cross = dComIfG_Bgsp().GroundCross(&gndchk);
|
||||
if (cross != -G_CM3D_F_INF) {
|
||||
if (dComIfG_Bgsp().ChkGrpInf(gndchk, 0x100)) {
|
||||
mDoAud_getCameraMapInfo(6);
|
||||
} else {
|
||||
mDoAud_getCameraMapInfo(dComIfG_Bgsp().GetMtrlSndId(gndchk));
|
||||
}
|
||||
mDoAud_setCameraGroupInfo(dComIfG_Bgsp().GetGrpSoundId(gndchk));
|
||||
Vec spDC;
|
||||
spDC.x = view->lookat.eye.x;
|
||||
spDC.y = cross;
|
||||
spDC.z = view->lookat.eye.z;
|
||||
Z2AudioMgr::getInterface()->setCameraPolygonPos(&spDC);
|
||||
} else {
|
||||
Z2AudioMgr::getInterface()->setCameraPolygonPos(nullptr);
|
||||
}
|
||||
|
||||
MTXCopy(view->viewMtx, view->viewMtxNoTrans);
|
||||
view->viewMtxNoTrans[0][3] = 0.0f;
|
||||
view->viewMtxNoTrans[1][3] = 0.0f;
|
||||
view->viewMtxNoTrans[2][3] = 0.0f;
|
||||
cMtx_concatProjView(view->projMtx, view->viewMtx, view->projViewMtx);
|
||||
|
||||
f32 far_;
|
||||
f32 var_f30;
|
||||
if (dComIfGp_getCameraAttentionStatus(0) & 8) {
|
||||
far_ = view->far_;
|
||||
} else {
|
||||
#if DEBUG
|
||||
if (g_envHIO.mOther.mAdjustCullFar != 0) {
|
||||
var_f30 = g_envHIO.mOther.mCullFarValue;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
var_f30 = dStage_stagInfo_GetCullPoint(dComIfGp_getStageStagInfo());
|
||||
}
|
||||
far_ = var_f30;
|
||||
}
|
||||
|
||||
mDoLib_clipper::setup(view->fovy, view->aspect, view->near_, far_);
|
||||
|
||||
s_presentation_depth = 1;
|
||||
}
|
||||
|
||||
void end_presentation_camera() {
|
||||
if (s_presentation_depth == 0) {
|
||||
return;
|
||||
}
|
||||
s_presentation_depth--;
|
||||
if (s_presentation_depth > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
view_class* const view = dComIfGd_getView();
|
||||
if (view != nullptr) {
|
||||
std::memcpy(view, &s_presentation_view_backup, sizeof(view_class));
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t alloc_simple_shadow_pair_base() {
|
||||
|
||||
@@ -725,7 +725,6 @@ void fapGm_After() {
|
||||
|
||||
#ifdef TARGET_PC
|
||||
static void fapGm_Before() {
|
||||
dusk::frame_interp::begin_record_camera();
|
||||
dusk::frame_interp::begin_record();
|
||||
}
|
||||
|
||||
|
||||
@@ -2229,13 +2229,11 @@ int mDoGph_Painter() {
|
||||
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
cXyz pres_eye;
|
||||
dusk::frame_interp::camera_eye_from_view_mtx(j3dSys.getViewMtx(), &pres_eye);
|
||||
// FRAME INTERP NOTE: Currently only recalculating points for Epona's reins. Need a more global solution.
|
||||
if (daHorse_c* horse = dComIfGp_getHorseActor()) {
|
||||
horse->lerpControlPoints(dusk::frame_interp::get_interpolation_step());
|
||||
}
|
||||
g_dComIfG_gameInfo.drawlist.refresh3DlineMats(pres_eye);
|
||||
g_dComIfG_gameInfo.drawlist.refresh3DlineMats(camera_p->view.lookat.eye);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -253,7 +253,10 @@ void main01(void) {
|
||||
accumulator -= kSimStepSeconds;
|
||||
}
|
||||
dusk::frame_interp::interpolate(static_cast<float>(accumulator / kSimStepSeconds));
|
||||
cAPIGph_Painter();
|
||||
{
|
||||
dusk::frame_interp::PresentationCameraScope presentation_camera;
|
||||
cAPIGph_Painter();
|
||||
}
|
||||
} else {
|
||||
accumulator = 0.0;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user