WIP: uncapped framerate 9

This commit is contained in:
MysterD 2022-04-29 21:28:14 -07:00
parent 58cee9098b
commit 582cae97ed
13 changed files with 215 additions and 175 deletions

View file

@ -124,7 +124,7 @@ struct GraphNodePerspective
struct DisplayListNode
{
Mtx *transform;
void *transformPrev;
Mtx *transformPrev;
void *displayList;
struct DisplayListNode *next;
};

View file

@ -162,9 +162,9 @@ static u8 sShadowInterpCount = 0;
struct {
Gfx *pos;
void *mtx;
Mtx *mtx;
Mtx *mtxPrev;
void *displayList;
void *mtxPrev;
Mtx interp;
} gMtxTbl[6400];
s32 gMtxTblSize;
@ -300,11 +300,13 @@ static void geo_process_master_list_sub(struct GraphNodeMasterList *node) {
if ((currList = node->listHeads[i]) != NULL) {
gDPSetRenderMode(gDisplayListHead++, modeList->modes[i], mode2List->modes[i]);
while (currList != NULL) {
detect_and_skip_mtx_interpolation(&currList->transform, &currList->transformPrev);
if ((u32) gMtxTblSize < sizeof(gMtxTbl) / sizeof(gMtxTbl[0])) {
gMtxTbl[gMtxTblSize].pos = gDisplayListHead;
gMtxTbl[gMtxTblSize].mtx = currList->transform;
gMtxTbl[gMtxTblSize].mtxPrev = currList->transformPrev;
gMtxTbl[gMtxTblSize++].displayList = currList->displayList;
gMtxTbl[gMtxTblSize].displayList = currList->displayList;
gMtxTblSize++;
}
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(currList->transformPrev),
G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);

View file

@ -131,7 +131,8 @@ unsigned int configNetworkSystem = 0;
char configPlayerName[MAX_PLAYER_STRING] = "";
unsigned int configPlayerModel = 0;
unsigned int configPlayerPalette = 0;
unsigned int config60Fps = 1;
bool configUncappedFramerate = true;
unsigned int configFrameLimit = 60;
unsigned int configDrawDistance = 5;
bool configDisablePopups = 0;
bool configDisableDownloadedModels = 0;
@ -196,9 +197,10 @@ static const struct ConfigOption options[] = {
{.name = "debug_offset", .type = CONFIG_TYPE_U64 , .u64Value = &gPcDebug.bhvOffset},
{.name = "debug_tags", .type = CONFIG_TYPE_U64 , .u64Value = gPcDebug.tags},
// coop-specific
{.name = "uncapped_framerate", .type = CONFIG_TYPE_BOOL , .boolValue = &configUncappedFramerate},
{.name = "frame_limit" , .type = CONFIG_TYPE_UINT , .uintValue = &configFrameLimit},
{.name = "amount_of_players", .type = CONFIG_TYPE_UINT , .uintValue = &configAmountofPlayers},
{.name = "bubble_death", .type = CONFIG_TYPE_BOOL , .boolValue = &configBubbleDeath},
{.name = "coop_60fps", .type = CONFIG_TYPE_UINT , .uintValue = &config60Fps},
{.name = "coop_draw_distance", .type = CONFIG_TYPE_UINT , .uintValue = &configDrawDistance},
{.name = "coop_host_port", .type = CONFIG_TYPE_UINT , .uintValue = &configHostPort},
{.name = "coop_host_save_slot", .type = CONFIG_TYPE_UINT , .uintValue = &configHostSaveSlot},

View file

@ -87,7 +87,8 @@ extern unsigned int configNetworkSystem;
extern char configPlayerName[];
extern unsigned int configPlayerModel;
extern unsigned int configPlayerPalette;
extern unsigned int config60Fps;
extern bool configUncappedFramerate;
extern unsigned int configFrameLimit;
extern unsigned int configDrawDistance;
extern bool configDisablePopups;
extern bool configDisableDownloadedModels;

View file

@ -22,10 +22,27 @@
// The full height for the body.
#define BODY_HEIGHT CHECKBOXES_FULL_SIZE + SELECTION_BOXES_FULL_SIZE + BUTTON_SIZES
static struct DjuiInputbox* sFrameLimitInput = NULL;
static void djui_panel_display_apply(UNUSED struct DjuiBase* caller) {
configWindow.settings_changed = true;
}
static void djui_panel_display_uncapped_change(UNUSED struct DjuiBase* caller) {
djui_base_set_enabled(&sFrameLimitInput->base, !configUncappedFramerate);
}
static void djui_panel_display_frame_limit_text_change(struct DjuiBase* caller) {
struct DjuiInputbox* inputbox1 = (struct DjuiInputbox*)caller;
s32 frameLimit = atoi(inputbox1->buffer);
if (frameLimit >= 30 && frameLimit <= 3000) {
djui_inputbox_set_text_color(inputbox1, 0, 0, 0, 255);
configFrameLimit = frameLimit;
} else {
djui_inputbox_set_text_color(inputbox1, 255, 0, 0, 255);
}
}
void djui_panel_display_create(struct DjuiBase* caller) {
f32 bodyHeight = BODY_HEIGHT;
@ -40,39 +57,60 @@ void djui_panel_display_create(struct DjuiBase* caller) {
djui_interactable_hook_value_change(&checkbox1->base, djui_panel_display_apply);
defaultBase = &checkbox1->base;
struct DjuiCheckbox* checkbox5 = djui_checkbox_create(&body->base, "Disable Popups", &configDisablePopups);
djui_base_set_size_type(&checkbox5->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&checkbox5->base, 1.0f, 32);
struct DjuiCheckbox* checkbox6 = djui_checkbox_create(&body->base, "Disable Downloaded Models", &configDisableDownloadedModels);
djui_base_set_size_type(&checkbox6->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&checkbox6->base, 1.0f, 32);
#ifdef EXTERNAL_DATA
struct DjuiCheckbox* checkbox7 = djui_checkbox_create(&body->base, "Preload Textures", &configPrecacheRes);
djui_base_set_size_type(&checkbox7->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&checkbox7->base, 1.0f, 32);
#endif
struct DjuiCheckbox* checkbox2 = djui_checkbox_create(&body->base, "VSync", &configWindow.vsync);
djui_base_set_size_type(&checkbox2->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&checkbox2->base, 1.0f, 32);
djui_interactable_hook_value_change(&checkbox2->base, djui_panel_display_apply);
struct DjuiCheckbox* checkbox3 = djui_checkbox_create(&body->base, "HUD", &configHUD);
struct DjuiCheckbox* checkbox3 = djui_checkbox_create(&body->base, "Uncapped Framerate", &configUncappedFramerate);
djui_base_set_size_type(&checkbox3->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&checkbox3->base, 1.0f, 32);
djui_interactable_hook_value_change(&checkbox3->base, djui_panel_display_uncapped_change);
struct DjuiCheckbox* checkbox4 = djui_checkbox_create(&body->base, "Disable Popups", &configDisablePopups);
djui_base_set_size_type(&checkbox4->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&checkbox4->base, 1.0f, 32);
struct DjuiRect* rect1 = djui_rect_create(&body->base);
djui_base_set_size_type(&rect1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&rect1->base, 1.0f, 32);
djui_base_set_color(&rect1->base, 0, 0, 0, 0);
{
if (configFrameLimit < 30) { configFrameLimit = 30; }
if (configFrameLimit > 3000) { configFrameLimit = 3000; }
struct DjuiText* text1 = djui_text_create(&rect1->base, "Frame Limit");
djui_base_set_size_type(&text1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_color(&text1->base, 200, 200, 200, 255);
djui_base_set_size(&text1->base, 0.485f, 64);
djui_base_set_alignment(&text1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP);
struct DjuiCheckbox* checkbox5 = djui_checkbox_create(&body->base, "Disable Downloaded Models", &configDisableDownloadedModels);
djui_base_set_size_type(&checkbox5->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&checkbox5->base, 1.0f, 32);
#ifdef EXTERNAL_DATA
struct DjuiCheckbox* checkbox6 = djui_checkbox_create(&body->base, "Preload Textures", &configPrecacheRes);
djui_base_set_size_type(&checkbox6->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&checkbox6->base, 1.0f, 32);
#endif
struct DjuiInputbox* inputbox1 = djui_inputbox_create(&rect1->base, 32);
djui_base_set_size_type(&inputbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&inputbox1->base, 0.5f, 32);
djui_base_set_alignment(&inputbox1->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
char frameLimitString[32] = { 0 };
snprintf(frameLimitString, 32, "%d", configFrameLimit);
djui_inputbox_set_text(inputbox1, frameLimitString);
djui_interactable_hook_value_change(&inputbox1->base, djui_panel_display_frame_limit_text_change);
djui_base_set_enabled(&inputbox1->base, !configUncappedFramerate);
sFrameLimitInput = inputbox1;
}
char* filterChoices[3] = { "Nearest", "Linear", "Tripoint" };
struct DjuiSelectionbox* selectionbox1 = djui_selectionbox_create(&body->base, "Filtering", filterChoices, 3, &configFiltering);
djui_base_set_size_type(&selectionbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&selectionbox1->base, 1.0f, 32);
char* fpsChoices[3] = { "30", "60" };
struct DjuiSelectionbox* selectionbox2 = djui_selectionbox_create(&body->base, "FPS", fpsChoices, 2, &config60Fps);
djui_base_set_size_type(&selectionbox2->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&selectionbox2->base, 1.0f, 32);
char* drawDistanceChoices[6] = { "0.5x", "1x", "1.5x", "3x", "10x", "100x" };
struct DjuiSelectionbox* selectionbox3 = djui_selectionbox_create(&body->base, "Draw Distance", drawDistanceChoices, 6, &configDrawDistance);
djui_base_set_size_type(&selectionbox3->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);

View file

@ -76,20 +76,11 @@ static bool gfx_dummy_wm_start_frame(void) {
return true;
}
static inline void sync_framerate_with_timer(void) {
f64 curTime = clock_elapsed_f64();
if (curTime < sFrameTargetTime) {
u32 delayMs = (sFrameTargetTime - curTime) * 1000.0;
if (delayMs > 0) {
sleep_ms(delayMs);
}
}
f64 frameTime = config60Fps ? (sFrameTime / 2.0) : sFrameTime;
sFrameTargetTime += frameTime;
static void gfx_dummy_wm_delay(u32 ms) {
sleep_ms(ms);
}
static void gfx_dummy_wm_swap_buffers_begin(void) {
sync_framerate_with_timer();
}
static void gfx_dummy_wm_swap_buffers_end(void) {
@ -209,7 +200,8 @@ struct GfxWindowManagerAPI gfx_dummy_wm_api = {
gfx_dummy_wm_stop_text_input,
gfx_dummy_wm_get_clipboard_text,
gfx_dummy_wm_set_clipboard_text,
gfx_dummy_wm_set_cursor_visible
gfx_dummy_wm_set_cursor_visible,
gfx_dummy_wm_delay
};
struct GfxRenderingAPI gfx_dummy_renderer_api = {

View file

@ -522,35 +522,12 @@ static bool gfx_dxgi_start_frame(void) {
}*/
dxgi.length_in_vsync_frames = configWindow.vsync;
f64 curTime = clock_elapsed_f64();
f64 frameTime = config60Fps ? (sFrameTime / 2.0) : sFrameTime;
if (curTime > sFrameTargetTime) {
sFrameTargetTime += frameTime;
if (curTime > sFrameTargetTime + frameTime * 3) {
sFrameTargetTime = curTime;
}
dxgi.dropped_frame = true;
return false;
}
dxgi.dropped_frame = false;
return true;
}
static inline void sync_framerate_with_timer(void) {
f64 curTime = clock_elapsed_f64();
if (curTime < sFrameTargetTime) {
u32 delayMs = (sFrameTargetTime - curTime) * 1000.0;
if (delayMs > 0) {
Sleep(delayMs);
}
}
f64 frameTime = config60Fps ? (sFrameTime / 2.0) : sFrameTime;
sFrameTargetTime += frameTime;
}
static void gfx_dxgi_swap_buffers_begin(void) {
sync_framerate_with_timer();
ThrowIfFailed(dxgi.swap_chain->Present(dxgi.length_in_vsync_frames, 0));
UINT this_present_id;
if (dxgi.swap_chain->GetLastPresentCount(&this_present_id) == S_OK) {
@ -654,6 +631,10 @@ ComPtr<IDXGISwapChain1> gfx_dxgi_create_swap_chain(IUnknown *device) {
return dxgi.swap_chain;
}
void gfx_dxgi_delay(u32 ms) {
Sleep(ms);
}
HWND gfx_dxgi_get_h_wnd(void) {
return dxgi.h_wnd;
}
@ -722,6 +703,7 @@ struct GfxWindowManagerAPI gfx_dxgi = {
gfx_dxgi_get_clipboard_text,
gfx_dxgi_set_clipboard_text,
gfx_dxgi_set_cursor_visible,
gfx_dxgi_delay,
};
#endif

View file

@ -168,18 +168,7 @@ static bool gfx_sdl_start_frame(void) {
return true;
}
static inline void sync_framerate_with_timer(void) {
static Uint32 last_time = 0;
// get base timestamp on the first frame (might be different from 0)
if (last_time == 0) last_time = SDL_GetTicks();
const int elapsed = SDL_GetTicks() - last_time;
if (elapsed < frame_time)
SDL_Delay(frame_time - elapsed);
last_time += frame_time;
}
static void gfx_sdl_swap_buffers_begin(void) {
sync_framerate_with_timer();
SDL_GL_SwapBuffers();
}
@ -190,6 +179,9 @@ static double gfx_sdl_get_time(void) {
return 0.0;
}
static void gfx_sdl_delay(u32 ms) {
SDL_Delay(ms);
}
static void gfx_sdl_shutdown(void) {
if (SDL_WasInit(0))
@ -218,6 +210,7 @@ struct GfxWindowManagerAPI gfx_sdl = {
gfx_sdl_get_clipboard_text,
gfx_sdl_set_clipboard_text,
gfx_sdl_set_cursor_visible,
gfx_sdl_delay,
};
#endif // BACKEND_WM

View file

@ -58,37 +58,6 @@ static void (*kb_text_input)(char*) = NULL;
#define IS_FULLSCREEN() ((SDL_GetWindowFlags(wnd) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0)
int test_vsync(void) {
// Even if SDL_GL_SetSwapInterval succeeds, it doesn't mean that VSync actually works.
// A 60 Hz monitor should have a swap interval of 16.67 milliseconds.
// Try to detect the length of a vsync by swapping buffers some times.
// Since the graphics card may enqueue a fixed number of frames,
// first send in four dummy frames to hopefully fill the queue.
// This method will fail if the refresh rate is changed, which, in
// combination with that we can't control the queue size (i.e. lag)
// is a reason this generic SDL2 backend should only be used as last resort.
for (int32_t i = 0; i < 8; ++i)
SDL_GL_SwapWindow(wnd);
Uint32 start = SDL_GetTicks();
SDL_GL_SwapWindow(wnd);
SDL_GL_SwapWindow(wnd);
SDL_GL_SwapWindow(wnd);
SDL_GL_SwapWindow(wnd);
Uint32 end = SDL_GetTicks();
const float average = 4.0 * 1000.0 / (end - start);
if (average > 27.0f && average < 33.0f) return 1;
if (average > 57.0f && average < 63.0f) return 2;
if (average > 86.0f && average < 94.0f) return 3;
if (average > 115.0f && average < 125.0f) return 4;
if (average > 234.0f && average < 246.0f) return 8;
return 0;
}
static inline void gfx_sdl_set_vsync(const bool enabled) {
SDL_GL_SetSwapInterval(enabled);
}
@ -248,33 +217,9 @@ static void gfx_sdl_set_keyboard_callbacks(kb_callback_t on_key_down, kb_callbac
static bool gfx_sdl_start_frame(void) {
return true;
f64 curTime = clock_elapsed_f64();
f64 frameTime = config60Fps ? (sFrameTime / 2.0) : sFrameTime;
if (curTime > sFrameTargetTime) {
sFrameTargetTime += frameTime;
if (curTime > sFrameTargetTime + frameTime * 3) {
sFrameTargetTime = curTime;
}
return false;
}
return true;
}
static inline void sync_framerate_with_timer(void) {
return;
f64 curTime = clock_elapsed_f64();
if (curTime < sFrameTargetTime) {
u32 delayMs = (sFrameTargetTime - curTime) * 1000.0;
if (delayMs > 0) {
SDL_Delay(delayMs);
}
}
f64 frameTime = config60Fps ? (sFrameTime / 2.0) : sFrameTime;
sFrameTargetTime += frameTime;
}
static void gfx_sdl_swap_buffers_begin(void) {
sync_framerate_with_timer();
SDL_GL_SwapWindow(wnd);
}
@ -285,6 +230,9 @@ static double gfx_sdl_get_time(void) {
return 0.0;
}
static void gfx_sdl_delay(u32 ms) {
SDL_Delay(ms);
}
static void gfx_sdl_shutdown(void) {
if (SDL_WasInit(0)) {
@ -316,6 +264,7 @@ struct GfxWindowManagerAPI gfx_sdl = {
gfx_sdl_get_clipboard_text,
gfx_sdl_set_clipboard_text,
gfx_sdl_set_cursor_visible,
gfx_sdl_delay,
};
#endif // BACKEND_WM

View file

@ -25,6 +25,7 @@ struct GfxWindowManagerAPI {
char* (*get_clipboard_text)(void);
void (*set_clipboard_text)(char*);
void (*set_cursor_visible)(bool);
void (*delay)(unsigned int ms);
};
#endif

View file

@ -69,6 +69,14 @@ f32 gRenderingDelta = 0;
f32 gGameSpeed = 1.0f; // DO NOT COMMIT
#define FRAMERATE 30
static const f64 sFrameTime = (1.0 / ((double)FRAMERATE));
static f64 sFrameTargetTime = 0;
static f64 sFrameTimeStart;
static f64 sLastFrameTimeStart;
static f32 sAvgFrames = 1;
static f32 sAvgFps = 0;
static struct AudioAPI *audio_api;
struct GfxWindowManagerAPI *wm_api;
static struct GfxRenderingAPI *rendering_api;
@ -142,37 +150,59 @@ static inline void patch_interpolations(f32 delta) {
patch_djui_interpolated(delta);
}
void produce_uncapped_frames(void) {
#define FRAMERATE 30
static const f64 sFrameTime = (1.0 / ((double)FRAMERATE));
static f64 sFrameTargetTime = 0;
static void delay_frame(void) {
if (configUncappedFramerate) { return; }
static f32 sDelayRounding = 0;
f64 targetDelta = 1.0 / (f64)configFrameLimit;
f64 actualDelta = clock_elapsed_f64() - sLastFrameTimeStart;
if (actualDelta < targetDelta) {
f64 delay = sDelayRounding + ((targetDelta - actualDelta) * 1000.0);
sDelayRounding = delay - (u32)delay;
wm_api->delay((u32)delay);
}
}
void produce_interpolation_frames_and_delay(void) {
gRenderingInterpolated = true;
f64 startTime = clock_elapsed_f64();
f64 curTime = startTime;
u64 frames = 0;
// sanity check target time to deal with hangs and such
f64 curTime = clock_elapsed_f64();
if (fabs(sFrameTargetTime - curTime) > 1) {
sFrameTargetTime = curTime;
}
delay_frame();
u64 frames = 1;
while ((curTime = clock_elapsed_f64()) < sFrameTargetTime) {
sLastFrameTimeStart = curTime;
gfx_start_frame();
f32 delta = MIN((curTime - startTime) / (sFrameTargetTime - startTime), 1);
f32 delta = (configWindow.vsync || !configUncappedFramerate)
? frames / sAvgFrames
: MIN((curTime - sFrameTimeStart) / (sFrameTargetTime - sFrameTimeStart), 1);
gRenderingDelta = delta;
patch_interpolations(delta);
send_display_list(gGfxSPTask);
gfx_end_frame();
frames++;
delay_frame();
}
f32 fps = frames / (clock_elapsed_f64() - sFrameTimeStart);
sAvgFps = sAvgFps * 0.99 + fps * 0.01;
sAvgFrames = sAvgFrames * 0.9 + frames * 0.1;
sFrameTargetTime += sFrameTime * gGameSpeed;
gRenderingInterpolated = false;
printf(">> frames %llu | %f\n", frames, gGameSpeed);
fflush(stdout);
sFrameTargetTime += sFrameTime * gGameSpeed;
//printf(">>> fpt: %llu, fps: %f :: %f\n", frames, sAvgFps, fps);
}
void produce_one_frame(void) {
network_update();
sFrameTimeStart = clock_elapsed_f64();
sLastFrameTimeStart = sFrameTimeStart;
patch_interpolations_before();
gfx_start_frame();
@ -202,10 +232,7 @@ void produce_one_frame(void) {
gfx_end_frame();
// uncapped
if (config60Fps) {
produce_uncapped_frames();
}
produce_interpolation_frames_and_delay();
}
void audio_shutdown(void) {
@ -334,7 +361,7 @@ void main_func(void) {
wm_api->set_keyboard_callbacks(keyboard_on_key_down, keyboard_on_key_up, keyboard_on_all_keys_up, keyboard_on_text_input);
#if defined(AAPI_SDL1) || defined(AAPI_SDL2)
if (audio_api == NULL && audio_sdl.init())
if (audio_api == NULL && audio_sdl.init())
audio_api = &audio_sdl;
#endif

View file

@ -10,6 +10,7 @@
#include "game/area.h"
#include "game/level_update.h"
#include "game/save_file.h"
#include "engine/math_util.h"
float smoothstep(float edge0, float edge1, float x) {
float t = (x - edge0) / (edge1 - edge0);
@ -116,6 +117,49 @@ next_get:
/////////////////
static f32 sm64_to_radians(f32 val) {
return val * M_PI / 0x8000;
}
static f32 radians_to_sm64(f32 val) {
return val * 0x8000 / M_PI;
}
static f32 asins(f32 val) {
return radians_to_sm64(asin(sm64_to_radians(val)));
}
void mtx_to_euler1(Mtx* mat, Vec3s rot) {
if (mat->m[1][1] == 1.0f) {
rot[0] = 0;
rot[1] = atan2(mat->m[1][3], mat->m[3][4]);
rot[2] = 0;
} else if (mat->m[1][1] == -1.0f) {
rot[0] = 0;
rot[1] = atan2(mat->m[1][3], mat->m[3][4]);
rot[2] = 0;
} else {
rot[0] = asin(mat->m[2][1]);
rot[1] = atan2(-mat->m[3][1], mat->m[1][1]);
rot[2] = atan2(-mat->m[2][3], mat->m[2][2]);
}
}
void mtx_to_euler2(Mtx* mat, Vec3s rot) {
if (mat->m[1][1] == 1.0f) {
rot[0] = 0;
rot[1] = atan2(mat->m[3][1], mat->m[4][3]);
rot[2] = 0;
} else if (mat->m[1][1] == -1.0f) {
rot[0] = 0;
rot[1] = atan2(mat->m[3][1], mat->m[4][3]);
rot[2] = 0;
} else {
rot[0] = asin(mat->m[1][2]);
rot[1] = atan2(-mat->m[1][3], mat->m[1][1]);
rot[2] = atan2(-mat->m[3][2], mat->m[2][2]);
}
}
f32 delta_interpolate_f32(f32 start, f32 end, f32 delta) {
return start * (1.0f - delta) + end * delta;
}
@ -145,15 +189,6 @@ void delta_interpolate_normal(s8* res, s8* a, s8* b, f32 delta) {
res[2] = ((a[2] * antiDelta) + (b[2] * delta));
}
void delta_interpolate_mtx(Mtx* out, Mtx* a, Mtx* b, f32 delta) {
f32 antiDelta = 1.0f - delta;
for (s32 i = 0; i < 4; i++) {
for (s32 j = 0; j < 4; j++) {
out->m[i][j] = (a->m[i][j] * antiDelta) + (b->m[i][j] * delta);
}
}
}
void delta_interpolate_rgba(u8* res, u8* a, u8* b, f32 delta) {
f32 antiDelta = 1.0f - delta;
res[0] = ((a[0] * antiDelta) + (b[0] * delta));
@ -162,21 +197,7 @@ void delta_interpolate_rgba(u8* res, u8* a, u8* b, f32 delta) {
res[3] = ((a[3] * antiDelta) + (b[3] * delta));
}
/*
void interpolate_vectors(Vec3f res, Vec3f a, Vec3f b) {
res[0] = (a[0] + b[0]) / 2.0f;
res[1] = (a[1] + b[1]) / 2.0f;
res[2] = (a[2] + b[2]) / 2.0f;
}
void interpolate_vectors_s16(Vec3s res, Vec3s a, Vec3s b) {
res[0] = (a[0] + b[0]) / 2;
res[1] = (a[1] + b[1]) / 2;
res[2] = (a[2] + b[2]) / 2;
}
static s16 interpolate_angle(s16 a, s16 b) {
static s16 delta_interpolate_angle(s16 a, s16 b, f32 delta) {
s32 absDiff = b - a;
if (absDiff < 0) {
absDiff = -absDiff;
@ -184,16 +205,47 @@ static s16 interpolate_angle(s16 a, s16 b) {
if (absDiff >= 0x4000 && absDiff <= 0xC000) {
return b;
}
f32 antiDelta = 1.0f - delta;
if (absDiff <= 0x8000) {
return (a + b) / 2;
return (a * antiDelta) + (b * delta);
} else {
return (a + b) / 2 + 0x8000;
return (a * antiDelta) + (b * delta) + 0x8000;
}
}
static void interpolate_angles(Vec3s res, Vec3s a, Vec3s b) {
res[0] = interpolate_angle(a[0], b[0]);
res[1] = interpolate_angle(a[1], b[1]);
res[2] = interpolate_angle(a[2], b[2]);
static void delta_interpolate_angles(Vec3s res, Vec3s a, Vec3s b, f32 delta) {
res[0] = delta_interpolate_angle(a[0], b[0], delta);
res[1] = delta_interpolate_angle(a[1], b[1], delta);
res[2] = delta_interpolate_angle(a[2], b[2], delta);
}
void delta_interpolate_mtx(Mtx* out, Mtx* a, Mtx* b, f32 delta) {
// this isn't the right way to do things.
f32 antiDelta = 1.0f - delta;
for (s32 i = 0; i < 4; i++) {
for (s32 j = 0; j < 4; j++) {
out->m[i][j] = (a->m[i][j] * antiDelta) + (b->m[i][j] * delta);
}
}
}
void detect_and_skip_mtx_interpolation(Mtx** mtxPrev, Mtx** mtx) {
// if the matrix has changed "too much", then skip interpolation
const f32 minDot = sqrt(2.0f) / -2.0f;
Vec3f prevX; vec3f_copy(prevX, (f32*)(*mtxPrev)->m[0]); vec3f_normalize(prevX);
Vec3f prevY; vec3f_copy(prevY, (f32*)(*mtxPrev)->m[1]); vec3f_normalize(prevY);
Vec3f prevZ; vec3f_copy(prevZ, (f32*)(*mtxPrev)->m[2]); vec3f_normalize(prevZ);
Vec3f nextX; vec3f_copy(nextX, (f32*)(*mtx)->m[0]); vec3f_normalize(nextX);
Vec3f nextY; vec3f_copy(nextY, (f32*)(*mtx)->m[1]); vec3f_normalize(nextY);
Vec3f nextZ; vec3f_copy(nextZ, (f32*)(*mtx)->m[2]); vec3f_normalize(nextZ);
f32 dotX = vec3f_dot(prevX, nextX);
f32 dotY = vec3f_dot(prevY, nextY);
f32 dotZ = vec3f_dot(prevZ, nextZ);
if ((dotX < minDot) | (dotY < minDot) || (dotZ < minDot)) {
*mtx = *mtxPrev;
}
}
*/

View file

@ -19,7 +19,8 @@ s32 delta_interpolate_s32(s32 a, s32 b, f32 delta);
void delta_interpolate_vec3f(Vec3f res, Vec3f a, Vec3f b, f32 delta);
void delta_interpolate_vec3s(Vec3s res, Vec3s a, Vec3s b, f32 delta);
void delta_interpolate_normal(s8* res, s8* a, s8* b, f32 delta);
void delta_interpolate_mtx(Mtx* out, Mtx* a, Mtx* b, f32 delta);
void delta_interpolate_rgba(u8* res, u8* a, u8* b, f32 delta);
void delta_interpolate_mtx(Mtx* out, Mtx* a, Mtx* b, f32 delta);
void detect_and_skip_mtx_interpolation(Mtx** mtxPrev, Mtx** mtx);
#endif