Added heuristics to determine when to skip camera interpolation, added ability to enable developer mode in RT64
This commit is contained in:
parent
45a13d28c2
commit
569d86d901
|
@ -12,7 +12,7 @@ namespace ultramodern {
|
|||
struct WindowHandle;
|
||||
}
|
||||
|
||||
RT64::Application* RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHandle window_handle);
|
||||
RT64::Application* RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHandle window_handle, bool developer_mode);
|
||||
void RT64UpdateConfig(RT64::Application* application, const ultramodern::GraphicsConfig& old_config, const ultramodern::GraphicsConfig& new_config);
|
||||
void RT64EnableInstantPresent(RT64::Application* application);
|
||||
void RT64SendDL(uint8_t* rdram, const OSTask* task);
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit ad88e63572251bc18daa5d5055a80c4c5f42c630
|
||||
Subproject commit e95a71768e6efb55dcc9daa02843879a9f79faa6
|
|
@ -1,36 +1,181 @@
|
|||
#include "patches.h"
|
||||
#include "transform_ids.h"
|
||||
#include "z64cutscene.h"
|
||||
#include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h"
|
||||
#include "overlays/gamestates/ovl_file_choose/z_file_select.h"
|
||||
|
||||
static bool interpolate_camera = true;
|
||||
static bool camera_interpolation_forced = false;
|
||||
static bool camera_skip_interpolation_forced = false;
|
||||
static bool camera_ignore_tracking = false;
|
||||
static bool in_kaleido = false;
|
||||
static bool prev_in_kaleido = false;
|
||||
|
||||
s32 Play_ChangeCameraStatus(PlayState* this, s16 camId, s16 status) {
|
||||
s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId;
|
||||
void camera_pre_play_update(PlayState* play) {
|
||||
}
|
||||
|
||||
if (status == CAM_STATUS_ACTIVE) {
|
||||
this->activeCamId = camIdx;
|
||||
void camera_post_play_update(PlayState* play) {
|
||||
// Track whether the game is in kaleido.
|
||||
prev_in_kaleido = in_kaleido;
|
||||
|
||||
if ((play->pauseCtx.state != 0) || (play->pauseCtx.debugEditor != DEBUG_EDITOR_NONE)) {
|
||||
in_kaleido = true;
|
||||
}
|
||||
else {
|
||||
in_kaleido = false;
|
||||
}
|
||||
|
||||
recomp_printf("Changed play camera status %d %d\n", camId, status);
|
||||
|
||||
return Camera_ChangeStatus(this->cameraPtrs[camIdx], status);
|
||||
}
|
||||
|
||||
s32 View_ApplyPerspective(View* view);
|
||||
s32 View_ApplyOrtho(View* view);
|
||||
|
||||
void force_camera_interpolation() {
|
||||
camera_interpolation_forced = true;
|
||||
}
|
||||
|
||||
void force_camera_skip_interpolation() {
|
||||
camera_skip_interpolation_forced = true;
|
||||
}
|
||||
|
||||
void force_camera_ignore_tracking() {
|
||||
camera_ignore_tracking = true;
|
||||
}
|
||||
|
||||
void KaleidoScope_SetView(PauseContext* pauseCtx, f32 eyeX, f32 eyeY, f32 eyeZ) {
|
||||
Vec3f eye;
|
||||
Vec3f at;
|
||||
Vec3f up;
|
||||
|
||||
eye.x = eyeX;
|
||||
eye.y = eyeY;
|
||||
eye.z = eyeZ;
|
||||
at.x = at.y = at.z = 0.0f;
|
||||
up.x = up.z = 0.0f;
|
||||
up.y = 1.0f;
|
||||
|
||||
// @recomp Force interpolation for this view and skip tracking positions.
|
||||
force_camera_interpolation();
|
||||
force_camera_ignore_tracking();
|
||||
|
||||
View_LookAt(&pauseCtx->view, &eye, &at, &up);
|
||||
View_Apply(&pauseCtx->view,
|
||||
VIEW_ALL | VIEW_FORCE_VIEWING | VIEW_FORCE_VIEWPORT | VIEW_FORCE_PROJECTION_PERSPECTIVE);
|
||||
}
|
||||
|
||||
|
||||
void FileSelect_SetView(FileSelectState* this, f32 eyeX, f32 eyeY, f32 eyeZ) {
|
||||
Vec3f eye;
|
||||
Vec3f lookAt;
|
||||
Vec3f up;
|
||||
|
||||
eye.x = eyeX;
|
||||
eye.y = eyeY;
|
||||
eye.z = eyeZ;
|
||||
|
||||
lookAt.x = lookAt.y = lookAt.z = 0.0f;
|
||||
|
||||
up.x = up.z = 0.0f;
|
||||
up.y = 1.0f;
|
||||
|
||||
// @recomp Force interpolation for this view and skip tracking positions.
|
||||
force_camera_interpolation();
|
||||
force_camera_ignore_tracking();
|
||||
|
||||
View_LookAt(&this->view, &eye, &lookAt, &up);
|
||||
View_Apply(&this->view, VIEW_ALL | VIEW_FORCE_VIEWING | VIEW_FORCE_VIEWPORT | VIEW_FORCE_PROJECTION_PERSPECTIVE);
|
||||
}
|
||||
|
||||
bool should_interpolate_perspective(Vec3f* eye, Vec3f* at) {
|
||||
static Vec3f prev_eye = {0,0,0};
|
||||
static Vec3f prev_at = {0,0,0};
|
||||
static Vec3f eye_velocity = {0,0,0};
|
||||
static Vec3f at_velocity = {0,0,0};
|
||||
|
||||
Vec3f predicted_eye;
|
||||
Vec3f predicted_at;
|
||||
// Predict the new eye and at positions based on the previous velocity and positions.
|
||||
Math_Vec3f_Sum(&prev_eye, &eye_velocity, &predicted_eye);
|
||||
Math_Vec3f_Sum(&prev_at, &at_velocity, &predicted_at);
|
||||
|
||||
// Calculate the current velocities from the previous and current positions.
|
||||
Math_Vec3f_Diff(eye, &prev_eye, &eye_velocity);
|
||||
Math_Vec3f_Diff(at, &prev_at, &at_velocity);
|
||||
|
||||
// Compare the predicted positions to the real positions.
|
||||
float eye_dist = Math_Vec3f_DistXYZ(&predicted_eye, eye);
|
||||
float at_dist = Math_Vec3f_DistXYZ(&predicted_at, at);
|
||||
|
||||
// Compare the velocities of the eye and at positions.
|
||||
float velocity_diff = Math_Vec3f_DistXYZ(&eye_velocity, &at_velocity);
|
||||
|
||||
// Update the tracking for the previous positions with the new ones.
|
||||
prev_eye = *eye;
|
||||
prev_at = *at;
|
||||
|
||||
// These numbers are all picked via testing.
|
||||
|
||||
// If the velocity of both positions was the same, then they're moving together and should interpolate.
|
||||
if (velocity_diff <= 3.0f && eye_dist <= 100.0f && at_dist <= 100.0f) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the focus or position are basically the same across frames and the eye didn't move too far then it should probably be interpolated.
|
||||
if (at_dist <= 10.0f && eye_dist <= 300.0f) {
|
||||
return true;
|
||||
}
|
||||
if (eye_dist <= 10.0f && at_dist <= 300.0f) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (velocity_diff > 50.0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (at_dist > 50.0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eye_dist > 300.0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply view to POLY_OPA_DISP, POLY_XLU_DISP (and OVERLAY_DISP if ortho)
|
||||
*/
|
||||
void View_Apply(View* view, s32 mask) {
|
||||
mask = (view->flags & mask) | (mask >> 4);
|
||||
|
||||
// @recomp Determine if the camera should be interpolated this frame.
|
||||
bool interpolate_camera = false;
|
||||
|
||||
if (mask & VIEW_PROJECTION_ORTHO) {
|
||||
View_ApplyOrtho(view);
|
||||
} else {
|
||||
View_ApplyPerspective(view);
|
||||
|
||||
// @recomp Determine if interpolation should occur based on the new eye and at positions.
|
||||
if (!camera_ignore_tracking) {
|
||||
interpolate_camera = should_interpolate_perspective(&view->eye, &view->at);
|
||||
}
|
||||
}
|
||||
|
||||
camera_ignore_tracking = false;
|
||||
|
||||
// Force skipping interpolation if the previous view was kaleido and this one isn't.
|
||||
if (prev_in_kaleido && !in_kaleido) {
|
||||
camera_skip_interpolation_forced = true;
|
||||
recomp_printf("exited kaleido\n");
|
||||
}
|
||||
|
||||
// @recomp Apply camera interpolation overrides.
|
||||
if (camera_skip_interpolation_forced) {
|
||||
interpolate_camera = false;
|
||||
}
|
||||
else if (camera_interpolation_forced) {
|
||||
interpolate_camera = true;
|
||||
}
|
||||
|
||||
// @recomp Tag the camera matrices
|
||||
GraphicsContext* gfxCtx = view->gfxCtx;
|
||||
OPEN_DISPS(gfxCtx);
|
||||
|
@ -48,10 +193,10 @@ void View_Apply(View* view, s32 mask) {
|
|||
G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_INTERPOLATE, G_EX_ORDER_LINEAR);
|
||||
gEXMatrixGroupSimple(POLY_XLU_DISP++, CAMERA_TRANSFORM_ID_START, G_EX_NOPUSH, G_MTX_PROJECTION,
|
||||
G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_INTERPOLATE, G_EX_ORDER_LINEAR);
|
||||
recomp_printf("Skipped camera interpolation\n");
|
||||
}
|
||||
|
||||
interpolate_camera = true;
|
||||
camera_interpolation_forced = false;
|
||||
camera_skip_interpolation_forced = false;
|
||||
|
||||
CLOSE_DISPS(gfxCtx);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "patches.h"
|
||||
#include "input.h"
|
||||
#include "z64debug_display.h"
|
||||
|
||||
void Message_FindMessage(PlayState* play, u16 textId);
|
||||
extern SceneEntranceTableEntry sSceneEntranceTable[];
|
||||
|
@ -55,43 +54,3 @@ void debug_play_update(PlayState* play) {
|
|||
do_warp(play, pending_warp);
|
||||
}
|
||||
}
|
||||
|
||||
extern Input D_801F6C18;
|
||||
|
||||
// @recomp Patched to add a point to modify state before the frame's update.
|
||||
void Play_Main(GameState* thisx) {
|
||||
static Input* prevInput = NULL;
|
||||
PlayState* this = (PlayState*)thisx;
|
||||
|
||||
// @recomp
|
||||
debug_play_update(this);
|
||||
|
||||
// @recomp avoid unused variable warning
|
||||
(void)prevInput;
|
||||
|
||||
prevInput = CONTROLLER1(&this->state);
|
||||
DebugDisplay_Init();
|
||||
|
||||
{
|
||||
GraphicsContext* gfxCtx = this->state.gfxCtx;
|
||||
|
||||
if (1) {
|
||||
this->state.gfxCtx = NULL;
|
||||
}
|
||||
Play_Update(this);
|
||||
this->state.gfxCtx = gfxCtx;
|
||||
}
|
||||
|
||||
{
|
||||
Input input = *CONTROLLER1(&this->state);
|
||||
|
||||
if (1) {
|
||||
*CONTROLLER1(&this->state) = D_801F6C18;
|
||||
}
|
||||
Play_Draw(this);
|
||||
*CONTROLLER1(&this->state) = input;
|
||||
}
|
||||
|
||||
CutsceneManager_Update();
|
||||
CutsceneManager_ClearWaiting();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
#include "play_patches.h"
|
||||
#include "z64debug_display.h"
|
||||
|
||||
extern Input D_801F6C18;
|
||||
|
||||
// @recomp Patched to add hooks for various added functionality.
|
||||
void Play_Main(GameState* thisx) {
|
||||
static Input* prevInput = NULL;
|
||||
PlayState* this = (PlayState*)thisx;
|
||||
|
||||
// @recomp
|
||||
debug_play_update(this);
|
||||
|
||||
// @recomp avoid unused variable warning
|
||||
(void)prevInput;
|
||||
|
||||
prevInput = CONTROLLER1(&this->state);
|
||||
DebugDisplay_Init();
|
||||
|
||||
{
|
||||
GraphicsContext* gfxCtx = this->state.gfxCtx;
|
||||
|
||||
if (1) {
|
||||
this->state.gfxCtx = NULL;
|
||||
}
|
||||
camera_pre_play_update(this);
|
||||
Play_Update(this);
|
||||
camera_post_play_update(this);
|
||||
this->state.gfxCtx = gfxCtx;
|
||||
}
|
||||
|
||||
{
|
||||
Input input = *CONTROLLER1(&this->state);
|
||||
|
||||
if (1) {
|
||||
*CONTROLLER1(&this->state) = D_801F6C18;
|
||||
}
|
||||
Play_Draw(this);
|
||||
*CONTROLLER1(&this->state) = input;
|
||||
}
|
||||
|
||||
CutsceneManager_Update();
|
||||
CutsceneManager_ClearWaiting();
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef __PLAY_PATCHES_H__
|
||||
#define __PLAY_PATCHES_H__
|
||||
|
||||
#include "patches.h"
|
||||
|
||||
void debug_play_update(PlayState* play);
|
||||
void camera_pre_play_update(PlayState* play);
|
||||
void camera_post_play_update(PlayState* play);
|
||||
|
||||
#endif
|
|
@ -34,4 +34,7 @@ static inline u32 actor_transform_id(Actor* actor) {
|
|||
return (actor_id * ACTOR_TRANSFORM_ID_COUNT) + ACTOR_TRANSFORM_ID_START;
|
||||
}
|
||||
|
||||
void force_camera_interpolation();
|
||||
void force_camera_skip_interpolation();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -15,6 +15,14 @@
|
|||
constexpr std::u8string_view graphics_filename = u8"graphics.json";
|
||||
constexpr std::u8string_view controls_filename = u8"controls.json";
|
||||
|
||||
constexpr auto res_default = ultramodern::Resolution::Auto;
|
||||
constexpr auto wm_default = ultramodern::WindowMode::Windowed;
|
||||
constexpr auto ar_default = RT64::UserConfiguration::AspectRatio::Expand;
|
||||
constexpr auto msaa_default = RT64::UserConfiguration::Antialiasing::MSAA4X;
|
||||
constexpr auto rr_default = RT64::UserConfiguration::RefreshRate::Display;
|
||||
constexpr int rr_manual_default = 60;
|
||||
constexpr bool developer_mode_default = false;
|
||||
|
||||
namespace ultramodern {
|
||||
void to_json(json& j, const GraphicsConfig& config) {
|
||||
j = json{
|
||||
|
@ -24,16 +32,29 @@ namespace ultramodern {
|
|||
{"msaa_option", config.msaa_option},
|
||||
{"rr_option", config.rr_option},
|
||||
{"rr_manual_value", config.rr_manual_value},
|
||||
{"developer_mode", config.developer_mode},
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void from_or_default(const json& j, const std::string& key, T& out, T default_value) {
|
||||
auto find_it = j.find(key);
|
||||
if (find_it != j.end()) {
|
||||
find_it->get_to(out);
|
||||
}
|
||||
else {
|
||||
out = default_value;
|
||||
}
|
||||
}
|
||||
|
||||
void from_json(const json& j, GraphicsConfig& config) {
|
||||
j.at("res_option") .get_to(config.res_option);
|
||||
j.at("wm_option") .get_to(config.wm_option);
|
||||
j.at("ar_option") .get_to(config.ar_option);
|
||||
j.at("msaa_option") .get_to(config.msaa_option);
|
||||
j.at("rr_option") .get_to(config.rr_option);
|
||||
j.at("rr_manual_value").get_to(config.rr_manual_value);
|
||||
from_or_default(j, "res_option", config.res_option, res_default);
|
||||
from_or_default(j, "wm_option", config.wm_option, wm_default);
|
||||
from_or_default(j, "ar_option", config.ar_option, ar_default);
|
||||
from_or_default(j, "msaa_option", config.msaa_option, msaa_default);
|
||||
from_or_default(j, "rr_option", config.rr_option, rr_default);
|
||||
from_or_default(j, "rr_manual_value", config.rr_manual_value, rr_manual_default);
|
||||
from_or_default(j, "developer_mode", config.developer_mode, developer_mode_default);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,12 +131,13 @@ void recomp::reset_input_bindings() {
|
|||
|
||||
void recomp::reset_graphics_options() {
|
||||
ultramodern::GraphicsConfig new_config{};
|
||||
new_config.res_option = ultramodern::Resolution::Auto;
|
||||
new_config.wm_option = ultramodern::WindowMode::Windowed;
|
||||
new_config.ar_option = RT64::UserConfiguration::AspectRatio::Expand;
|
||||
new_config.msaa_option = RT64::UserConfiguration::Antialiasing::MSAA4X;
|
||||
new_config.rr_option = RT64::UserConfiguration::RefreshRate::Display;
|
||||
new_config.rr_manual_value = 60;
|
||||
new_config.res_option = res_default;
|
||||
new_config.wm_option = wm_default;
|
||||
new_config.ar_option = ar_default;
|
||||
new_config.msaa_option = msaa_default;
|
||||
new_config.rr_option = rr_default;
|
||||
new_config.rr_manual_value = rr_manual_default;
|
||||
new_config.developer_mode = developer_mode_default;
|
||||
ultramodern::set_graphics_config(new_config);
|
||||
}
|
||||
|
||||
|
|
|
@ -61,11 +61,11 @@ typedef struct {
|
|||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
extern "C" RT64::Application* InitiateGFXWindows(GFX_INFO Gfx_Info, HWND hwnd, DWORD threadId);
|
||||
extern "C" RT64::Application* InitiateGFXWindows(GFX_INFO Gfx_Info, HWND hwnd, DWORD threadId, uint8_t debug);
|
||||
#elif defined(__ANDROID__)
|
||||
static_assert(false && "Unimplemented");
|
||||
#elif defined(__linux__)
|
||||
extern "C" RT64::Application* InitiateGFXLinux(GFX_INFO Gfx_Info, Window window, Display *display);
|
||||
extern "C" RT64::Application* InitiateGFXLinux(GFX_INFO Gfx_Info, Window window, Display *display, uint8_t debug);
|
||||
#else
|
||||
static_assert(false && "Unimplemented");
|
||||
#endif
|
||||
|
@ -130,22 +130,10 @@ RT64::UserConfiguration::Antialiasing compute_max_supported_aa(RT64::RenderSampl
|
|||
return RT64::UserConfiguration::Antialiasing::None;
|
||||
}
|
||||
|
||||
RT64::Application* RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHandle window_handle) {
|
||||
RT64::Application* RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHandle window_handle, bool debug) {
|
||||
set_rt64_hooks();
|
||||
// Dynamic loading
|
||||
//auto RT64 = LoadLibrary("RT64.dll");
|
||||
//if (RT64 == 0) {
|
||||
// fprintf(stdout, "Failed to load RT64\n");
|
||||
// std::exit(EXIT_FAILURE);
|
||||
//}
|
||||
//GET_FUNC(RT64, InitiateGFX);
|
||||
//GET_FUNC(RT64, ProcessRDPList);
|
||||
//GET_FUNC(RT64, ProcessDList);
|
||||
//GET_FUNC(RT64, UpdateScreen);
|
||||
|
||||
GFX_INFO gfx_info{};
|
||||
// gfx_info.hWnd = window_handle;
|
||||
// gfx_info.hStatusBar = nullptr;
|
||||
|
||||
gfx_info.HEADER = rom;
|
||||
gfx_info.RDRAM = rdram;
|
||||
|
@ -185,11 +173,11 @@ RT64::Application* RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHan
|
|||
gfx_info.RDRAM_SIZE = &RDRAM_SIZE;
|
||||
|
||||
#if defined(_WIN32)
|
||||
RT64::Application* ret = InitiateGFXWindows(gfx_info, window_handle.window, window_handle.thread_id);
|
||||
RT64::Application* ret = InitiateGFXWindows(gfx_info, window_handle.window, window_handle.thread_id, debug);
|
||||
#elif defined(__ANDROID__)
|
||||
static_assert(false && "Unimplemented");
|
||||
#elif defined(__linux__)
|
||||
RT64::Application* ret = InitiateGFXLinux(gfx_info, window_handle.window, window_handle.display);
|
||||
RT64::Application* ret = InitiateGFXLinux(gfx_info, window_handle.window, window_handle.display, debug);
|
||||
#else
|
||||
static_assert(false && "Unimplemented");
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace ultramodern {
|
|||
RT64::UserConfiguration::Antialiasing msaa_option;
|
||||
RT64::UserConfiguration::RefreshRate rr_option;
|
||||
int rr_manual_value;
|
||||
bool developer_mode;
|
||||
|
||||
auto operator<=>(const GraphicsConfig& rhs) const = default;
|
||||
};
|
||||
|
|
|
@ -276,7 +276,7 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read
|
|||
|
||||
ultramodern::GraphicsConfig old_config;
|
||||
|
||||
RT64::Application* application = RT64Init(rom, rdram, window_handle);
|
||||
RT64::Application* application = RT64Init(rom, rdram, window_handle, cur_config.load().developer_mode);
|
||||
|
||||
if (application == nullptr) {
|
||||
throw std::runtime_error("Failed to initialize RT64!");
|
||||
|
|
Loading…
Reference in New Issue