diff --git a/CMakeLists.txt b/CMakeLists.txt index f6593655..a9210ef0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,10 @@ file(GLOB_RECURSE ALL_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/port/*.h" "src/port/*.c" "src/port/*.cpp" + "src/port/Enhancements/*.h" + "src/port/Enhancements/*.cpp" + "src/port/Enhancements/game-interactor/*.h" + "src/port/Enhancements/game-interactor/*.cpp" "src/port/importer/*.cpp" "src/port/importer/types/*.cpp" "text/*.c" diff --git a/assets/fonts/Fipps-Regular.otf b/assets/fonts/Fipps-Regular.otf new file mode 100644 index 00000000..9334dad5 Binary files /dev/null and b/assets/fonts/Fipps-Regular.otf differ diff --git a/assets/fonts/PressStart2P-Regular.ttf b/assets/fonts/PressStart2P-Regular.ttf new file mode 100644 index 00000000..2442affb Binary files /dev/null and b/assets/fonts/PressStart2P-Regular.ttf differ diff --git a/assets/textures/buttons/ABtn.png b/assets/textures/buttons/ABtn.png new file mode 100644 index 00000000..c1b06571 Binary files /dev/null and b/assets/textures/buttons/ABtn.png differ diff --git a/assets/textures/buttons/BBtn.png b/assets/textures/buttons/BBtn.png new file mode 100644 index 00000000..99b1197c Binary files /dev/null and b/assets/textures/buttons/BBtn.png differ diff --git a/assets/textures/buttons/CDown.png b/assets/textures/buttons/CDown.png new file mode 100644 index 00000000..741188ea Binary files /dev/null and b/assets/textures/buttons/CDown.png differ diff --git a/assets/textures/buttons/CLeft.png b/assets/textures/buttons/CLeft.png new file mode 100644 index 00000000..5e26a206 Binary files /dev/null and b/assets/textures/buttons/CLeft.png differ diff --git a/assets/textures/buttons/CRight.png b/assets/textures/buttons/CRight.png new file mode 100644 index 00000000..9e618063 Binary files /dev/null and b/assets/textures/buttons/CRight.png differ diff --git a/assets/textures/buttons/CUp.png b/assets/textures/buttons/CUp.png new file mode 100644 index 00000000..6c0e29d2 Binary files /dev/null and b/assets/textures/buttons/CUp.png differ diff --git a/assets/textures/buttons/LBtn.png b/assets/textures/buttons/LBtn.png new file mode 100644 index 00000000..2e0a8f00 Binary files /dev/null and b/assets/textures/buttons/LBtn.png differ diff --git a/assets/textures/buttons/RBtn.png b/assets/textures/buttons/RBtn.png new file mode 100644 index 00000000..c255643c Binary files /dev/null and b/assets/textures/buttons/RBtn.png differ diff --git a/assets/textures/buttons/StartBtn.png b/assets/textures/buttons/StartBtn.png new file mode 100644 index 00000000..c3e08dc3 Binary files /dev/null and b/assets/textures/buttons/StartBtn.png differ diff --git a/assets/textures/buttons/ZBtn.png b/assets/textures/buttons/ZBtn.png new file mode 100644 index 00000000..def8d9a6 Binary files /dev/null and b/assets/textures/buttons/ZBtn.png differ diff --git a/assets/textures/icons/gIcon.png b/assets/textures/icons/gIcon.png new file mode 100644 index 00000000..4cad9d48 Binary files /dev/null and b/assets/textures/icons/gIcon.png differ diff --git a/assets/textures/virtual/gEmptyTexture.rgba32.png b/assets/textures/virtual/gEmptyTexture.rgba32.png new file mode 100644 index 00000000..eaf806b7 Binary files /dev/null and b/assets/textures/virtual/gEmptyTexture.rgba32.png differ diff --git a/include/banks_table.h b/include/banks_table.h deleted file mode 100644 index b1a3bad5..00000000 --- a/include/banks_table.h +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once -#include "align_asset_macro.h" - -#define dgBank00 "__OTR__sound/banks/00_bank" -static const ALIGN_ASSET(2) char gBank00[] = dgBank00; - -#define dgBank01 "__OTR__sound/banks/01_bank" -static const ALIGN_ASSET(2) char gBank01[] = dgBank01; - -#define dgBank02 "__OTR__sound/banks/02_bank" -static const ALIGN_ASSET(2) char gBank02[] = dgBank02; - -#define dgBank03 "__OTR__sound/banks/03_bank" -static const ALIGN_ASSET(2) char gBank03[] = dgBank03; - -#define dgBank04 "__OTR__sound/banks/04_bank" -static const ALIGN_ASSET(2) char gBank04[] = dgBank04; - -#define dgBank05 "__OTR__sound/banks/05_bank" -static const ALIGN_ASSET(2) char gBank05[] = dgBank05; - -#define dgBank06 "__OTR__sound/banks/06_bank" -static const ALIGN_ASSET(2) char gBank06[] = dgBank06; - -#define dgBank07 "__OTR__sound/banks/07_bank" -static const ALIGN_ASSET(2) char gBank07[] = dgBank07; - -#define dgBank08 "__OTR__sound/banks/08_bank" -static const ALIGN_ASSET(2) char gBank08[] = dgBank08; - -#define dgBank09 "__OTR__sound/banks/09_bank" -static const ALIGN_ASSET(2) char gBank09[] = dgBank09; - -#define dgBank0A "__OTR__sound/banks/0A_bank" -static const ALIGN_ASSET(2) char gBank0A[] = dgBank0A; - -#define dgBank0B "__OTR__sound/banks/0B_bank" -static const ALIGN_ASSET(2) char gBank0B[] = dgBank0B; - -#define dgBank0C "__OTR__sound/banks/0C_bank" -static const ALIGN_ASSET(2) char gBank0C[] = dgBank0C; - -#define dgBank0D "__OTR__sound/banks/0D_bank" -static const ALIGN_ASSET(2) char gBank0D[] = dgBank0D; - -#define dgBank0E "__OTR__sound/banks/0E_bank" -static const ALIGN_ASSET(2) char gBank0E[] = dgBank0E; - -#define dgBank0F "__OTR__sound/banks/0F_bank" -static const ALIGN_ASSET(2) char gBank0F[] = dgBank0F; - -#define dgBank10 "__OTR__sound/banks/10_bank" -static const ALIGN_ASSET(2) char gBank10[] = dgBank10; - -#define dgBank11 "__OTR__sound/banks/11_bank" -static const ALIGN_ASSET(2) char gBank11[] = dgBank11; - -#define dgBank12 "__OTR__sound/banks/12_bank" -static const ALIGN_ASSET(2) char gBank12[] = dgBank12; - -#define dgBank13 "__OTR__sound/banks/13_bank" -static const ALIGN_ASSET(2) char gBank13[] = dgBank13; - -#define dgBank14 "__OTR__sound/banks/14_bank" -static const ALIGN_ASSET(2) char gBank14[] = dgBank14; - -#define dgBank15 "__OTR__sound/banks/15_bank" -static const ALIGN_ASSET(2) char gBank15[] = dgBank15; - -#define dgBank16 "__OTR__sound/banks/16_bank" -static const ALIGN_ASSET(2) char gBank16[] = dgBank16; - -#define dgBank17 "__OTR__sound/banks/17_bank" -static const ALIGN_ASSET(2) char gBank17[] = dgBank17; - -#define dgBank18 "__OTR__sound/banks/18_bank" -static const ALIGN_ASSET(2) char gBank18[] = dgBank18; - -#define dgBank19 "__OTR__sound/banks/19_bank" -static const ALIGN_ASSET(2) char gBank19[] = dgBank19; - -#define dgBank1A "__OTR__sound/banks/1A_bank" -static const ALIGN_ASSET(2) char gBank1A[] = dgBank1A; - -#define dgBank1B "__OTR__sound/banks/1B_bank" -static const ALIGN_ASSET(2) char gBank1B[] = dgBank1B; - -#define dgBank1C "__OTR__sound/banks/1C_bank" -static const ALIGN_ASSET(2) char gBank1C[] = dgBank1C; - -#define dgBank1D "__OTR__sound/banks/1D_bank" -static const ALIGN_ASSET(2) char gBank1D[] = dgBank1D; - -#define dgBank1E "__OTR__sound/banks/1E_bank" -static const ALIGN_ASSET(2) char gBank1E[] = dgBank1E; - -#define dgBank1F "__OTR__sound/banks/1F_bank" -static const ALIGN_ASSET(2) char gBank1F[] = dgBank1F; - -#define dgBank20 "__OTR__sound/banks/20_bank" -static const ALIGN_ASSET(2) char gBank20[] = dgBank20; - -#define dgBank21 "__OTR__sound/banks/21_bank" -static const ALIGN_ASSET(2) char gBank21[] = dgBank21; - -#define dgBank22 "__OTR__sound/banks/22_bank" -static const ALIGN_ASSET(2) char gBank22[] = dgBank22; - -#define dgBank23 "__OTR__sound/banks/23_bank" -static const ALIGN_ASSET(2) char gBank23[] = dgBank23; - -#define dgBank24 "__OTR__sound/banks/24_bank" -static const ALIGN_ASSET(2) char gBank24[] = dgBank24; - -#define dgBank25 "__OTR__sound/banks/25_bank" -static const ALIGN_ASSET(2) char gBank25[] = dgBank25; - -static const char* gBankTable[] = { - gBank00, gBank01, gBank02, gBank03, - gBank04, gBank05, gBank06, gBank07, - gBank08, gBank09, gBank0A, gBank0B, - gBank0C, gBank0D, gBank0E, gBank0F, - gBank10, gBank11, gBank12, gBank13, - gBank14, gBank15, gBank16, gBank17, - gBank18, gBank19, gBank1A, gBank1B, - gBank1C, gBank1D, gBank1E, gBank1F, - gBank20, gBank21, gBank22, gBank23, - gBank24, gBank25, -}; diff --git a/include/samples_table.h b/include/samples_table.h deleted file mode 100644 index 5f1a3aeb..00000000 --- a/include/samples_table.h +++ /dev/null @@ -1,717 +0,0 @@ -#pragma once -#include "align_asset_macro.h" - -#define dgSample00 "__OTR__sound/samples/sfx_1/00_twirl" -static const ALIGN_ASSET(2) char gSample00[] = dgSample00; - -#define dgSample01 "__OTR__sound/samples/sfx_1/01_brushing" -static const ALIGN_ASSET(2) char gSample01[] = dgSample01; - -#define dgSample02 "__OTR__sound/samples/sfx_1/02_hand_touch" -static const ALIGN_ASSET(2) char gSample02[] = dgSample02; - -#define dgSample03 "__OTR__sound/samples/sfx_1/03_yoshi" -static const ALIGN_ASSET(2) char gSample03[] = dgSample03; - -#define dgSample04 "__OTR__sound/samples/sfx_1/04_plop" -static const ALIGN_ASSET(2) char gSample04[] = dgSample04; - -#define dgSample05 "__OTR__sound/samples/sfx_1/05_heavy_landing" -static const ALIGN_ASSET(2) char gSample05[] = dgSample05; - -#define dgSample06 "__OTR__sound/samples/sfx_terrain/00_step_default" -static const ALIGN_ASSET(2) char gSample06[] = dgSample06; - -#define dgSample07 "__OTR__sound/samples/sfx_terrain/01_step_grass" -static const ALIGN_ASSET(2) char gSample07[] = dgSample07; - -#define dgSample08 "__OTR__sound/samples/sfx_terrain/02_step_stone" -static const ALIGN_ASSET(2) char gSample08[] = dgSample08; - -#define dgSample09 "__OTR__sound/samples/sfx_terrain/03_step_spooky" -static const ALIGN_ASSET(2) char gSample09[] = dgSample09; - -#define dgSample0A "__OTR__sound/samples/sfx_terrain/04_step_snow" -static const ALIGN_ASSET(2) char gSample0A[] = dgSample0A; - -#define dgSample0B "__OTR__sound/samples/sfx_terrain/05_step_ice" -static const ALIGN_ASSET(2) char gSample0B[] = dgSample0B; - -#define dgSample0C "__OTR__sound/samples/sfx_terrain/06_step_metal" -static const ALIGN_ASSET(2) char gSample0C[] = dgSample0C; - -#define dgSample0D "__OTR__sound/samples/sfx_terrain/07_step_sand" -static const ALIGN_ASSET(2) char gSample0D[] = dgSample0D; - -#define dgSample0E "__OTR__sound/samples/sfx_water/00_plunge" -static const ALIGN_ASSET(2) char gSample0E[] = dgSample0E; - -#define dgSample0F "__OTR__sound/samples/sfx_water/01_splash" -static const ALIGN_ASSET(2) char gSample0F[] = dgSample0F; - -#define dgSample10 "__OTR__sound/samples/sfx_water/02_swim" -static const ALIGN_ASSET(2) char gSample10[] = dgSample10; - -#define dgSample11 "__OTR__sound/samples/sfx_4/00" -static const ALIGN_ASSET(2) char gSample11[] = dgSample11; - -#define dgSample12 "__OTR__sound/samples/sfx_4/01" -static const ALIGN_ASSET(2) char gSample12[] = dgSample12; - -#define dgSample13 "__OTR__sound/samples/sfx_4/02" -static const ALIGN_ASSET(2) char gSample13[] = dgSample13; - -#define dgSample14 "__OTR__sound/samples/sfx_4/03" -static const ALIGN_ASSET(2) char gSample14[] = dgSample14; - -#define dgSample15 "__OTR__sound/samples/sfx_4/04" -static const ALIGN_ASSET(2) char gSample15[] = dgSample15; - -#define dgSample16 "__OTR__sound/samples/sfx_4/05" -static const ALIGN_ASSET(2) char gSample16[] = dgSample16; - -#define dgSample17 "__OTR__sound/samples/sfx_4/06" -static const ALIGN_ASSET(2) char gSample17[] = dgSample17; - -#define dgSample18 "__OTR__sound/samples/sfx_4/07" -static const ALIGN_ASSET(2) char gSample18[] = dgSample18; - -#define dgSample19 "__OTR__sound/samples/sfx_4/08" -static const ALIGN_ASSET(2) char gSample19[] = dgSample19; - -#define dgSample1A "__OTR__sound/samples/sfx_4/09" -static const ALIGN_ASSET(2) char gSample1A[] = dgSample1A; - -#define dgSample1B "__OTR__sound/samples/sfx_5/00" -static const ALIGN_ASSET(2) char gSample1B[] = dgSample1B; - -#define dgSample1C "__OTR__sound/samples/sfx_5/01" -static const ALIGN_ASSET(2) char gSample1C[] = dgSample1C; - -#define dgSample1D "__OTR__sound/samples/sfx_5/02" -static const ALIGN_ASSET(2) char gSample1D[] = dgSample1D; - -#define dgSample1E "__OTR__sound/samples/sfx_5/03" -static const ALIGN_ASSET(2) char gSample1E[] = dgSample1E; - -#define dgSample1F "__OTR__sound/samples/sfx_5/04" -static const ALIGN_ASSET(2) char gSample1F[] = dgSample1F; - -#define dgSample20 "__OTR__sound/samples/sfx_5/05" -static const ALIGN_ASSET(2) char gSample20[] = dgSample20; - -#define dgSample21 "__OTR__sound/samples/sfx_5/06" -static const ALIGN_ASSET(2) char gSample21[] = dgSample21; - -#define dgSample22 "__OTR__sound/samples/sfx_5/07" -static const ALIGN_ASSET(2) char gSample22[] = dgSample22; - -#define dgSample23 "__OTR__sound/samples/sfx_5/08" -static const ALIGN_ASSET(2) char gSample23[] = dgSample23; - -#define dgSample24 "__OTR__sound/samples/sfx_5/09" -static const ALIGN_ASSET(2) char gSample24[] = dgSample24; - -#define dgSample25 "__OTR__sound/samples/sfx_5/0A" -static const ALIGN_ASSET(2) char gSample25[] = dgSample25; - -#define dgSample26 "__OTR__sound/samples/sfx_5/0B" -static const ALIGN_ASSET(2) char gSample26[] = dgSample26; - -#define dgSample27 "__OTR__sound/samples/sfx_5/0C" -static const ALIGN_ASSET(2) char gSample27[] = dgSample27; - -#define dgSample28 "__OTR__sound/samples/sfx_5/0D" -static const ALIGN_ASSET(2) char gSample28[] = dgSample28; - -#define dgSample29 "__OTR__sound/samples/sfx_5/0E" -static const ALIGN_ASSET(2) char gSample29[] = dgSample29; - -#define dgSample2A "__OTR__sound/samples/sfx_5/0F" -static const ALIGN_ASSET(2) char gSample2A[] = dgSample2A; - -#define dgSample2B "__OTR__sound/samples/sfx_5/10" -static const ALIGN_ASSET(2) char gSample2B[] = dgSample2B; - -#define dgSample2C "__OTR__sound/samples/sfx_5/11" -static const ALIGN_ASSET(2) char gSample2C[] = dgSample2C; - -#define dgSample2D "__OTR__sound/samples/sfx_5/12" -static const ALIGN_ASSET(2) char gSample2D[] = dgSample2D; - -#define dgSample2E "__OTR__sound/samples/sfx_5/13" -static const ALIGN_ASSET(2) char gSample2E[] = dgSample2E; - -#define dgSample2F "__OTR__sound/samples/sfx_5/14" -static const ALIGN_ASSET(2) char gSample2F[] = dgSample2F; - -#define dgSample30 "__OTR__sound/samples/sfx_5/15" -static const ALIGN_ASSET(2) char gSample30[] = dgSample30; - -#define dgSample31 "__OTR__sound/samples/sfx_5/16" -static const ALIGN_ASSET(2) char gSample31[] = dgSample31; - -#define dgSample32 "__OTR__sound/samples/sfx_5/17" -static const ALIGN_ASSET(2) char gSample32[] = dgSample32; - -#define dgSample33 "__OTR__sound/samples/sfx_5/18" -static const ALIGN_ASSET(2) char gSample33[] = dgSample33; - -#define dgSample34 "__OTR__sound/samples/sfx_5/19" -static const ALIGN_ASSET(2) char gSample34[] = dgSample34; - -#define dgSample35 "__OTR__sound/samples/sfx_5/1A" -static const ALIGN_ASSET(2) char gSample35[] = dgSample35; - -#define dgSample36 "__OTR__sound/samples/sfx_5/1B" -static const ALIGN_ASSET(2) char gSample36[] = dgSample36; - -#define dgSample37 "__OTR__sound/samples/sfx_5/1C" -static const ALIGN_ASSET(2) char gSample37[] = dgSample37; - -#define dgSample38 "__OTR__sound/samples/sfx_6/00" -static const ALIGN_ASSET(2) char gSample38[] = dgSample38; - -#define dgSample39 "__OTR__sound/samples/sfx_6/01" -static const ALIGN_ASSET(2) char gSample39[] = dgSample39; - -#define dgSample3A "__OTR__sound/samples/sfx_6/02" -static const ALIGN_ASSET(2) char gSample3A[] = dgSample3A; - -#define dgSample3B "__OTR__sound/samples/sfx_6/03" -static const ALIGN_ASSET(2) char gSample3B[] = dgSample3B; - -#define dgSample3C "__OTR__sound/samples/sfx_6/04" -static const ALIGN_ASSET(2) char gSample3C[] = dgSample3C; - -#define dgSample3D "__OTR__sound/samples/sfx_6/05" -static const ALIGN_ASSET(2) char gSample3D[] = dgSample3D; - -#define dgSample3E "__OTR__sound/samples/sfx_6/06" -static const ALIGN_ASSET(2) char gSample3E[] = dgSample3E; - -#define dgSample3F "__OTR__sound/samples/sfx_6/07" -static const ALIGN_ASSET(2) char gSample3F[] = dgSample3F; - -#define dgSample40 "__OTR__sound/samples/sfx_6/08" -static const ALIGN_ASSET(2) char gSample40[] = dgSample40; - -#define dgSample41 "__OTR__sound/samples/sfx_6/09" -static const ALIGN_ASSET(2) char gSample41[] = dgSample41; - -#define dgSample42 "__OTR__sound/samples/sfx_6/0A" -static const ALIGN_ASSET(2) char gSample42[] = dgSample42; - -#define dgSample43 "__OTR__sound/samples/sfx_6/0B" -static const ALIGN_ASSET(2) char gSample43[] = dgSample43; - -#define dgSample44 "__OTR__sound/samples/sfx_6/0C" -static const ALIGN_ASSET(2) char gSample44[] = dgSample44; - -#define dgSample45 "__OTR__sound/samples/sfx_6/0D" -static const ALIGN_ASSET(2) char gSample45[] = dgSample45; - -#define dgSample46 "__OTR__sound/samples/sfx_7/00" -static const ALIGN_ASSET(2) char gSample46[] = dgSample46; - -#define dgSample47 "__OTR__sound/samples/sfx_7/01" -static const ALIGN_ASSET(2) char gSample47[] = dgSample47; - -#define dgSample48 "__OTR__sound/samples/sfx_7/02" -static const ALIGN_ASSET(2) char gSample48[] = dgSample48; - -#define dgSample49 "__OTR__sound/samples/sfx_7/03" -static const ALIGN_ASSET(2) char gSample49[] = dgSample49; - -#define dgSample4A "__OTR__sound/samples/sfx_7/04" -static const ALIGN_ASSET(2) char gSample4A[] = dgSample4A; - -#define dgSample4B "__OTR__sound/samples/sfx_7/05" -static const ALIGN_ASSET(2) char gSample4B[] = dgSample4B; - -#define dgSample4C "__OTR__sound/samples/sfx_7/06" -static const ALIGN_ASSET(2) char gSample4C[] = dgSample4C; - -#define dgSample4D "__OTR__sound/samples/sfx_7/07" -static const ALIGN_ASSET(2) char gSample4D[] = dgSample4D; - -#define dgSample4E "__OTR__sound/samples/sfx_7/08" -static const ALIGN_ASSET(2) char gSample4E[] = dgSample4E; - -#define dgSample4F "__OTR__sound/samples/sfx_7/09" -static const ALIGN_ASSET(2) char gSample4F[] = dgSample4F; - -#define dgSample50 "__OTR__sound/samples/sfx_7/0A" -static const ALIGN_ASSET(2) char gSample50[] = dgSample50; - -#define dgSample51 "__OTR__sound/samples/sfx_7/0B" -static const ALIGN_ASSET(2) char gSample51[] = dgSample51; - -#define dgSample52 "__OTR__sound/samples/sfx_7/0C" -static const ALIGN_ASSET(2) char gSample52[] = dgSample52; - -#define dgSample53 "__OTR__sound/samples/sfx_7/0D_chain_chomp_bark" -static const ALIGN_ASSET(2) char gSample53[] = dgSample53; - -#define dgSample54 "__OTR__sound/samples/sfx_mario/00_mario_jump_hoo" -static const ALIGN_ASSET(2) char gSample54[] = dgSample54; - -#define dgSample55 "__OTR__sound/samples/sfx_mario/01_mario_jump_wah" -static const ALIGN_ASSET(2) char gSample55[] = dgSample55; - -#define dgSample56 "__OTR__sound/samples/sfx_mario/02_mario_yah" -static const ALIGN_ASSET(2) char gSample56[] = dgSample56; - -#define dgSample57 "__OTR__sound/samples/sfx_mario/03_mario_haha" -static const ALIGN_ASSET(2) char gSample57[] = dgSample57; - -#define dgSample58 "__OTR__sound/samples/sfx_mario/04_mario_yahoo" -static const ALIGN_ASSET(2) char gSample58[] = dgSample58; - -#define dgSample59 "__OTR__sound/samples/sfx_mario/05_mario_uh" -static const ALIGN_ASSET(2) char gSample59[] = dgSample59; - -#define dgSample5A "__OTR__sound/samples/sfx_mario/06_mario_hrmm" -static const ALIGN_ASSET(2) char gSample5A[] = dgSample5A; - -#define dgSample5B "__OTR__sound/samples/sfx_mario/07_mario_wah2" -static const ALIGN_ASSET(2) char gSample5B[] = dgSample5B; - -#define dgSample5C "__OTR__sound/samples/sfx_mario/08_mario_whoa" -static const ALIGN_ASSET(2) char gSample5C[] = dgSample5C; - -#define dgSample5D "__OTR__sound/samples/sfx_mario/09_mario_eeuh" -static const ALIGN_ASSET(2) char gSample5D[] = dgSample5D; - -#define dgSample5E "__OTR__sound/samples/sfx_mario/0A_mario_attacked" -static const ALIGN_ASSET(2) char gSample5E[] = dgSample5E; - -#define dgSample5F "__OTR__sound/samples/sfx_mario/0B_mario_ooof" -static const ALIGN_ASSET(2) char gSample5F[] = dgSample5F; - -#define dgSample60 "__OTR__sound/samples/sfx_mario/0C_mario_here_we_go" -static const ALIGN_ASSET(2) char gSample60[] = dgSample60; - -#define dgSample61 "__OTR__sound/samples/sfx_mario/0D_mario_yawning" -static const ALIGN_ASSET(2) char gSample61[] = dgSample61; - -#define dgSample62 "__OTR__sound/samples/sfx_mario/0E_mario_snoring1" -static const ALIGN_ASSET(2) char gSample62[] = dgSample62; - -#define dgSample63 "__OTR__sound/samples/sfx_mario/0F_mario_snoring2" -static const ALIGN_ASSET(2) char gSample63[] = dgSample63; - -#define dgSample64 "__OTR__sound/samples/sfx_mario/10_mario_doh" -static const ALIGN_ASSET(2) char gSample64[] = dgSample64; - -#define dgSample65 "__OTR__sound/samples/sfx_mario/11_mario_game_over" -static const ALIGN_ASSET(2) char gSample65[] = dgSample65; - -#define dgSample66 "__OTR__sound/samples/sfx_mario/12_mario_hello" -static const ALIGN_ASSET(2) char gSample66[] = dgSample66; - -#define dgSample67 "__OTR__sound/samples/sfx_mario/13_mario_press_start_to_play" -static const ALIGN_ASSET(2) char gSample67[] = dgSample67; - -#define dgSample68 "__OTR__sound/samples/sfx_mario/14_mario_twirl_bounce" -static const ALIGN_ASSET(2) char gSample68[] = dgSample68; - -#define dgSample69 "__OTR__sound/samples/sfx_mario/15_mario_snoring3" -static const ALIGN_ASSET(2) char gSample69[] = dgSample69; - -#define dgSample6A "__OTR__sound/samples/sfx_mario/16_mario_so_longa_bowser" -static const ALIGN_ASSET(2) char gSample6A[] = dgSample6A; - -#define dgSample6B "__OTR__sound/samples/sfx_mario/17_mario_ima_tired" -static const ALIGN_ASSET(2) char gSample6B[] = dgSample6B; - -#define dgSample6C "__OTR__sound/samples/sfx_mario/18_mario_waha" -static const ALIGN_ASSET(2) char gSample6C[] = dgSample6C; - -#define dgSample6D "__OTR__sound/samples/sfx_mario/19_mario_yippee" -static const ALIGN_ASSET(2) char gSample6D[] = dgSample6D; - -#define dgSample6E "__OTR__sound/samples/sfx_mario/1A_mario_lets_a_go" -static const ALIGN_ASSET(2) char gSample6E[] = dgSample6E; - -#define dgSample6F "__OTR__sound/samples/sfx_9/00" -static const ALIGN_ASSET(2) char gSample6F[] = dgSample6F; - -#define dgSample70 "__OTR__sound/samples/sfx_9/01" -static const ALIGN_ASSET(2) char gSample70[] = dgSample70; - -#define dgSample71 "__OTR__sound/samples/sfx_9/02" -static const ALIGN_ASSET(2) char gSample71[] = dgSample71; - -#define dgSample72 "__OTR__sound/samples/sfx_9/03" -static const ALIGN_ASSET(2) char gSample72[] = dgSample72; - -#define dgSample73 "__OTR__sound/samples/sfx_9/04_camera_buzz" -static const ALIGN_ASSET(2) char gSample73[] = dgSample73; - -#define dgSample74 "__OTR__sound/samples/sfx_9/05_camera_shutter" -static const ALIGN_ASSET(2) char gSample74[] = dgSample74; - -#define dgSample75 "__OTR__sound/samples/sfx_9/06" -static const ALIGN_ASSET(2) char gSample75[] = dgSample75; - -#define dgSample76 "__OTR__sound/samples/sfx_mario_peach/00_mario_waaaooow" -static const ALIGN_ASSET(2) char gSample76[] = dgSample76; - -#define dgSample77 "__OTR__sound/samples/sfx_mario_peach/01_mario_hoohoo" -static const ALIGN_ASSET(2) char gSample77[] = dgSample77; - -#define dgSample78 "__OTR__sound/samples/sfx_mario_peach/02_mario_panting" -static const ALIGN_ASSET(2) char gSample78[] = dgSample78; - -#define dgSample79 "__OTR__sound/samples/sfx_mario_peach/03_mario_dying" -static const ALIGN_ASSET(2) char gSample79[] = dgSample79; - -#define dgSample7A "__OTR__sound/samples/sfx_mario_peach/04_mario_on_fire" -static const ALIGN_ASSET(2) char gSample7A[] = dgSample7A; - -#define dgSample7B "__OTR__sound/samples/sfx_mario_peach/05_mario_uh2" -static const ALIGN_ASSET(2) char gSample7B[] = dgSample7B; - -#define dgSample7C "__OTR__sound/samples/sfx_mario_peach/06_mario_coughing" -static const ALIGN_ASSET(2) char gSample7C[] = dgSample7C; - -#define dgSample7D "__OTR__sound/samples/sfx_mario_peach/07_mario_its_a_me_mario" -static const ALIGN_ASSET(2) char gSample7D[] = dgSample7D; - -#define dgSample7E "__OTR__sound/samples/sfx_mario_peach/08_mario_punch_yah" -static const ALIGN_ASSET(2) char gSample7E[] = dgSample7E; - -#define dgSample7F "__OTR__sound/samples/sfx_mario_peach/09_mario_punch_hoo" -static const ALIGN_ASSET(2) char gSample7F[] = dgSample7F; - -#define dgSample80 "__OTR__sound/samples/sfx_mario_peach/0A_mario_mama_mia" -static const ALIGN_ASSET(2) char gSample80[] = dgSample80; - -#define dgSample81 "__OTR__sound/samples/sfx_mario_peach/0B_mario_okey_dokey" -static const ALIGN_ASSET(2) char gSample81[] = dgSample81; - -#define dgSample82 "__OTR__sound/samples/sfx_mario_peach/0C_mario_drowning" -static const ALIGN_ASSET(2) char gSample82[] = dgSample82; - -#define dgSample83 "__OTR__sound/samples/sfx_mario_peach/0D_mario_thank_you_playing_my_game" -static const ALIGN_ASSET(2) char gSample83[] = dgSample83; - -#define dgSample84 "__OTR__sound/samples/sfx_mario_peach/0E_peach_dear_mario" -static const ALIGN_ASSET(2) char gSample84[] = dgSample84; - -#define dgSample85 "__OTR__sound/samples/sfx_mario_peach/0F_peach_mario" -static const ALIGN_ASSET(2) char gSample85[] = dgSample85; - -#define dgSample86 "__OTR__sound/samples/sfx_mario_peach/10_peach_power_of_the_stars" -static const ALIGN_ASSET(2) char gSample86[] = dgSample86; - -#define dgSample87 "__OTR__sound/samples/sfx_mario_peach/11_peach_thanks_to_you" -static const ALIGN_ASSET(2) char gSample87[] = dgSample87; - -#define dgSample88 "__OTR__sound/samples/sfx_mario_peach/12_peach_thank_you_mario" -static const ALIGN_ASSET(2) char gSample88[] = dgSample88; - -#define dgSample89 "__OTR__sound/samples/sfx_mario_peach/13_peach_something_special" -static const ALIGN_ASSET(2) char gSample89[] = dgSample89; - -#define dgSample8A "__OTR__sound/samples/sfx_mario_peach/14_peach_bake_a_cake" -static const ALIGN_ASSET(2) char gSample8A[] = dgSample8A; - -#define dgSample8B "__OTR__sound/samples/sfx_mario_peach/15_peach_for_mario" -static const ALIGN_ASSET(2) char gSample8B[] = dgSample8B; - -#define dgSample8C "__OTR__sound/samples/sfx_mario_peach/16_peach_mario2" -static const ALIGN_ASSET(2) char gSample8C[] = dgSample8C; - -#define dgSample8D "__OTR__sound/samples/instruments/00" -static const ALIGN_ASSET(2) char gSample8D[] = dgSample8D; - -#define dgSample8E "__OTR__sound/samples/instruments/01_banjo_1" -static const ALIGN_ASSET(2) char gSample8E[] = dgSample8E; - -#define dgSample8F "__OTR__sound/samples/instruments/02" -static const ALIGN_ASSET(2) char gSample8F[] = dgSample8F; - -#define dgSample90 "__OTR__sound/samples/instruments/03_human_whistle" -static const ALIGN_ASSET(2) char gSample90[] = dgSample90; - -#define dgSample91 "__OTR__sound/samples/instruments/04_bright_piano" -static const ALIGN_ASSET(2) char gSample91[] = dgSample91; - -#define dgSample92 "__OTR__sound/samples/instruments/05_acoustic_bass" -static const ALIGN_ASSET(2) char gSample92[] = dgSample92; - -#define dgSample93 "__OTR__sound/samples/instruments/06_kick_drum_1" -static const ALIGN_ASSET(2) char gSample93[] = dgSample93; - -#define dgSample94 "__OTR__sound/samples/instruments/07_rimshot" -static const ALIGN_ASSET(2) char gSample94[] = dgSample94; - -#define dgSample95 "__OTR__sound/samples/instruments/08" -static const ALIGN_ASSET(2) char gSample95[] = dgSample95; - -#define dgSample96 "__OTR__sound/samples/instruments/09" -static const ALIGN_ASSET(2) char gSample96[] = dgSample96; - -#define dgSample97 "__OTR__sound/samples/instruments/0A_tambourine" -static const ALIGN_ASSET(2) char gSample97[] = dgSample97; - -#define dgSample98 "__OTR__sound/samples/instruments/0B" -static const ALIGN_ASSET(2) char gSample98[] = dgSample98; - -#define dgSample99 "__OTR__sound/samples/instruments/0C_conga_stick" -static const ALIGN_ASSET(2) char gSample99[] = dgSample99; - -#define dgSample9A "__OTR__sound/samples/instruments/0D_clave" -static const ALIGN_ASSET(2) char gSample9A[] = dgSample9A; - -#define dgSample9B "__OTR__sound/samples/instruments/0E_hihat_closed" -static const ALIGN_ASSET(2) char gSample9B[] = dgSample9B; - -#define dgSample9C "__OTR__sound/samples/instruments/0F_hihat_open" -static const ALIGN_ASSET(2) char gSample9C[] = dgSample9C; - -#define dgSample9D "__OTR__sound/samples/instruments/10_cymbal_bell" -static const ALIGN_ASSET(2) char gSample9D[] = dgSample9D; - -#define dgSample9E "__OTR__sound/samples/instruments/11_splash_cymbal" -static const ALIGN_ASSET(2) char gSample9E[] = dgSample9E; - -#define dgSample9F "__OTR__sound/samples/instruments/12_snare_drum_1" -static const ALIGN_ASSET(2) char gSample9F[] = dgSample9F; - -#define dgSampleA0 "__OTR__sound/samples/instruments/13_snare_drum_2" -static const ALIGN_ASSET(2) char gSampleA0[] = dgSampleA0; - -#define dgSampleA1 "__OTR__sound/samples/instruments/14_strings_5" -static const ALIGN_ASSET(2) char gSampleA1[] = dgSampleA1; - -#define dgSampleA2 "__OTR__sound/samples/instruments/15_strings_4" -static const ALIGN_ASSET(2) char gSampleA2[] = dgSampleA2; - -#define dgSampleA3 "__OTR__sound/samples/instruments/16_french_horns" -static const ALIGN_ASSET(2) char gSampleA3[] = dgSampleA3; - -#define dgSampleA4 "__OTR__sound/samples/instruments/17_trumpet" -static const ALIGN_ASSET(2) char gSampleA4[] = dgSampleA4; - -#define dgSampleA5 "__OTR__sound/samples/instruments/18_timpani" -static const ALIGN_ASSET(2) char gSampleA5[] = dgSampleA5; - -#define dgSampleA6 "__OTR__sound/samples/instruments/19_brass" -static const ALIGN_ASSET(2) char gSampleA6[] = dgSampleA6; - -#define dgSampleA7 "__OTR__sound/samples/instruments/1A_slap_bass" -static const ALIGN_ASSET(2) char gSampleA7[] = dgSampleA7; - -#define dgSampleA8 "__OTR__sound/samples/instruments/1B_organ_2" -static const ALIGN_ASSET(2) char gSampleA8[] = dgSampleA8; - -#define dgSampleA9 "__OTR__sound/samples/instruments/1C" -static const ALIGN_ASSET(2) char gSampleA9[] = dgSampleA9; - -#define dgSampleAA "__OTR__sound/samples/instruments/1D" -static const ALIGN_ASSET(2) char gSampleAA[] = dgSampleAA; - -#define dgSampleAB "__OTR__sound/samples/instruments/1E_closed_triangle" -static const ALIGN_ASSET(2) char gSampleAB[] = dgSampleAB; - -#define dgSampleAC "__OTR__sound/samples/instruments/1F_open_triangle" -static const ALIGN_ASSET(2) char gSampleAC[] = dgSampleAC; - -#define dgSampleAD "__OTR__sound/samples/instruments/20_cabasa" -static const ALIGN_ASSET(2) char gSampleAD[] = dgSampleAD; - -#define dgSampleAE "__OTR__sound/samples/instruments/21_sine_bass" -static const ALIGN_ASSET(2) char gSampleAE[] = dgSampleAE; - -#define dgSampleAF "__OTR__sound/samples/instruments/22_boys_choir" -static const ALIGN_ASSET(2) char gSampleAF[] = dgSampleAF; - -#define dgSampleB0 "__OTR__sound/samples/instruments/23_strings_1" -static const ALIGN_ASSET(2) char gSampleB0[] = dgSampleB0; - -#define dgSampleB1 "__OTR__sound/samples/instruments/24_strings_2" -static const ALIGN_ASSET(2) char gSampleB1[] = dgSampleB1; - -#define dgSampleB2 "__OTR__sound/samples/instruments/25_strings_3" -static const ALIGN_ASSET(2) char gSampleB2[] = dgSampleB2; - -#define dgSampleB3 "__OTR__sound/samples/instruments/26_crystal_rhodes" -static const ALIGN_ASSET(2) char gSampleB3[] = dgSampleB3; - -#define dgSampleB4 "__OTR__sound/samples/instruments/27_harpsichord" -static const ALIGN_ASSET(2) char gSampleB4[] = dgSampleB4; - -#define dgSampleB5 "__OTR__sound/samples/instruments/28_sitar_1" -static const ALIGN_ASSET(2) char gSampleB5[] = dgSampleB5; - -#define dgSampleB6 "__OTR__sound/samples/instruments/29_orchestra_hit" -static const ALIGN_ASSET(2) char gSampleB6[] = dgSampleB6; - -#define dgSampleB7 "__OTR__sound/samples/instruments/2A" -static const ALIGN_ASSET(2) char gSampleB7[] = dgSampleB7; - -#define dgSampleB8 "__OTR__sound/samples/instruments/2B" -static const ALIGN_ASSET(2) char gSampleB8[] = dgSampleB8; - -#define dgSampleB9 "__OTR__sound/samples/instruments/2C" -static const ALIGN_ASSET(2) char gSampleB9[] = dgSampleB9; - -#define dgSampleBA "__OTR__sound/samples/instruments/2D_trombone" -static const ALIGN_ASSET(2) char gSampleBA[] = dgSampleBA; - -#define dgSampleBB "__OTR__sound/samples/instruments/2E_accordion" -static const ALIGN_ASSET(2) char gSampleBB[] = dgSampleBB; - -#define dgSampleBC "__OTR__sound/samples/instruments/2F_sleigh_bells" -static const ALIGN_ASSET(2) char gSampleBC[] = dgSampleBC; - -#define dgSampleBD "__OTR__sound/samples/instruments/30_rarefaction-lahna" -static const ALIGN_ASSET(2) char gSampleBD[] = dgSampleBD; - -#define dgSampleBE "__OTR__sound/samples/instruments/31_rarefaction-convolution" -static const ALIGN_ASSET(2) char gSampleBE[] = dgSampleBE; - -#define dgSampleBF "__OTR__sound/samples/instruments/32_metal_rimshot" -static const ALIGN_ASSET(2) char gSampleBF[] = dgSampleBF; - -#define dgSampleC0 "__OTR__sound/samples/instruments/33_kick_drum_2" -static const ALIGN_ASSET(2) char gSampleC0[] = dgSampleC0; - -#define dgSampleC1 "__OTR__sound/samples/instruments/34_alto_flute" -static const ALIGN_ASSET(2) char gSampleC1[] = dgSampleC1; - -#define dgSampleC2 "__OTR__sound/samples/instruments/35_gospel_organ" -static const ALIGN_ASSET(2) char gSampleC2[] = dgSampleC2; - -#define dgSampleC3 "__OTR__sound/samples/instruments/36_sawtooth_synth" -static const ALIGN_ASSET(2) char gSampleC3[] = dgSampleC3; - -#define dgSampleC4 "__OTR__sound/samples/instruments/37_square_synth" -static const ALIGN_ASSET(2) char gSampleC4[] = dgSampleC4; - -#define dgSampleC5 "__OTR__sound/samples/instruments/38_electric_kick_drum" -static const ALIGN_ASSET(2) char gSampleC5[] = dgSampleC5; - -#define dgSampleC6 "__OTR__sound/samples/instruments/39_sitar_2" -static const ALIGN_ASSET(2) char gSampleC6[] = dgSampleC6; - -#define dgSampleC7 "__OTR__sound/samples/instruments/3A_music_box" -static const ALIGN_ASSET(2) char gSampleC7[] = dgSampleC7; - -#define dgSampleC8 "__OTR__sound/samples/instruments/3B_banjo_2" -static const ALIGN_ASSET(2) char gSampleC8[] = dgSampleC8; - -#define dgSampleC9 "__OTR__sound/samples/instruments/3C_acoustic_guitar" -static const ALIGN_ASSET(2) char gSampleC9[] = dgSampleC9; - -#define dgSampleCA "__OTR__sound/samples/instruments/3D" -static const ALIGN_ASSET(2) char gSampleCA[] = dgSampleCA; - -#define dgSampleCB "__OTR__sound/samples/instruments/3E_monk_choir" -static const ALIGN_ASSET(2) char gSampleCB[] = dgSampleCB; - -#define dgSampleCC "__OTR__sound/samples/instruments/3F" -static const ALIGN_ASSET(2) char gSampleCC[] = dgSampleCC; - -#define dgSampleCD "__OTR__sound/samples/instruments/40_bell" -static const ALIGN_ASSET(2) char gSampleCD[] = dgSampleCD; - -#define dgSampleCE "__OTR__sound/samples/instruments/41_pan_flute" -static const ALIGN_ASSET(2) char gSampleCE[] = dgSampleCE; - -#define dgSampleCF "__OTR__sound/samples/instruments/42_vibraphone" -static const ALIGN_ASSET(2) char gSampleCF[] = dgSampleCF; - -#define dgSampleD0 "__OTR__sound/samples/instruments/43_harmonica" -static const ALIGN_ASSET(2) char gSampleD0[] = dgSampleD0; - -#define dgSampleD1 "__OTR__sound/samples/instruments/44_grand_piano" -static const ALIGN_ASSET(2) char gSampleD1[] = dgSampleD1; - -#define dgSampleD2 "__OTR__sound/samples/instruments/45_french_horns_lq" -static const ALIGN_ASSET(2) char gSampleD2[] = dgSampleD2; - -#define dgSampleD3 "__OTR__sound/samples/instruments/46_pizzicato_strings_1" -static const ALIGN_ASSET(2) char gSampleD3[] = dgSampleD3; - -#define dgSampleD4 "__OTR__sound/samples/instruments/47_pizzicato_strings_2" -static const ALIGN_ASSET(2) char gSampleD4[] = dgSampleD4; - -#define dgSampleD5 "__OTR__sound/samples/instruments/48_steel_drum" -static const ALIGN_ASSET(2) char gSampleD5[] = dgSampleD5; - -#define dgSampleD6 "__OTR__sound/samples/piranha_music_box/00_music_box" -static const ALIGN_ASSET(2) char gSampleD6[] = dgSampleD6; - -#define dgSampleD7 "__OTR__sound/samples/course_start/00_la" -static const ALIGN_ASSET(2) char gSampleD7[] = dgSampleD7; - -#define dgSampleD8 "__OTR__sound/samples/bowser_organ/00_organ_1" -static const ALIGN_ASSET(2) char gSampleD8[] = dgSampleD8; - -#define dgSampleD9 "__OTR__sound/samples/bowser_organ/01_organ_1_lq" -static const ALIGN_ASSET(2) char gSampleD9[] = dgSampleD9; - -#define dgSampleDA "__OTR__sound/samples/bowser_organ/02_boys_choir" -static const ALIGN_ASSET(2) char gSampleDA[] = dgSampleDA; - -static const char* gSampleTable[] = { - gSample00, gSample01, gSample02, gSample03, - gSample04, gSample05, gSample06, gSample07, - gSample08, gSample09, gSample0A, gSample0B, - gSample0C, gSample0D, gSample0E, gSample0F, - gSample10, gSample11, gSample12, gSample13, - gSample14, gSample15, gSample16, gSample17, - gSample18, gSample19, gSample1A, gSample1B, - gSample1C, gSample1D, gSample1E, gSample1F, - gSample20, gSample21, gSample22, gSample23, - gSample24, gSample25, gSample26, gSample27, - gSample28, gSample29, gSample2A, gSample2B, - gSample2C, gSample2D, gSample2E, gSample2F, - gSample30, gSample31, gSample32, gSample33, - gSample34, gSample35, gSample36, gSample37, - gSample38, gSample39, gSample3A, gSample3B, - gSample3C, gSample3D, gSample3E, gSample3F, - gSample40, gSample41, gSample42, gSample43, - gSample44, gSample45, gSample46, gSample47, - gSample48, gSample49, gSample4A, gSample4B, - gSample4C, gSample4D, gSample4E, gSample4F, - gSample50, gSample51, gSample52, gSample53, - gSample54, gSample55, gSample56, gSample57, - gSample58, gSample59, gSample5A, gSample5B, - gSample5C, gSample5D, gSample5E, gSample5F, - gSample60, gSample61, gSample62, gSample63, - gSample64, gSample65, gSample66, gSample67, - gSample68, gSample69, gSample6A, gSample6B, - gSample6C, gSample6D, gSample6E, gSample6F, - gSample70, gSample71, gSample72, gSample73, - gSample74, gSample75, gSample76, gSample77, - gSample78, gSample79, gSample7A, gSample7B, - gSample7C, gSample7D, gSample7E, gSample7F, - gSample80, gSample81, gSample82, gSample83, - gSample84, gSample85, gSample86, gSample87, - gSample88, gSample89, gSample8A, gSample8B, - gSample8C, gSample8D, gSample8E, gSample8F, - gSample90, gSample91, gSample92, gSample93, - gSample94, gSample95, gSample96, gSample97, - gSample98, gSample99, gSample9A, gSample9B, - gSample9C, gSample9D, gSample9E, gSample9F, - gSampleA0, gSampleA1, gSampleA2, gSampleA3, - gSampleA4, gSampleA5, gSampleA6, gSampleA7, - gSampleA8, gSampleA9, gSampleAA, gSampleAB, - gSampleAC, gSampleAD, gSampleAE, gSampleAF, - gSampleB0, gSampleB1, gSampleB2, gSampleB3, - gSampleB4, gSampleB5, gSampleB6, gSampleB7, - gSampleB8, gSampleB9, gSampleBA, gSampleBB, - gSampleBC, gSampleBD, gSampleBE, gSampleBF, - gSampleC0, gSampleC1, gSampleC2, gSampleC3, - gSampleC4, gSampleC5, gSampleC6, gSampleC7, - gSampleC8, gSampleC9, gSampleCA, gSampleCB, - gSampleCC, gSampleCD, gSampleCE, gSampleCF, - gSampleD0, gSampleD1, gSampleD2, gSampleD3, - gSampleD4, gSampleD5, gSampleD6, gSampleD7, - gSampleD8, gSampleD9, gSampleDA, -}; diff --git a/include/sequences_table.h b/include/sequences_table.h deleted file mode 100644 index 5572b8f1..00000000 --- a/include/sequences_table.h +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once -#include "align_asset_macro.h" - -#define dgSequence00 "__OTR__sound/sequences/00_sound_player" -static const ALIGN_ASSET(2) char gSequence00[] = dgSequence00; - -#define dgSequence01 "__OTR__sound/sequences/01_cutscene_collect_star" -static const ALIGN_ASSET(2) char gSequence01[] = dgSequence01; - -#define dgSequence02 "__OTR__sound/sequences/02_menu_title_screen" -static const ALIGN_ASSET(2) char gSequence02[] = dgSequence02; - -#define dgSequence03 "__OTR__sound/sequences/03_level_grass" -static const ALIGN_ASSET(2) char gSequence03[] = dgSequence03; - -#define dgSequence04 "__OTR__sound/sequences/04_level_inside_castle" -static const ALIGN_ASSET(2) char gSequence04[] = dgSequence04; - -#define dgSequence05 "__OTR__sound/sequences/05_level_water" -static const ALIGN_ASSET(2) char gSequence05[] = dgSequence05; - -#define dgSequence06 "__OTR__sound/sequences/06_level_hot" -static const ALIGN_ASSET(2) char gSequence06[] = dgSequence06; - -#define dgSequence07 "__OTR__sound/sequences/07_level_boss_koopa" -static const ALIGN_ASSET(2) char gSequence07[] = dgSequence07; - -#define dgSequence08 "__OTR__sound/sequences/08_level_snow" -static const ALIGN_ASSET(2) char gSequence08[] = dgSequence08; - -#define dgSequence09 "__OTR__sound/sequences/09_level_slide" -static const ALIGN_ASSET(2) char gSequence09[] = dgSequence09; - -#define dgSequence0A "__OTR__sound/sequences/0A_level_spooky" -static const ALIGN_ASSET(2) char gSequence0A[] = dgSequence0A; - -#define dgSequence0B "__OTR__sound/sequences/0B_event_piranha_plant" -static const ALIGN_ASSET(2) char gSequence0B[] = dgSequence0B; - -#define dgSequence0C "__OTR__sound/sequences/0C_level_underground" -static const ALIGN_ASSET(2) char gSequence0C[] = dgSequence0C; - -#define dgSequence0D "__OTR__sound/sequences/0D_menu_star_select" -static const ALIGN_ASSET(2) char gSequence0D[] = dgSequence0D; - -#define dgSequence0E "__OTR__sound/sequences/0E_event_powerup" -static const ALIGN_ASSET(2) char gSequence0E[] = dgSequence0E; - -#define dgSequence0F "__OTR__sound/sequences/0F_event_metal_cap" -static const ALIGN_ASSET(2) char gSequence0F[] = dgSequence0F; - -#define dgSequence10 "__OTR__sound/sequences/10_event_koopa_message" -static const ALIGN_ASSET(2) char gSequence10[] = dgSequence10; - -#define dgSequence11 "__OTR__sound/sequences/11_level_koopa_road" -static const ALIGN_ASSET(2) char gSequence11[] = dgSequence11; - -#define dgSequence12 "__OTR__sound/sequences/12_event_high_score" -static const ALIGN_ASSET(2) char gSequence12[] = dgSequence12; - -#define dgSequence13 "__OTR__sound/sequences/13_event_merry_go_round" -static const ALIGN_ASSET(2) char gSequence13[] = dgSequence13; - -#define dgSequence14 "__OTR__sound/sequences/14_event_race" -static const ALIGN_ASSET(2) char gSequence14[] = dgSequence14; - -#define dgSequence15 "__OTR__sound/sequences/15_cutscene_star_spawn" -static const ALIGN_ASSET(2) char gSequence15[] = dgSequence15; - -#define dgSequence16 "__OTR__sound/sequences/16_event_boss" -static const ALIGN_ASSET(2) char gSequence16[] = dgSequence16; - -#define dgSequence17 "__OTR__sound/sequences/17_cutscene_collect_key" -static const ALIGN_ASSET(2) char gSequence17[] = dgSequence17; - -#define dgSequence18 "__OTR__sound/sequences/18_event_endless_stairs" -static const ALIGN_ASSET(2) char gSequence18[] = dgSequence18; - -#define dgSequence19 "__OTR__sound/sequences/19_level_boss_koopa_final" -static const ALIGN_ASSET(2) char gSequence19[] = dgSequence19; - -#define dgSequence1A "__OTR__sound/sequences/1A_cutscene_credits" -static const ALIGN_ASSET(2) char gSequence1A[] = dgSequence1A; - -#define dgSequence1B "__OTR__sound/sequences/1B_event_solve_puzzle" -static const ALIGN_ASSET(2) char gSequence1B[] = dgSequence1B; - -#define dgSequence1C "__OTR__sound/sequences/1C_event_toad_message" -static const ALIGN_ASSET(2) char gSequence1C[] = dgSequence1C; - -#define dgSequence1D "__OTR__sound/sequences/1D_event_peach_message" -static const ALIGN_ASSET(2) char gSequence1D[] = dgSequence1D; - -#define dgSequence1E "__OTR__sound/sequences/1E_cutscene_intro" -static const ALIGN_ASSET(2) char gSequence1E[] = dgSequence1E; - -#define dgSequence1F "__OTR__sound/sequences/1F_cutscene_victory" -static const ALIGN_ASSET(2) char gSequence1F[] = dgSequence1F; - -#define dgSequence20 "__OTR__sound/sequences/20_cutscene_ending" -static const ALIGN_ASSET(2) char gSequence20[] = dgSequence20; - -#define dgSequence21 "__OTR__sound/sequences/21_menu_file_select" -static const ALIGN_ASSET(2) char gSequence21[] = dgSequence21; - -#define dgSequence22 "__OTR__sound/sequences/22_cutscene_lakitu" -static const ALIGN_ASSET(2) char gSequence22[] = dgSequence22; - -static const char* gSequenceTable[] = { - gSequence00, gSequence01, gSequence02, gSequence03, - gSequence04, gSequence05, gSequence06, gSequence07, - gSequence08, gSequence09, gSequence0A, gSequence0B, - gSequence0C, gSequence0D, gSequence0E, gSequence0F, - gSequence10, gSequence11, gSequence12, gSequence13, - gSequence14, gSequence15, gSequence16, gSequence17, - gSequence18, gSequence19, gSequence1A, gSequence1B, - gSequence1C, gSequence1D, gSequence1E, gSequence1F, - gSequence20, gSequence21, gSequence22, -}; diff --git a/include/sm64.h b/include/sm64.h index 8e030a61..b8f9be50 100644 --- a/include/sm64.h +++ b/include/sm64.h @@ -500,6 +500,21 @@ extern void osCreateViManager(OSPri); extern uintptr_t osVirtualToPhysical(void *addr); extern void * osPhysicalToVirtual(u32); extern void osMapTLB(int32_t a, uint32_t b, void* c, uint32_t d, uint32_t e, uint32_t f); + +// Interpolation + +extern void *vec3s_copy(Vec3s dest, Vec3s src); +extern void interpolate_vectors(Vec3f res, Vec3f a, Vec3f b); +extern void interpolate_vectors_s16(Vec3s res, Vec3s a, Vec3s b); +extern void mtx_patch_interpolated(void); +extern void patch_screen_transition_interpolated(void); +extern void patch_title_screen_scales(void); +extern void patch_interpolated_dialog(void); +extern void patch_interpolated_hud(void); +extern void patch_interpolated_paintings(void); +extern void patch_interpolated_bubble_particles(void); +extern void patch_interpolated_snow_particles(void); + #ifndef TRUE #define TRUE 1 #endif diff --git a/include/types.h b/include/types.h index bd088d5a..4592c7f9 100644 --- a/include/types.h +++ b/include/types.h @@ -109,6 +109,10 @@ struct AnimInfo { /*0x0A 0x42*/ u16 animTimer; /*0x0C 0x44*/ s32 animFrameAccelAssist; /*0x10 0x48*/ s32 animAccel; + s16 prevAnimFrame; + s16 prevAnimID; + u32 prevAnimFrameTimestamp; + struct Animation *prevAnimPtr; }; struct GraphNodeObject { @@ -118,11 +122,22 @@ struct GraphNodeObject { /*0x19*/ s8 activeAreaIndex; /*0x1A*/ Vec3s angle; /*0x20*/ Vec3f pos; + Vec3s prevAngle; + Vec3f prevPos; + u32 prevTimestamp; + Vec3f prevShadowPos; + u32 prevShadowPosTimestamp; /*0x2C*/ Vec3f scale; + Vec3f prevScale; + u32 prevScaleTimestamp; /*0x38*/ struct AnimInfo animInfo; /*0x4C*/ struct SpawnInfo *unk4C; /*0x50*/ Mat4 *throwMatrix; // matrix ptr + Mat4 prevThrowMatrix; + u32 prevThrowMatrixTimestamp; + Mat4 *throwMatrixInterpolated; /*0x54*/ Vec3f cameraToObject; + u32 skipInterpolationTimestamp; }; struct ObjectNode { @@ -227,6 +242,10 @@ struct Surface { } normal; /*0x28*/ f32 originOffset; /*0x2C*/ struct Object *object; + Vec3s prevVertex1; + Vec3s prevVertex2; + Vec3s prevVertex3; + u32 modifiedTimestamp; }; struct MarioBodyState { diff --git a/levels/menu/header.h b/levels/menu/header.h index 170c16c1..ed95cd45 100644 --- a/levels/menu/header.h +++ b/levels/menu/header.h @@ -31,7 +31,8 @@ extern const Gfx dl_menu_generic_button[]; extern const Gfx dl_menu_idle_hand[]; extern const Gfx dl_menu_grabbing_hand[]; extern const u8 *const menu_hud_lut[]; -extern const u8 *const menu_font_lut[]; +extern const u8 *const menu_font_lut_us[]; +extern const u8 *const menu_font_lut_jp[]; extern const Gfx dl_menu_ia8_text_begin[]; extern const Gfx dl_menu_ia8_text_end[]; extern const Gfx dl_menu_rgba16_wood_course[]; diff --git a/levels/menu/leveldata.c b/levels/menu/leveldata.c index f2d7375d..d2d877d4 100644 --- a/levels/menu/leveldata.c +++ b/levels/menu/leveldata.c @@ -526,7 +526,6 @@ const Texture *const menu_hud_lut[] = { texture_menu_hud_char_katakana_sa, texture_menu_hud_char_katakana_u, texture_menu_hud_char_katakana_n, texture_menu_hud_char_kana_dakuten_do, }; -#if defined(VERSION_JP) || defined(VERSION_SH) UNUSED static const u64 menu_unused_0 = 0; // 0x0700AC48 @@ -922,8 +921,6 @@ ALIGNED8 static const Texture texture_menu_font_char_jp_multiply[] = "__OTR__lev // 0x0700CCC8 ALIGNED8 static const Texture texture_menu_font_char_jp_exclamation[] = "__OTR__levels/menu/main_menu_seg7.0CCC8"; -#else - // 0x0700AC40 ALIGNED8 static const Texture texture_menu_font_char_0[] = "__OTR__levels/menu/main_menu_seg7.0AC40"; @@ -1084,7 +1081,6 @@ ALIGNED8 static const Texture texture_menu_font_char_period[] = "__OTR__levels/m // 0x0700B800 ALIGNED8 static const Texture texture_menu_font_char_ampersand[] = "__OTR__levels/menu/main_menu_seg7.0B800"; -#endif #ifdef VERSION_EU // 0x0700B840 @@ -1099,73 +1095,7 @@ ALIGNED8 static const Texture texture_menu_font_char_colon[] = "__OTR__levels/me // Menu small font print table // 0x0700CD08 -const Texture *const menu_font_lut[] = { -#if defined(VERSION_JP) || defined(VERSION_SH) - texture_menu_font_char_jp_0, texture_menu_font_char_jp_1, texture_menu_font_char_jp_2, texture_menu_font_char_jp_3, - texture_menu_font_char_jp_4, texture_menu_font_char_jp_5, texture_menu_font_char_jp_6, texture_menu_font_char_jp_7, - texture_menu_font_char_jp_8, texture_menu_font_char_jp_9, texture_menu_font_char_jp_A, texture_menu_font_char_jp_B, - texture_menu_font_char_jp_C, texture_menu_font_char_jp_D, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - texture_menu_font_char_jp_hiragana_a, texture_menu_font_char_jp_hiragana_i, texture_menu_font_char_jp_hiragana_u, texture_menu_font_char_jp_hiragana_c, - texture_menu_font_char_jp_hiragana_o, texture_menu_font_char_jp_hiragana_ka, texture_menu_font_char_jp_hiragana_ki, texture_menu_font_char_jp_hiragana_ku, - texture_menu_font_char_jp_hiragana_ke, texture_menu_font_char_jp_hiragana_ko, texture_menu_font_char_jp_hiragana_sa, texture_menu_font_char_jp_hiragana_shi, - texture_menu_font_char_jp_hiragana_su, texture_menu_font_char_jp_hiragana_se, texture_menu_font_char_jp_hiragana_so, texture_menu_font_char_jp_hiragana_ta, - texture_menu_font_char_jp_hiragana_chi, texture_menu_font_char_jp_hiragana_tsu, texture_menu_font_char_jp_hiragana_te, texture_menu_font_char_jp_hiragana_to, - texture_menu_font_char_jp_hiragana_na, texture_menu_font_char_jp_hiragana_ni, texture_menu_font_char_jp_hiragana_nu, texture_menu_font_char_jp_hiragana_ne, - texture_menu_font_char_jp_hiragana_no, texture_menu_font_char_jp_hiragana_ha, texture_menu_font_char_jp_hiragana_hi, texture_menu_font_char_jp_hiragana_hu, - texture_menu_font_char_jp_hiragana_he, texture_menu_font_char_jp_hiragana_ho, texture_menu_font_char_jp_hiragana_ma, texture_menu_font_char_jp_hiragana_mi, - texture_menu_font_char_jp_hiragana_mu, texture_menu_font_char_jp_hiragana_me, texture_menu_font_char_jp_hiragana_mo, texture_menu_font_char_jp_hiragana_ya, - texture_menu_font_char_jp_hiragana_yu, texture_menu_font_char_jp_hiragana_yo, texture_menu_font_char_jp_hiragana_ra, texture_menu_font_char_jp_hiragana_ri, - texture_menu_font_char_jp_hiragana_ru, texture_menu_font_char_jp_hiragana_re, texture_menu_font_char_jp_hiragana_ro, texture_menu_font_char_jp_hiragana_wa, - texture_menu_font_char_jp_hiragana_wo, texture_menu_font_char_jp_hiragana_n, 0x0, 0x0, - texture_menu_font_char_jp_katakana_a, texture_menu_font_char_jp_katakana_i, texture_menu_font_char_jp_katakana_u, texture_menu_font_char_jp_katakana_e, - texture_menu_font_char_jp_katakana_o, texture_menu_font_char_jp_katakana_ka, texture_menu_font_char_jp_katakana_ki, texture_menu_font_char_jp_katakana_ku, - texture_menu_font_char_jp_katakana_ke, texture_menu_font_char_jp_katakana_ko, texture_menu_font_char_jp_katakana_sa, texture_menu_font_char_jp_katakana_shi, - texture_menu_font_char_jp_katakana_su, texture_menu_font_char_jp_katakana_se, texture_menu_font_char_jp_katakana_so, texture_menu_font_char_jp_katakana_ta, - texture_menu_font_char_jp_katakana_chi, texture_menu_font_char_jp_katakana_tsu, texture_menu_font_char_jp_katakana_te, texture_menu_font_char_jp_katakana_to, - texture_menu_font_char_jp_katakana_na, texture_menu_font_char_jp_katakana_ni, texture_menu_font_char_jp_katakana_nu, texture_menu_font_char_jp_katakana_ne, - texture_menu_font_char_jp_katakana_no, texture_menu_font_char_jp_katakana_ha, texture_menu_font_char_jp_katakana_hi, texture_menu_font_char_jp_katakana_hu, - texture_menu_font_char_jp_katakana_he, texture_menu_font_char_jp_katakana_ho, texture_menu_font_char_jp_katakana_ma, texture_menu_font_char_jp_katakana_mi, - texture_menu_font_char_jp_katakana_mu, texture_menu_font_char_jp_katakana_me, texture_menu_font_char_jp_katakana_mo, texture_menu_font_char_jp_katakana_ya, - texture_menu_font_char_jp_katakana_yu, texture_menu_font_char_jp_katakana_yo, texture_menu_font_char_jp_katakana_ra, texture_menu_font_char_jp_katakana_ri, - texture_menu_font_char_jp_katakana_ru, texture_menu_font_char_jp_katakana_re, texture_menu_font_char_jp_katakana_ro, texture_menu_font_char_jp_katakana_wa, - texture_menu_font_char_jp_katakana_wo, texture_menu_font_char_jp_katakana_n, 0x0, texture_menu_font_char_jp_long_vowel, - texture_menu_font_char_jp_hiragana_small_e, texture_menu_font_char_jp_hiragana_small_tsu, texture_menu_font_char_jp_hiragana_small_ka, texture_menu_font_char_jp_hiragana_small_yu, - texture_menu_font_char_jp_hiragana_small_yo, texture_menu_font_char_jp_hiragana_small_a, texture_menu_font_char_jp_hiragana_small_i, texture_menu_font_char_jp_hiragana_small_u, - texture_menu_font_char_jp_hiragana_small_o, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - texture_menu_font_char_jp_katakana_small_e, texture_menu_font_char_jp_katakana_small_tsu, texture_menu_font_char_jp_katakana_small_ka, texture_menu_font_char_jp_katakana_small_yu, - texture_menu_font_char_jp_katakana_small_yo, texture_menu_font_char_jp_katakana_small_a, texture_menu_font_char_jp_katakana_small_i, texture_menu_font_char_jp_katakana_small_u, - texture_menu_font_char_jp_katakana_small_o, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - texture_menu_font_char_jp_dakuten, texture_menu_font_char_jp_handakuten, texture_menu_font_char_jp_exclamation, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, texture_menu_font_char_jp_coin, texture_menu_font_char_jp_star_filled, texture_menu_font_char_jp_multiply, - 0x0, 0x0, 0x0, 0x0, -#else +const Texture *const menu_font_lut_us[] = { texture_menu_font_char_0, texture_menu_font_char_1, texture_menu_font_char_2, texture_menu_font_char_3, texture_menu_font_char_4, texture_menu_font_char_5, texture_menu_font_char_6, texture_menu_font_char_7, texture_menu_font_char_8, texture_menu_font_char_9, texture_menu_font_char_A, texture_menu_font_char_B, @@ -1236,7 +1166,73 @@ const Texture *const menu_font_lut[] = { texture_menu_font_char_question, 0x0, 0x0, 0x0, 0x0, texture_menu_font_char_coin, texture_menu_font_char_star_filled, texture_menu_font_char_multiply, 0x0, 0x0, 0x0, 0x0, -#endif +}; + +const Texture *const menu_font_lut_jp[] = { + texture_menu_font_char_jp_0, texture_menu_font_char_jp_1, texture_menu_font_char_jp_2, texture_menu_font_char_jp_3, + texture_menu_font_char_jp_4, texture_menu_font_char_jp_5, texture_menu_font_char_jp_6, texture_menu_font_char_jp_7, + texture_menu_font_char_jp_8, texture_menu_font_char_jp_9, texture_menu_font_char_jp_A, texture_menu_font_char_jp_B, + texture_menu_font_char_jp_C, texture_menu_font_char_jp_D, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + texture_menu_font_char_jp_hiragana_a, texture_menu_font_char_jp_hiragana_i, texture_menu_font_char_jp_hiragana_u, texture_menu_font_char_jp_hiragana_c, + texture_menu_font_char_jp_hiragana_o, texture_menu_font_char_jp_hiragana_ka, texture_menu_font_char_jp_hiragana_ki, texture_menu_font_char_jp_hiragana_ku, + texture_menu_font_char_jp_hiragana_ke, texture_menu_font_char_jp_hiragana_ko, texture_menu_font_char_jp_hiragana_sa, texture_menu_font_char_jp_hiragana_shi, + texture_menu_font_char_jp_hiragana_su, texture_menu_font_char_jp_hiragana_se, texture_menu_font_char_jp_hiragana_so, texture_menu_font_char_jp_hiragana_ta, + texture_menu_font_char_jp_hiragana_chi, texture_menu_font_char_jp_hiragana_tsu, texture_menu_font_char_jp_hiragana_te, texture_menu_font_char_jp_hiragana_to, + texture_menu_font_char_jp_hiragana_na, texture_menu_font_char_jp_hiragana_ni, texture_menu_font_char_jp_hiragana_nu, texture_menu_font_char_jp_hiragana_ne, + texture_menu_font_char_jp_hiragana_no, texture_menu_font_char_jp_hiragana_ha, texture_menu_font_char_jp_hiragana_hi, texture_menu_font_char_jp_hiragana_hu, + texture_menu_font_char_jp_hiragana_he, texture_menu_font_char_jp_hiragana_ho, texture_menu_font_char_jp_hiragana_ma, texture_menu_font_char_jp_hiragana_mi, + texture_menu_font_char_jp_hiragana_mu, texture_menu_font_char_jp_hiragana_me, texture_menu_font_char_jp_hiragana_mo, texture_menu_font_char_jp_hiragana_ya, + texture_menu_font_char_jp_hiragana_yu, texture_menu_font_char_jp_hiragana_yo, texture_menu_font_char_jp_hiragana_ra, texture_menu_font_char_jp_hiragana_ri, + texture_menu_font_char_jp_hiragana_ru, texture_menu_font_char_jp_hiragana_re, texture_menu_font_char_jp_hiragana_ro, texture_menu_font_char_jp_hiragana_wa, + texture_menu_font_char_jp_hiragana_wo, texture_menu_font_char_jp_hiragana_n, 0x0, 0x0, + texture_menu_font_char_jp_katakana_a, texture_menu_font_char_jp_katakana_i, texture_menu_font_char_jp_katakana_u, texture_menu_font_char_jp_katakana_e, + texture_menu_font_char_jp_katakana_o, texture_menu_font_char_jp_katakana_ka, texture_menu_font_char_jp_katakana_ki, texture_menu_font_char_jp_katakana_ku, + texture_menu_font_char_jp_katakana_ke, texture_menu_font_char_jp_katakana_ko, texture_menu_font_char_jp_katakana_sa, texture_menu_font_char_jp_katakana_shi, + texture_menu_font_char_jp_katakana_su, texture_menu_font_char_jp_katakana_se, texture_menu_font_char_jp_katakana_so, texture_menu_font_char_jp_katakana_ta, + texture_menu_font_char_jp_katakana_chi, texture_menu_font_char_jp_katakana_tsu, texture_menu_font_char_jp_katakana_te, texture_menu_font_char_jp_katakana_to, + texture_menu_font_char_jp_katakana_na, texture_menu_font_char_jp_katakana_ni, texture_menu_font_char_jp_katakana_nu, texture_menu_font_char_jp_katakana_ne, + texture_menu_font_char_jp_katakana_no, texture_menu_font_char_jp_katakana_ha, texture_menu_font_char_jp_katakana_hi, texture_menu_font_char_jp_katakana_hu, + texture_menu_font_char_jp_katakana_he, texture_menu_font_char_jp_katakana_ho, texture_menu_font_char_jp_katakana_ma, texture_menu_font_char_jp_katakana_mi, + texture_menu_font_char_jp_katakana_mu, texture_menu_font_char_jp_katakana_me, texture_menu_font_char_jp_katakana_mo, texture_menu_font_char_jp_katakana_ya, + texture_menu_font_char_jp_katakana_yu, texture_menu_font_char_jp_katakana_yo, texture_menu_font_char_jp_katakana_ra, texture_menu_font_char_jp_katakana_ri, + texture_menu_font_char_jp_katakana_ru, texture_menu_font_char_jp_katakana_re, texture_menu_font_char_jp_katakana_ro, texture_menu_font_char_jp_katakana_wa, + texture_menu_font_char_jp_katakana_wo, texture_menu_font_char_jp_katakana_n, 0x0, texture_menu_font_char_jp_long_vowel, + texture_menu_font_char_jp_hiragana_small_e, texture_menu_font_char_jp_hiragana_small_tsu, texture_menu_font_char_jp_hiragana_small_ka, texture_menu_font_char_jp_hiragana_small_yu, + texture_menu_font_char_jp_hiragana_small_yo, texture_menu_font_char_jp_hiragana_small_a, texture_menu_font_char_jp_hiragana_small_i, texture_menu_font_char_jp_hiragana_small_u, + texture_menu_font_char_jp_hiragana_small_o, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + texture_menu_font_char_jp_katakana_small_e, texture_menu_font_char_jp_katakana_small_tsu, texture_menu_font_char_jp_katakana_small_ka, texture_menu_font_char_jp_katakana_small_yu, + texture_menu_font_char_jp_katakana_small_yo, texture_menu_font_char_jp_katakana_small_a, texture_menu_font_char_jp_katakana_small_i, texture_menu_font_char_jp_katakana_small_u, + texture_menu_font_char_jp_katakana_small_o, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + texture_menu_font_char_jp_dakuten, texture_menu_font_char_jp_handakuten, texture_menu_font_char_jp_exclamation, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, texture_menu_font_char_jp_coin, texture_menu_font_char_jp_star_filled, texture_menu_font_char_jp_multiply, + 0x0, 0x0, 0x0, 0x0, }; // 0x0700D108 - 0x0700D160 diff --git a/libultraship b/libultraship index 91ea351a..0b486122 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 91ea351a475f83a2150be85e73d52e58aa3a0b94 +Subproject commit 0b486122c8711c8d124449435d4d05640e3f4a83 diff --git a/src/audio/internal.h b/src/audio/internal.h index fc742066..617e7587 100644 --- a/src/audio/internal.h +++ b/src/audio/internal.h @@ -222,7 +222,7 @@ struct AudioBank { struct CtlEntry { #ifndef VERSION_SH - u8 unused; + u8 bankId; #endif u8 numInstruments; u8 numDrums; @@ -238,6 +238,7 @@ struct AudioSequenceData { u32 bankCount; u8* banks; u8* data; + u8 id; }; struct M64ScriptState { diff --git a/src/audio/load.c b/src/audio/load.c index a67ab03f..131932e9 100644 --- a/src/audio/load.c +++ b/src/audio/load.c @@ -8,7 +8,6 @@ #include "load.h" #include "seqplayer.h" #include "sound/sound_data.h" -#include "sequences_table.h" #include #include @@ -393,7 +392,7 @@ uint8_t* load_sequence_immediate(s32 seqId, s32 arg1) { struct CtlEntry* load_banks_immediate(s32 seqId, u8 *outDefaultBank) { u32 bankId; - struct AudioSequenceData *seqData = ResourceGetDataByName(gSequenceTable[seqId]); + struct AudioSequenceData *seqData = GameEngine_LoadSequence(seqId); struct CtlEntry *output; for(size_t i = 0; i < seqData->bankCount; i++) { output = GameEngine_LoadBank(bankId = seqData->banks[i]); @@ -538,7 +537,7 @@ void audio_init() { #endif // Load headers for sounds and sequences - gSequenceCount = sizeof(gSequenceTable) / sizeof(gSequenceTable[0]); + gSequenceCount = GameEngine_GetSequenceCount(); init_sequence_players(); gAudioLoadLock = AUDIO_LOCK_NOT_LOADING; diff --git a/src/audio/seqplayer.c b/src/audio/seqplayer.c index b9d33c8b..90e29a85 100644 --- a/src/audio/seqplayer.c +++ b/src/audio/seqplayer.c @@ -954,7 +954,6 @@ u8 audioString107[] = "Error: Your assignchannel is stolen.\n"; #else // US/JP version with macros to simulate inlining by copt. Edit if you dare. #include "copt/seq_channel_layer_process_script_copt.inc.c" -#include "sequences_table.h" #endif @@ -1784,7 +1783,7 @@ void sequence_channel_process_script(struct SequenceChannel *seqChannel) { loBits = *(sp38 + gAlBankSets); cmd = gAlBankSets[(s32)sp38 + loBits - cmd]; #else - struct AudioSequenceData *sequence = ResourceGetDataByName(gSequenceTable[seqPlayer->seqId]); + struct AudioSequenceData *sequence = GameEngine_LoadSequence(seqPlayer->seqId); cmd = sequence->banks[cmd]; #endif #ifdef VERSION_SH diff --git a/src/engine/graph_node.h b/src/engine/graph_node.h index b397932c..27b40957 100644 --- a/src/engine/graph_node.h +++ b/src/engine/graph_node.h @@ -105,6 +105,8 @@ struct GraphNodePerspective { /*0x1C*/ f32 fov; // horizontal field of view in degrees /*0x20*/ s16 near; // near clipping plane /*0x22*/ s16 far; // far clipping plane + f32 prevFov; + f32 prevTimestamp; }; /** An entry in the master list. It is a linked list of display lists @@ -112,7 +114,9 @@ struct GraphNodePerspective { */ struct DisplayListNode { Mtx *transform; + void *transformInterpolated; void *displayList; + void *displayListInterpolated; struct DisplayListNode *next; }; @@ -174,7 +178,11 @@ struct GraphNodeCamera { } config; /*0x1C*/ Vec3f pos; /*0x28*/ Vec3f focus; + Vec3f prevPos; + Vec3f prevFocus; + u32 prevTimestamp; /*0x34*/ Mat4 *matrixPtr; // pointer to look-at matrix of this camera as a Mat4 + Mat4 *matrixPtrInterpolated; /*0x38*/ s16 roll; // roll in look at matrix. Doesn't account for light direction unlike rollScreen. /*0x3A*/ s16 rollScreen; // rolls screen while keeping the light direction consistent }; @@ -212,7 +220,8 @@ struct GraphNodeRotation { /*0x00*/ struct GraphNode node; /*0x14*/ void *displayList; /*0x18*/ Vec3s rotation; - u8 filler[2]; + Vec3s prevRotation; + u32 prevTimestamp; }; /** GraphNode part that transforms itself and its children based on animation @@ -301,6 +310,9 @@ struct GraphNodeBackground { /*0x00*/ struct FnGraphNode fnNode; /*0x18*/ s32 unused; /*0x1C*/ s32 background; // background ID, or rgba5551 color if fnNode.func is null + Vec3f prevCameraPos; + Vec3f prevCameraFocus; + u32 prevCameraTimestamp; }; /** Renders the object that Mario is holding. @@ -310,6 +322,8 @@ struct GraphNodeHeldObject { /*0x18*/ s32 playerIndex; /*0x1C*/ struct Object *objNode; /*0x20*/ Vec3s translation; + Vec3f prevShadowPos; + u32 prevShadowPosTimestamp; }; /** A node that allows an object to specify a different culling radius than the diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c index fad021b0..26599187 100644 --- a/src/engine/surface_collision.c +++ b/src/engine/surface_collision.c @@ -7,6 +7,7 @@ #include "game/object_list_processor.h" #include "surface_collision.h" #include "surface_load.h" +#include "game/game_init.h" /************************************************** * WALLS * @@ -395,26 +396,44 @@ f32 find_floor_height_and_data(f32 xPos, f32 yPos, f32 zPos, struct FloorGeometr return floorHeight; } +u8 gInterpolatingSurfaces; + /** * Iterate through the list of floors and find the first floor under a given point. */ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 x, s32 y, s32 z, f32 *pheight) { register struct Surface *surf; - register s32 x1, z1, x2, z2, x3, z3; + register f32 x1, z1, x2, z2, x3, z3; f32 nx, ny, nz; f32 oo; f32 height; struct Surface *floor = NULL; + s32 interpolate; // Iterate through the list of floors until there are no more floors. while (surfaceNode != NULL) { surf = surfaceNode->surface; surfaceNode = surfaceNode->next; + interpolate = gInterpolatingSurfaces && surf->modifiedTimestamp == gGlobalTimer; x1 = surf->vertex1[0]; z1 = surf->vertex1[2]; x2 = surf->vertex2[0]; z2 = surf->vertex2[2]; + if (interpolate) { + f32 diff = (surf->prevVertex1[0] - x1) * (surf->prevVertex1[0] - x1); + diff += (surf->prevVertex1[1] - surf->vertex1[1]) * (surf->prevVertex1[1] - surf->vertex1[1]); + diff += (surf->prevVertex1[2] - z1) * (surf->prevVertex1[2] - z1); + //printf("%f\n", sqrtf(diff)); + if (diff > 10000) { + interpolate = FALSE; + } else { + x1 = (surf->prevVertex1[0] + x1) / 2; + z1 = (surf->prevVertex1[2] + z1) / 2; + x2 = (surf->prevVertex2[0] + x2) / 2; + z2 = (surf->prevVertex2[2] + z2) / 2; + } + } // Check that the point is within the triangle bounds. if ((z1 - z) * (x2 - x1) - (x1 - x) * (z2 - z1) < 0) { @@ -424,6 +443,10 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 // To slightly save on computation time, set this later. x3 = surf->vertex3[0]; z3 = surf->vertex3[2]; + if (interpolate) { + x3 = (surf->prevVertex3[0] + x3) / 2; + z3 = (surf->prevVertex3[2] + z3) / 2; + } if ((z2 - z) * (x3 - x2) - (x2 - x) * (z3 - z2) < 0) { continue; @@ -443,10 +466,30 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 continue; } - nx = surf->normal.x; - ny = surf->normal.y; - nz = surf->normal.z; - oo = surf->originOffset; + if (interpolate) { + f32 y1, y2, y3; + f32 mag; + y1 = (surf->prevVertex1[1] + surf->vertex1[1]) / 2; + y2 = (surf->prevVertex2[1] + surf->vertex2[1]) / 2; + y3 = (surf->prevVertex3[1] + surf->vertex3[1]) / 2; + nx = (y2 - y1) * (z3 - z2) - (z2 - z1) * (y3 - y2); + ny = (z2 - z1) * (x3 - x2) - (x2 - x1) * (z3 - z2); + nz = (x2 - x1) * (y3 - y2) - (y2 - y1) * (x3 - x2); + mag = sqrtf(nx * nx + ny * ny + nz * nz); + if (mag < 0.0001) { + continue; + } + mag = (f32)(1.0 / mag); + nx *= mag; + ny *= mag; + nz *= mag; + oo = -(nx * x1 + ny * y1 + nz * z1); + } else { + nx = surf->normal.x; + ny = surf->normal.y; + nz = surf->normal.z; + oo = surf->originOffset; + } // If a wall, ignore it. Likely a remnant, should never occur. if (ny == 0.0f) { @@ -461,6 +504,15 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 } *pheight = height; + if (interpolate) { + static struct Surface s; + s.type = surf->type; + s.normal.x = nx; + s.normal.y = ny; + s.normal.z = nz; + s.originOffset = oo; + return &s; + } floor = surf; break; } diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c index 689f95fd..93147805 100644 --- a/src/engine/surface_load.c +++ b/src/engine/surface_load.c @@ -1,6 +1,7 @@ #include #include "sm64.h" +#include "engine/math_util.h" #include "game/ingame_menu.h" #include "graph_node.h" #include "behavior_script.h" @@ -12,6 +13,7 @@ #include "game/mario.h" #include "game/object_list_processor.h" #include "surface_load.h" +#include "game/game_init.h" s32 unused8038BE90; @@ -357,6 +359,11 @@ static struct Surface *read_surface_data(s16 *vertexData, s16 **vertexIndices) { surface = alloc_surface(); + vec3s_copy(surface->prevVertex1, surface->vertex1); + vec3s_copy(surface->prevVertex2, surface->vertex2); + vec3s_copy(surface->prevVertex3, surface->vertex3); + surface->modifiedTimestamp = gGlobalTimer; + surface->vertex1[0] = x1; surface->vertex2[0] = x2; surface->vertex3[0] = x3; diff --git a/src/game/camera.c b/src/game/camera.c index 79d7c7de..bb3d06cf 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -487,6 +487,10 @@ CameraTransition sModeTransitions[] = { extern u8 sDanceCutsceneIndexTable[][4]; extern u8 sZoomOutAreaMasks[]; +static void skip_camera_interpolation(void) { + gLakituState.skipCameraInterpolationTimestamp = gGlobalTimer; +} + /** * Starts a camera shake triggered by an interaction */ @@ -5504,6 +5508,7 @@ s32 set_camera_mode_fixed(struct Camera *c, s16 x, s16 y, s16 z) { c->mode = CAMERA_MODE_FIXED; vec3f_set(c->pos, sFixedModeBasePosition[0], sMarioCamState->pos[1], sFixedModeBasePosition[2]); + skip_camera_interpolation(); } return basePosSet; } @@ -5654,6 +5659,7 @@ BAD_RETURN(s32) cam_rr_enter_building_side(struct Camera *c) { if (c->mode != CAMERA_MODE_FIXED) { sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT; c->mode = CAMERA_MODE_FIXED; + skip_camera_interpolation(); } } @@ -5849,6 +5855,7 @@ BAD_RETURN(s32) cam_castle_enter_lobby(struct Camera *c) { sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT; set_fixed_cam_axis_sa_lobby(c->mode); c->mode = CAMERA_MODE_FIXED; + skip_camera_interpolation(); } } @@ -7212,6 +7219,7 @@ BAD_RETURN(s32) cutscene_unused_loop(UNUSED struct Camera *c) { BAD_RETURN(s32) cutscene_ending_mario_fall_start(struct Camera *c) { vec3f_set(c->focus, -26.f, 0.f, -137.f); vec3f_set(c->pos, 165.f, 4725.f, 324.f); + skip_camera_interpolation(); } /** @@ -7244,6 +7252,7 @@ BAD_RETURN(s32) cutscene_ending_mario_fall(struct Camera *c) { BAD_RETURN(s32) cutscene_ending_mario_land_closeup(struct Camera *c) { vec3f_set(c->focus, 85.f, 826.f, 250.f); vec3f_set(c->pos, -51.f, 988.f, -202.f); + skip_camera_interpolation(); player2_rotate_cam(c, -0x2000, 0x2000, -0x2000, 0x2000); } @@ -7253,6 +7262,7 @@ BAD_RETURN(s32) cutscene_ending_mario_land_closeup(struct Camera *c) { BAD_RETURN(s32) cutscene_ending_reset_spline(UNUSED struct Camera *c) { sCutsceneVars[9].point[0] = 0.f; cutscene_reset_spline(); + skip_camera_interpolation(); } /** @@ -7288,6 +7298,7 @@ BAD_RETURN(s32) cutscene_ending_peach_appear_closeup(struct Camera *c) { vec3f_set(c->pos, 179.f, 2463.f, -1216.f); c->pos[1] = gCutsceneFocus->oPosY + 35.f; vec3f_set(c->focus, gCutsceneFocus->oPosX, gCutsceneFocus->oPosY + 125.f, gCutsceneFocus->oPosZ); + skip_camera_interpolation(); } /** @@ -7306,6 +7317,7 @@ BAD_RETURN(s32) cutscene_ending_peach_appears(struct Camera *c) { BAD_RETURN(s32) cutscene_ending_peach_descends_start(UNUSED struct Camera *c) { cutscene_reset_spline(); sCutsceneVars[2].point[1] = 150.f; + skip_camera_interpolation(); } /** @@ -7393,6 +7405,7 @@ BAD_RETURN(s32) cutscene_ending_dialog(struct Camera *c) { vec3f_set(c->focus, 11.f, 983.f, -1273.f); vec3f_set(c->pos, -473.f, 970.f, -1152.f); player2_rotate_cam(c, -0x800, 0x2000, -0x2000, 0x2000); + skip_camera_interpolation(); } /** @@ -7402,6 +7415,7 @@ BAD_RETURN(s32) cutscene_ending_kiss_closeup(struct Camera *c) { set_fov_function(CAM_FOV_SET_29); vec3f_set(c->focus, 350.f, 1034.f, -1216.f); vec3f_set(c->pos, -149.f, 1021.f, -1216.f); + skip_camera_interpolation(); } /** @@ -7437,6 +7451,7 @@ BAD_RETURN(s32) cutscene_ending_kiss(struct Camera *c) { BAD_RETURN(s32) cutscene_ending_look_at_sky(struct Camera *c) { move_point_along_spline(c->focus, sEndingLookAtSkyFocus, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress); vec3f_set(c->pos, 699.f, 1680.f, -703.f); + skip_camera_interpolation(); } /** @@ -10257,6 +10272,7 @@ BAD_RETURN(s32) cutscene_door_start(struct Camera *c) { BAD_RETURN(s32) cutscene_door_fix_cam(struct Camera *c) { vec3f_copy(c->pos, sCutsceneVars[0].point); vec3f_copy(c->focus, sCutsceneVars[1].point); + skip_camera_interpolation(); } /** @@ -10290,6 +10306,7 @@ BAD_RETURN(s32) cutscene_door_move_behind_mario(struct Camera *c) { } offset_rotated(c->pos, sMarioCamState->pos, camOffset, sCutsceneVars[0].angle); + skip_camera_interpolation(); } /** diff --git a/src/game/camera.h b/src/game/camera.h index 81a2034b..4aca8be6 100644 --- a/src/game/camera.h +++ b/src/game/camera.h @@ -639,6 +639,8 @@ struct LakituState { /// Mario's action from the previous frame. Only used to determine if Mario just finished a dive. /*0xB8*/ u32 lastFrameAction; /*0xBC*/ s16 unused; + + u32 skipCameraInterpolationTimestamp; }; // bss order hack to not affect BSS order. if possible, remove me, but it will be hard to match otherwise diff --git a/src/game/envfx_bubbles.c b/src/game/envfx_bubbles.c index feda81d2..84acee31 100644 --- a/src/game/envfx_bubbles.c +++ b/src/game/envfx_bubbles.c @@ -10,6 +10,7 @@ #include "engine/behavior_script.h" #include "audio/external.h" #include "textures.h" +#include "rendering_graph_node.h" /** * This file implements environment effects that are not snow: @@ -35,6 +36,20 @@ Vtx_t gBubbleTempVtx[3] = { { { 0, 0, 0 }, 0, { -498, 964 }, { 0xFF, 0xFF, 0xFF, 0xFF } }, }; +static Gfx sGfxSaved[60 / 5]; +static Gfx *sBubbleInterpolatedDisplayListPos[60 / 5]; +static Vec3s sPrevBubblePositions[60]; + +void patch_interpolated_bubble_particles(void) { + s32 i; + for (i = 0; i < 60 / 5; i++) { + if (sBubbleInterpolatedDisplayListPos[i] != NULL) { + *sBubbleInterpolatedDisplayListPos[i] = sGfxSaved[i]; + sBubbleInterpolatedDisplayListPos[i] = NULL; + } + } +} + /** * Check whether the particle with the given index is * laterally within distance of point (x, z). Used to @@ -239,6 +254,7 @@ void envfx_update_whirlpool(void) { (gEnvFxBuffer + i)->yPos = (i + gEnvFxBuffer)->bubbleY; (gEnvFxBuffer + i)->unusedBubbleVar = 0; (gEnvFxBuffer + i)->isAlive = TRUE; + (gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer; envfx_rotate_around_whirlpool(&(gEnvFxBuffer + i)->xPos, &(gEnvFxBuffer + i)->yPos, &(gEnvFxBuffer + i)->zPos); @@ -297,6 +313,7 @@ void envfx_update_jetstream(void) { + coss((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1]; (gEnvFxBuffer + i)->yPos = gEnvFxBubbleConfig[ENVFX_STATE_SRC_Y] + (random_float() * 400.0f - 200.0f); + (gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer; } else { (gEnvFxBuffer + i)->angleAndDist[1] += 10; (gEnvFxBuffer + i)->xPos += sins((gEnvFxBuffer + i)->angleAndDist[0]) * 10.0f; @@ -471,6 +488,12 @@ Gfx *envfx_update_bubble_particles(s32 mode, UNUSED Vec3s marioPos, Vec3s camFro Vec3s vertex1; Vec3s vertex2; Vec3s vertex3; + Vec3s interpolatedVertices[3]; + + static Vec3s prevVertex1; + static Vec3s prevVertex2; + static Vec3s prevVertex3; + static u32 prevTimestamp; Gfx *gfxStart = alloc_display_list(((sBubbleParticleMaxCount / 5) * 10 + sBubbleParticleMaxCount + 3) * sizeof(Gfx)); @@ -484,18 +507,53 @@ Gfx *envfx_update_bubble_particles(s32 mode, UNUSED Vec3s marioPos, Vec3s camFro envfx_bubbles_update_switch(mode, camTo, vertex1, vertex2, vertex3); rotate_triangle_vertices(vertex1, vertex2, vertex3, pitch, yaw); + if (gGlobalTimer == prevTimestamp + 1) { + interpolate_vectors_s16(interpolatedVertices[0], prevVertex1, vertex1); + interpolate_vectors_s16(interpolatedVertices[1], prevVertex2, vertex2); + interpolate_vectors_s16(interpolatedVertices[2], prevVertex3, vertex3); + } + vec3s_copy(prevVertex1, vertex1); + vec3s_copy(prevVertex2, vertex2); + vec3s_copy(prevVertex3, vertex3); + prevTimestamp = gGlobalTimer; + gSPDisplayList(sGfxCursor++, &tiny_bubble_dl_0B006D38); for (i = 0; i < sBubbleParticleMaxCount; i += 5) { + Vtx *interpolatedVertBuf = alloc_display_list(15 * sizeof(Vtx)); + s32 j, k; gDPPipeSync(sGfxCursor++); envfx_set_bubble_texture(mode, i); append_bubble_vertex_buffer(sGfxCursor++, i, vertex1, vertex2, vertex3, (Vtx *) gBubbleTempVtx); + sBubbleInterpolatedDisplayListPos[i / 5] = sGfxCursor; + for (j = 0; j < 5; j++) { + for (k = 0; k < 3; k++) { + Vtx *v = &interpolatedVertBuf[j * 3 + k]; + v->v = gBubbleTempVtx[k]; + if (gGlobalTimer != gEnvFxBuffer[i + j].spawnTimestamp && mode != ENVFX_LAVA_BUBBLES) { + v->v.ob[0] = (sPrevBubblePositions[i + j][0] + gEnvFxBuffer[i + j].xPos) / 2.0f + interpolatedVertices[k][0]; + v->v.ob[1] = (sPrevBubblePositions[i + j][1] + gEnvFxBuffer[i + j].yPos) / 2.0f + interpolatedVertices[k][1]; + v->v.ob[2] = (sPrevBubblePositions[i + j][2] + gEnvFxBuffer[i + j].zPos) / 2.0f + interpolatedVertices[k][2]; + } else { + v->v.ob[0] = gEnvFxBuffer[i + j].xPos + interpolatedVertices[k][0]; + v->v.ob[1] = gEnvFxBuffer[i + j].yPos + interpolatedVertices[k][1]; + v->v.ob[2] = gEnvFxBuffer[i + j].zPos + interpolatedVertices[k][2]; + } + } + } + gSPVertex(sGfxCursor++, VIRTUAL_TO_PHYSICAL(interpolatedVertBuf), 15, 0); + append_bubble_vertex_buffer(&sGfxSaved[i / 5], i, vertex1, vertex2, vertex3, (Vtx *) gBubbleTempVtx); gSP1Triangle(sGfxCursor++, 0, 1, 2, 0); gSP1Triangle(sGfxCursor++, 3, 4, 5, 0); gSP1Triangle(sGfxCursor++, 6, 7, 8, 0); gSP1Triangle(sGfxCursor++, 9, 10, 11, 0); gSP1Triangle(sGfxCursor++, 12, 13, 14, 0); } + for (i = 0; i < sBubbleParticleMaxCount; i++) { + sPrevBubblePositions[i][0] = gEnvFxBuffer[i].xPos; + sPrevBubblePositions[i][1] = gEnvFxBuffer[i].yPos; + sPrevBubblePositions[i][2] = gEnvFxBuffer[i].zPos; + } gSPDisplayList(sGfxCursor++, &tiny_bubble_dl_0B006AB0); gSPEndDisplayList(sGfxCursor++); diff --git a/src/game/envfx_snow.c b/src/game/envfx_snow.c index 5e1a032d..1b263271 100644 --- a/src/game/envfx_snow.c +++ b/src/game/envfx_snow.c @@ -55,6 +55,26 @@ extern void *tiny_bubble_dl_0B006AB0; extern void *tiny_bubble_dl_0B006A50; extern void *tiny_bubble_dl_0B006CD8; +static struct { + Gfx *pos; + Vtx vertices[15]; +} sPrevSnowVertices[140 / 5]; +static s16 sPrevSnowParticleCount; +static u32 sPrevSnowTimestamp; + +void patch_interpolated_snow_particles(void) { + int i; + + if (gGlobalTimer != sPrevSnowTimestamp + 1) { + return; + } + + for (i = 0; i < sPrevSnowParticleCount; i += 5) { + gSPVertex(sPrevSnowVertices[i / 5].pos, + VIRTUAL_TO_PHYSICAL(sPrevSnowVertices[i / 5].vertices), 15, 0); + } +} + /** * Initialize snow particles by allocating a buffer for storing their state * and setting a start amount. @@ -220,6 +240,7 @@ void envfx_update_snow_normal(s32 snowCylinderX, s32 snowCylinderY, s32 snowCyli 400.0f * random_float() - 200.0f + snowCylinderZ + (s16)(deltaZ * 2); (gEnvFxBuffer + i)->yPos = 200.0f * random_float() + snowCylinderY; (gEnvFxBuffer + i)->isAlive = TRUE; + (gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer; } else { (gEnvFxBuffer + i)->xPos += random_float() * 2 - 1.0f + (s16)(deltaX / 1.2); (gEnvFxBuffer + i)->yPos -= 2 -(s16)(deltaY * 0.8); @@ -254,6 +275,7 @@ void envfx_update_snow_blizzard(s32 snowCylinderX, s32 snowCylinderY, s32 snowCy 400.0f * random_float() - 200.0f + snowCylinderZ + (s16)(deltaZ * 2); (gEnvFxBuffer + i)->yPos = 400.0f * random_float() - 200.0f + snowCylinderY; (gEnvFxBuffer + i)->isAlive = TRUE; + (gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer; } else { (gEnvFxBuffer + i)->xPos += random_float() * 2 - 1.0f + (s16)(deltaX / 1.2) + 20.0f; (gEnvFxBuffer + i)->yPos -= 5 -(s16)(deltaY * 0.8); @@ -297,6 +319,7 @@ void envfx_update_snow_water(s32 snowCylinderX, s32 snowCylinderY, s32 snowCylin (gEnvFxBuffer + i)->zPos = 400.0f * random_float() - 200.0f + snowCylinderZ; (gEnvFxBuffer + i)->yPos = 400.0f * random_float() - 200.0f + snowCylinderY; (gEnvFxBuffer + i)->isAlive = TRUE; + (gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer; } } } @@ -347,29 +370,30 @@ void rotate_triangle_vertices(Vec3s vertex1, Vec3s vertex2, Vec3s vertex3, s16 p void append_snowflake_vertex_buffer(Gfx *gfx, s32 index, Vec3s vertex1, Vec3s vertex2, Vec3s vertex3) { s32 i = 0; Vtx *vertBuf = (Vtx *) alloc_display_list(15 * sizeof(Vtx)); + Vtx *vertBufInterpolated = (Vtx *) alloc_display_list(15 * sizeof(Vtx)); + Vtx *v; if (vertBuf == NULL) { return; } - for (i = 0; i < 15; i += 3) { - vertBuf[i] = gSnowTempVtx[0]; - (vertBuf + i)->v.ob[0] = (gEnvFxBuffer + (index + i / 3))->xPos + vertex1[0]; - (vertBuf + i)->v.ob[1] = (gEnvFxBuffer + (index + i / 3))->yPos + vertex1[1]; - (vertBuf + i)->v.ob[2] = (gEnvFxBuffer + (index + i / 3))->zPos + vertex1[2]; - - vertBuf[i + 1] = gSnowTempVtx[1]; - (vertBuf + i + 1)->v.ob[0] = (gEnvFxBuffer + (index + i / 3))->xPos + vertex2[0]; - (vertBuf + i + 1)->v.ob[1] = (gEnvFxBuffer + (index + i / 3))->yPos + vertex2[1]; - (vertBuf + i + 1)->v.ob[2] = (gEnvFxBuffer + (index + i / 3))->zPos + vertex2[2]; - - vertBuf[i + 2] = gSnowTempVtx[2]; - (vertBuf + i + 2)->v.ob[0] = (gEnvFxBuffer + (index + i / 3))->xPos + vertex3[0]; - (vertBuf + i + 2)->v.ob[1] = (gEnvFxBuffer + (index + i / 3))->yPos + vertex3[1]; - (vertBuf + i + 2)->v.ob[2] = (gEnvFxBuffer + (index + i / 3))->zPos + vertex3[2]; + for (i = 0; i < 15; i++) { + v = &sPrevSnowVertices[index / 5].vertices[i]; + vertBufInterpolated[i] = gSnowTempVtx[i % 3]; + if (index < sPrevSnowParticleCount && gGlobalTimer == sPrevSnowTimestamp + 1 && + gGlobalTimer != gEnvFxBuffer[index + i / 3].spawnTimestamp) { + vertBufInterpolated[i].v.ob[0] = (v->v.ob[0] + vertBuf[i].v.ob[0]) / 2; + vertBufInterpolated[i].v.ob[1] = (v->v.ob[1] + vertBuf[i].v.ob[1]) / 2; + vertBufInterpolated[i].v.ob[2] = (v->v.ob[2] + vertBuf[i].v.ob[2]) / 2; + } else { + vertBufInterpolated[i].v.ob[0] = vertBuf[i].v.ob[0]; + vertBufInterpolated[i].v.ob[1] = vertBuf[i].v.ob[1]; + vertBufInterpolated[i].v.ob[2] = vertBuf[i].v.ob[2]; + } + *v = vertBuf[i]; } - - gSPVertex(gfx, VIRTUAL_TO_PHYSICAL(vertBuf), 15, 0); + sPrevSnowVertices[index / 5].pos = gfx; + gSPVertex(gfx, VIRTUAL_TO_PHYSICAL(vertBufInterpolated), 15, 0); } /** @@ -453,6 +477,8 @@ Gfx *envfx_update_snow(s32 snowMode, Vec3s marioPos, Vec3s camFrom, Vec3s camTo) gSP1Triangle(gfx++, 9, 10, 11, 0); gSP1Triangle(gfx++, 12, 13, 14, 0); } + sPrevSnowParticleCount = gSnowParticleCount; + sPrevSnowTimestamp = gGlobalTimer; gSPDisplayList(gfx++, &tiny_bubble_dl_0B006AB0) gSPEndDisplayList(gfx++); diff --git a/src/game/envfx_snow.h b/src/game/envfx_snow.h index 92462d5d..23c6ff60 100644 --- a/src/game/envfx_snow.h +++ b/src/game/envfx_snow.h @@ -25,7 +25,7 @@ struct EnvFxParticle { s32 angleAndDist[2]; // for whirpools, [0] = angle from center, [1] = distance from center s32 unusedBubbleVar; // set to zero for bubbles when respawning, never used elsewhere s32 bubbleY; // for Bubbles, yPos is always set to this - u8 filler[24]; + u32 spawnTimestamp; }; extern s8 gEnvFxMode; diff --git a/src/game/hud.c b/src/game/hud.c index b6966622..c672798c 100644 --- a/src/game/hud.c +++ b/src/game/hud.c @@ -57,6 +57,20 @@ UNUSED static struct UnusedHUDStruct sUnusedHUDValues = { 0x00, 0x0A, 0x00 }; static struct CameraHUD sCameraHUD = { CAM_STATUS_NONE }; +static u32 sPowerMeterLastRenderTimestamp; +static s16 sPowerMeterLastY; +static Gfx *sPowerMeterDisplayListPos; + +void patch_interpolated_hud(void) { + if (sPowerMeterDisplayListPos != NULL) { + Mtx *mtx = alloc_display_list(sizeof(Mtx)); + guTranslate(mtx, (f32) sPowerMeterHUD.x, (f32) sPowerMeterHUD.y, 0); + gSPMatrix(sPowerMeterDisplayListPos, VIRTUAL_TO_PHYSICAL(mtx), + G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); + sPowerMeterDisplayListPos = NULL; + } +} + /** * Renders a rgba16 16x16 glyph texture from a table list. */ @@ -107,12 +121,21 @@ void render_power_meter_health_segment(s16 numHealthWedges) { */ void render_dl_power_meter(s16 numHealthWedges) { Mtx *mtx = alloc_display_list(sizeof(Mtx)); + f32 interpolatedY; if (mtx == NULL) { return; } - guTranslate(mtx, (f32) sPowerMeterHUD.x, (f32) sPowerMeterHUD.y, 0); + if (gGlobalTimer == sPowerMeterLastRenderTimestamp + 1) { + interpolatedY = (sPowerMeterLastY + sPowerMeterHUD.y) / 2.0f; + } else { + interpolatedY = sPowerMeterHUD.y; + } + guTranslate(mtx, (f32) sPowerMeterHUD.x, interpolatedY, 0); + sPowerMeterLastY = sPowerMeterHUD.y; + sPowerMeterLastRenderTimestamp = gGlobalTimer; + sPowerMeterDisplayListPos = gDisplayListHead; gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx++), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); diff --git a/src/game/ingame_menu.c b/src/game/ingame_menu.c index e87f20ec..9ae57fa6 100644 --- a/src/game/ingame_menu.c +++ b/src/game/ingame_menu.c @@ -116,6 +116,41 @@ u8 gMenuHoldKeyIndex = 0; u8 gMenuHoldKeyTimer = 0; s32 gDialogResponse = DIALOG_RESPONSE_NONE; +static Gfx *sInterpolatedDialogOffsetPos; +static f32 sInterpolatedDialogOffset; +static Gfx *sInterpolatedDialogRotationPos; +static f32 sInterpolatedDialogScale; +static f32 sInterpolatedDialogRotation; +static Gfx *sInterpolatedDialogZoomPos; + +void patch_interpolated_dialog(void) { + Mtx *matrix; + + if (sInterpolatedDialogOffsetPos != NULL) { + matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); + guTranslate(matrix, 0, sInterpolatedDialogOffset, 0); + gSPMatrix(sInterpolatedDialogOffsetPos, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); + sInterpolatedDialogOffsetPos = NULL; + } + if (sInterpolatedDialogRotationPos != NULL) { + matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); + guScale(matrix, 1.0 / sInterpolatedDialogScale, 1.0 / sInterpolatedDialogScale, 1.0f); + gSPMatrix(sInterpolatedDialogRotationPos++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); + matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); + guRotate(matrix, sInterpolatedDialogRotation * 4.0f, 0, 0, 1.0f); + gSPMatrix(sInterpolatedDialogRotationPos, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); + sInterpolatedDialogRotationPos = NULL; + } + if (sInterpolatedDialogZoomPos != NULL) { + matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); + guTranslate(matrix, 65.0 - (65.0 / sInterpolatedDialogScale), (40.0 / sInterpolatedDialogScale) - 40, 0); + gSPMatrix(sInterpolatedDialogZoomPos++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); + matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); + guScale(matrix, 1.0 / sInterpolatedDialogScale, 1.0 / sInterpolatedDialogScale, 1.0f); + gSPMatrix(sInterpolatedDialogZoomPos, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); + sInterpolatedDialogZoomPos = NULL; + } +} void create_dl_identity_matrix(void) { Mtx *matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); @@ -217,6 +252,7 @@ static u8 *alloc_ia8_text_from_i1(u16 *in, s16 width, s16 height) { u8 *out; s16 outPos = 0; + in = ResourceGetDataByName(in); out = (u8 *) alloc_display_list((u32) width * (u32) height); if (out == NULL) { @@ -242,21 +278,16 @@ static u8 *alloc_ia8_text_from_i1(u16 *in, s16 width, s16 height) { } void render_generic_char(u8 c) { - void **fontLUT = segmented_to_virtual(main_font_lut_us); + void **fontLUT = segmented_to_virtual(ROM_JP ? main_font_lut_jp : main_font_lut_us); void *packedTexture = segmented_to_virtual(fontLUT[c]); -#if defined(VERSION_JP) || defined(VERSION_SH) - void *unpackedTexture = alloc_ia8_text_from_i1(packedTexture, 8, 16); -#endif -#ifndef VERSION_EU + if(packedTexture == NULL) { + return; + } + gDPPipeSync(gDisplayListHead++); -#endif -#if defined(VERSION_JP) || defined(VERSION_SH) - gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_8b, 1, VIRTUAL_TO_PHYSICAL(unpackedTexture)); -#else - gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_16b, 1, VIRTUAL_TO_PHYSICAL(packedTexture)); -#endif - gSPDisplayList(gDisplayListHead++, dl_ia_text_tex_settings_us); + gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_16b, 1, ROM_JP ? alloc_ia8_text_from_i1(packedTexture, 8, 16) : packedTexture); + gSPDisplayList(gDisplayListHead++, ROM_JP ? dl_ia_text_tex_settings_jp : dl_ia_text_tex_settings_us); #ifdef VERSION_EU gSPTextureRectangleFlip(gDisplayListHead++, gDialogX << 2, (gDialogY - 16) << 2, (gDialogX + 8) << 2, gDialogY << 2, G_TX_RENDERTILE, 8 << 6, 4 << 6, 1 << 10, 1 << 10); @@ -549,57 +580,67 @@ void print_hud_lut_string(s8 hudLUT, s16 x, s16 y, const u8 *str) { if (hudLUT == HUD_LUT_JPMENU) { xStride = 16; } else { // HUD_LUT_GLOBAL -#ifdef VERSION_JP - xStride = 14; -#else - xStride = 12; //? Shindou uses this. -#endif + xStride = ROM_JP ? 14 : 12; } while (str[strPos] != GLOBAR_CHAR_TERMINATOR) { -#ifndef VERSION_JP - switch (str[strPos]) { -#ifdef VERSION_EU - case GLOBAL_CHAR_SPACE: - curX += xStride / 2; - break; - case HUD_CHAR_A_UMLAUT: - print_hud_char_umlaut(curX, curY, ASCII_TO_DIALOG('A')); - curX += xStride; - break; - case HUD_CHAR_O_UMLAUT: - print_hud_char_umlaut(curX, curY, ASCII_TO_DIALOG('O')); - curX += xStride; - break; - case HUD_CHAR_U_UMLAUT: - print_hud_char_umlaut(curX, curY, ASCII_TO_DIALOG('U')); - curX += xStride; - break; -#else - case GLOBAL_CHAR_SPACE: - curX += 8; - break; -#endif - default: -#endif - gDPPipeSync(gDisplayListHead++); + if(!ROM_JP){ + switch (str[strPos]) { + #ifdef VERSION_EU + case GLOBAL_CHAR_SPACE: + curX += xStride / 2; + break; + case HUD_CHAR_A_UMLAUT: + print_hud_char_umlaut(curX, curY, ASCII_TO_DIALOG('A')); + curX += xStride; + break; + case HUD_CHAR_O_UMLAUT: + print_hud_char_umlaut(curX, curY, ASCII_TO_DIALOG('O')); + curX += xStride; + break; + case HUD_CHAR_U_UMLAUT: + print_hud_char_umlaut(curX, curY, ASCII_TO_DIALOG('U')); + curX += xStride; + break; + #else + case GLOBAL_CHAR_SPACE: + curX += 8; + break; + #endif + default: + gDPPipeSync(gDisplayListHead++); - if (hudLUT == HUD_LUT_JPMENU) { - gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, hudLUT1[str[strPos]]); - } + if (hudLUT == HUD_LUT_JPMENU) { + gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, hudLUT1[str[strPos]]); + } - if (hudLUT == HUD_LUT_GLOBAL) { - gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, hudLUT2[str[strPos]]); - } + if (hudLUT == HUD_LUT_GLOBAL) { + gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, hudLUT2[str[strPos]]); + } - gSPDisplayList(gDisplayListHead++, dl_rgba16_load_tex_block); - gSPTextureRectangle(gDisplayListHead++, curX << 2, curY << 2, (curX + 16) << 2, - (curY + 16) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + gSPDisplayList(gDisplayListHead++, dl_rgba16_load_tex_block); + gSPTextureRectangle(gDisplayListHead++, curX << 2, curY << 2, (curX + 16) << 2, + (curY + 16) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); - curX += xStride; -#ifndef VERSION_JP + curX += xStride; + } + } else { + gDPPipeSync(gDisplayListHead++); + + if (hudLUT == HUD_LUT_JPMENU) { + gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, hudLUT1[str[strPos]]); + } + + if (hudLUT == HUD_LUT_GLOBAL) { + gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, hudLUT2[str[strPos]]); + } + + gSPDisplayList(gDisplayListHead++, dl_rgba16_load_tex_block); + gSPTextureRectangle(gDisplayListHead++, curX << 2, curY << 2, (curX + 16) << 2, + (curY + 16) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + + curX += xStride; } -#endif strPos++; } gSPDisableFiltering(gDisplayListHead++, FALSE); @@ -626,7 +667,7 @@ void print_menu_generic_string(s16 x, s16 y, const u8 *str) { s32 strPos = 0; u32 curX = x; u32 curY = y; - void **fontLUT = segmented_to_virtual(menu_font_lut); + void **fontLUT = segmented_to_virtual(ROM_JP ? menu_font_lut_jp : menu_font_lut_us); gSPDisableFiltering(gDisplayListHead++, TRUE); while (str[strPos] != DIALOG_CHAR_TERMINATOR) { @@ -957,6 +998,14 @@ void render_dialog_box_type(struct DialogEntry *dialog, s8 linesPerBox) { switch (gDialogBoxType) { case DIALOG_TYPE_ROTATE: // Renders a dialog black box with zoom and rotation if (gDialogBoxState == DIALOG_STATE_OPENING || gDialogBoxState == DIALOG_STATE_CLOSING) { + sInterpolatedDialogRotationPos = gDisplayListHead; + if (gDialogBoxState == DIALOG_STATE_OPENING) { + sInterpolatedDialogScale = gDialogBoxScale - 2 / 2; + sInterpolatedDialogRotation = gDialogBoxOpenTimer - 7.5f / 2; + } else { + sInterpolatedDialogScale = gDialogBoxScale + 2 / 2; + sInterpolatedDialogRotation = gDialogBoxOpenTimer + 7.5f / 2; + } create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.0 / gDialogBoxScale, 1.0 / gDialogBoxScale, 1.0f); // convert the speed into angle create_dl_rotation_matrix(MENU_MTX_NOPUSH, gDialogBoxOpenTimer * 4.0f, 0, 0, 1.0f); @@ -965,6 +1014,12 @@ void render_dialog_box_type(struct DialogEntry *dialog, s8 linesPerBox) { break; case DIALOG_TYPE_ZOOM: // Renders a dialog white box with zoom if (gDialogBoxState == DIALOG_STATE_OPENING || gDialogBoxState == DIALOG_STATE_CLOSING) { + sInterpolatedDialogZoomPos = gDisplayListHead; + if (gDialogBoxState == DIALOG_STATE_OPENING) { + sInterpolatedDialogScale = gDialogBoxScale - 2 / 2; + } else { + sInterpolatedDialogScale = gDialogBoxScale + 2 / 2; + } create_dl_translation_matrix(MENU_MTX_NOPUSH, 65.0 - (65.0 / gDialogBoxScale), (40.0 / gDialogBoxScale) - 40, 0); create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.0 / gDialogBoxScale, 1.0 / gDialogBoxScale, 1.0f); @@ -1236,6 +1291,8 @@ void handle_dialog_text_and_pages(s8 colorMode, struct DialogEntry *dialog, s8 l #ifdef VERSION_EU gDialogY -= gDialogScrollOffsetY; #else + sInterpolatedDialogOffset = gDialogScrollOffsetY + dialog->linesPerBox; + sInterpolatedDialogOffsetPos = gDisplayListHead; create_dl_translation_matrix(MENU_MTX_NOPUSH, 0, (f32) gDialogScrollOffsetY, 0); #endif } diff --git a/src/game/ingame_menu.h b/src/game/ingame_menu.h index 4118b3f4..2d2dea1c 100644 --- a/src/game/ingame_menu.h +++ b/src/game/ingame_menu.h @@ -21,11 +21,7 @@ #define HUD_LUT_GLOBAL 2 // For file select JP HUD difference -#if defined(VERSION_JP) || defined(VERSION_SH) -#define HUD_LUT_DIFF HUD_LUT_JPMENU -#else -#define HUD_LUT_DIFF HUD_LUT_GLOBAL -#endif +#define HUD_LUT_DIFF (ROM_JP ? HUD_LUT_JPMENU : HUD_LUT_GLOBAL) enum MenuMode { MENU_MODE_NONE = -1, diff --git a/src/game/level_geo.c b/src/game/level_geo.c index 0e274a89..e3946700 100644 --- a/src/game/level_geo.c +++ b/src/game/level_geo.c @@ -34,12 +34,16 @@ Gfx *geo_envfx_main(s32 callContext, struct GraphNode *node, Mat4 mtxf) { vec3f_to_vec3s(marioPos, gPlayerCameraState->pos); particleList = envfx_update_particles(snowMode, marioPos, camTo, camFrom); if (particleList != NULL) { +#if FALSE Mtx *mtx = alloc_display_list(sizeof(*mtx)); gfx = alloc_display_list(2 * sizeof(*gfx)); mtxf_to_mtx(mtx, mtxf); gSPMatrix(&gfx[0], VIRTUAL_TO_PHYSICAL(mtx), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); gSPBranchList(&gfx[1], VIRTUAL_TO_PHYSICAL(particleList)); +#else + gfx = particleList; +#endif execNode->fnNode.node.flags = (execNode->fnNode.node.flags & 0xFF) | 0x400; } SET_HIGH_U16_OF_32(*params, gAreaUpdateCounter); diff --git a/src/game/level_update.c b/src/game/level_update.c index 034a3131..9cdcc24b 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -29,6 +29,8 @@ #include "course_table.h" #include "rumble_init.h" +#include "port/Enhancements/game-interactor/GameInteractor_Hooks.h" + #define PLAY_MODE_NORMAL 0 #define PLAY_MODE_PAUSED 2 #define PLAY_MODE_CHANGE_AREA 3 @@ -1153,6 +1155,9 @@ s32 update_level(void) { enable_background_sound(); } + // Updates only when in game, i.e. past the save select screen + GameInteractor_ExecuteOnGameFrameUpdate(); + return changeLevel; } @@ -1194,7 +1199,7 @@ s32 init_level(void) { if (gMarioState->action != ACT_UNINITIALIZED) { if (save_file_exists(gCurrSaveFileNum - 1)) { set_mario_action(gMarioState, ACT_IDLE, 0); - } else { + } else if (CVarGetInteger("gDisablePeachCutscene", 0) == 0){ set_mario_action(gMarioState, ACT_INTRO_CUTSCENE, 0); val4 = TRUE; } @@ -1263,7 +1268,7 @@ s32 lvl_init_from_save_file(UNUSED s16 arg0, s32 levelNum) { #endif sWarpDest.type = WARP_TYPE_NOT_WARPING; sDelayedWarpOp = WARP_OP_NONE; - gNeverEnteredCastle = !save_file_exists(gCurrSaveFileNum - 1); + gNeverEnteredCastle = !save_file_exists(gCurrSaveFileNum - 1) || CVarGetInteger("gDisablePeachCutscene", 0) == 0; gCurrLevelNum = levelNum; gCurrCourseNum = COURSE_NONE; diff --git a/src/game/main.h b/src/game/main.h index 6028cbdb..c533b6ab 100644 --- a/src/game/main.h +++ b/src/game/main.h @@ -63,11 +63,12 @@ extern struct SPTask *gActiveSPTask; extern u32 gNumVblanks; extern s8 gResetTimer; extern s8 gNmiResetBarsTimer; -extern s8 gDebugLevelSelect; extern s8 D_8032C650; extern s8 gShowProfiler; extern s8 gShowDebugText; +#define gDebugLevelSelect (CVarGetInteger("gEnableDebugMode", 0) == 1) + void exec_display_list(struct SPTask *spTask); #endif // MAIN_H diff --git a/src/game/mario.c b/src/game/mario.c index 7334741e..8224f5e0 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -35,6 +35,8 @@ #include "rumble_init.h" #include "animation_table.h" +#include "port/Enhancements/game-interactor/GameInteractor_Hooks.h" + u32 unused80339F10; u8 unused80339F1C[20]; @@ -1455,6 +1457,7 @@ void update_mario_health(struct MarioState *m) { if ((m->input & INPUT_IN_POISON_GAS) && !(m->action & ACT_FLAG_INTANGIBLE)) { if (!(m->flags & MARIO_METAL_CAP) && !gDebugLevelSelect) { m->health -= 4; + GameInteractor_ExecuteOnHealthChange(m->health); } } else { if ((m->action & ACT_FLAG_SWIMMING) && !(m->action & ACT_FLAG_INTANGIBLE)) { @@ -1465,8 +1468,10 @@ void update_mario_health(struct MarioState *m) { // If using the debug level select, do not lose any HP to water. if ((m->pos[1] >= (m->waterLevel - 140)) && !terrainIsSnow) { m->health += 0x1A; + GameInteractor_ExecuteOnHealthChange(m->health); } else if (!gDebugLevelSelect) { m->health -= (terrainIsSnow ? 3 : 1); + GameInteractor_ExecuteOnHealthChange(m->health); } } } @@ -1475,10 +1480,12 @@ void update_mario_health(struct MarioState *m) { if (m->healCounter > 0) { m->health += 0x40; m->healCounter--; + GameInteractor_ExecuteOnHealthChange(m->health); } if (m->hurtCounter > 0) { m->health -= 0x40; m->hurtCounter--; + GameInteractor_ExecuteOnHealthChange(m->health); } if (m->health > 0x880) { diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index 68885128..9d9ea5c1 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -1536,6 +1536,7 @@ void cur_obj_set_pos_to_home(void) { o->oPosX = o->oHomeX; o->oPosY = o->oHomeY; o->oPosZ = o->oHomeZ; + o->header.gfx.skipInterpolationTimestamp = gGlobalTimer; } void cur_obj_set_pos_to_home_and_stop(void) { diff --git a/src/game/paintings.c b/src/game/paintings.c index 9bfca393..f0ec4800 100644 --- a/src/game/paintings.c +++ b/src/game/paintings.c @@ -189,6 +189,32 @@ struct Painting **sPaintingGroups[] = { s16 gPaintingUpdateCounter = 1; s16 gLastPaintingUpdateCounter = 0; +static Vtx sLastVertices[2 * 264 * 3]; +static u32 sLastVerticesTimestamp; +static Vtx *sVerticesPtr[2]; +static s32 sVerticesCount; + +void patch_interpolated_paintings(void) { + if (sVerticesPtr[0] != NULL) { + s32 i; + if (sVerticesPtr[1] != NULL) { + for (i = 0; i < sVerticesCount / 2; i++) { + sVerticesPtr[0][i] = sLastVertices[i]; + } + for (; i < sVerticesCount; i++) { + sVerticesPtr[1][i - sVerticesCount / 2] = sLastVertices[i]; + } + } else { + for (i = 0; i < sVerticesCount; i++) { + sVerticesPtr[0][i] = sLastVertices[i]; + } + } + sVerticesPtr[0] = NULL; + sVerticesPtr[1] = NULL; + sVerticesCount = 0; + } +} + /** * Stop paintings in paintingGroup from rippling if their id is different from *idptr. */ @@ -893,6 +919,23 @@ Gfx *render_painting(u8 *img, s16 tWidth, s16 tHeight, s16 *textureMap, s16 mapV gSP1Triangle(gfx++, group * 3, group * 3 + 1, group * 3 + 2, 0); } + if (sVerticesCount >= numVtx * 2) { + sVerticesCount = 0; + } + for (map = 0; map < numVtx; map++) { + Vtx v = verts[map]; + if (gGlobalTimer == sLastVerticesTimestamp + 1) { + s32 i; + for (i = 0; i < 3; i++) { + verts[map].n.ob[i] = (v.n.ob[i] + sLastVertices[sVerticesCount + map].n.ob[i]) / 2; + verts[map].n.n[i] = (v.n.n[i] + sLastVertices[sVerticesCount + map].n.n[i]) / 2; + } + } + sLastVertices[sVerticesCount + map] = v; + } + sVerticesPtr[sVerticesCount / numVtx] = verts; + sVerticesCount += numVtx; + gSPEndDisplayList(gfx); return dlist; } @@ -957,6 +1000,7 @@ Gfx *painting_ripple_image(struct Painting *painting) { meshTris = textureMap[meshVerts * 3 + 1]; gSPDisplayList(gfx++, render_painting(textures[i], tWidth, tHeight, textureMap, meshVerts, meshTris, painting->alpha)); } + sLastVerticesTimestamp = gGlobalTimer; // Update the ripple, may automatically reset the painting's state. painting_update_ripple_state(painting); @@ -994,6 +1038,7 @@ Gfx *painting_ripple_env_mapped(struct Painting *painting) { meshVerts = textureMap[0]; meshTris = textureMap[meshVerts * 3 + 1]; gSPDisplayList(gfx++, render_painting(tArray[0], tWidth, tHeight, textureMap, meshVerts, meshTris, painting->alpha)); + sLastVerticesTimestamp = gGlobalTimer; // Update the ripple, may automatically reset the painting's state. painting_update_ripple_state(painting); diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c index f077c184..ed366fa2 100644 --- a/src/game/rendering_graph_node.c +++ b/src/game/rendering_graph_node.c @@ -39,6 +39,10 @@ s16 gMatStackIndex; Mat4 gMatStack[32]; Mtx *gMatStackFixed[32]; +Mat4 gMatStackInterpolated[32]; +Mtx *gMatStackInterpolatedFixed[32]; + +extern float gInterpolationStep; /** * Animation nodes have state in global variables, so this struct captures @@ -52,6 +56,7 @@ struct GeoAnimState { /*0x04*/ f32 translationMultiplier; /*0x08*/ u16 *attribute; /*0x0C*/ s16 *data; + s16 prevFrame; }; // For some reason, this is a GeoAnimState struct, but the current state consists @@ -61,6 +66,7 @@ struct GeoAnimState gGeoTempState; u8 gCurrAnimType; u8 gCurrAnimEnabled; s16 gCurrAnimFrame; +s16 gPrevAnimFrame; f32 gCurrAnimTranslationMultiplier; u16 *gCurrAnimAttribute; s16 *gCurrAnimData; @@ -129,6 +135,46 @@ u16 gAreaUpdateCounter = 0; LookAt lookAt; #endif +static Gfx *sPerspectivePos; +static Mtx *sPerspectiveMtx; + +struct { + Gfx *pos; + void *mtx; + void *displayList; +} gMtxTbl[6400]; +s32 gMtxTblSize; + +static Gfx *sViewportPos; +static Vp sPrevViewport; + +void mtx_patch_interpolated(void) { + s32 i; + + if (sPerspectivePos != NULL) { + gSPMatrix(sPerspectivePos, VIRTUAL_TO_PHYSICAL(sPerspectiveMtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); + } + + for (i = 0; i < gMtxTblSize; i++) { + Gfx *pos = gMtxTbl[i].pos; + gSPMatrix(pos++, VIRTUAL_TO_PHYSICAL(gMtxTbl[i].mtx), + G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); + gSPDisplayList(pos++, gMtxTbl[i].displayList); + } + + if (sViewportPos != NULL) { + Gfx *saved = gDisplayListHead; + gDisplayListHead = sViewportPos; + make_viewport_clip_rect(&sPrevViewport); + gSPViewport(gDisplayListHead, VIRTUAL_TO_PHYSICAL(&sPrevViewport)); + gDisplayListHead = saved; + } + + gMtxTblSize = 0; + sPerspectivePos = NULL; + sViewportPos = NULL; +} + /** * Process a master list node. */ @@ -156,9 +202,14 @@ 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) { - gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(currList->transform), + if ((u32) gMtxTblSize < sizeof(gMtxTbl) / sizeof(gMtxTbl[0])) { + gMtxTbl[gMtxTblSize].pos = gDisplayListHead; + gMtxTbl[gMtxTblSize].mtx = currList->transform; + gMtxTbl[gMtxTblSize++].displayList = currList->displayList; + } + gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(currList->transformInterpolated), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); - gSPDisplayList(gDisplayListHead++, currList->displayList); + gSPDisplayList(gDisplayListHead++, currList->displayListInterpolated); currList = currList->next; } } @@ -174,7 +225,7 @@ static void geo_process_master_list_sub(struct GraphNodeMasterList *node) { * parameter. Look at the RenderModeContainer struct to see the corresponding * render modes of layers. */ -static void geo_append_display_list(void *displayList, s16 layer) { +static void geo_append_display_list2(void *displayList, void *displayListInterpolated, s16 layer) { #ifdef F3DEX_GBI_2 gSPLookAt(gDisplayListHead++, &lookAt); @@ -184,7 +235,9 @@ static void geo_append_display_list(void *displayList, s16 layer) { alloc_only_pool_alloc(gDisplayListHeap, sizeof(struct DisplayListNode)); listNode->transform = gMatStackFixed[gMatStackIndex]; + listNode->transformInterpolated = gMatStackInterpolatedFixed[gMatStackIndex]; listNode->displayList = displayList; + listNode->displayListInterpolated = displayListInterpolated; listNode->next = 0; if (gCurGraphNodeMasterList->listHeads[layer] == 0) { gCurGraphNodeMasterList->listHeads[layer] = listNode; @@ -195,12 +248,16 @@ static void geo_append_display_list(void *displayList, s16 layer) { } } +static void geo_append_display_list(void *displayList, s16 layer) { + geo_append_display_list2(displayList, displayList, layer); +} + /** * Process the master list node. */ static void geo_process_master_list(struct GraphNodeMasterList *node) { s32 i; - UNUSED u8 filler[4]; + UNUSED s32 sp1C; if (gCurGraphNodeMasterList == NULL && node->node.children != NULL) { gCurGraphNodeMasterList = node; @@ -241,7 +298,9 @@ static void geo_process_perspective(struct GraphNodePerspective *node) { } if (node->fnNode.node.children != NULL) { u16 perspNorm; + Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); Mtx *mtx = alloc_display_list(sizeof(*mtx)); + f32 fovInterpolated; #ifdef VERSION_EU f32 aspect = ((f32) gCurGraphNodeRoot->width / (f32) gCurGraphNodeRoot->height) * 1.1f; @@ -250,9 +309,23 @@ static void geo_process_perspective(struct GraphNodePerspective *node) { #endif guPerspective(mtx, &perspNorm, node->fov, aspect, node->near, node->far, 1.0f); - gSPPerspNormalize(gDisplayListHead++, perspNorm); - gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); + if (gGlobalTimer == node->prevTimestamp + 1 && gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) { + + fovInterpolated = (node->prevFov + node->fov) / 2.0f; + guPerspective(mtxInterpolated, &perspNorm, fovInterpolated, aspect, node->near, node->far, 1.0f); + gSPPerspNormalize(gDisplayListHead++, perspNorm); + + sPerspectivePos = gDisplayListHead; + sPerspectiveMtx = mtx; + gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtxInterpolated), + G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); + } else { + gSPPerspNormalize(gDisplayListHead++, perspNorm); + gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); + } + node->prevFov = node->fov; + node->prevTimestamp = gGlobalTimer; gCurGraphNodeCamFrustum = node; geo_process_node_and_siblings(node->fnNode.node.children); @@ -308,6 +381,77 @@ static void geo_process_switch(struct GraphNodeSwitchCase *node) { } } +float lerp(f32 o, f32 n) { + return (1 - gInterpolationStep) * o + gInterpolationStep * n; +} + +s16 lerp_s(s16 o, s16 n) { + if (o == n) + return n; + int diff = o - n; + float w = 1 - gInterpolationStep; + if (-0x8000 <= diff && diff <= 0x8000) { + return (s16)(w * o + gInterpolationStep * n); + } else { + if (o < n) { + o += 0x10000; + } else { + n += 0x10000; + } + diff = o - n; + return (s16)(w * o + gInterpolationStep * n); + } +} + +void interpolate_vectors(Vec3f res, Vec3f a, Vec3f b) { + res[0] = lerp(a[0], b[0]); + res[1] = lerp(a[1], b[1]); + res[2] = lerp(a[2], b[2]); +} + +void interpolate_vectors_s16(Vec3s res, Vec3s a, Vec3s b) { + res[0] = lerp_s(a[0], b[0]); + res[1] = lerp_s(a[1], b[1]); + res[2] = lerp_s(a[2], b[2]); +} + +static 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; + float w = 1 - gInterpolationStep; + if (-0x8000 <= diff && diff <= 0x8000) { + if (diff < -0x4000 || diff > 0x4000) { + return ns; + } + res = (u16)(w * o + gInterpolationStep * n); + } else { + if (o < n) { + o += 0x10000; + } else { + n += 0x10000; + } + diff = o - n; + if (diff < -0x4000 || diff > 0x4000) { + return ns; + } + res = (u16)(w * o + gInterpolationStep * n); + } + if (os / 327 == ns / 327 && (s16)res / 327 != os / 327) { + int bp = 0; + } + return res; +} + +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]); +} + /** * Process a camera node. */ @@ -315,6 +459,9 @@ static void geo_process_camera(struct GraphNodeCamera *node) { Mat4 cameraTransform; Mtx *rollMtx = alloc_display_list(sizeof(*rollMtx)); Mtx *mtx = alloc_display_list(sizeof(*mtx)); + Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); + Vec3f posInterpolated; + Vec3f focusInterpolated; if (node->fnNode.func != NULL) { node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]); @@ -325,12 +472,40 @@ static void geo_process_camera(struct GraphNodeCamera *node) { mtxf_lookat(cameraTransform, node->pos, node->focus, node->roll); mtxf_mul(gMatStack[gMatStackIndex + 1], cameraTransform, gMatStack[gMatStackIndex]); + + if (gGlobalTimer == node->prevTimestamp + 1 && gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) { + interpolate_vectors(posInterpolated, node->prevPos, node->pos); + interpolate_vectors(focusInterpolated, node->prevFocus, node->focus); + float magnitude = 0; + for (int i = 0; i < 3; i++) { + float diff = node->pos[i] - node->prevPos[i]; + magnitude += diff * diff; + } + if (magnitude > 500000) { + // Observed ~479000 in BBH when toggling R camera + // Can get over 3 million in VCUTM though... + vec3f_copy(posInterpolated, node->pos); + vec3f_copy(focusInterpolated, node->focus); + } + } else { + vec3f_copy(posInterpolated, node->pos); + vec3f_copy(focusInterpolated, node->focus); + } + vec3f_copy(node->prevPos, node->pos); + vec3f_copy(node->prevFocus, node->focus); + node->prevTimestamp = gGlobalTimer; + mtxf_lookat(cameraTransform, posInterpolated, focusInterpolated, node->roll); + mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], cameraTransform, gMatStackInterpolated[gMatStackIndex]); + gMatStackIndex++; mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); gMatStackFixed[gMatStackIndex] = mtx; + mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); + gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; if (node->fnNode.node.children != 0) { gCurGraphNodeCamera = node; node->matrixPtr = &gMatStack[gMatStackIndex]; + node->matrixPtrInterpolated = &gMatStackInterpolated[gMatStackIndex]; geo_process_node_and_siblings(node->fnNode.node.children); gCurGraphNodeCamera = NULL; } @@ -347,13 +522,17 @@ static void geo_process_translation_rotation(struct GraphNodeTranslationRotation Mat4 mtxf; Vec3f translation; Mtx *mtx = alloc_display_list(sizeof(*mtx)); + Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); vec3s_to_vec3f(translation, node->translation); mtxf_rotate_zxy_and_translate(mtxf, translation, node->rotation); mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); + mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]); gMatStackIndex++; mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); gMatStackFixed[gMatStackIndex] = mtx; + mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); + gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; if (node->displayList != NULL) { geo_append_display_list(node->displayList, node->node.flags >> 8); } @@ -372,13 +551,17 @@ static void geo_process_translation(struct GraphNodeTranslation *node) { Mat4 mtxf; Vec3f translation; Mtx *mtx = alloc_display_list(sizeof(*mtx)); + Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); vec3s_to_vec3f(translation, node->translation); mtxf_rotate_zxy_and_translate(mtxf, translation, gVec3sZero); mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); + mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]); gMatStackIndex++; mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); gMatStackFixed[gMatStackIndex] = mtx; + mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); + gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; if (node->displayList != NULL) { geo_append_display_list(node->displayList, node->node.flags >> 8); } @@ -396,12 +579,23 @@ static void geo_process_translation(struct GraphNodeTranslation *node) { static void geo_process_rotation(struct GraphNodeRotation *node) { Mat4 mtxf; Mtx *mtx = alloc_display_list(sizeof(*mtx)); + Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); + Vec3s rotationInterpolated; mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, node->rotation); mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); + if (gGlobalTimer == node->prevTimestamp + 1) { + interpolate_angles(rotationInterpolated, node->prevRotation, node->rotation); + mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, rotationInterpolated); + } + vec3s_copy(node->prevRotation, node->rotation); + node->prevTimestamp = gGlobalTimer; + mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]); gMatStackIndex++; mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); gMatStackFixed[gMatStackIndex] = mtx; + mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); + gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; if (node->displayList != NULL) { geo_append_display_list(node->displayList, node->node.flags >> 8); } @@ -420,12 +614,16 @@ static void geo_process_scale(struct GraphNodeScale *node) { UNUSED Mat4 transform; Vec3f scaleVec; Mtx *mtx = alloc_display_list(sizeof(*mtx)); + Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); vec3f_set(scaleVec, node->scale, node->scale, node->scale); mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], scaleVec); + mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex], scaleVec); gMatStackIndex++; mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); gMatStackFixed[gMatStackIndex] = mtx; + mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); + gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; if (node->displayList != NULL) { geo_append_display_list(node->displayList, node->node.flags >> 8); } @@ -444,21 +642,30 @@ static void geo_process_scale(struct GraphNodeScale *node) { static void geo_process_billboard(struct GraphNodeBillboard *node) { Vec3f translation; Mtx *mtx = alloc_display_list(sizeof(*mtx)); + Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); gMatStackIndex++; vec3s_to_vec3f(translation, node->translation); mtxf_billboard(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex - 1], translation, gCurGraphNodeCamera->roll); + mtxf_billboard(gMatStackInterpolated[gMatStackIndex], gMatStackInterpolated[gMatStackIndex - 1], translation, + gCurGraphNodeCamera->roll); if (gCurGraphNodeHeldObject != NULL) { mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex], gCurGraphNodeHeldObject->objNode->header.gfx.scale); + mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex], gMatStackInterpolated[gMatStackIndex], + gCurGraphNodeHeldObject->objNode->header.gfx.scale); } else if (gCurGraphNodeObject != NULL) { mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex], gCurGraphNodeObject->scale); + mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex], gMatStackInterpolated[gMatStackIndex], + gCurGraphNodeObject->scale); } mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); gMatStackFixed[gMatStackIndex] = mtx; + mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); + gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; if (node->displayList != NULL) { geo_append_display_list(node->displayList, node->node.flags >> 8); } @@ -507,15 +714,46 @@ static void geo_process_generated_list(struct GraphNodeGenerated *node) { */ static void geo_process_background(struct GraphNodeBackground *node) { Gfx *list = NULL; + Gfx *listInterpolated = NULL; if (node->fnNode.func != NULL) { + Vec3f posCopy; + Vec3f focusCopy; + Vec3f posInterpolated; + Vec3f focusInterpolated; + + if (gGlobalTimer == node->prevCameraTimestamp + 1 && + gGlobalTimer != gLakituState.skipCameraInterpolationTimestamp) { + interpolate_vectors(posInterpolated, node->prevCameraPos, gLakituState.pos); + interpolate_vectors(focusInterpolated, node->prevCameraFocus, gLakituState.focus); + } else { + vec3f_copy(posInterpolated, gLakituState.pos); + vec3f_copy(focusInterpolated, gLakituState.focus); + } + vec3f_copy(node->prevCameraPos, gLakituState.pos); + vec3f_copy(node->prevCameraFocus, gLakituState.focus); + node->prevCameraTimestamp = gGlobalTimer; + list = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, (struct AllocOnlyPool *) gMatStack[gMatStackIndex]); + vec3f_copy(posCopy, gLakituState.pos); + vec3f_copy(focusCopy, gLakituState.focus); + vec3f_copy(gLakituState.pos, posInterpolated); + vec3f_copy(gLakituState.focus, focusInterpolated); + listInterpolated = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, NULL); + vec3f_copy(gLakituState.pos, posCopy); + vec3f_copy(gLakituState.focus, focusCopy); } + if (list != NULL) { - geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(list), node->fnNode.node.flags >> 8); + geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(list), + (void *) VIRTUAL_TO_PHYSICAL(listInterpolated), node->fnNode.node.flags >> 8); } else if (gCurGraphNodeMasterList != NULL) { +#ifndef F3DEX_GBI_2E + Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 7); +#else Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 8); +#endif Gfx *gfx = gfxStart; gDPPipeSync(gfx++); @@ -534,6 +772,47 @@ static void geo_process_background(struct GraphNodeBackground *node) { } } +static void anim_process(Vec3f translation, Vec3s rotation, u8 *animType, s16 animFrame, u16 **animAttribute) { + if (*animType == ANIM_TYPE_TRANSLATION) { + translation[0] += gCurrAnimData[retrieve_animation_index(animFrame, animAttribute)] + * gCurrAnimTranslationMultiplier; + translation[1] += gCurrAnimData[retrieve_animation_index(animFrame, animAttribute)] + * gCurrAnimTranslationMultiplier; + translation[2] += gCurrAnimData[retrieve_animation_index(animFrame, animAttribute)] + * gCurrAnimTranslationMultiplier; + *animType = ANIM_TYPE_ROTATION; + } else { + if (*animType == ANIM_TYPE_LATERAL_TRANSLATION) { + translation[0] += + gCurrAnimData[retrieve_animation_index(animFrame, animAttribute)] + * gCurrAnimTranslationMultiplier; + *animAttribute += 2; + translation[2] += + gCurrAnimData[retrieve_animation_index(animFrame, animAttribute)] + * gCurrAnimTranslationMultiplier; + *animType = ANIM_TYPE_ROTATION; + } else { + if (*animType == ANIM_TYPE_VERTICAL_TRANSLATION) { + *animAttribute += 2; + translation[1] += + gCurrAnimData[retrieve_animation_index(animFrame, animAttribute)] + * gCurrAnimTranslationMultiplier; + *animAttribute += 2; + *animType = ANIM_TYPE_ROTATION; + } else if (*animType == ANIM_TYPE_NO_TRANSLATION) { + *animAttribute += 6; + *animType = ANIM_TYPE_ROTATION; + } + } + } + + if (*animType == ANIM_TYPE_ROTATION) { + rotation[0] = gCurrAnimData[retrieve_animation_index(animFrame, animAttribute)]; + rotation[1] = gCurrAnimData[retrieve_animation_index(animFrame, animAttribute)]; + rotation[2] = gCurrAnimData[retrieve_animation_index(animFrame, animAttribute)]; + } +} + /** * Render an animated part. The current animation state is not part of the node * but set in global variables. If an animated part is skipped, everything afterwards desyncs. @@ -542,53 +821,32 @@ static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) { Mat4 matrix; Vec3s rotation; Vec3f translation; + Vec3s rotationInterpolated; + Vec3f translationInterpolated; Mtx *matrixPtr = alloc_display_list(sizeof(*matrixPtr)); + Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); + u16 *animAttribute = gCurrAnimAttribute; + u8 animType = gCurrAnimType; vec3s_copy(rotation, gVec3sZero); vec3f_set(translation, node->translation[0], node->translation[1], node->translation[2]); - if (gCurrAnimType == ANIM_TYPE_TRANSLATION) { - translation[0] += gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] - * gCurrAnimTranslationMultiplier; - translation[1] += gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] - * gCurrAnimTranslationMultiplier; - translation[2] += gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] - * gCurrAnimTranslationMultiplier; - gCurrAnimType = ANIM_TYPE_ROTATION; - } else { - if (gCurrAnimType == ANIM_TYPE_LATERAL_TRANSLATION) { - translation[0] += - gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] - * gCurrAnimTranslationMultiplier; - gCurrAnimAttribute += 2; - translation[2] += - gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] - * gCurrAnimTranslationMultiplier; - gCurrAnimType = ANIM_TYPE_ROTATION; - } else { - if (gCurrAnimType == ANIM_TYPE_VERTICAL_TRANSLATION) { - gCurrAnimAttribute += 2; - translation[1] += - gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] - * gCurrAnimTranslationMultiplier; - gCurrAnimAttribute += 2; - gCurrAnimType = ANIM_TYPE_ROTATION; - } else if (gCurrAnimType == ANIM_TYPE_NO_TRANSLATION) { - gCurrAnimAttribute += 6; - gCurrAnimType = ANIM_TYPE_ROTATION; - } - } - } + vec3s_copy(rotationInterpolated, rotation); + vec3f_copy(translationInterpolated, translation); + + anim_process(translationInterpolated, rotationInterpolated, &animType, gPrevAnimFrame, &animAttribute); + anim_process(translation, rotation, &gCurrAnimType, gCurrAnimFrame, &gCurrAnimAttribute); + interpolate_vectors(translationInterpolated, translationInterpolated, translation); + interpolate_angles(rotationInterpolated, rotationInterpolated, rotation); - if (gCurrAnimType == ANIM_TYPE_ROTATION) { - rotation[0] = gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]; - rotation[1] = gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]; - rotation[2] = gCurrAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]; - } mtxf_rotate_xyz_and_translate(matrix, translation, rotation); mtxf_mul(gMatStack[gMatStackIndex + 1], matrix, gMatStack[gMatStackIndex]); + mtxf_rotate_xyz_and_translate(matrix, translationInterpolated, rotationInterpolated); + mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], matrix, gMatStackInterpolated[gMatStackIndex]); gMatStackIndex++; mtxf_to_mtx(matrixPtr, gMatStack[gMatStackIndex]); gMatStackFixed[gMatStackIndex] = matrixPtr; + mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); + gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; if (node->displayList != NULL) { geo_append_display_list(node->displayList, node->node.flags >> 8); } @@ -620,6 +878,17 @@ void geo_set_animation_globals(struct AnimInfo *node, s32 hasAnimation) { } gCurrAnimFrame = node->animFrame; + if (node->prevAnimPtr == anim && node->prevAnimID == node->animID && + gGlobalTimer == node->prevAnimFrameTimestamp + 1) { + gPrevAnimFrame = node->prevAnimFrame; + } else { + gPrevAnimFrame = node->animFrame; + } + node->prevAnimPtr = anim; + node->prevAnimID = node->animID; + node->prevAnimFrame = node->animFrame; + node->prevAnimFrameTimestamp = gGlobalTimer; + gCurrAnimEnabled = (anim->flags & ANIM_FLAG_5) == 0; gCurrAnimAttribute = segmented_to_virtual((void *) anim->index); gCurrAnimData = segmented_to_virtual((void *) anim->values); @@ -638,8 +907,10 @@ void geo_set_animation_globals(struct AnimInfo *node, s32 hasAnimation) { */ static void geo_process_shadow(struct GraphNodeShadow *node) { Gfx *shadowList; + Gfx *shadowListInterpolated; Mat4 mtxf; Vec3f shadowPos; + Vec3f shadowPosInterpolated; Vec3f animOffset; f32 objScale; f32 shadowScale; @@ -647,6 +918,7 @@ static void geo_process_shadow(struct GraphNodeShadow *node) { f32 cosAng; struct GraphNode *geo; Mtx *mtx; + Mtx *mtxInterpolated; if (gCurGraphNodeCamera != NULL && gCurGraphNodeObject != NULL) { if (gCurGraphNodeHeldObject != NULL) { @@ -685,21 +957,57 @@ static void geo_process_shadow(struct GraphNodeShadow *node) { } } + if (gCurGraphNodeHeldObject != NULL) { + if (gGlobalTimer == gCurGraphNodeHeldObject->prevShadowPosTimestamp + 1) { + interpolate_vectors(shadowPosInterpolated, gCurGraphNodeHeldObject->prevShadowPos, shadowPos); + } else { + vec3f_copy(shadowPosInterpolated, shadowPos); + } + vec3f_copy(gCurGraphNodeHeldObject->prevShadowPos, shadowPos); + gCurGraphNodeHeldObject->prevShadowPosTimestamp = gGlobalTimer; + } else { + if (gGlobalTimer == gCurGraphNodeObject->prevShadowPosTimestamp + 1 && + gGlobalTimer != gCurGraphNodeObject->skipInterpolationTimestamp) { + interpolate_vectors(shadowPosInterpolated, gCurGraphNodeObject->prevShadowPos, shadowPos); + } else { + vec3f_copy(shadowPosInterpolated, shadowPos); + } + vec3f_copy(gCurGraphNodeObject->prevShadowPos, shadowPos); + gCurGraphNodeObject->prevShadowPosTimestamp = gGlobalTimer; + } + + extern u8 gInterpolatingSurfaces; + gInterpolatingSurfaces = TRUE; + shadowListInterpolated = create_shadow_below_xyz(shadowPosInterpolated[0], shadowPosInterpolated[1], + shadowPosInterpolated[2], shadowScale, + node->shadowSolidity, node->shadowType); + gInterpolatingSurfaces = FALSE; shadowList = create_shadow_below_xyz(shadowPos[0], shadowPos[1], shadowPos[2], shadowScale, node->shadowSolidity, node->shadowType); - if (shadowList != NULL) { + if (shadowListInterpolated != NULL && shadowList != NULL) { mtx = alloc_display_list(sizeof(*mtx)); + mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); gMatStackIndex++; + mtxf_translate(mtxf, shadowPos); mtxf_mul(gMatStack[gMatStackIndex], mtxf, *gCurGraphNodeCamera->matrixPtr); mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); gMatStackFixed[gMatStackIndex] = mtx; + + mtxf_translate(mtxf, shadowPosInterpolated); + mtxf_mul(gMatStackInterpolated[gMatStackIndex], mtxf, *gCurGraphNodeCamera->matrixPtrInterpolated); + mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); + gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; + if (gShadowAboveWaterOrLava == TRUE) { - geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 4); + geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowList), + (void *) VIRTUAL_TO_PHYSICAL(shadowListInterpolated), 4); } else if (gMarioOnIceOrCarpet == 1) { - geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 5); + geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowList), + (void *) VIRTUAL_TO_PHYSICAL(shadowListInterpolated), 5); } else { - geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 6); + geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowList), + (void *) VIRTUAL_TO_PHYSICAL(shadowListInterpolated), 6); } gMatStackIndex--; } @@ -795,28 +1103,87 @@ static s32 obj_is_in_view(struct GraphNodeObject *node, Mat4 matrix) { return TRUE; } +static void interpolate_matrix(Mat4 result, Mat4 a, Mat4 b) { + s32 i, j; + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + result[i][j] = (1 - gInterpolationStep) * a[i][j] + gInterpolationStep * b[i][j]; + } + } +} + /** * Process an object node. */ static void geo_process_object(struct Object *node) { Mat4 mtxf; s32 hasAnimation = (node->header.gfx.node.flags & GRAPH_RENDER_HAS_ANIMATION) != 0; + Vec3f scaleInterpolated; if (node->header.gfx.areaIndex == gCurGraphNodeRoot->areaIndex) { if (node->header.gfx.throwMatrix != NULL) { mtxf_mul(gMatStack[gMatStackIndex + 1], *node->header.gfx.throwMatrix, gMatStack[gMatStackIndex]); + if (gGlobalTimer == node->header.gfx.prevThrowMatrixTimestamp + 1 && + gGlobalTimer != node->header.gfx.skipInterpolationTimestamp) { + interpolate_matrix(mtxf, *node->header.gfx.throwMatrix, node->header.gfx.prevThrowMatrix); + mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, + gMatStackInterpolated[gMatStackIndex]); + } else { + mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], (void *) node->header.gfx.throwMatrix, + gMatStackInterpolated[gMatStackIndex]); + } + mtxf_copy(node->header.gfx.prevThrowMatrix, *node->header.gfx.throwMatrix); + node->header.gfx.prevThrowMatrixTimestamp = gGlobalTimer; } else if (node->header.gfx.node.flags & GRAPH_RENDER_BILLBOARD) { + Vec3f posInterpolated; + if (gGlobalTimer == node->header.gfx.prevTimestamp + 1 && + gGlobalTimer != node->header.gfx.skipInterpolationTimestamp) { + interpolate_vectors(posInterpolated, node->header.gfx.prevPos, node->header.gfx.pos); + } else { + vec3f_copy(posInterpolated, node->header.gfx.pos); + } + vec3f_copy(node->header.gfx.prevPos, node->header.gfx.pos); + node->header.gfx.prevTimestamp = gGlobalTimer; mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], node->header.gfx.pos, gCurGraphNodeCamera->roll); + mtxf_billboard(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex], + posInterpolated, gCurGraphNodeCamera->roll); } else { + Vec3f posInterpolated; + Vec3s angleInterpolated; + if (gGlobalTimer == node->header.gfx.prevTimestamp + 1 && + gGlobalTimer != node->header.gfx.skipInterpolationTimestamp) { + interpolate_vectors(posInterpolated, node->header.gfx.prevPos, node->header.gfx.pos); + interpolate_angles(angleInterpolated, node->header.gfx.prevAngle, node->header.gfx.angle); + } else { + vec3f_copy(posInterpolated, node->header.gfx.pos); + vec3s_copy(angleInterpolated, node->header.gfx.angle); + } + vec3f_copy(node->header.gfx.prevPos, node->header.gfx.pos); + vec3s_copy(node->header.gfx.prevAngle, node->header.gfx.angle); + node->header.gfx.prevTimestamp = gGlobalTimer; mtxf_rotate_zxy_and_translate(mtxf, node->header.gfx.pos, node->header.gfx.angle); mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); + mtxf_rotate_zxy_and_translate(mtxf, posInterpolated, angleInterpolated); + mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mtxf, gMatStackInterpolated[gMatStackIndex]); } + if (gGlobalTimer == node->header.gfx.prevScaleTimestamp + 1 && + gGlobalTimer != node->header.gfx.skipInterpolationTimestamp) { + interpolate_vectors(scaleInterpolated, node->header.gfx.prevScale, node->header.gfx.scale); + } else { + vec3f_copy(scaleInterpolated, node->header.gfx.scale); + } + vec3f_copy(node->header.gfx.prevScale, node->header.gfx.scale); + node->header.gfx.prevScaleTimestamp = gGlobalTimer; + mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1], node->header.gfx.scale); + mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex + 1], + scaleInterpolated); node->header.gfx.throwMatrix = &gMatStack[++gMatStackIndex]; + node->header.gfx.throwMatrixInterpolated = &gMatStackInterpolated[gMatStackIndex]; node->header.gfx.cameraToObject[0] = gMatStack[gMatStackIndex][3][0]; node->header.gfx.cameraToObject[1] = gMatStack[gMatStackIndex][3][1]; node->header.gfx.cameraToObject[2] = gMatStack[gMatStackIndex][3][2]; @@ -827,9 +1194,12 @@ static void geo_process_object(struct Object *node) { } if (obj_is_in_view(&node->header.gfx, gMatStack[gMatStackIndex])) { Mtx *mtx = alloc_display_list(sizeof(*mtx)); + Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); gMatStackFixed[gMatStackIndex] = mtx; + mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); + gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; if (node->header.gfx.sharedChild != NULL) { gCurGraphNodeObject = (struct GraphNodeObject *) node; node->header.gfx.sharedChild->parent = &node->header.gfx.node; @@ -840,11 +1210,16 @@ static void geo_process_object(struct Object *node) { if (node->header.gfx.node.children != NULL) { geo_process_node_and_siblings(node->header.gfx.node.children); } + } else { + node->header.gfx.prevThrowMatrixTimestamp = 0; + node->header.gfx.prevTimestamp = 0; + node->header.gfx.prevScaleTimestamp = 0; } gMatStackIndex--; gCurrAnimType = ANIM_TYPE_NONE; node->header.gfx.throwMatrix = NULL; + node->header.gfx.throwMatrixInterpolated = NULL; } } @@ -871,6 +1246,8 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { Mat4 mat; Vec3f translation; Mtx *mtx = alloc_display_list(sizeof(*mtx)); + Mtx *mtxInterpolated = alloc_display_list(sizeof(*mtxInterpolated)); + Vec3f scaleInterpolated; #ifdef F3DEX_GBI_2 gSPLookAt(gDisplayListHead++, &lookAt); @@ -886,6 +1263,14 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { translation[1] = node->translation[1] / 4.0f; translation[2] = node->translation[2] / 4.0f; + if (gGlobalTimer == node->objNode->header.gfx.prevScaleTimestamp + 1) { + interpolate_vectors(scaleInterpolated, node->objNode->header.gfx.prevScale, node->objNode->header.gfx.scale); + } else { + vec3f_copy(scaleInterpolated, node->objNode->header.gfx.scale); + } + vec3f_copy(node->objNode->header.gfx.prevScale, node->objNode->header.gfx.scale); + node->objNode->header.gfx.prevScaleTimestamp = gGlobalTimer; + mtxf_translate(mat, translation); mtxf_copy(gMatStack[gMatStackIndex + 1], *gCurGraphNodeObject->throwMatrix); gMatStack[gMatStackIndex + 1][3][0] = gMatStack[gMatStackIndex][3][0]; @@ -894,6 +1279,13 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { mtxf_mul(gMatStack[gMatStackIndex + 1], mat, gMatStack[gMatStackIndex + 1]); mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1], node->objNode->header.gfx.scale); + mtxf_copy(gMatStackInterpolated[gMatStackIndex + 1], (void *) gCurGraphNodeObject->throwMatrixInterpolated); + gMatStackInterpolated[gMatStackIndex + 1][3][0] = gMatStackInterpolated[gMatStackIndex][3][0]; + gMatStackInterpolated[gMatStackIndex + 1][3][1] = gMatStackInterpolated[gMatStackIndex][3][1]; + gMatStackInterpolated[gMatStackIndex + 1][3][2] = gMatStackInterpolated[gMatStackIndex][3][2]; + mtxf_mul(gMatStackInterpolated[gMatStackIndex + 1], mat, gMatStackInterpolated[gMatStackIndex + 1]); + mtxf_scale_vec3f(gMatStackInterpolated[gMatStackIndex + 1], gMatStackInterpolated[gMatStackIndex + 1], + scaleInterpolated); if (node->fnNode.func != NULL) { node->fnNode.func(GEO_CONTEXT_HELD_OBJ, &node->fnNode.node, (struct AllocOnlyPool *) gMatStack[gMatStackIndex + 1]); @@ -901,12 +1293,15 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { gMatStackIndex++; mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); gMatStackFixed[gMatStackIndex] = mtx; + mtxf_to_mtx(mtxInterpolated, gMatStackInterpolated[gMatStackIndex]); + gMatStackInterpolatedFixed[gMatStackIndex] = mtxInterpolated; gGeoTempState.type = gCurrAnimType; gGeoTempState.enabled = gCurrAnimEnabled; gGeoTempState.frame = gCurrAnimFrame; gGeoTempState.translationMultiplier = gCurrAnimTranslationMultiplier; gGeoTempState.attribute = gCurrAnimAttribute; gGeoTempState.data = gCurrAnimData; + gGeoTempState.prevFrame = gPrevAnimFrame; gCurrAnimType = 0; gCurGraphNodeHeldObject = (void *) node; if (node->objNode->header.gfx.animInfo.curAnim != NULL) { @@ -921,6 +1316,7 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { gCurrAnimTranslationMultiplier = gGeoTempState.translationMultiplier; gCurrAnimAttribute = gGeoTempState.attribute; gCurrAnimData = gGeoTempState.data; + gPrevAnimFrame = gGeoTempState.prevFrame; gMatStackIndex--; } @@ -1037,11 +1433,12 @@ void geo_process_node_and_siblings(struct GraphNode *firstNode) { * to set up the projection and draw display lists. */ void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor) { - UNUSED u8 filler[4]; + UNUSED s32 unused; if (node->node.flags & GRAPH_RENDER_ACTIVE) { Mtx *initialMatrix; Vp *viewport = alloc_display_list(sizeof(*viewport)); + Vp *viewportInterpolated = viewport; gDisplayListHeap = alloc_only_pool_init(main_pool_available() - sizeof(struct AllocOnlyPool), MEMORY_POOL_LEFT); @@ -1052,7 +1449,12 @@ void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor) vec3s_set(viewport->vp.vscale, node->width * 4, node->height * 4, 511); if (b != NULL) { clear_framebuffer(clearColor); - make_viewport_clip_rect(b); + viewportInterpolated = alloc_display_list(sizeof(*viewportInterpolated)); + interpolate_vectors_s16(viewportInterpolated->vp.vtrans, sPrevViewport.vp.vtrans, b->vp.vtrans); + interpolate_vectors_s16(viewportInterpolated->vp.vscale, sPrevViewport.vp.vscale, b->vp.vscale); + + sViewportPos = gDisplayListHead; + make_viewport_clip_rect(viewportInterpolated); *viewport = *b; } @@ -1060,11 +1462,16 @@ void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor) clear_framebuffer(clearColor); make_viewport_clip_rect(c); } + sPrevViewport = *viewport; mtxf_identity(gMatStack[gMatStackIndex]); mtxf_to_mtx(initialMatrix, gMatStack[gMatStackIndex]); gMatStackFixed[gMatStackIndex] = initialMatrix; - gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(viewport)); + + mtxf_identity(gMatStackInterpolated[gMatStackIndex]); + gMatStackInterpolatedFixed[gMatStackIndex] = initialMatrix; + + gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(viewportInterpolated)); gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(gMatStackFixed[gMatStackIndex]), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); gCurGraphNodeRoot = node; diff --git a/src/game/rendering_graph_node.h b/src/game/rendering_graph_node.h index 20a69f61..2e889be8 100644 --- a/src/game/rendering_graph_node.h +++ b/src/game/rendering_graph_node.h @@ -29,5 +29,7 @@ extern u16 gAreaUpdateCounter; void geo_process_node_and_siblings(struct GraphNode *firstNode); void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor); +void interpolate_vectors(Vec3f res, Vec3f a, Vec3f b); +void interpolate_vectors_s16(Vec3s res, Vec3s a, Vec3s b); #endif // RENDERING_GRAPH_NODE_H diff --git a/src/game/screen_transition.c b/src/game/screen_transition.c index e0926f85..d1cd93e6 100644 --- a/src/game/screen_transition.c +++ b/src/game/screen_transition.c @@ -16,6 +16,19 @@ u8 sTransitionColorFadeCount[4] = { 0 }; u16 sTransitionTextureFadeCount[2] = { 0 }; +static Gfx *sScreenTransitionVerticesPos[2]; +static Vtx *sScreenTransitionVertices; + +void patch_screen_transition_interpolated(void) { + if (sScreenTransitionVerticesPos[0] != NULL) { + gSPVertex(sScreenTransitionVerticesPos[0], VIRTUAL_TO_PHYSICAL(sScreenTransitionVertices), 8, 0); + gSPVertex(sScreenTransitionVerticesPos[1], VIRTUAL_TO_PHYSICAL(sScreenTransitionVertices), 4, 0); + sScreenTransitionVerticesPos[0] = NULL; + sScreenTransitionVerticesPos[1] = NULL; + sScreenTransitionVertices = NULL; + } +} + s32 set_and_reset_transition_fade_timer(s8 fadeTimer, u8 transTime) { s32 reset = FALSE; @@ -85,6 +98,8 @@ s32 render_fade_transition_into_color(s8 fadeTimer, u8 transTime, struct WarpTra return dl_transition_color(fadeTimer, transTime, transData, alpha); } +#if FALSE + s16 calc_tex_transition_radius(s8 fadeTimer, s8 transTime, struct WarpTransitionData *transData) { f32 texRadius = transData->endTexRadius - transData->startTexRadius; f32 radiusTime = sTransitionColorFadeCount[fadeTimer] * texRadius / (f32)(transTime - 1); @@ -93,6 +108,19 @@ s16 calc_tex_transition_radius(s8 fadeTimer, s8 transTime, struct WarpTransition return (s16)(result + 0.5); } +#else + +s16 calc_tex_transition_radius(s8 fadeTimer, f32 interpolationFraction, s8 transTime, struct WarpTransitionData *transData) { + f32 texRadius = transData->endTexRadius - transData->startTexRadius; + f32 radiusTime = (sTransitionColorFadeCount[fadeTimer] == 0 ? 0 : + sTransitionColorFadeCount[fadeTimer] - 1 + interpolationFraction) * texRadius / (f32)(transTime - 1); + f32 result = transData->startTexRadius + radiusTime; + + return (s16)(result + 0.5); +} + +#endif + f32 calc_tex_transition_time(s8 fadeTimer, s8 transTime, struct WarpTransitionData *transData) { f32 startX = transData->startTexX; f32 startY = transData->startTexY; @@ -166,6 +194,8 @@ void *sTextureTransitionID[] = { texture_transition_bowser_half, }; +#if 0 + s32 render_textured_transition(s8 fadeTimer, s8 transTime, struct WarpTransitionData *transData, s8 texID, s8 transTexType) { f32 texTransTime = calc_tex_transition_time(fadeTimer, transTime, transData); u16 texTransPos = convert_tex_transition_angle_to_pos(transData); @@ -206,6 +236,56 @@ s32 render_textured_transition(s8 fadeTimer, s8 transTime, struct WarpTransition return set_and_reset_transition_fade_timer(fadeTimer, transTime); } +#else + +s32 render_textured_transition(s8 fadeTimer, s8 transTime, struct WarpTransitionData *transData, s8 texID, s8 transTexType) { + f32 texTransTime = calc_tex_transition_time(fadeTimer, transTime, transData); + u16 texTransPos = convert_tex_transition_angle_to_pos(transData); + s16 centerTransX = center_tex_transition_x(transData, texTransTime, texTransPos); + s16 centerTransY = center_tex_transition_y(transData, texTransTime, texTransPos); + s16 texTransRadius = calc_tex_transition_radius(fadeTimer, 1.0f, transTime, transData); + s16 texTransRadiusInterpolated = calc_tex_transition_radius(fadeTimer, 0.5f, transTime, transData); + Vtx *verts = alloc_display_list(8 * sizeof(*verts)); + Vtx *vertsInterpolated = alloc_display_list(8 * sizeof(*vertsInterpolated)); + + if (verts != NULL && vertsInterpolated != NULL) { + load_tex_transition_vertex(verts, fadeTimer, transData, centerTransX, centerTransY, texTransRadius, transTexType); + load_tex_transition_vertex(vertsInterpolated, fadeTimer, transData, centerTransX, centerTransY, texTransRadiusInterpolated, transTexType); + sScreenTransitionVertices = verts; + gSPDisplayList(gDisplayListHead++, dl_proj_mtx_fullscreen) + gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE); + gDPSetRenderMode(gDisplayListHead++, G_RM_AA_OPA_SURF, G_RM_AA_OPA_SURF2); + sScreenTransitionVerticesPos[0] = gDisplayListHead; + gSPVertex(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(vertsInterpolated), 8, 0); + gSPDisplayList(gDisplayListHead++, dl_transition_draw_filled_region); + gDPPipeSync(gDisplayListHead++); + gDPSetCombineMode(gDisplayListHead++, G_CC_MODULATEIDECALA, G_CC_MODULATEIDECALA); + gDPSetRenderMode(gDisplayListHead++, G_RM_AA_XLU_SURF, G_RM_AA_XLU_SURF2); + gDPSetTextureFilter(gDisplayListHead++, G_TF_BILERP); + switch (transTexType) { + case TRANS_TYPE_MIRROR: + gDPLoadTextureBlock(gDisplayListHead++, sTextureTransitionID[texID], G_IM_FMT_IA, G_IM_SIZ_8b, 32, 64, 0, + G_TX_WRAP | G_TX_MIRROR, G_TX_WRAP | G_TX_MIRROR, 5, 6, G_TX_NOLOD, G_TX_NOLOD); + break; + case TRANS_TYPE_CLAMP: + gDPLoadTextureBlock(gDisplayListHead++, sTextureTransitionID[texID], G_IM_FMT_IA, G_IM_SIZ_8b, 64, 64, 0, + G_TX_CLAMP, G_TX_CLAMP, 6, 6, G_TX_NOLOD, G_TX_NOLOD); + break; + } + gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); + sScreenTransitionVerticesPos[1] = gDisplayListHead; + gSPVertex(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(vertsInterpolated), 4, 0); + gSPDisplayList(gDisplayListHead++, dl_draw_quad_verts_0123); + gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF); + gSPDisplayList(gDisplayListHead++, dl_screen_transition_end); + sTransitionTextureFadeCount[fadeTimer] += transData->texTimer; + } else { + } + return set_and_reset_transition_fade_timer(fadeTimer, transTime); +} + +#endif + s32 render_screen_transition(s8 fadeTimer, s8 transType, u8 transTime, struct WarpTransitionData *transData) { switch (transType) { case WARP_TRANSITION_FADE_FROM_COLOR: diff --git a/src/game/segment7.h b/src/game/segment7.h index 65a0c7ba..4416dc09 100644 --- a/src/game/segment7.h +++ b/src/game/segment7.h @@ -8,7 +8,8 @@ extern u8 dl_menu_idle_hand[]; extern u8 dl_menu_grabbing_hand[]; extern u8 menu_hud_lut[]; -extern u8 menu_font_lut[]; +extern u8 menu_font_lut_us[]; +extern u8 menu_font_lut_jp[]; extern u8 dl_menu_ia8_text_begin[]; extern u8 dl_menu_ia8_text_end[]; extern u8 dl_menu_rgba16_wood_course[]; diff --git a/src/menu/intro_geo.c b/src/menu/intro_geo.c index a16c8237..55aacdb9 100644 --- a/src/menu/intro_geo.c +++ b/src/menu/intro_geo.c @@ -1,5 +1,6 @@ #include +#include "engine/math_util.h" #include "game/memory.h" #include "game/segment2.h" #include "game/segment7.h" @@ -11,6 +12,7 @@ #include "game/game_init.h" #include "audio/external.h" #include "gfx_dimensions.h" +#include "game/rendering_graph_node.h" // frame counts for the zoom in, hold, and zoom out of title model #define INTRO_STEPS_ZOOM_IN 20 @@ -36,6 +38,18 @@ static s32 sGameOverTableIndex; static s16 sIntroFrameCounter; static s32 sTmCopyrightAlpha; +static Gfx *sIntroScalePos; +static Vec3f sIntroScale; + +void patch_title_screen_scales(void) { + if (sIntroScalePos != NULL) { + Mtx *scaleMat = alloc_display_list(sizeof(*scaleMat)); + guScale(scaleMat, sIntroScale[0], sIntroScale[1], sIntroScale[2]); + gSPMatrix(sIntroScalePos, scaleMat, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); + sIntroScalePos = NULL; + } +} + /** * Geo callback to render the "Super Mario 64" logo on the title screen */ @@ -49,6 +63,8 @@ Gfx *geo_intro_super_mario_64_logo(s32 state, struct GraphNode *node, UNUSED voi f32 scaleX; f32 scaleY; f32 scaleZ; + Vec3f scale; + Vec3f scaleInterpolated; if (state != 1) { sIntroFrameCounter = 0; @@ -80,7 +96,12 @@ Gfx *geo_intro_super_mario_64_logo(s32 state, struct GraphNode *node, UNUSED voi scaleY = 0.0f; scaleZ = 0.0f; } - guScale(scaleMat, scaleX, scaleY, scaleZ); + + vec3f_set(scale, scaleX, scaleY, scaleZ); + interpolate_vectors(scaleInterpolated, sIntroScale, scale); + vec3f_set(sIntroScale, scaleX, scaleY, scaleZ); + guScale(scaleMat, scaleInterpolated[0], scaleInterpolated[1], scaleInterpolated[2]); + sIntroScalePos = dlIter; gSPMatrix(dlIter++, scaleMat, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); gSPDisplayList(dlIter++, &intro_seg7_dl_0700B3A0); // draw model diff --git a/src/menu/title_screen.c b/src/menu/title_screen.c index e640ea0e..0c7d6694 100644 --- a/src/menu/title_screen.c +++ b/src/menu/title_screen.c @@ -143,7 +143,7 @@ s16 intro_level_select(void) { // ... the level select quit combo is being pressed, which uses START. If this // is the case, quit the menu instead. if (gPlayer1Controller->buttonDown == QUIT_LEVEL_SELECT_COMBO) { - gDebugLevelSelect = FALSE; + // CVarSetInteger("gEnableDebugMode", 0); return -1; } play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource); diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp index 45e364a5..2d6fee5b 100644 --- a/src/port/Engine.cpp +++ b/src/port/Engine.cpp @@ -4,13 +4,13 @@ #include "port/importer/AudioBankFactory.h" #include "port/importer/AudioSampleFactory.h" #include "port/importer/AudioSequenceFactory.h" -#include "banks_table.h" -#include "sequences_table.h" #include "audio/GameAudio.h" #include "ZAPDUtils/Utils/StringHelper.h" #include "texts_table.h" #include "port/importer/DialogFactory.h" #include "port/importer/DictionaryFactory.h" +#include "port/Enhancements/game-interactor/GameInteractor.h" +#include "port/Enhancements/mods.h" #include #include @@ -23,6 +23,7 @@ extern "C" { } GameEngine* GameEngine::Instance; +GameInteractor* GameInteractor::Instance; GameEngine::GameEngine(){ std::vector OTRFiles; @@ -30,9 +31,9 @@ GameEngine::GameEngine(){ if (std::filesystem::exists(cubePath)) { OTRFiles.push_back(cubePath); } - std::string sohOtrPath = LUS::Context::GetPathRelativeToAppBundle("soh.otr"); - if (std::filesystem::exists(sohOtrPath)) { - OTRFiles.push_back(sohOtrPath); + std::string sm64OtrPath = LUS::Context::GetPathRelativeToAppBundle("sm64.otr"); + if (std::filesystem::exists(sm64OtrPath)) { + OTRFiles.push_back(sm64OtrPath); } std::string patchesPath = LUS::Context::GetPathRelativeToAppDirectory("mods"); if (patchesPath.length() > 0 && std::filesystem::exists(patchesPath)) { @@ -44,8 +45,8 @@ GameEngine::GameEngine(){ } } } - this->context = LUS::Context::CreateInstance("Ghostship", "sm64", "ghostship.cfg.json", OTRFiles, { 0xFF2B5A63 }, 3); - this->context->GetWindow()->SetTargetFps(30); + this->context = LUS::Context::CreateInstance("Ghostship", "sm64", "ghostship.cfg.json", OTRFiles, { 0xFF2B5A63, 0xE3DAA4E }, 3); + this->context->GetWindow()->SetTargetFps(60); this->context->GetWindow()->SetMaximumFrameLatency(1); this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SAnim, "Animation", std::make_shared()); this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::Bank, "Bank", std::make_shared()); @@ -53,17 +54,19 @@ GameEngine::GameEngine(){ this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::Sequence, "Sequence", std::make_shared()); this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SDialog, "Dialog", std::make_shared()); this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::Dictionary, "Dictionary", std::make_shared()); - GameEngine::AudioInit(); } void GameEngine::Create(){ - GameEngine::Instance = new GameEngine(); + auto instance = GameEngine::Instance = new GameEngine(); + GameInteractor::Instance = new GameInteractor(); + InitMods(); GameUI::SetupGuiElements(); + instance->AudioInit(); + instance->LoadDictionary(); } void GameEngine::Destroy(){ GameEngine::AudioExit(); - this->context = nullptr; } bool ShouldClearTextureCacheAtEndOfFrame = false; @@ -98,6 +101,18 @@ void GameEngine::ProcessFrame(void (*run_one_game_iter)()) const { this->context->GetWindow()->MainLoop(run_one_game_iter); } +uint32_t GameEngine::GetInterpolationFPS() { + if (LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() == LUS::WindowBackend::DX11) { + return CVarGetInteger("gInterpolationFPS", 30); + } + + if (CVarGetInteger("gMatchRefreshRate", 0)) { + return LUS::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate(); + } + + return std::min(LUS::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate(), CVarGetInteger("gInterpolationFPS", 30)); +} + // Audio void GameEngine::HandleAudioThread(){ @@ -148,7 +163,23 @@ void GameEngine::EndAudioFrame(){ } void GameEngine::AudioInit() { - LUS::Context::GetInstance()->GetResourceManager()->LoadDirectory("sound"); + auto resourceMgr = LUS::Context::GetInstance()->GetResourceManager(); + resourceMgr->LoadDirectory("sound"); + auto banksFiles = resourceMgr->GetArchive()->ListFiles("sound/banks/*"); + + for(auto& bank : *banksFiles){ + auto path = "__OTR__" + bank; + auto ctl = static_cast(ResourceGetDataByName(path.c_str())); + this->bankMapTable[bank] = ctl->bankId; + } + + auto sequencesFiles = resourceMgr->GetArchive()->ListFiles("sound/sequences/*"); + + for(auto& sequence : *sequencesFiles){ + auto path = "__OTR__" + sequence; + auto seq = static_cast(ResourceGetDataByName(path.c_str())); + this->sequencesMapTable[seq->id] = path; + } if (!audio.running) { audio.running = true; @@ -169,7 +200,18 @@ void GameEngine::AudioExit() { void GameEngine::LoadDictionary() { this->dictionary = static_cast> *>(ResourceGetDataByName("__OTR__texts/strings/global")); - int bp = 0; +} + +uint8_t GameEngine::GetBankIdByName(const std::string& name) { + auto engine = GameEngine::Instance; + if(engine->bankMapTable.contains(name)){ + return engine->bankMapTable[name]; + } + return 0; +} + +extern "C" uint32_t GameEngine_GetInterpolatedFPS() { + return GameEngine::GetInterpolationFPS(); } extern "C" uint32_t GameEngine_GetSampleRate() { @@ -197,15 +239,20 @@ extern "C" float GameEngine_GetAspectRatio() { extern "C" CtlEntry* GameEngine_LoadBank(uint8_t bankId) { auto engine = GameEngine::Instance; - if(bankId > (sizeof(gBankTable) / sizeof(gBankTable[0])) - 1){ + if(bankId >= engine->bankMapTable.size()){ return nullptr; } if(engine->banks.contains(bankId)){ return engine->banks[bankId]; } - auto ctl = static_cast(ResourceGetDataByName(gBankTable[bankId])); - engine->banks[bankId] = ctl; - return ctl; + for(auto& bank : engine->bankMapTable){ + if(bank.second == bankId){ + auto ctl = static_cast(ResourceGetDataByName(("__OTR__" + bank.first).c_str())); + engine->banks[bankId] = ctl; + return ctl; + } + } + return nullptr; } extern "C" uint8_t GameEngine_IsBankLoaded(uint8_t bankId) { @@ -223,17 +270,24 @@ extern "C" void GameEngine_UnloadBank(uint8_t bankId) { extern "C" AudioSequenceData* GameEngine_LoadSequence(uint8_t seqId) { auto engine = GameEngine::Instance; - if(seqId > (sizeof(gSequenceTable) / sizeof(gSequenceTable[0])) - 1){ + if(!engine->sequencesMapTable.contains(seqId)){ return nullptr; } + if(engine->sequences.contains(seqId)){ return engine->sequences[seqId]; } - auto sequences = static_cast(ResourceGetDataByName(gSequenceTable[seqId])); + + auto sequences = static_cast(ResourceGetDataByName(engine->sequencesMapTable[seqId].c_str())); engine->sequences[seqId] = sequences; return sequences; } +extern "C" uint32_t GameEngine_GetSequenceCount(){ + auto engine = GameEngine::Instance; + return engine->sequencesMapTable.size(); +} + extern "C" uint8_t GameEngine_IsSequenceLoaded(uint8_t seqId) { auto engine = GameEngine::Instance; GameEngine_LoadSequence(seqId); @@ -268,11 +322,8 @@ extern "C" uint8_t* GameEngine_LoadTranslation(const char* key) { auto engine = GameEngine::Instance; auto dictionary = engine->dictionary; - if (dictionary == nullptr) { - engine->LoadDictionary(); - } - assert(engine->dictionary != nullptr); assert(engine->dictionary->contains(key)); + return engine->dictionary->at(key).data(); } \ No newline at end of file diff --git a/src/port/Engine.h b/src/port/Engine.h index f3cbc5b3..735a3e08 100644 --- a/src/port/Engine.h +++ b/src/port/Engine.h @@ -22,24 +22,28 @@ class GameEngine { std::shared_ptr context; std::unordered_map banks; std::unordered_map sequences; + std::unordered_map bankMapTable; + std::unordered_map sequencesMapTable; std::unordered_map>* dictionary; GameEngine(); static void Create(); + void AudioInit(); void StartFrame() const; static void RunCommands(Gfx* Commands); void ProcessFrame(void (*run_one_game_iter)()) const; + static uint32_t GetInterpolationFPS(); static void HandleAudioThread(); static void StartAudioFrame(); static void EndAudioFrame(); - static void AudioInit(); static void AudioExit(); + static uint8_t GetBankIdByName(const std::string& name); void LoadDictionary(); void Destroy(); }; #else - +uint32_t GameEngine_GetInterpolatedFPS(); uint32_t GameEngine_GetSampleRate(); uint32_t GameEngine_GetSamplesPerFrame(); float GameEngine_GetAspectRatio(); @@ -47,6 +51,7 @@ struct CtlEntry* GameEngine_LoadBank(uint8_t bankId); uint8_t GameEngine_IsBankLoaded(uint8_t bankId); void GameEngine_UnloadBank(uint8_t bankId); struct AudioSequenceData* GameEngine_LoadSequence(uint8_t seqId); +uint32_t GameEngine_GetSequenceCount(); uint8_t GameEngine_IsSequenceLoaded(uint8_t seqId); void GameEngine_UnloadSequence(uint8_t seqId); uint32_t GameEngine_GetGameVersion(); diff --git a/src/port/Enhancements/game-interactor/GameInteractor.cpp b/src/port/Enhancements/game-interactor/GameInteractor.cpp new file mode 100644 index 00000000..93854f3b --- /dev/null +++ b/src/port/Enhancements/game-interactor/GameInteractor.cpp @@ -0,0 +1,16 @@ +#include "GameInteractor.h" + +#include "game/level_update.h" + +extern int16_t sCurrPlayMode; + +// Taken from game/level_update.c +#define PLAY_MODE_NORMAL 0 +#define PLAY_MODE_PAUSED 2 +#define PLAY_MODE_CHANGE_AREA 3 +#define PLAY_MODE_CHANGE_LEVEL 4 +#define PLAY_MODE_FRAME_ADVANCE 5 + +bool GameInteractor::IsGameplayPaused() { + return sCurrPlayMode == PLAY_MODE_PAUSED; +} diff --git a/src/port/Enhancements/game-interactor/GameInteractor.h b/src/port/Enhancements/game-interactor/GameInteractor.h new file mode 100644 index 00000000..0124557a --- /dev/null +++ b/src/port/Enhancements/game-interactor/GameInteractor.h @@ -0,0 +1,55 @@ +#ifndef GameInteractor_h +#define GameInteractor_h + +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif + + +#ifdef __cplusplus + +#include +#include + +#define DEFINE_HOOK(name, type) \ + struct name { \ + typedef std::function fn; \ + } + +class GameInteractor { +public: + static GameInteractor* Instance; + + // Game State + class State { + + }; + + // Game Hooks + template struct RegisteredGameHooks { inline static std::vector functions; }; + template void RegisterGameHook(typename H::fn h) { RegisteredGameHooks::functions.push_back(h); } + template void ExecuteHooks(Args&&... args) { + for (auto& fn : RegisteredGameHooks::functions) { + fn(std::forward(args)...); + } + } + + DEFINE_HOOK(OnGameFrameUpdate, void()); + DEFINE_HOOK(OnHealthChange, void(int16_t health)); + + // Helpers + static bool IsGameplayPaused(); + + // Game Actions + class RawAction { + + }; + +}; + +#endif // __cplusplus + +#endif // GameInteractor_h diff --git a/src/port/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/src/port/Enhancements/game-interactor/GameInteractor_Hooks.cpp new file mode 100644 index 00000000..285330c0 --- /dev/null +++ b/src/port/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -0,0 +1,12 @@ +#include "GameInteractor_Hooks.h" + +// Gameplay + +// Updates only when in game, i.e. past the save select screen +void GameInteractor_ExecuteOnGameFrameUpdate() { + GameInteractor::Instance->ExecuteHooks(); +} + +void GameInteractor_ExecuteOnHealthChange(int16_t health) { + GameInteractor::Instance->ExecuteHooks(health); +} diff --git a/src/port/Enhancements/game-interactor/GameInteractor_Hooks.h b/src/port/Enhancements/game-interactor/GameInteractor_Hooks.h new file mode 100644 index 00000000..997ad1f4 --- /dev/null +++ b/src/port/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -0,0 +1,13 @@ +#include "GameInteractor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Gameplay +void GameInteractor_ExecuteOnGameFrameUpdate(); +void GameInteractor_ExecuteOnHealthChange(int16_t health); + +#ifdef __cplusplus +} +#endif diff --git a/src/port/Enhancements/mods.cpp b/src/port/Enhancements/mods.cpp new file mode 100644 index 00000000..0da46ce9 --- /dev/null +++ b/src/port/Enhancements/mods.cpp @@ -0,0 +1,34 @@ +#include "mods.h" +#include +#include "game-interactor/GameInteractor.h" + +#include "game/level_update.h" +extern MarioState* gMarioState; + +#define MARIO_HEALTH_MAX 0x880 + +void RegisterInfiniteHealth() { + GameInteractor::Instance->RegisterGameHook([](int16_t health) { + if (!gMarioState) return; + + if (CVarGetInteger("gInfiniteHealth", 0) != 0) { + gMarioState->health = MARIO_HEALTH_MAX; + } + }); +} + +void RegisterInfiniteLives() { + // There may be a better hook that can be made, rather than doing this every frame + GameInteractor::Instance->RegisterGameHook([]() { + if (!gMarioState) return; + + if (CVarGetInteger("gInfiniteLives", 0) != 0) { + gMarioState->numLives = 100; + } + }); +} + +void InitMods() { + RegisterInfiniteHealth(); + RegisterInfiniteLives(); +} diff --git a/src/port/Enhancements/mods.h b/src/port/Enhancements/mods.h new file mode 100644 index 00000000..f56e68e7 --- /dev/null +++ b/src/port/Enhancements/mods.h @@ -0,0 +1,9 @@ +#ifdef __cplusplus +extern "C" { +#endif + +void InitMods(); + +#ifdef __cplusplus +} +#endif diff --git a/src/port/Game.cpp b/src/port/Game.cpp index 92ed6460..db4fd50a 100644 --- a/src/port/Game.cpp +++ b/src/port/Game.cpp @@ -6,17 +6,62 @@ extern "C" { #include "audio/external.h" #include "game/game_init.h" +#include "sm64.h" } +float gInterpolationStep = 0.0f; + void alloc_pool() { static u64 pool[1024 * 1024 * 4]; main_pool_init(pool, pool + sizeof(pool) / sizeof(pool[0])); gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT); } +void patch_interpolations() { + mtx_patch_interpolated(); + patch_screen_transition_interpolated(); + patch_title_screen_scales(); + patch_interpolated_dialog(); + patch_interpolated_hud(); + patch_interpolated_paintings(); + patch_interpolated_bubble_particles(); + patch_interpolated_snow_particles(); +} + extern "C" void exec_display_list(SPTask *spTask) { - GameEngine::RunCommands((Gfx*) spTask->task.t.data_ptr); + int target_fps = GameEngine::GetInterpolationFPS(); + static int last_fps; + static int time; + int fps = target_fps; + int original_fps = 30; + + if (target_fps == 30 || original_fps > target_fps) { + fps = original_fps; + } + + if (last_fps != fps) { + time = 0; + } + + int next_original_frame = fps; + while (time + original_fps <= next_original_frame) { + time += original_fps; + if (time != next_original_frame) { + gInterpolationStep = (float)time / next_original_frame; + } + GameEngine::RunCommands((Gfx *) spTask->task.t.data_ptr); + patch_interpolations(); + } + + time -= fps; + + GameEngine::Instance->context->GetWindow()->SetTargetFps(fps); + + int threshold = CVarGetInteger("gExtraLatencyThreshold", 80); + GameEngine::Instance->context->GetWindow()->SetMaximumFrameLatency(threshold > 0 && target_fps >= threshold ? 2 : 1); + + last_fps = fps; } void push_frame() { diff --git a/src/port/Variables.cpp b/src/port/Variables.cpp index ffce12a3..1fccfaa4 100644 --- a/src/port/Variables.cpp +++ b/src/port/Variables.cpp @@ -15,7 +15,6 @@ u64 osClockRate = 62500000; s8 gShowProfiler = FALSE; s8 gShowDebugText = FALSE; -s8 gDebugLevelSelect = FALSE; OSIoMesg gDmaIoMesg; OSMesg gMainReceivedMesg; diff --git a/src/port/importer/AudioBankFactory.cpp b/src/port/importer/AudioBankFactory.cpp index 10931d4a..cbd79cf1 100644 --- a/src/port/importer/AudioBankFactory.cpp +++ b/src/port/importer/AudioBankFactory.cpp @@ -30,11 +30,11 @@ void CubeOS::AudioBankFactoryV0::ParseFileBinary(std::shared_ptrReadUInt32(); uint32_t instrumentCount = reader->ReadUInt32(); for(size_t i = 0; i < instrumentCount; i++){ auto* instrument = new Instrument(); -// memset(instrument, 0, sizeof(Instrument)); bool valid = reader->ReadUByte(); if(!valid){ bank->instruments.push_back(nullptr); @@ -104,7 +104,7 @@ void CubeOS::AudioBankFactoryV0::ParseFileBinary(std::shared_ptrdrums.push_back(drum); } - bank->mData.unused = 1; + bank->mData.bankId = bankId; bank->mData.numInstruments = instrumentCount; bank->mData.numDrums = drumCount; bank->mData.instruments = bank->instruments.data(); diff --git a/src/port/importer/AudioSequenceFactory.cpp b/src/port/importer/AudioSequenceFactory.cpp index 88496bf8..f260617c 100644 --- a/src/port/importer/AudioSequenceFactory.cpp +++ b/src/port/importer/AudioSequenceFactory.cpp @@ -1,6 +1,8 @@ #include "AudioSequenceFactory.h" #include "port/importer/types/AudioSequence.h" #include "spdlog/spdlog.h" +#include "port/Engine.h" +#include "port/importer/types/AudioBank.h" std::shared_ptr CubeOS::AudioSequenceFactory::ReadResource(std::shared_ptr initData, std::shared_ptr reader) { auto resource = std::make_shared(initData); @@ -27,9 +29,11 @@ void CubeOS::AudioSequenceFactoryV0::ParseFileBinary(std::shared_ptrReadUInt32(); size_t bankCount = reader->ReadUInt32(); for(size_t i = 0; i < bankCount; i++){ - bank->banks.push_back(reader->ReadUByte()); + std::string bankName = reader->ReadString(); + bank->banks.push_back(GameEngine::GetBankIdByName(bankName)); } size_t sampleSize = reader->ReadUInt32(); @@ -40,4 +44,5 @@ void CubeOS::AudioSequenceFactoryV0::ParseFileBinary(std::shared_ptrmData.bankCount = bankCount; bank->mData.banks = bank->banks.data(); bank->mData.data = bank->sampleData.data(); + bank->mData.id = id; } \ No newline at end of file diff --git a/src/port/importer/types/AudioBank.h b/src/port/importer/types/AudioBank.h index fbb24d11..bb3c965c 100644 --- a/src/port/importer/types/AudioBank.h +++ b/src/port/importer/types/AudioBank.h @@ -34,7 +34,7 @@ struct Drum { }; struct CtlEntry { - uint8_t unused; + uint8_t bankId; uint8_t numInstruments; uint8_t numDrums; struct Instrument **instruments; diff --git a/src/port/importer/types/AudioSequence.h b/src/port/importer/types/AudioSequence.h index 8f29c92b..5001e60b 100644 --- a/src/port/importer/types/AudioSequence.h +++ b/src/port/importer/types/AudioSequence.h @@ -7,6 +7,7 @@ struct AudioSequenceData { uint32_t bankCount; uint8_t* banks; uint8_t* data; + uint8_t id; }; namespace CubeOS { diff --git a/src/port/ui/ImguiUI.cpp b/src/port/ui/ImguiUI.cpp index d6feeec3..19b8c17d 100644 --- a/src/port/ui/ImguiUI.cpp +++ b/src/port/ui/ImguiUI.cpp @@ -1,5 +1,6 @@ #include "ImguiUI.h" #include "UIWidgets.h" +#include "ResolutionEditor.h" #include #include @@ -7,6 +8,7 @@ #include #include #include +#include "port/Engine.h" extern "C" { #include "audio/external.h" } @@ -16,6 +18,7 @@ std::shared_ptr mGameMenuBar; std::shared_ptr mConsoleWindow; std::shared_ptr mStatsWindow; std::shared_ptr mInputEditorWindow; +std::shared_ptr mAdvancedResolutionSettingsWindow; void SetupGuiElements() { auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui(); @@ -37,13 +40,27 @@ void SetupGuiElements() { SPDLOG_ERROR("Could not find input editor window"); return; } + + mAdvancedResolutionSettingsWindow = std::make_shared("gAdvancedResolutionEditorEnabled", "Advanced Resolution Settings"); + gui->AddGuiWindow(mAdvancedResolutionSettingsWindow); } void Destroy() { + mAdvancedResolutionSettingsWindow = nullptr; mConsoleWindow = nullptr; mStatsWindow = nullptr; mInputEditorWindow = nullptr; } + +std::string GetWindowButtonText(const char* text, bool menuOpen) { + char buttonText[100] = ""; + if (menuOpen) { + strcat(buttonText, ICON_FA_CHEVRON_RIGHT " "); + } + strcat(buttonText, text); + if (!menuOpen) { strcat(buttonText, " "); } + return buttonText; +} } static const char* filters[3] = { @@ -131,11 +148,21 @@ void DrawSettingsMenu(){ ImGui::SetCursorPosY(0.0f); if (ImGui::BeginMenu("Graphics")) { -//#ifndef __APPLE__ - UIWidgets::EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f, 2.0f, "", 1.0f, true); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f)); + UIWidgets::Spacer(0); + if (GameUI::mAdvancedResolutionSettingsWindow) { + if (ImGui::Button(GameUI::GetWindowButtonText("Advanced Resolution", CVarGetInteger("gAdvancedResolutionEditorEnabled", 0)).c_str(), ImVec2(-1.0f, 0.0f))) { + GameUI::mAdvancedResolutionSettingsWindow->ToggleVisibility(); + } + } + ImGui::PopStyleColor(1); + ImGui::PopStyleVar(3); + UIWidgets::Tooltip("Multiplies your output resolution by the value inputted, as a more intensive but effective form of anti-aliasing"); LUS::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(CVarGetFloat("gInternalResolution", 1)); -//#endif #ifndef __WIIU__ UIWidgets::PaddedEnhancementSliderInt("MSAA: %d", "##IMSAA", "gMSAAValue", 1, 8, "", 1, true, true, false); UIWidgets::Tooltip("Activates multi-sample anti-aliasing when above 1x up to 8x for 8 samples for every pixel"); @@ -143,6 +170,111 @@ void DrawSettingsMenu(){ #endif UIWidgets::Tooltip("Matches interpolation value to the current game's window refresh rate"); + { // FPS Slider + const int minFps = 30; + static int maxFps; + if (LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() == LUS::WindowBackend::DX11) { + maxFps = 360; + } else { + maxFps = LUS::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate(); + } + int currentFps = fmax(fmin(GameEngine::GetInterpolationFPS(), maxFps), minFps); + #ifdef __WIIU__ + UIWidgets::Spacer(0); + // only support divisors of 60 on the Wii U + if (currentFps > 60) { + currentFps = 60; + } else { + currentFps = 60 / (60 / currentFps); + } + + int fpsSlider = 1; + if (currentFps == 30) { + ImGui::Text("FPS: Original (30)"); + } else { + ImGui::Text("FPS: %d", currentFps); + if (currentFps == 30) { + fpsSlider = 2; + } else { // currentFps == 60 + fpsSlider = 3; + } + } + if (CVarGetInteger("gMatchRefreshRate", 0)) { + UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f); + } + + if (ImGui::Button(" - ##WiiUFPS")) { + fpsSlider--; + } + ImGui::SameLine(); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); + + UIWidgets::Spacer(0); + + ImGui::PushItemWidth(std::min((ImGui::GetContentRegionAvail().x - 60.0f), 260.0f)); + ImGui::SliderInt("##WiiUFPSSlider", &fpsSlider, 1, 3, "", ImGuiSliderFlags_AlwaysClamp); + ImGui::PopItemWidth(); + + ImGui::SameLine(); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); + if (ImGui::Button(" + ##WiiUFPS")) { + fpsSlider++; + } + + if (CVarGetInteger("gMatchRefreshRate", 0)) { + UIWidgets::ReEnableComponent(""); + } + if (fpsSlider > 3) { + fpsSlider = 3; + } else if (fpsSlider < 1) { + fpsSlider = 1; + } + + if (fpsSlider == 1) { + currentFps = 20; + } else if (fpsSlider == 2) { + currentFps = 30; + } else if (fpsSlider == 3) { + currentFps = 60; + } + CVarSetInteger("gInterpolationFPS", currentFps); + LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); + #else + bool matchingRefreshRate = + CVarGetInteger("gMatchRefreshRate", 0) && LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() != LUS::WindowBackend::DX11; + UIWidgets::PaddedEnhancementSliderInt( + (currentFps == 20) ? "FPS: Original (20)" : "FPS: %d", + "##FPSInterpolation", "gInterpolationFPS", minFps, maxFps, "", 20, true, true, false, matchingRefreshRate); + #endif + if (LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() == LUS::WindowBackend::DX11) { + UIWidgets::Tooltip( + "Uses Matrix Interpolation to create extra frames, resulting in smoother graphics. This is purely " + "visual and does not impact game logic, execution of glitches etc.\n\n" + "A higher target FPS than your monitor's refresh rate will waste resources, and might give a worse result." + ); + } else { + UIWidgets::Tooltip( + "Uses Matrix Interpolation to create extra frames, resulting in smoother graphics. This is purely " + "visual and does not impact game logic, execution of glitches etc." + ); + } + } // END FPS Slider + + if (LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() == LUS::WindowBackend::DX11) { + UIWidgets::Spacer(0); + if (ImGui::Button("Match Refresh Rate")) { + int hz = LUS::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate(); + if (hz >= 30 && hz <= 360) { + CVarSetInteger("gInterpolationFPS", hz); + LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); + } + } + } else { + UIWidgets::PaddedEnhancementCheckbox("Match Refresh Rate", "gMatchRefreshRate", true, false); + } + + UIWidgets::Tooltip("Matches interpolation value to the current game's window refresh rate"); + if (LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() == LUS::WindowBackend::DX11) { UIWidgets::PaddedEnhancementSliderInt(CVarGetInteger("gExtraLatencyThreshold", 80) == 0 ? "Jitter fix: Off" : "Jitter fix: >= %d FPS", "##ExtraLatencyThreshold", "gExtraLatencyThreshold", 0, 360, "", 80, true, true, false); @@ -243,7 +375,7 @@ void DrawGameMenu() { if (ImGui::BeginMenu("Ghostship")) { if (ImGui::MenuItem("Reset", #ifdef __APPLE__ - "Command-R" + "Command-R" #else "Ctrl+R" #endif @@ -272,6 +404,8 @@ void DrawEnhancementsMenu() { UIWidgets::Tooltip("Disable Level of Detail (LOD) to avoid models using lower poly versions at a distance"); UIWidgets::PaddedEnhancementCheckbox("Collecting Stars Will Not Exit Level", "gStarNoExit", true, false); UIWidgets::Tooltip("Stars act like the 100 coin star and will not take you out of the level"); + UIWidgets::PaddedEnhancementCheckbox("Avoid playing peach cutscene", "gDisablePeachCutscene", true, false); + UIWidgets::Tooltip("Avoid playing the peach cutscene when starting a new game"); ImGui::EndMenu(); } @@ -279,6 +413,53 @@ void DrawEnhancementsMenu() { } } +void DrawCheatsMenu() { + if (ImGui::BeginMenu("Cheats")) { + UIWidgets::PaddedEnhancementCheckbox("Infinite Health", "gInfiniteHealth", true, false); + UIWidgets::PaddedEnhancementCheckbox("Infinite Lives", "gInfiniteLives", true, false); + + + ImGui::EndMenu(); + } +} + +void DrawDebugMenu() { + if (ImGui::BeginMenu("Developer")) { + + UIWidgets::PaddedEnhancementCheckbox("Enable level selector", "gEnableDebugMode", true, false); + UIWidgets::Tooltip("Enable the level selector in the main menu"); + UIWidgets::Spacer(0); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f)); + if (GameUI::mStatsWindow) { + if (ImGui::Button( + GameUI::GetWindowButtonText("Stats", CVarGetInteger("gStatsEnabled", 0)).c_str(), + ImVec2(-1.0f, 0.0f))) { + GameUI::mStatsWindow->ToggleVisibility(); + } + UIWidgets::Tooltip("Shows the stats window, with your FPS and frametimes, and the OS " + "you're playing on"); + } + if (GameUI::mConsoleWindow) { + if (ImGui::Button( + GameUI::GetWindowButtonText("Console", CVarGetInteger("gConsoleEnabled", 0)) + .c_str(), + ImVec2(-1.0f, 0.0f))) { + GameUI::mConsoleWindow->ToggleVisibility(); + } + UIWidgets::Tooltip("Enables the console window, allowing you to input commands, type " + "help for some examples"); + } + + ImGui::PopStyleVar(3); + ImGui::PopStyleColor(1); + + ImGui::EndMenu(); + } +} + void GameMenuBar::DrawElement() { if(ImGui::BeginMenuBar()){ DrawMenuBarIcon(); @@ -297,6 +478,14 @@ void GameMenuBar::DrawElement() { DrawEnhancementsMenu(); + ImGui::SetCursorPosY(0.0f); + + DrawCheatsMenu(); + + ImGui::SetCursorPosY(0.0f); + + DrawDebugMenu(); + ImGui::PopStyleVar(1); ImGui::EndMenuBar(); } diff --git a/src/port/ui/ResolutionEditor.cpp b/src/port/ui/ResolutionEditor.cpp new file mode 100644 index 00000000..3d5293d4 --- /dev/null +++ b/src/port/ui/ResolutionEditor.cpp @@ -0,0 +1,421 @@ +#include "ResolutionEditor.h" +#include "UIWidgets.h" + +#include +#include +#include + +/* Console Variables are grouped under gAdvancedResolution. (e.g. "gAdvancedResolution.Enabled") + + The following CVars are used in Libultraship and can be edited here: + - Enabled - Turns Advanced Resolution Mode on. + - AspectRatioX, AspectRatioY - Aspect ratio controls. To toggle off, set either to zero. + - VerticalPixelCount, VerticalResolutionToggle - Resolution controls. + - PixelPerfectMode, IntegerScale.Factor - Pixel Perfect Mode a.k.a. integer scaling controls. + (Waiting on a second PR merge on LUS for this to fully function.): + - IntegerScale.FitAutomatically - Automatic resizing for Pixel Perfect Mode. + - IntegerScale.NeverExceedBounds - Prevents manual resizing from exceeding screen bounds. + + The following CVars are also implemented in LUS for niche use cases: + - IgnoreAspectCorrection - Stretch framebuffer to fill screen. + This is something of a power-user setting for niche setups that most people won't need or care about, + but may be useful if playing the Switch/Wii U ports on a 4:3 television. + - IntegerScale.ExceedBoundsBy - Offset the max screen bounds, usually by +1. + This isn't that useful at the moment. +*/ + +namespace AdvancedResolutionSettings { + enum setting { UPDATE_aspectRatioX, UPDATE_aspectRatioY, UPDATE_verticalPixelCount }; + + const char* aspectRatioPresetLabels[] = { + "Off", "Custom", "Original (4:3)", "Widescreen (16:9)", "Nintendo 3DS (5:3)", "16:10 (8:5)", "Ultrawide (21:9)" + }; + const float aspectRatioPresetsX[] = { 0.0f, 12.0f, 4.0f, 16.0f, 5.0f, 16.0f, 21.0f }; + const float aspectRatioPresetsY[] = { 0.0f, 9.0f, 3.0f, 9.0f, 3.0f, 10.0f, 9.0f }; + const int default_aspectRatio = 1; // Default combo list option + + const char* pixelCountPresetLabels[] = { "Custom", "Native N64 (240p)", "2x (480p)", "3x (720p)", "4x (960p)", + "5x (1200p)", "6x (1440p)", "Full HD (1080p)", "4K (2160p)" }; + const int pixelCountPresets[] = { 480, 240, 480, 720, 960, 1200, 1440, 1080, 2160, 480 }; + const int default_pixelCount = 0; // Default combo list option + + const uint32_t minVerticalPixelCount = 240; // see: LUS::AdvancedResolution() + const uint32_t maxVerticalPixelCount = 4320; + + const unsigned short default_maxIntegerScaleFactor = 6; // Default size of Integer scale factor slider. + + const float enhancementSpacerHeight = 19.0f; +// This will need to be determined more intelligently when Hi-DPI UI support is added. + + void AdvancedResolutionSettingsWindow::InitElement() { + } + + void AdvancedResolutionSettingsWindow::DrawElement() { + ImGui::SetNextWindowSize(ImVec2(497, 532), ImGuiCond_FirstUseEver); + if (ImGui::Begin("Advanced Resolution Settings", &mIsVisible)) { + // Initialise update flags. + bool update[sizeof(setting)]; + for (unsigned short i = 0; i < sizeof(setting); i++) + update[i] = false; + static short updateCountdown = 0; + short countdownStartingValue = CVarGetInteger("gInterpolationFPS", 20) / 2; // half of a second, in frames. + + // Initialise integer scale bounds. + short max_integerScaleFactor = default_maxIntegerScaleFactor; // default value, which may or may not get + // overridden depending on viewport res + + short integerScale_maximumBounds = 1; // can change when window is resized + // This is mostly just for UX purposes, as Fit Automatically logic is part of LUS. + if (((float)gfx_current_game_window_viewport.width / gfx_current_game_window_viewport.height) > + ((float)gfx_current_dimensions.width / gfx_current_dimensions.height)) { + // Scale to window height + integerScale_maximumBounds = gfx_current_game_window_viewport.height / gfx_current_dimensions.height; + } else { + // Scale to window width + integerScale_maximumBounds = gfx_current_game_window_viewport.width / gfx_current_dimensions.width; + } + // Lower-clamping maximum bounds value to 1 is no-longer necessary as that's accounted for in LUS. + // Letting it go below 1 in this Editor will even allow for checking if screen bounds are being exceeded. + if (default_maxIntegerScaleFactor < integerScale_maximumBounds) { + max_integerScaleFactor = + integerScale_maximumBounds + CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0); + } + + // Stored Values for non-UIWidgets elements + static float aspectRatioX = CVarGetFloat("gAdvancedResolution.AspectRatioX", 16.0f); + static float aspectRatioY = CVarGetFloat("gAdvancedResolution.AspectRatioY", 9.0f); + static int verticalPixelCount = CVarGetInteger("gAdvancedResolution.VerticalPixelCount", 480); + // Combo List defaults + static int item_aspectRatio = default_aspectRatio; + static int item_pixelCount = default_pixelCount; + // Additional settings + static bool showHorizontalResField = false; + static int horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; + +#ifdef __APPLE__ + ImGui::Text("Note: these settings may behave incorrectly on Apple Retina Displays."); + UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); +#endif + + // The original resolution slider (for convenience) + if (UIWidgets::EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f, 2.0f, + "", 1.0f, true, true, + (CVarGetInteger("gAdvancedResolution.VerticalResolutionToggle", 0) && + CVarGetInteger("gAdvancedResolution.Enabled", 0)) || + CVarGetInteger("gLowResMode", 0))) { + LUS::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(CVarGetFloat("gInternalResolution", 1)); + } + UIWidgets::Tooltip("Multiplies your output resolution by the value entered, as a more intensive but effective " + "form of anti-aliasing"); // Description pulled from SohMenuBar.cpp + + // N64 Mode toggle (again for convenience) + // UIWidgets::PaddedEnhancementCheckbox("(Enhancements>Graphics) N64 Mode", "gLowResMode", false, false, false, + // "", UIWidgets::CheckboxGraphics::Cross, false); + + UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); + // Activator + UIWidgets::PaddedEnhancementCheckbox("Enable advanced settings.", "gAdvancedResolution.Enabled", false, false, + false, "", UIWidgets::CheckboxGraphics::Cross, false); + // Error/Warning display + if (!CVarGetInteger("gLowResMode", 0)) { + if (IsDroppingFrames()) { // Significant frame drop warning + ImGui::TextColored({ 0.85f, 0.85f, 0.0f, 1.0f }, + ICON_FA_EXCLAMATION_TRIANGLE " Significant frame rate (FPS) drops may be occuring."); + UIWidgets::Spacer(2); + } else { // No warnings + UIWidgets::Spacer(enhancementSpacerHeight); + } + } else { // N64 Mode warning + ImGui::TextColored({ 0.0f, 0.85f, 0.85f, 1.0f }, + ICON_FA_QUESTION_CIRCLE " \"N64 Mode\" is overriding these settings."); + ImGui::SameLine(); + if (ImGui::Button("Click to disable")) { + CVarSetInteger("gLowResMode", (int)false); + CVarSave(); + } + } + // Resolution visualiser + ImGui::Text("Viewport dimensions: %d x %d", gfx_current_game_window_viewport.width, + gfx_current_game_window_viewport.height); + ImGui::Text("Internal resolution: %d x %d", gfx_current_dimensions.width, gfx_current_dimensions.height); + + UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); + + // Aspect Ratio + ImGui::Text("Force aspect ratio:"); + ImGui::SameLine(); + ImGui::TextColored({ 0.75f, 0.75f, 0.75f, 1.0f }, "(Select \"Off\" to disable.)"); + if (ImGui::Combo(" ", &item_aspectRatio, aspectRatioPresetLabels, + IM_ARRAYSIZE(aspectRatioPresetLabels)) && + item_aspectRatio != default_aspectRatio) { // don't change anything if "Custom" is selected. + aspectRatioX = aspectRatioPresetsX[item_aspectRatio]; + aspectRatioY = aspectRatioPresetsY[item_aspectRatio]; + update[UPDATE_aspectRatioX] = true; + update[UPDATE_aspectRatioY] = true; + + if (showHorizontalResField) { + horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; + } + } + if (item_aspectRatio == default_aspectRatio && !showHorizontalResField) { + // Declaring the Y input interaction in particular as a variable beforehand + // will prevent a bug where the Y field would disappear when modifying X. + bool inputX = ImGui::InputFloat("X", &aspectRatioX, 0.1f, 1.0f, "%.3f"); + bool inputY = ImGui::InputFloat("Y", &aspectRatioY, 0.1f, 1.0f, "%.3f"); + if (inputX || inputY) { + item_aspectRatio = default_aspectRatio; + update[UPDATE_aspectRatioX] = true; + update[UPDATE_aspectRatioY] = true; + } + } else if (showHorizontalResField) { // Show calculated aspect ratio + if (item_aspectRatio) { + UIWidgets::Spacer(2); + float resolvedAspectRatio = (float)gfx_current_dimensions.height / gfx_current_dimensions.width; + ImGui::Text("Aspect ratio: %.4f", resolvedAspectRatio); + } else { + UIWidgets::Spacer(enhancementSpacerHeight); + } + } + + UIWidgets::Spacer(0); + // Vertical Resolution + UIWidgets::PaddedEnhancementCheckbox("Set fixed vertical resolution (disables Resolution slider)", + "gAdvancedResolution.VerticalResolutionToggle", true, false, false, "", + UIWidgets::CheckboxGraphics::Cross, false); + UIWidgets::Tooltip( + "Override the resolution scale slider and use the settings below, irrespective of window size."); + if (ImGui::Combo("Pixel Count Presets", &item_pixelCount, pixelCountPresetLabels, + IM_ARRAYSIZE(pixelCountPresetLabels)) && + item_pixelCount != default_pixelCount) { // don't change anything if "Custom" is selected. + verticalPixelCount = pixelCountPresets[item_pixelCount]; + update[UPDATE_verticalPixelCount] = true; + + if (showHorizontalResField) { + horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; + } + } + // Horizontal Resolution, if visibility is enabled for it. + if (showHorizontalResField) { + // Only show the field if Aspect Ratio is being enforced. + if ((aspectRatioX > 0.0f) && (aspectRatioY > 0.0f)) { + // So basically we're "faking" this one by setting aspectRatioX instead. + if (ImGui::InputInt("Horiz. Pixel Count", &horizontalPixelCount, 8, 320)) { + item_aspectRatio = default_aspectRatio; + if (horizontalPixelCount < (minVerticalPixelCount / 3.0f) * 4.0f) { + horizontalPixelCount = (minVerticalPixelCount / 3.0f) * 4.0f; + } + aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount; + update[UPDATE_aspectRatioX] = true; + } + } else { // Display a notice instead. + ImGui::TextColored({ 0.0f, 0.85f, 0.85f, 1.0f }, + ICON_FA_QUESTION_CIRCLE " \"Force aspect ratio\" required."); + // ImGui::Text(" "); + ImGui::SameLine(); + if (ImGui::Button("Click to resolve")) { + item_aspectRatio = default_aspectRatio; // Set it to Custom + aspectRatioX = aspectRatioPresetsX[2]; // but use the 4:3 defaults + aspectRatioY = aspectRatioPresetsY[2]; + update[UPDATE_aspectRatioX] = true; + update[UPDATE_aspectRatioY] = true; + horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; + } + } + } + // Vertical Resolution part 2 + if (ImGui::InputInt("Vertical Pixel Count", &verticalPixelCount, 8, 240)) { + item_pixelCount = default_pixelCount; + update[UPDATE_verticalPixelCount] = true; + + // Account for the natural instinct to enter horizontal first. + // Ignore vertical resolutions that are below the lower clamp constant. + if (showHorizontalResField && !(verticalPixelCount < minVerticalPixelCount)) { + aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount; + update[UPDATE_aspectRatioX] = true; + } + } + + UIWidgets::Spacer(0); + // UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); + + // Integer scaling settings group + if (ImGui::CollapsingHeader("Integer Scaling Settings")) { + // Pixel-perfect Mode + UIWidgets::PaddedEnhancementCheckbox("Pixel-perfect Mode", "gAdvancedResolution.PixelPerfectMode", true, + true, + !CVarGetInteger("gAdvancedResolution.VerticalResolutionToggle", 0), "", + UIWidgets::CheckboxGraphics::Cross, false); + UIWidgets::Tooltip("Don't scale image to fill window."); + if (!CVarGetInteger("gAdvancedResolution.VerticalResolutionToggle", 0)) { + CVarSetInteger("gAdvancedResolution.PixelPerfectMode", (int)false); + CVarSave(); + } + + // Integer Scaling + UIWidgets::EnhancementSliderInt("Integer scale factor: %d", "##ARSIntScale", + "gAdvancedResolution.IntegerScale.Factor", 1, max_integerScaleFactor, "%d", + 1, true, + !CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) || + CVarGetInteger("gAdvancedResolution.IntegerScale.FitAutomatically", 0)); + UIWidgets::Tooltip("Integer scales the image. Only available in pixel-perfect mode."); + // Display warning if size is being clamped or if framebuffer is larger than viewport. + if (CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) && + (CVarGetInteger("gAdvancedResolution.IntegerScale.NeverExceedBounds", 1) && + CVarGetInteger("gAdvancedResolution.IntegerScale.Factor", 1) > integerScale_maximumBounds)) { + ImGui::SameLine(); + ImGui::TextColored({ 0.85f, 0.85f, 0.0f, 1.0f }, ICON_FA_EXCLAMATION_TRIANGLE " Window exceeded."); + } + + UIWidgets::PaddedEnhancementCheckbox("Automatically scale image to fit viewport", + "gAdvancedResolution.IntegerScale.FitAutomatically", true, true, + !CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0), "", + UIWidgets::CheckboxGraphics::Cross, false); + UIWidgets::Tooltip("Automatically sets scale factor to fit window. Only available in pixel-perfect mode."); + if (CVarGetInteger("gAdvancedResolution.IntegerScale.FitAutomatically", 0)) { + // This is just here to update the value shown on the slider. + // The function in LUS to handle this setting will ignore IntegerScaleFactor while active. + CVarSetInteger("gAdvancedResolution.IntegerScale.Factor", integerScale_maximumBounds); + // CVarSave(); + } + } + + UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f); + + // Collapsible panel for additional settings + if (ImGui::CollapsingHeader("Additional Settings")) { + UIWidgets::Spacer(0); +#if defined(__SWITCH__) || defined(__WIIU__) + // Disable aspect correction, stretching the framebuffer to fill the viewport. + // This option is only really needed on systems limited to 16:9 TV resolutions, such as consoles. + // The associated CVar is still functional on PC platforms if you want to use it though. + UIWidgets::PaddedEnhancementCheckbox("Disable aspect correction and stretch the output image.\n" + "(Might be useful for 4:3 televisions!)\n" + "Not available in Pixel Perfect Mode.", + "gAdvancedResolution.IgnoreAspectCorrection", false, true, + CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0), "", + UIWidgets::CheckboxGraphics::Cross, false); +#else + if (CVarGetInteger("gAdvancedResolution.IgnoreAspectCorrection", 0)) { + // This setting is intentionally not exposed on PC platforms, + // but may be accidentally activated for varying reasons. + // Having this button should hopefully prevent support headaches. + ImGui::TextColored({ 0.0f, 0.85f, 0.85f, 1.0f }, ICON_FA_QUESTION_CIRCLE + " If the image is stretched and you don't know why, click this."); + if (ImGui::Button("Click to reenable aspect correction.")) { + CVarSetInteger("gAdvancedResolution.IgnoreAspectCorrection", (int)false); + CVarSave(); + } + UIWidgets::Spacer(2); + } +#endif + + if (ImGui::Checkbox("Show a horizontal resolution field.", &showHorizontalResField)) { + if (!showHorizontalResField && (aspectRatioX > 0.0f)) { // when turning this setting off + // Refresh relevant values + aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount; + horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; + } else { // when turning this setting on + item_aspectRatio = default_aspectRatio; + if (aspectRatioX > 0.0f) { + // Refresh relevant values in the opposite order + horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX; + aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount; + } + } + update[UPDATE_aspectRatioX] = true; + } + + UIWidgets::PaddedEnhancementCheckbox( + "Don't allow integer scaling to exceed screen bounds.\n" + "(Makes screen bounds take priority over specified factor.)", + "gAdvancedResolution.IntegerScale.NeverExceedBounds", true, false, + !CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) || + CVarGetInteger("gAdvancedResolution.IntegerScale.FitAutomatically", 0), + "", UIWidgets::CheckboxGraphics::Cross, true); + + if (!CVarGetInteger("gAdvancedResolution.IntegerScale.NeverExceedBounds", 1) || + CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0)) { + ImGui::TextColored({ 0.0f, 0.85f, 0.85f, 1.0f }, + " " ICON_FA_QUESTION_CIRCLE + " A scroll bar may become visible if screen bounds are exceeded."); + // Another helpful button for an unused CVar. + if (CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0)) { + if (ImGui::Button("Click to reset an unused CVar that may be causing this.")) { + CVarSetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0); + CVarSave(); + } + } + } else { + UIWidgets::Spacer(enhancementSpacerHeight); + } + + // I've ended up dummying this one out because it doesn't function in a satisfactory way. + // Consider this idea on the table, but I don't deem it an important enough feature to push for. + /* + UIWidgets::PaddedEnhancementCheckbox("Allow integer scale factor to go 1x above maximum screen bounds.", + "gAdvancedResolution.IntegerScale.ExceedBoundsBy", false, false, + !CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0), "", + UIWidgets::CheckboxGraphics::Cross, false); + if (CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0)) { + ImGui::TextColored({ 0.0f, 0.85f, 0.85f, 1.0f }, + " " ICON_FA_QUESTION_CIRCLE + " A scroll bar may become visible if screen bounds are exceeded."); + }*/ + + } // end of Additional Settings + + // Clamp and update the CVars that don't use UIWidgets + if (IsBoolArrayTrue(update)) { + if (update[UPDATE_aspectRatioX]) { + if (aspectRatioX < 0.0f) { + aspectRatioX = 0.0f; + } + CVarSetFloat("gAdvancedResolution.AspectRatioX", aspectRatioX); + } + if (update[UPDATE_aspectRatioY]) { + if (aspectRatioY < 0.0f) { + aspectRatioY = 0.0f; + } + CVarSetFloat("gAdvancedResolution.AspectRatioY", aspectRatioY); + } + if (update[UPDATE_verticalPixelCount]) { + // There's a upper and lower clamp on the Libultraship side too, + // so clamping it here is purely visual, so the vertical resolution field reflects it. + if (verticalPixelCount < minVerticalPixelCount) { + verticalPixelCount = minVerticalPixelCount; + } + if (verticalPixelCount > maxVerticalPixelCount) { + verticalPixelCount = maxVerticalPixelCount; + } + CVarSetInteger("gAdvancedResolution.VerticalPixelCount", verticalPixelCount); + } + // Delay saving this set of CVars by a predetermined length of time, in frames. + updateCountdown = countdownStartingValue; + } + if (updateCountdown > 0) { + updateCountdown--; + } else { + CVarSave(); + } + } + ImGui::End(); + } + + void AdvancedResolutionSettingsWindow::UpdateElement() { + } + + bool AdvancedResolutionSettingsWindow::IsDroppingFrames() { + // a rather imprecise way of checking for frame drops. + // but it's mostly there to inform the player of large drops. + const short targetFPS = CVarGetInteger("gInterpolationFPS", 20); + const float threshold = targetFPS / 20.0f + 4.1f; + return ImGui::GetIO().Framerate < targetFPS - threshold; + } + + bool AdvancedResolutionSettingsWindow::IsBoolArrayTrue(bool* foo) { + for (unsigned short i = 0; i < sizeof(&foo); i++) + if (&foo[i]) + return true; + return false; + } +} // namespace AdvancedResolutionSettings \ No newline at end of file diff --git a/src/port/ui/ResolutionEditor.h b/src/port/ui/ResolutionEditor.h new file mode 100644 index 00000000..38af5607 --- /dev/null +++ b/src/port/ui/ResolutionEditor.h @@ -0,0 +1,19 @@ +#pragma once +#include + +namespace AdvancedResolutionSettings { + class AdvancedResolutionSettingsWindow : public LUS::GuiWindow { + private: + bool IsDroppingFrames(); + + protected: + bool IsBoolArrayTrue(bool*); + + public: + using LUS::GuiWindow::GuiWindow; + + void InitElement() override; + void DrawElement() override; + void UpdateElement() override; + }; +} // namespace AdvancedResolutionSettings \ No newline at end of file