From 90a33e9756388a2d5cefd2c9d30f010e622799fe Mon Sep 17 00:00:00 2001 From: GreenSwede <42186507+GreenSwede@users.noreply.github.com> Date: Sun, 29 May 2022 18:14:46 +0200 Subject: [PATCH 1/8] [LINUX] Add compiler flags to fix floating point precision error (#399) These flags are known to fix one known issue exclusive to Linux: the Volvagia boss battle sequence. The softlock that occurs points towards a possible floating point precision error, possibly tied to the camera/Volvagia movement. This does not occur for the Windows build. It's possible that there are more issues that gets fixed by these flags. These flags will ensure that the compiler follows the IEEE 754 standard, which so happens to be the same behaviour that Windows uses. For more details, read this informative stackoverflow post: https://stackoverflow.com/a/16395650 --- soh/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/soh/Makefile b/soh/Makefile index be46f55e7..74a112ecb 100644 --- a/soh/Makefile +++ b/soh/Makefile @@ -18,8 +18,8 @@ WARN := \ -funsigned-char \ -m32 -mhard-float -fno-stack-protector -fno-common -fno-zero-initialized-in-bss -fno-strict-aliasing -fno-inline-functions -fno-inline-small-functions -fno-toplevel-reorder -ffreestanding -fwrapv \ -CXXFLAGS := $(WARN) -std=c++20 -D_GNU_SOURCE -fpermissive -no-pie -nostdlib -march=i386 -CFLAGS := $(WARN) -std=c99 -D_GNU_SOURCE -no-pie -nostdlib -march=i386 +CXXFLAGS := $(WARN) -std=c++20 -D_GNU_SOURCE -fpermissive -no-pie -nostdlib -march=i386 -msse2 -mfpmath=sse +CFLAGS := $(WARN) -std=c99 -D_GNU_SOURCE -no-pie -nostdlib -march=i386 -msse2 -mfpmath=sse LDFLAGS := -m32 CPPFLAGS := -MMD From ccab94c765c5518e9ed8aa3a04b10c2d6a2d9159 Mon Sep 17 00:00:00 2001 From: Emill Date: Sun, 29 May 2022 18:16:23 +0200 Subject: [PATCH 2/8] Implement AnyFPS + option to queue one rendered frame (#401) --- .../Lib/Fast3D/gfx_direct3d11.cpp | 37 +++-- .../libultraship/Lib/Fast3D/gfx_dxgi.cpp | 145 ++++++++++++------ .../libultraship/Lib/Fast3D/gfx_dxgi.h | 8 +- .../libultraship/Lib/Fast3D/gfx_pc.cpp | 15 +- libultraship/libultraship/Lib/Fast3D/gfx_pc.h | 4 +- .../libultraship/Lib/Fast3D/gfx_sdl2.cpp | 39 +++-- .../Lib/Fast3D/gfx_window_manager_api.h | 4 +- libultraship/libultraship/SohImGuiImpl.cpp | 69 ++++++++- libultraship/libultraship/Window.cpp | 11 +- libultraship/libultraship/Window.h | 3 +- soh/soh/OTRGlobals.cpp | 40 ++++- soh/soh/frame_interpolation.cpp | 2 +- soh/src/code/z_actor.c | 2 +- .../ovl_file_choose/z_file_choose.c | 8 + 14 files changed, 298 insertions(+), 89 deletions(-) diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_direct3d11.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_direct3d11.cpp index 4664a0faa..f1661f426 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_direct3d11.cpp +++ b/libultraship/libultraship/Lib/Fast3D/gfx_direct3d11.cpp @@ -96,7 +96,6 @@ static struct { uint32_t msaa_num_quality_levels[D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT]; ComPtr device; - ComPtr swap_chain; ComPtr context; ComPtr rasterizer_state; ComPtr depth_stencil_state; @@ -252,7 +251,24 @@ static void gfx_d3d11_init(void) { }); // Create the swap chain - d3d.swap_chain = gfx_dxgi_create_swap_chain(d3d.device.Get()); + gfx_dxgi_create_swap_chain(d3d.device.Get(), []() { + d3d.framebuffers[0].render_target_view.Reset(); + d3d.textures[d3d.framebuffers[0].texture_id].texture.Reset(); + d3d.context->ClearState(); + d3d.context->Flush(); + + d3d.last_shader_program = nullptr; + d3d.last_vertex_buffer_stride = 0; + d3d.last_blend_state.Reset(); + d3d.last_resource_views[0].Reset(); + d3d.last_resource_views[1].Reset(); + d3d.last_sampler_states[0].Reset(); + d3d.last_sampler_states[1].Reset(); + d3d.last_depth_test = -1; + d3d.last_depth_mask = -1; + d3d.last_zmode_decal = -1; + d3d.last_primitive_topology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; + }); // Create D3D Debug device if in debug mode @@ -266,7 +282,7 @@ static void gfx_d3d11_init(void) { // Check the size of the window DXGI_SWAP_CHAIN_DESC1 swap_chain_desc; - ThrowIfFailed(d3d.swap_chain->GetDesc1(&swap_chain_desc)); + ThrowIfFailed(gfx_dxgi_get_swap_chain()->GetDesc1(&swap_chain_desc)); d3d.textures[fb.texture_id].width = swap_chain_desc.Width; d3d.textures[fb.texture_id].height = swap_chain_desc.Height; fb.msaa_level = 1; @@ -303,8 +319,6 @@ static void gfx_d3d11_init(void) { ThrowIfFailed(d3d.device->CreateBuffer(&constant_buffer_desc, nullptr, d3d.per_frame_cb.GetAddressOf()), gfx_dxgi_get_h_wnd(), "Failed to create per-frame constant buffer."); - d3d.context->PSSetConstantBuffers(0, 1, d3d.per_frame_cb.GetAddressOf()); - // Create per-draw constant buffer constant_buffer_desc.Usage = D3D11_USAGE_DYNAMIC; @@ -316,8 +330,6 @@ static void gfx_d3d11_init(void) { ThrowIfFailed(d3d.device->CreateBuffer(&constant_buffer_desc, nullptr, d3d.per_draw_cb.GetAddressOf()), gfx_dxgi_get_h_wnd(), "Failed to create per-draw constant buffer."); - d3d.context->PSSetConstantBuffers(1, 1, d3d.per_draw_cb.GetAddressOf()); - // Create compute shader that can be used to retrieve depth buffer values const char* shader_source = R"( @@ -737,6 +749,8 @@ static void gfx_d3d11_on_resize(void) { static void gfx_d3d11_start_frame(void) { // Set per-frame constant buffer + ID3D11Buffer* buffers[2] = { d3d.per_frame_cb.Get(), d3d.per_draw_cb.Get() }; + d3d.context->PSSetConstantBuffers(0, 2, buffers); d3d.per_frame_cb_data.noise_frame++; if (d3d.per_frame_cb_data.noise_frame > 150) { @@ -803,15 +817,16 @@ static void gfx_d3d11_update_framebuffer_parameters(int fb_id, uint32_t width, u if (msaa_level <= 1) { ThrowIfFailed(d3d.device->CreateShaderResourceView(tex.texture.Get(), nullptr, tex.resource_view.ReleaseAndGetAddressOf())); } - } else if (diff) { + } else if (diff || (render_target && tex.texture.Get() == nullptr)) { DXGI_SWAP_CHAIN_DESC1 desc1; - ThrowIfFailed(d3d.swap_chain->GetDesc1(&desc1)); + IDXGISwapChain1* swap_chain = gfx_dxgi_get_swap_chain(); + ThrowIfFailed(swap_chain->GetDesc1(&desc1)); if (desc1.Width != width || desc1.Height != height) { fb.render_target_view.Reset(); tex.texture.Reset(); - ThrowIfFailed(d3d.swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, desc1.Flags)); + ThrowIfFailed(swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, desc1.Flags)); } - ThrowIfFailed(d3d.swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *)tex.texture.ReleaseAndGetAddressOf())); + ThrowIfFailed(swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *)tex.texture.ReleaseAndGetAddressOf())); } if (render_target) { ThrowIfFailed(d3d.device->CreateRenderTargetView(tex.texture.Get(), nullptr, fb.render_target_view.ReleaseAndGetAddressOf())); diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.cpp index 49abe0b28..d257d7b08 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.cpp +++ b/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.cpp @@ -34,15 +34,8 @@ #define WINCLASS_NAME L"N64GAME" #define GFX_API_NAME "DirectX" -#ifdef VERSION_EU -#define FRAME_INTERVAL_US_NUMERATOR_ 60000 -#define FRAME_INTERVAL_US_DENOMINATOR 3 -#else -#define FRAME_INTERVAL_US_NUMERATOR_ 50000 -#define FRAME_INTERVAL_US_DENOMINATOR 3 -#endif - -#define FRAME_INTERVAL_US_NUMERATOR (FRAME_INTERVAL_US_NUMERATOR_ * dxgi.frame_divisor) +#define FRAME_INTERVAL_NS_NUMERATOR 1000000000 +#define FRAME_INTERVAL_NS_DENOMINATOR (dxgi.target_fps) using namespace Microsoft::WRL; // For ComPtr @@ -66,14 +59,19 @@ static struct { ComPtr factory; ComPtr swap_chain; HANDLE waitable_object; + ComPtr swap_chain_device; // D3D11 Device or D3D12 Command Queue + std::function before_destroy_swap_chain_fn; uint64_t qpc_init, qpc_freq; - uint64_t frame_timestamp; // in units of 1/FRAME_INTERVAL_US_DENOMINATOR microseconds + uint64_t frame_timestamp; // in units of 1/FRAME_INTERVAL_NS_DENOMINATOR nanoseconds std::map frame_stats; std::set> pending_frame_stats; bool dropped_frame; bool zero_latency; + float detected_hz; UINT length_in_vsync_frames; - uint32_t frame_divisor; + uint32_t target_fps; + uint32_t maximum_frame_latency; + uint32_t applied_maximum_frame_latency; HANDLE timer; bool use_timer; LARGE_INTEGER previous_present_time; @@ -143,6 +141,22 @@ static void run_as_dpi_aware(Fun f) { } } +static void apply_maximum_frame_latency(bool first) { + ComPtr swap_chain2; + if (dxgi.swap_chain->QueryInterface(__uuidof(IDXGISwapChain2), &swap_chain2) == S_OK) { + ThrowIfFailed(swap_chain2->SetMaximumFrameLatency(dxgi.maximum_frame_latency)); + if (first) { + dxgi.waitable_object = swap_chain2->GetFrameLatencyWaitableObject(); + WaitForSingleObject(dxgi.waitable_object, INFINITE); + } + } else { + ComPtr device1; + ThrowIfFailed(dxgi.swap_chain->GetDevice(__uuidof(IDXGIDevice1), &device1)); + ThrowIfFailed(device1->SetMaximumFrameLatency(dxgi.maximum_frame_latency)); + } + dxgi.applied_maximum_frame_latency = dxgi.maximum_frame_latency; +} + static void toggle_borderless_window_full_screen(bool enable, bool call_callback) { // Windows 7 + flip mode + waitable object can't go to exclusive fullscreen, // so do borderless instead. If DWM is enabled, this means we get one monitor @@ -271,7 +285,8 @@ void gfx_dxgi_init(const char *game_name, bool start_in_fullscreen) { dxgi.qpc_init = qpc_init.QuadPart; dxgi.qpc_freq = qpc_freq.QuadPart; - dxgi.frame_divisor = 1; + dxgi.target_fps = 60; + dxgi.maximum_frame_latency = 1; dxgi.timer = CreateWaitableTimer(nullptr, false, nullptr); // Prepare window title @@ -367,8 +382,8 @@ static void gfx_dxgi_handle_events(void) { }*/ } -static uint64_t qpc_to_us(uint64_t qpc) { - return qpc / dxgi.qpc_freq * 1000000 + qpc % dxgi.qpc_freq * 1000000 / dxgi.qpc_freq; +static uint64_t qpc_to_ns(uint64_t qpc) { + return qpc / dxgi.qpc_freq * 1000000000 + qpc % dxgi.qpc_freq * 1000000000 / dxgi.qpc_freq; } static uint64_t qpc_to_100ns(uint64_t qpc) { @@ -406,7 +421,7 @@ static bool gfx_dxgi_start_frame(void) { dxgi.use_timer = false; - dxgi.frame_timestamp += FRAME_INTERVAL_US_NUMERATOR; + dxgi.frame_timestamp += FRAME_INTERVAL_NS_NUMERATOR; if (dxgi.frame_stats.size() >= 2) { DXGI_FRAME_STATISTICS *first = &dxgi.frame_stats.begin()->second; @@ -421,14 +436,16 @@ static bool gfx_dxgi_start_frame(void) { } double estimated_vsync_interval = (double)sync_qpc_diff / (double)sync_vsync_diff; - uint64_t estimated_vsync_interval_us = qpc_to_us(estimated_vsync_interval); - //printf("Estimated vsync_interval: %d\n", (int)estimated_vsync_interval_us); - if (estimated_vsync_interval_us < 2 || estimated_vsync_interval_us > 1000000) { + uint64_t estimated_vsync_interval_ns = qpc_to_ns(estimated_vsync_interval); + //printf("Estimated vsync_interval: %d\n", (int)estimated_vsync_interval_ns); + if (estimated_vsync_interval_ns < 2000 || estimated_vsync_interval_ns > 1000000000) { // Unreasonable, maybe a monitor change - estimated_vsync_interval_us = 16666; - estimated_vsync_interval = estimated_vsync_interval_us * dxgi.qpc_freq / 1000000; + estimated_vsync_interval_ns = 16666666; + estimated_vsync_interval = estimated_vsync_interval_ns * dxgi.qpc_freq / 1000000000; } + dxgi.detected_hz = (float)((double)1000000000 / (double)estimated_vsync_interval_ns); + UINT queued_vsyncs = 0; bool is_first = true; for (const std::pair& p : dxgi.pending_frame_stats) { @@ -440,21 +457,21 @@ static bool gfx_dxgi_start_frame(void) { } uint64_t last_frame_present_end_qpc = (last->SyncQPCTime.QuadPart - dxgi.qpc_init) + estimated_vsync_interval * queued_vsyncs; - uint64_t last_end_us = qpc_to_us(last_frame_present_end_qpc); + uint64_t last_end_ns = qpc_to_ns(last_frame_present_end_qpc); - double vsyncs_to_wait = (double)(int64_t)(dxgi.frame_timestamp / FRAME_INTERVAL_US_DENOMINATOR - last_end_us) / estimated_vsync_interval_us; - //printf("ts: %llu, last_end_us: %llu, Init v: %f\n", dxgi.frame_timestamp / 3, last_end_us, vsyncs_to_wait); + double vsyncs_to_wait = (double)(int64_t)(dxgi.frame_timestamp / FRAME_INTERVAL_NS_DENOMINATOR - last_end_ns) / estimated_vsync_interval_ns; + //printf("ts: %llu, last_end_ns: %llu, Init v: %f\n", dxgi.frame_timestamp / 3, last_end_ns, vsyncs_to_wait); if (vsyncs_to_wait <= 0) { // Too late - if ((int64_t)(dxgi.frame_timestamp / FRAME_INTERVAL_US_DENOMINATOR - last_end_us) < -66666) { + if ((int64_t)(dxgi.frame_timestamp / FRAME_INTERVAL_NS_DENOMINATOR - last_end_ns) < -66666666) { // The application must have been paused or similar - vsyncs_to_wait = round(((double)FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR) / estimated_vsync_interval_us); + vsyncs_to_wait = round(((double)FRAME_INTERVAL_NS_NUMERATOR / FRAME_INTERVAL_NS_DENOMINATOR) / estimated_vsync_interval_ns); if (vsyncs_to_wait < 1) { vsyncs_to_wait = 1; } - dxgi.frame_timestamp = FRAME_INTERVAL_US_DENOMINATOR * (last_end_us + vsyncs_to_wait * estimated_vsync_interval_us); + dxgi.frame_timestamp = FRAME_INTERVAL_NS_DENOMINATOR * (last_end_ns + vsyncs_to_wait * estimated_vsync_interval_ns); } else { // Drop frame //printf("Dropping frame\n"); @@ -464,9 +481,9 @@ static bool gfx_dxgi_start_frame(void) { } double orig_wait = vsyncs_to_wait; if (floor(vsyncs_to_wait) != vsyncs_to_wait) { - uint64_t left = last_end_us + floor(vsyncs_to_wait) * estimated_vsync_interval_us; - uint64_t right = last_end_us + ceil(vsyncs_to_wait) * estimated_vsync_interval_us; - uint64_t adjusted_desired_time = dxgi.frame_timestamp / FRAME_INTERVAL_US_DENOMINATOR + (last_end_us + (FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR) > dxgi.frame_timestamp / FRAME_INTERVAL_US_DENOMINATOR ? 2000 : -2000); + uint64_t left = last_end_ns + floor(vsyncs_to_wait) * estimated_vsync_interval_ns; + uint64_t right = last_end_ns + ceil(vsyncs_to_wait) * estimated_vsync_interval_ns; + uint64_t adjusted_desired_time = dxgi.frame_timestamp / FRAME_INTERVAL_NS_DENOMINATOR + (last_end_ns + (FRAME_INTERVAL_NS_NUMERATOR / FRAME_INTERVAL_NS_DENOMINATOR) > dxgi.frame_timestamp / FRAME_INTERVAL_NS_DENOMINATOR ? 2000000 : -2000000); int64_t diff_left = adjusted_desired_time - left; int64_t diff_right = right - adjusted_desired_time; if (diff_left < 0) { @@ -506,7 +523,7 @@ static void gfx_dxgi_swap_buffers_begin(void) { LARGE_INTEGER t; if (dxgi.use_timer) { QueryPerformanceCounter(&t); - int64_t next = qpc_to_100ns(dxgi.previous_present_time.QuadPart) + 10 * FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR; + int64_t next = qpc_to_100ns(dxgi.previous_present_time.QuadPart) + FRAME_INTERVAL_NS_NUMERATOR / (FRAME_INTERVAL_NS_DENOMINATOR * 100); int64_t left = next - qpc_to_100ns(t.QuadPart); if (left > 0) { LARGE_INTEGER li; @@ -531,6 +548,32 @@ static void gfx_dxgi_swap_buffers_end(void) { QueryPerformanceCounter(&t0); QueryPerformanceCounter(&t1); + if (dxgi.applied_maximum_frame_latency > dxgi.maximum_frame_latency) { + // There seems to be a bug that if latency is decreased, there is no effect of that operation, so recreate swap chain + if (dxgi.waitable_object != nullptr) { + if (!dxgi.dropped_frame) { + // Wait the last time on this swap chain + WaitForSingleObject(dxgi.waitable_object, INFINITE); + } + CloseHandle(dxgi.waitable_object); + dxgi.waitable_object = nullptr; + } + + dxgi.before_destroy_swap_chain_fn(); + + dxgi.swap_chain.Reset(); + + gfx_dxgi_create_swap_chain(dxgi.swap_chain_device.Get(), move(dxgi.before_destroy_swap_chain_fn)); + + dxgi.frame_timestamp = 0; + dxgi.frame_stats.clear(); + dxgi.pending_frame_stats.clear(); + + return; // Make sure we don't wait a second time on the waitable object, since that would hang the program + } else if (dxgi.applied_maximum_frame_latency != dxgi.maximum_frame_latency) { + apply_maximum_frame_latency(false); + } + if (!dxgi.dropped_frame) { if (dxgi.waitable_object != nullptr) { WaitForSingleObject(dxgi.waitable_object, INFINITE); @@ -554,8 +597,20 @@ static double gfx_dxgi_get_time(void) { return (double)(t.QuadPart - dxgi.qpc_init) / dxgi.qpc_freq; } -static void gfx_dxgi_set_frame_divisor(int divisor) { - dxgi.frame_divisor = divisor; +static void gfx_dxgi_set_target_fps(int fps) { + uint32_t old_fps = dxgi.target_fps; + uint64_t t0 = dxgi.frame_timestamp / old_fps; + uint32_t t1 = dxgi.frame_timestamp % old_fps; + dxgi.target_fps = fps; + dxgi.frame_timestamp = t0 * dxgi.target_fps + t1 * dxgi.target_fps / old_fps; +} + +static void gfx_dxgi_set_maximum_frame_latency(int latency) { + dxgi.maximum_frame_latency = latency; +} + +static float gfx_dxgi_get_detected_hz() { + return dxgi.detected_hz; } void gfx_dxgi_create_factory_and_device(bool debug, int d3d_version, bool (*create_device_fn)(IDXGIAdapter1 *adapter, bool test_only)) { @@ -592,12 +647,12 @@ void gfx_dxgi_create_factory_and_device(bool debug, int d3d_version, bool (*crea SetWindowTextW(dxgi.h_wnd, w_title); } -ComPtr gfx_dxgi_create_swap_chain(IUnknown *device) { +void gfx_dxgi_create_swap_chain(IUnknown *device, std::function&& before_destroy_fn) { bool win8 = IsWindows8OrGreater(); // DXGI_SCALING_NONE is only supported on Win8 and beyond bool dxgi_13 = dxgi.CreateDXGIFactory2 != nullptr; // DXGI 1.3 introduced waitable object DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {}; - swap_chain_desc.BufferCount = 2; + swap_chain_desc.BufferCount = 3; swap_chain_desc.Width = 0; swap_chain_desc.Height = 0; swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; @@ -617,28 +672,24 @@ ComPtr gfx_dxgi_create_swap_chain(IUnknown *device) { }); ThrowIfFailed(dxgi.factory->MakeWindowAssociation(dxgi.h_wnd, DXGI_MWA_NO_ALT_ENTER)); - ComPtr swap_chain2; - if (dxgi.swap_chain->QueryInterface(__uuidof(IDXGISwapChain2), &swap_chain2) == S_OK) { - ThrowIfFailed(swap_chain2->SetMaximumFrameLatency(1)); - dxgi.waitable_object = swap_chain2->GetFrameLatencyWaitableObject(); - WaitForSingleObject(dxgi.waitable_object, INFINITE); - } else { - ComPtr device1; - ThrowIfFailed(device->QueryInterface(IID_PPV_ARGS(&device1))); - ThrowIfFailed(device1->SetMaximumFrameLatency(1)); - } + apply_maximum_frame_latency(true); ThrowIfFailed(dxgi.swap_chain->GetDesc1(&swap_chain_desc)); dxgi.current_width = swap_chain_desc.Width; dxgi.current_height = swap_chain_desc.Height; - return dxgi.swap_chain; + dxgi.swap_chain_device = device; + dxgi.before_destroy_swap_chain_fn = std::move(before_destroy_fn); } HWND gfx_dxgi_get_h_wnd(void) { return dxgi.h_wnd; } +IDXGISwapChain1* gfx_dxgi_get_swap_chain() { + return dxgi.swap_chain.Get(); +} + void ThrowIfFailed(HRESULT res) { if (FAILED(res)) { fprintf(stderr, "Error: 0x%08X\n", res); @@ -668,7 +719,9 @@ extern "C" struct GfxWindowManagerAPI gfx_dxgi_api = { gfx_dxgi_swap_buffers_begin, gfx_dxgi_swap_buffers_end, gfx_dxgi_get_time, - gfx_dxgi_set_frame_divisor, + gfx_dxgi_set_target_fps, + gfx_dxgi_set_maximum_frame_latency, + gfx_dxgi_get_detected_hz, }; #endif diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.h b/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.h index d590daa66..134ebed64 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.h +++ b/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.h @@ -4,9 +4,15 @@ #include "gfx_rendering_api.h" #ifdef DECLARE_GFX_DXGI_FUNCTIONS + +#include + +#include + void gfx_dxgi_create_factory_and_device(bool debug, int d3d_version, bool (*create_device_fn)(IDXGIAdapter1 *adapter, bool test_only)); -Microsoft::WRL::ComPtr gfx_dxgi_create_swap_chain(IUnknown *device); +void gfx_dxgi_create_swap_chain(IUnknown *device, std::function&& before_destroy_fn); HWND gfx_dxgi_get_h_wnd(void); +IDXGISwapChain1* gfx_dxgi_get_swap_chain(); void ThrowIfFailed(HRESULT res); void ThrowIfFailed(HRESULT res, HWND h_wnd, const char *message); #endif diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp index 98bab7f78..452b511d8 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp +++ b/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp @@ -2789,6 +2789,9 @@ void gfx_run(Gfx *commands, const std::unordered_map& mtx_replaceme gfx_rapi->start_frame(); gfx_rapi->start_draw_to_framebuffer(game_renders_to_framebuffer ? game_framebuffer : 0, (float)gfx_current_dimensions.height / SCREEN_HEIGHT); gfx_rapi->clear_framebuffer(); + rdp.viewport_or_scissor_changed = true; + rendering_state.viewport = {}; + rendering_state.scissor = {}; gfx_run_dl(commands); gfx_flush(); SohUtils::saveEnvironmentVar("framebuffer", string()); @@ -2825,8 +2828,16 @@ void gfx_end_frame(void) { } } -void gfx_set_framedivisor(int divisor) { - gfx_wapi->set_frame_divisor(divisor); +void gfx_set_target_fps(int fps) { + gfx_wapi->set_target_fps(fps); +} + +void gfx_set_maximum_frame_latency(int latency) { + gfx_wapi->set_maximum_frame_latency(latency); +} + +float gfx_get_detected_hz(void) { + return gfx_wapi->get_detected_hz(); } int gfx_create_framebuffer(uint32_t width, uint32_t height) { diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_pc.h b/libultraship/libultraship/Lib/Fast3D/gfx_pc.h index 2ecb68898..65a6b57ba 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_pc.h +++ b/libultraship/libultraship/Lib/Fast3D/gfx_pc.h @@ -67,7 +67,9 @@ struct GfxRenderingAPI* gfx_get_current_rendering_api(void); void gfx_start_frame(void); void gfx_run(Gfx* commands, const std::unordered_map& mtx_replacements); void gfx_end_frame(void); -void gfx_set_framedivisor(int); +void gfx_set_target_fps(int); +void gfx_set_maximum_frame_latency(int latency); +float gfx_get_detected_hz(void); void gfx_texture_cache_clear(); extern "C" int gfx_create_framebuffer(uint32_t width, uint32_t height); void gfx_get_pixel_depth_prepare(float x, float y); diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp index e80097c81..94c3ef5ca 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp +++ b/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp @@ -123,11 +123,10 @@ static uint64_t previous_time; static HANDLE timer; #endif -static int frameDivisor = 1; +static int target_fps = 60; -#define FRAME_INTERVAL_US_NUMERATOR_ 50000 -#define FRAME_INTERVAL_US_DENOMINATOR 3 -#define FRAME_INTERVAL_US_NUMERATOR (FRAME_INTERVAL_US_NUMERATOR_ * frameDivisor) +#define FRAME_INTERVAL_US_NUMERATOR 1000000 +#define FRAME_INTERVAL_US_DENOMINATOR (target_fps) static void gfx_sdl_init(const char *game_name, bool start_in_fullscreen) { SDL_Init(SDL_INIT_VIDEO); @@ -266,15 +265,16 @@ static uint64_t qpc_to_100ns(uint64_t qpc) { static inline void sync_framerate_with_timer(void) { uint64_t t; - t = SDL_GetPerformanceCounter(); + t = qpc_to_100ns(SDL_GetPerformanceCounter()); - const int64_t next = qpc_to_100ns(previous_time) + 10 * FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR; - const int64_t left = next - qpc_to_100ns(t); + const int64_t next = previous_time + 10 * FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR; + const int64_t left = next - t; if (left > 0) { #ifdef __linux__ const timespec spec = { 0, left * 100 }; nanosleep(&spec, nullptr); #else + // The accuracy of this timer seems to usually be within +- 1.0 ms LARGE_INTEGER li; li.QuadPart = -left; SetWaitableTimer(timer, &li, 0, nullptr, nullptr, false); @@ -282,7 +282,13 @@ static inline void sync_framerate_with_timer(void) { #endif } - t = SDL_GetPerformanceCounter(); + t = qpc_to_100ns(SDL_GetPerformanceCounter()); + if (left > 0 && t - next < 10000) { + // In case it takes some time for the application to wake up after sleep, + // or inaccurate timer, + // don't let that slow down the framerate. + t = next; + } previous_time = t; } @@ -299,9 +305,16 @@ static double gfx_sdl_get_time(void) { return 0.0; } -static void gfx_sdl_set_framedivisor(int divisor) -{ - frameDivisor = divisor; +static void gfx_sdl_set_target_fps(int fps) { + target_fps = fps; +} + +static void gfx_sdl_set_maximum_frame_latency(int latency) { + // Not supported by SDL :( +} + +static float gfx_sdl_get_detected_hz(void) { + return 0; } struct GfxWindowManagerAPI gfx_sdl = { @@ -317,7 +330,9 @@ struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_swap_buffers_begin, gfx_sdl_swap_buffers_end, gfx_sdl_get_time, - gfx_sdl_set_framedivisor + gfx_sdl_set_target_fps, + gfx_sdl_set_maximum_frame_latency, + gfx_sdl_get_detected_hz }; #endif diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_window_manager_api.h b/libultraship/libultraship/Lib/Fast3D/gfx_window_manager_api.h index 5d7442390..bd45ccdf4 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_window_manager_api.h +++ b/libultraship/libultraship/Lib/Fast3D/gfx_window_manager_api.h @@ -17,7 +17,9 @@ struct GfxWindowManagerAPI { void (*swap_buffers_begin)(void); void (*swap_buffers_end)(void); double (*get_time)(void); // For debug - void (*set_frame_divisor)(int); + void (*set_target_fps)(int fps); + void (*set_maximum_frame_latency)(int latency); + float (*get_detected_hz)(void); }; #endif diff --git a/libultraship/libultraship/SohImGuiImpl.cpp b/libultraship/libultraship/SohImGuiImpl.cpp index 2a897437f..35643838f 100644 --- a/libultraship/libultraship/SohImGuiImpl.cpp +++ b/libultraship/libultraship/SohImGuiImpl.cpp @@ -680,6 +680,34 @@ namespace SohImGui { Tooltip("Activates anti-aliasing when above 1, up to 8x for 8 samples for every pixel"); gfx_msaa_level = CVar_GetS32("gMSAAValue", 1); + if (impl.backend == Backend::DX11) + { + const char* cvar = "gExtraLatencyThreshold"; + int val = CVar_GetS32(cvar, 80); + val = MAX(MIN(val, 250), 0); + int fps = val; + + if (fps == 0) + { + ImGui::Text("Jitter fix: Off"); + } + else + { + ImGui::Text("Jitter fix: >= %d FPS", fps); + } + + if (ImGui::SliderInt("##ExtraLatencyThreshold", &val, 0, 250, "", ImGuiSliderFlags_AlwaysClamp)) + { + CVar_SetS32(cvar, val); + needs_save = true; + } + + Tooltip("When Interpolation FPS setting is at least this threshold,\n" + "add one frame of input lag (e.g. 16.6 ms for 60 FPS) in order to avoid jitter.\n" + "This setting allows the CPU to work on one frame while GPU works on the previous frame.\n" + "This setting should be used when your computer is too slow to do CPU + GPU work in time."); + } + EXPERIMENTAL(); ImGui::Text("Texture Filter (Needs reload)"); GfxRenderingAPI* gapi = gfx_get_current_rendering_api(); @@ -764,7 +792,46 @@ namespace SohImGui { EXPERIMENTAL(); - EnhancementCheckbox("60FPS Interpolation", "g60FPS"); + const char* fps_cvar = "gInterpolationFPS"; + { + int val = CVar_GetS32(fps_cvar, 20); + val = MAX(MIN(val, 250), 20); + int fps = val; + + if (fps == 20) + { + ImGui::Text("Frame interpolation: Off"); + } + else + { + ImGui::Text("Frame interpolation: %d FPS", fps); + } + + if (ImGui::SliderInt("##FPSInterpolation", &val, 20, 250, "", ImGuiSliderFlags_AlwaysClamp)) + { + CVar_SetS32(fps_cvar, val); + needs_save = true; + } + + Tooltip("Interpolate extra frames to get smoother graphics.\n" + "Set to match your monitor's refresh rate, or a divisor of it.\n" + "A higher target FPS than your monitor's refresh rate will just waste resources,\n" + "and might give a worse result.\n" + "For consistent input lag, set this value and your monitor's refresh rate to a multiple of 20.\n" + "Ctrl+Click for keyboard input."); + } + if (impl.backend == Backend::DX11) + { + if (ImGui::Button("Match Refresh Rate")) + { + int hz = roundf(gfx_get_detected_hz()); + if (hz >= 20 && hz <= 250) + { + CVar_SetS32(fps_cvar, hz); + needs_save = true; + } + } + } EnhancementCheckbox("Disable LOD", "gDisableLOD"); Tooltip("Turns off the level of detail setting, making models always use their higher poly variants"); diff --git a/libultraship/libultraship/Window.cpp b/libultraship/libultraship/Window.cpp index 5c2cf3cae..8319332ad 100644 --- a/libultraship/libultraship/Window.cpp +++ b/libultraship/libultraship/Window.cpp @@ -272,13 +272,14 @@ namespace Ship { gfx_run(Commands, m); gfx_end_frame(); } - gfx_run(Commands, {}); - gfx_end_frame(); } - void Window::SetFrameDivisor(int divisor) { - gfx_set_framedivisor(divisor); - //gfx_set_framedivisor(0); + void Window::SetTargetFps(int fps) { + gfx_set_target_fps(fps); + } + + void Window::SetMaximumFrameLatency(int latency) { + gfx_set_maximum_frame_latency(latency); } void Window::GetPixelDepthPrepare(float x, float y) { diff --git a/libultraship/libultraship/Window.h b/libultraship/libultraship/Window.h index 04886c53e..a3087bf0c 100644 --- a/libultraship/libultraship/Window.h +++ b/libultraship/libultraship/Window.h @@ -20,7 +20,8 @@ namespace Ship { void Init(); void StartFrame(); void RunCommands(Gfx* Commands, const std::vector>& mtx_replacements); - void SetFrameDivisor(int divisor); + void SetTargetFps(int fps); + void SetMaximumFrameLatency(int latency); void GetPixelDepthPrepare(float x, float y); uint16_t GetPixelDepth(float x, float y); void ToggleFullscreen(); diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index e8e1b31c1..9401d514a 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -175,8 +175,6 @@ extern "C" void Graph_StartFrame() { // C->C++ Bridge extern "C" void Graph_ProcessGfxCommands(Gfx* commands) { - OTRGlobals::Instance->context->GetWindow()->SetFrameDivisor(CVar_GetS32("g60FPS", 0) == 0 ? R_UPDATE_RATE : 1); - if (!audio.initialized) { audio.initialized = true; std::thread([]() { @@ -226,15 +224,45 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) { audio.cv_to_thread.notify_one(); std::vector> mtx_replacements; - if (CVar_GetS32("g60FPS", 0) != 0) { - int to = R_UPDATE_RATE; - for (int i = 1; i < to; i++) { - mtx_replacements.push_back(FrameInterpolation_Interpolate(i / (float)to)); + int target_fps = CVar_GetS32("gInterpolationFPS", 20); + static int last_fps; + static int last_update_rate; + static int time; + int fps = target_fps; + int original_fps = 60 / R_UPDATE_RATE; + + if (target_fps == 20 || original_fps > target_fps) { + fps = original_fps; + } + + if (last_fps != fps || last_update_rate != R_UPDATE_RATE) { + time = 0; + } + + // time_base = fps * original_fps (one second) + int next_original_frame = fps; + + while (time + original_fps <= next_original_frame) { + time += original_fps; + if (time != next_original_frame) { + mtx_replacements.push_back(FrameInterpolation_Interpolate((float)time / next_original_frame)); + } else { + mtx_replacements.emplace_back(); } } + time -= fps; + + OTRGlobals::Instance->context->GetWindow()->SetTargetFps(fps); + + int threshold = CVar_GetS32("gExtraLatencyThreshold", 80); + OTRGlobals::Instance->context->GetWindow()->SetMaximumFrameLatency(threshold > 0 && target_fps >= threshold ? 2 : 1); + OTRGlobals::Instance->context->GetWindow()->RunCommands(commands, mtx_replacements); + last_fps = fps; + last_update_rate = R_UPDATE_RATE; + { std::unique_lock Lock(audio.mutex); while (audio.processing) { diff --git a/soh/soh/frame_interpolation.cpp b/soh/soh/frame_interpolation.cpp index bd85d41ce..44c255db1 100644 --- a/soh/soh/frame_interpolation.cpp +++ b/soh/soh/frame_interpolation.cpp @@ -451,7 +451,7 @@ void FrameInterpolation_StartRecord(void) { current_recording = {}; current_path.clear(); current_path.push_back(¤t_recording.root_path); - if (CVar_GetS32("g60FPS", 0) != 0) { + if (CVar_GetS32("gInterpolationFPS", 20) != 20) { is_recording = true; } } diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index ba5d4dcd6..bdb2fe78c 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -2290,8 +2290,8 @@ void Actor_DrawFaroresWindPointer(GlobalContext* globalCtx) { ((void)0, gSaveContext.respawn[RESPAWN_MODE_TOP].pos.y) + yOffset, ((void)0, gSaveContext.respawn[RESPAWN_MODE_TOP].pos.z), 255, 255, 255, lightRadius); - CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 5474); } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 5474); } void func_80030488(GlobalContext* globalCtx) { diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index 570eedd1f..03a93e949 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -5,6 +5,8 @@ #include "textures/title_static/title_static.h" #include "textures/parameter_static/parameter_static.h" +#include "soh/frame_interpolation.h" + static s16 sUnused = 106; static s16 sScreenFillAlpha = 255; @@ -1136,6 +1138,8 @@ void FileChoose_ConfigModeDraw(GameState* thisx) { FileChoose_SetWindowVtx(&this->state); FileChoose_SetWindowContentVtx(&this->state); + FrameInterpolation_RecordOpenChild(this, this->configMode); + if ((this->configMode != CM_NAME_ENTRY) && (this->configMode != CM_START_NAME_ENTRY)) { gDPPipeSync(POLY_OPA_DISP++); gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); @@ -1227,6 +1231,8 @@ void FileChoose_ConfigModeDraw(GameState* thisx) { gDPPipeSync(POLY_OPA_DISP++); FileChoose_SetView(this, 0.0f, 0.0f, 64.0f); + FrameInterpolation_RecordCloseChild(); + CLOSE_DISPS(this->state.gfxCtx, "../z_file_choose.c", 2352); } @@ -1669,7 +1675,9 @@ void FileChoose_Main(GameState* thisx) { FileChoose_PulsateCursor(&this->state); gFileSelectUpdateFuncs[this->menuMode](&this->state); + FrameInterpolation_StartRecord(); gFileSelectDrawFuncs[this->menuMode](&this->state); + FrameInterpolation_StopRecord(); // do not draw controls text in the options menu if ((this->configMode <= CM_NAME_ENTRY_TO_MAIN) || (this->configMode >= CM_UNUSED_DELAY)) { From 79133981104e2dedcdce5b941353babfaa55bd45 Mon Sep 17 00:00:00 2001 From: Baoulettes Date: Sun, 29 May 2022 18:57:09 +0200 Subject: [PATCH 3/8] EnhancementColor3 tweak SohImGui Clean (#400) * HUD Logic fix and cleaning * array revert it was just fo rme to read it better * Fix tunic logic seem like my cleaning was not good * I had declared default in CPP It resulted to overwrite what user would make oof. Also a ! was missing. * There, Default here is better. * magic bar and used bar what switched while updating EnhancementColor * hud mod update * Update SohImGuiImpl.h * should fix build --- libultraship/libultraship/SohImGuiImpl.cpp | 393 +++++++++++------- libultraship/libultraship/SohImGuiImpl.h | 13 +- soh/src/code/z_lifemeter.c | 49 ++- soh/src/code/z_player_lib.c | 46 +- .../ovl_kaleido_scope/z_kaleido_scope_PAL.c | 27 ++ 5 files changed, 334 insertions(+), 194 deletions(-) diff --git a/libultraship/libultraship/SohImGuiImpl.cpp b/libultraship/libultraship/SohImGuiImpl.cpp index 35643838f..c91f31750 100644 --- a/libultraship/libultraship/SohImGuiImpl.cpp +++ b/libultraship/libultraship/SohImGuiImpl.cpp @@ -66,29 +66,38 @@ namespace SohImGui { std::vector CustomTexts; int SelectedLanguage = CVar_GetS32("gLanguages", 0); //Default Language to 0=English 1=German 2=French int SelectedHUD = CVar_GetS32("gHudColors", 1); //Default colors to Gamecube. - float hearts_colors[3] = {0,0,0}; - float hearts_dd_colors[3] = {0,0,0}; - float a_btn_colors[3] = {0,0,0}; - float b_btn_colors[3] = {0,0,0}; - float c_btn_colors[3] = {0,0,0}; - float start_btn_colors[3] = {0,0,0}; - float magic_border_colors[3] = {0,0,0}; - float magic_remaining_colors[3] = {0,0,0}; - float magic_use_colors[3] = {0,0,0}; - float minimap_colors[3] = {0,0,0}; - float rupee_colors[3] = {0,0,0}; - float smolekey_colors[3] = {0,0,0}; - float kokiri_col[3] = { 0.118f, 0.41f, 0.106f }; - float goron_col[3] = { 0.392f, 0.078f, 0.0f }; - float zora_col[3] = { 0.0f, 0.235f, 0.392f }; - float navi_idle_i_col[3] = { 0.0f, 0.0f, 0.0f }; - float navi_idle_o_col[3] = { 0.0f, 0.0f, 0.0f }; - float navi_npc_i_col[3] = { 0.0f, 0.0f, 0.0f }; - float navi_npc_o_col[3] = { 0.0f, 0.0f, 0.0f }; - float navi_enemy_i_col[3] = { 0.0f, 0.0f, 0.0f }; - float navi_enemy_o_col[3] = { 0.0f, 0.0f, 0.0f }; - float navi_prop_i_col[3] = { 0.0f, 0.0f, 0.0f }; - float navi_prop_o_col[3] = { 0.0f, 0.0f, 0.0f }; + ImVec4 hearts_colors; + ImVec4 hearts_dd_colors; + ImVec4 a_btn_colors; + ImVec4 b_btn_colors; + ImVec4 c_btn_colors; + ImVec4 start_btn_colors; + ImVec4 magic_border_colors; + ImVec4 magic_remaining_colors; + ImVec4 magic_use_colors; + ImVec4 minimap_colors; + ImVec4 rupee_colors; + ImVec4 smolekey_colors; + ImVec4 kokiri_col; + ImVec4 goron_col; + ImVec4 zora_col; + ImVec4 navi_idle_i_col; + ImVec4 navi_idle_o_col; + ImVec4 navi_npc_i_col; + ImVec4 navi_npc_o_col; + ImVec4 navi_enemy_i_col; + ImVec4 navi_enemy_o_col; + ImVec4 navi_prop_i_col; + ImVec4 navi_prop_o_col; + + const char* RainbowColorCvarList[] = { + //This is the list of possible CVars that has rainbow effect. + "gTunic_Kokiri_","gTunic_Goron_","gTunic_Zora_", + "gCCHeartsPrim","gDDCCHeartsPrim", + "gCCABtnPrim","gCCBBtnPrim","gCCCBtnPrim","gCCStartBtnPrim", + "gCCMagicBorderPrim","gCCMagicPrim","gCCMagicUsePrim", + "gCCMinimapPrim","gCCRupeePrim","gCCKeysPrim" + }; const char* filters[3] = { "Three-Point", @@ -99,6 +108,15 @@ namespace SohImGui { std::map> windowCategories; std::map customWindows; + int ClampFloatToInt(float value, int min, int max){ + return fmin(fmax(value,min),max); + } + + void Tooltip(const char* text) { + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", text); + } + void ImGuiWMInit() { switch (impl.backend) { case Backend::SDL: @@ -240,82 +258,64 @@ namespace SohImGui { stbi_image_free(img_data); } - void LoadInterfaceEditor(){//This function is necessary as without it IMGui wont load the updated float array. - hearts_colors[0] = (float)CVar_GetS32("gCCHeartsPrimR", 255)/255; - hearts_colors[1] = (float)CVar_GetS32("gCCHeartsPrimG", 70)/255; - hearts_colors[2] = (float)CVar_GetS32("gCCHeartsPrimB", 50)/255; - hearts_dd_colors[0] = (float)CVar_GetS32("gDDCCHeartsPrimR", 255)/255; - hearts_dd_colors[1] = (float)CVar_GetS32("gDDCCHeartsPrimG", 255)/255; - hearts_dd_colors[2] = (float)CVar_GetS32("gDDCCHeartsPrimB", 255)/255; - a_btn_colors[0] = (float)CVar_GetS32("gCCABtnPrimR", 90)/255; - a_btn_colors[1] = (float)CVar_GetS32("gCCABtnPrimG", 90)/255; - a_btn_colors[2] = (float)CVar_GetS32("gCCABtnPrimB", 255)/255; - b_btn_colors[0] = (float)CVar_GetS32("gCCBBtnPrimR", 0)/255; - b_btn_colors[1] = (float)CVar_GetS32("gCCBBtnPrimG", 150)/255; - b_btn_colors[2] = (float)CVar_GetS32("gCCBBtnPrimB", 0)/255; - c_btn_colors[0] = (float)CVar_GetS32("gCCCBtnPrimR", 255)/255; - c_btn_colors[1] = (float)CVar_GetS32("gCCCBtnPrimG", 160)/255; - c_btn_colors[2] = (float)CVar_GetS32("gCCCBtnPrimB", 0)/255; - start_btn_colors[0] = (float)CVar_GetS32("gCCStartBtnPrimR", 120)/255; - start_btn_colors[1] = (float)CVar_GetS32("gCCStartBtnPrimG", 120)/255; - start_btn_colors[2] = (float)CVar_GetS32("gCCStartBtnPrimB", 120)/255; - magic_border_colors[0] = (float)CVar_GetS32("gCCMagicBorderPrimR", 255)/255; - magic_border_colors[1] = (float)CVar_GetS32("gCCMagicBorderPrimG", 255)/255; - magic_border_colors[2] = (float)CVar_GetS32("gCCMagicBorderPrimB", 255)/255; - magic_use_colors[0] = (float)CVar_GetS32("gCCMagicPrimR", 250)/255; - magic_use_colors[1] = (float)CVar_GetS32("gCCMagicPrimG", 250)/255; - magic_use_colors[2] = (float)CVar_GetS32("gCCMagicPrimB", 0)/255; - magic_remaining_colors[0] = (float)CVar_GetS32("gCCMagicUsePrimR", 0)/255; - magic_remaining_colors[1] = (float)CVar_GetS32("gCCMagicUsePrimG", 200)/255; - magic_remaining_colors[2] = (float)CVar_GetS32("gCCMagicUsePrimB", 0)/255; - minimap_colors[0] = (float)CVar_GetS32("gCCMinimapPrimR", 0)/255; - minimap_colors[1] = (float)CVar_GetS32("gCCMinimapPrimG", 255)/255; - minimap_colors[2] = (float)CVar_GetS32("gCCMinimapPrimB", 255)/255; - rupee_colors[0] = (float)CVar_GetS32("gCCRupeePrimR", 200)/255; - rupee_colors[1] = (float)CVar_GetS32("gCCRupeePrimG", 255)/255; - rupee_colors[2] = (float)CVar_GetS32("gCCRupeePrimB", 100)/255; - smolekey_colors[0] = (float)CVar_GetS32("gCCKeysPrimR", 200)/255; - smolekey_colors[1] = (float)CVar_GetS32("gCCKeysPrimG", 230)/255; - smolekey_colors[2] = (float)CVar_GetS32("gCCKeysPrimB", 255)/255; - kokiri_col[0] = (float)CVar_GetS32("gTunic_Kokiri_R", 30)/255; - kokiri_col[1] = (float)CVar_GetS32("gTunic_Kokiri_G", 105)/255; - kokiri_col[2] = (float)CVar_GetS32("gTunic_Kokiri_B", 27)/255; - goron_col[0] = (float)CVar_GetS32("gTunic_Goron_R", 100)/255; - goron_col[1] = (float)CVar_GetS32("gTunic_Goron_G", 20)/255; - goron_col[2] = (float)CVar_GetS32("gTunic_Goron_B", 0)/255; - zora_col[0] = (float)CVar_GetS32("gTunic_Zora_R", 0)/255; - zora_col[1] = (float)CVar_GetS32("gTunic_Zora_G", 60)/255; - zora_col[2] = (float)CVar_GetS32("gTunic_Zora_B", 100)/255; - navi_idle_i_col[0] = (float)CVar_GetS32("gNavi_Idle_Inner_R", 255)/255; - navi_idle_i_col[1] = (float)CVar_GetS32("gNavi_Idle_Inner_G", 255)/255; - navi_idle_i_col[2] = (float)CVar_GetS32("gNavi_Idle_Inner_B", 255)/255; - navi_idle_o_col[0] = (float)CVar_GetS32("gNavi_Idle_Outer_R", 115)/255; - navi_idle_o_col[1] = (float)CVar_GetS32("gNavi_Idle_Outer_G", 230)/255; - navi_idle_o_col[2] = (float)CVar_GetS32("gNavi_Idle_Outer_B", 255)/255; - navi_npc_i_col[0] = (float)CVar_GetS32("gNavi_NPC_Inner_R", 100)/255; - navi_npc_i_col[1] = (float)CVar_GetS32("gNavi_NPC_Inner_G", 100)/255; - navi_npc_i_col[2] = (float)CVar_GetS32("gNavi_NPC_Inner_B", 255)/255; - navi_npc_o_col[0] = (float)CVar_GetS32("gNavi_NPC_Outer_R", 90)/255; - navi_npc_o_col[1] = (float)CVar_GetS32("gNavi_NPC_Outer_G", 90)/255; - navi_npc_o_col[2] = (float)CVar_GetS32("gNavi_NPC_Outer_B", 255)/255; - navi_enemy_i_col[0] = (float)CVar_GetS32("gNavi_Enemy_Inner_R", 255)/255; - navi_enemy_i_col[1] = (float)CVar_GetS32("gNavi_Enemy_Inner_G", 255)/255; - navi_enemy_i_col[2] = (float)CVar_GetS32("gNavi_Enemy_Inner_B", 0)/255; - navi_enemy_o_col[0] = (float)CVar_GetS32("gNavi_Enemy_Outer_R", 220)/255; - navi_enemy_o_col[1] = (float)CVar_GetS32("gNavi_Enemy_Outer_G", 220)/255; - navi_enemy_o_col[2] = (float)CVar_GetS32("gNavi_Enemy_Outer_B", 0)/255; - navi_prop_i_col[0] = (float)CVar_GetS32("gNavi_Prop_Inner_R", 0)/255; - navi_prop_i_col[1] = (float)CVar_GetS32("gNavi_Prop_Inner_G", 255)/255; - navi_prop_i_col[2] = (float)CVar_GetS32("gNavi_Prop_Inner_B", 0)/255; - navi_prop_o_col[0] = (float)CVar_GetS32("gNavi_Prop_Outer_R", 0)/255; - navi_prop_o_col[1] = (float)CVar_GetS32("gNavi_Prop_Outer_G", 220)/255; - navi_prop_o_col[2] = (float)CVar_GetS32("gNavi_Prop_Outer_B", 0)/255; - if (CVar_GetS32("gHudColors", 1) ==0) { - SelectedHUD = 0; - } else if (CVar_GetS32("gHudColors", 1) == 1) { - SelectedHUD = 1; - } else if (CVar_GetS32("gHudColors", 1) == 2) { - SelectedHUD = 2; + void LoadRainbowColor() { + for (uint16_t s=0; s <= sizeof(RainbowColorCvarList); s++) { + std::string cvarName = RainbowColorCvarList[s]; + std::string Cvar_Red = cvarName; + Cvar_Red += "R"; + std::string Cvar_Green = cvarName; + Cvar_Green += "G"; + std::string Cvar_Blue = cvarName; + Cvar_Blue += "B"; + std::string Cvar_RBM = cvarName; + Cvar_RBM += "RBM"; + std::string RBM_HUE = cvarName; + RBM_HUE+="Hue"; + f32 Canon = 10.f*s; + ImVec4 NewColor; + const f32 deltaTime = 1.0f / ImGui::GetIO().Framerate; + f32 hue = CVar_GetFloat(RBM_HUE.c_str(), 0.0f); + f32 newHue = hue + CVar_GetS32("gColorRainbowSpeed", 1) * 36.0f * deltaTime; + if (newHue >= 360) + newHue = 0; + CVar_SetFloat(RBM_HUE.c_str(), newHue); + f32 current_hue = CVar_GetFloat(RBM_HUE.c_str(), 0); + u8 i = current_hue / 60 + 1; + u8 a = (-current_hue / 60.0f + i) * 255; + u8 b = (current_hue / 60.0f + (1 - i)) * 255; + + switch (i) { + case 1: NewColor.x = 255; NewColor.y = b; NewColor.z = 0; break; + case 2: NewColor.x = a; NewColor.y = 255; NewColor.z = 0; break; + case 3: NewColor.x = 0; NewColor.y = 255; NewColor.z = b; break; + case 4: NewColor.x = 0; NewColor.y = a; NewColor.z = 255; break; + case 5: NewColor.x = b; NewColor.y = 0; NewColor.z = 255; break; + case 6: NewColor.x = 255; NewColor.y = 0; NewColor.z = a; break; + } + + if(CVar_GetS32(Cvar_RBM.c_str(), 0) != 0) { + CVar_SetS32(Cvar_Red.c_str(), ClampFloatToInt(NewColor.x,0,255)); + CVar_SetS32(Cvar_Green.c_str(), ClampFloatToInt(NewColor.y,0,255)); + CVar_SetS32(Cvar_Blue.c_str(), ClampFloatToInt(NewColor.z,0,255)); + } + } + } + + void LoadPickersColors(ImVec4& ColorArray, const char* cvarname, const ImVec4& default_colors, bool has_alpha) { + std::string Cvar_Red = cvarname; + Cvar_Red += "R"; + std::string Cvar_Green = cvarname; + Cvar_Green += "G"; + std::string Cvar_Blue = cvarname; + Cvar_Blue += "B"; + std::string Cvar_Alpha = cvarname; + Cvar_Alpha += "A"; + + ColorArray.x = (float)CVar_GetS32(Cvar_Red.c_str(), default_colors.x)/255; + ColorArray.y = (float)CVar_GetS32(Cvar_Green.c_str(), default_colors.y)/255; + ColorArray.z = (float)CVar_GetS32(Cvar_Blue.c_str(), default_colors.z)/255; + if (has_alpha) { + ColorArray.w = (float)CVar_GetS32(Cvar_Alpha.c_str(), default_colors.w)/255; } } @@ -530,31 +530,123 @@ namespace SohImGui { } } - int ClampFloatToInt(float value, int min, int max) { - return fmin(fmax(value, min), max); + void RandomizeColor(const char* cvarName, ImVec4* colors) { + std::string Cvar_Red = cvarName; + Cvar_Red += "R"; + std::string Cvar_Green = cvarName; + Cvar_Green += "G"; + std::string Cvar_Blue = cvarName; + Cvar_Blue += "B"; + std::string Cvar_RBM = cvarName; + Cvar_RBM += "RBM"; + std::string MakeInvisible = "##"; + MakeInvisible += cvarName; + MakeInvisible += "Random"; + std::string FullName = "Random"; + FullName+=MakeInvisible; + if (ImGui::Button(FullName.c_str())) { + s16 RND_R = rand() % (255 - 0); + s16 RND_G = rand() % (255 - 0); + s16 RND_B = rand() % (255 - 0); + colors->x = (float)RND_R/255; + colors->y = (float)RND_G/255; + colors->z = (float)RND_B/255; + CVar_SetS32(Cvar_Red.c_str(), ClampFloatToInt(colors->x*255,0,255)); + CVar_SetS32(Cvar_Green.c_str(), ClampFloatToInt(colors->y*255,0,255)); + CVar_SetS32(Cvar_Blue.c_str(), ClampFloatToInt(colors->z*255,0,255)); + CVar_SetS32(Cvar_RBM.c_str(), 0); //On click disable rainbow mode. + needs_save = true; + } + Tooltip("Clicking this button will make a random color for the colors at it's right.\nPrevious color will be overwrite and will not be recoverable"); } - void EnhancementColor3(const char* text, const char* cvarName, float ColorRGB[3], bool TitleSameLine) { - //Simplified. - std::string cvarNameString(cvarName); + void RainbowColor(const char* cvarName, ImVec4* colors) { + std::string Cvar_RBM = cvarName; + Cvar_RBM += "RBM"; + std::string MakeInvisible = "Rainbow"; + MakeInvisible += "##"; + MakeInvisible += cvarName; + MakeInvisible += "Rainbow"; + + EnhancementCheckbox(MakeInvisible.c_str(), Cvar_RBM.c_str()); + Tooltip("Clicking this button will make color cycling\nPrevious color will be overwrite and will not be recoverable"); + } + + void ResetColor(const char* cvarName, ImVec4* colors, ImVec4 defaultcolors, bool has_alpha) { + std::string Cvar_Red = cvarName; + Cvar_Red += "R"; + std::string Cvar_Green = cvarName; + Cvar_Green += "G"; + std::string Cvar_Blue = cvarName; + Cvar_Blue += "B"; + std::string Cvar_Alpha = cvarName; + Cvar_Alpha += "A"; + std::string Cvar_RBM = cvarName; + Cvar_RBM += "RBM"; + std::string MakeInvisible = "Reset"; + MakeInvisible += "##"; + MakeInvisible += cvarName; + MakeInvisible += "Reset"; + if (ImGui::Button(MakeInvisible.c_str())) { + colors->x = defaultcolors.x/255; + colors->y = defaultcolors.y/255; + colors->z = defaultcolors.z/255; + if (has_alpha) { colors->w = defaultcolors.w/255;}; + CVar_SetS32(Cvar_Red.c_str(), ClampFloatToInt(colors->x*255,0,255)); + CVar_SetS32(Cvar_Green.c_str(), ClampFloatToInt(colors->y*255,0,255)); + CVar_SetS32(Cvar_Blue.c_str(), ClampFloatToInt(colors->z*255,0,255)); + if (has_alpha) { CVar_SetS32(Cvar_Alpha.c_str(), ClampFloatToInt(colors->w*255,0,255)); }; + CVar_SetS32(Cvar_RBM.c_str(), 0); //On click disable rainbow mode. + needs_save = true; + } + Tooltip("Clicking this button will to the game original color (GameCube version)\nPrevious color will be overwrite and will not be recoverable"); + } + + void EnhancementColor(const char* text, const char* cvarName, ImVec4 ColorRGBA, ImVec4 default_colors, bool allow_rainbow, bool has_alpha, bool TitleSameLine) { + std::string Cvar_Red = cvarName; + Cvar_Red += "R"; + std::string Cvar_Green = cvarName; + Cvar_Green += "G"; + std::string Cvar_Blue = cvarName; + Cvar_Blue += "B"; + std::string Cvar_Alpha = cvarName; + Cvar_Alpha += "A"; + std::string Cvar_RBM = cvarName; + Cvar_RBM += "RBM"; + + LoadPickersColors(ColorRGBA, cvarName, default_colors, has_alpha); ImGuiColorEditFlags flags = ImGuiColorEditFlags_None; - if (!TitleSameLine) { + if (!TitleSameLine){ ImGui::Text("%s", text); flags = ImGuiColorEditFlags_NoLabel; } - if (ImGui::ColorEdit3(text, ColorRGB, flags)) { - CVar_SetS32((cvarNameString + "R").c_str(), ClampFloatToInt(ColorRGB[0] * 255, 0, 255)); - CVar_SetS32((cvarNameString + "G").c_str(), ClampFloatToInt(ColorRGB[1] * 255, 0, 255)); - CVar_SetS32((cvarNameString + "B").c_str(), ClampFloatToInt(ColorRGB[2] * 255, 0, 255)); - needs_save = true; + if (!has_alpha) { + if (ImGui::ColorEdit3(text, (float *)&ColorRGBA, flags)) { + CVar_SetS32(Cvar_Red.c_str(), ClampFloatToInt(ColorRGBA.x*255,0,255)); + CVar_SetS32(Cvar_Green.c_str(), ClampFloatToInt(ColorRGBA.y*255,0,255)); + CVar_SetS32(Cvar_Blue.c_str(), ClampFloatToInt(ColorRGBA.z*255,0,255)); + needs_save = true; + } + } else { + if (ImGui::ColorEdit4(text, (float *)&ColorRGBA, flags)) { + CVar_SetS32(Cvar_Red.c_str(), ClampFloatToInt(ColorRGBA.x*255,0,255)); + CVar_SetS32(Cvar_Green.c_str(), ClampFloatToInt(ColorRGBA.y*255,0,255)); + CVar_SetS32(Cvar_Blue.c_str(), ClampFloatToInt(ColorRGBA.z*255,0,255)); + CVar_SetS32(Cvar_Alpha.c_str(), ClampFloatToInt(ColorRGBA.w*255,0,255)); + needs_save = true; + } + + } + ImGui::SameLine(); + ResetColor(cvarName, &ColorRGBA, default_colors, has_alpha); + ImGui::SameLine(); + RandomizeColor(cvarName, &ColorRGBA); + if (allow_rainbow) { + //Not all draw support rainbow, like Navi. + ImGui::SameLine(); + RainbowColor(cvarName, &ColorRGBA); } - } - - - void Tooltip(const char* text) { - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", text); } void DrawMainMenuAndCalculateGameSize(void) { @@ -562,7 +654,6 @@ namespace SohImGui { ImGuiBackendNewFrame(); ImGuiWMNewFrame(); ImGui::NewFrame(); - LoadInterfaceEditor(); const std::shared_ptr wnd = GlobalCtx2::GetInstance()->GetWindow(); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoBackground | @@ -938,36 +1029,36 @@ namespace SohImGui { if (ImGui::BeginTabBar("Cosmetics Editor", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { if (ImGui::BeginTabItem("Navi")) { EnhancementCheckbox("Custom colors for Navi", "gUseNaviCol"); - Tooltip("Enable/Disable custom Navi's colors. \nIf disabled you will have original colors for Navi.\nColors are refreshed when Navi goes back in your pockets"); - EnhancementColor3("Navi Idle Inner", "gNavi_Idle_Inner_", navi_idle_i_col, false); + Tooltip("Enable/Disable custom Navi's colors. \nIf disabled you will have original colors for Navi.\nColors are refreshed when Navi goes back in your pockets."); + EnhancementColor("Navi Idle Inner", "gNavi_Idle_Inner_", navi_idle_i_col, ImVec4(255,255,255,255), false); Tooltip("Inner color for Navi (idle flying around)"); - EnhancementColor3("Navi Idle Outer", "gNavi_Idle_Outer_", navi_idle_o_col, false); + EnhancementColor("Navi Idle Outer", "gNavi_Idle_Outer_", navi_idle_o_col, ImVec4(115,230,255,255), false); Tooltip("Outer color for Navi (idle flying around)"); ImGui::Separator(); - EnhancementColor3("Navi NPC Inner", "gNavi_NPC_Inner_", navi_npc_i_col, false); + EnhancementColor("Navi NPC Inner", "gNavi_NPC_Inner_", navi_npc_i_col, ImVec4(100,100,255,255), false); Tooltip("Inner color for Navi (when Navi fly around NPCs)"); - EnhancementColor3("Navi NPC Outer", "gNavi_NPC_Outer_", navi_npc_o_col, false); + EnhancementColor("Navi NPC Outer", "gNavi_NPC_Outer_", navi_npc_o_col, ImVec4(90,90,255,255), false); Tooltip("Outer color for Navi (when Navi fly around NPCs)"); ImGui::Separator(); - EnhancementColor3("Navi Enemy Inner", "gNavi_Enemy_Inner_", navi_enemy_i_col, false); + EnhancementColor("Navi Enemy Inner", "gNavi_Enemy_Inner_", navi_enemy_i_col, ImVec4(255,255,0,255), false); Tooltip("Inner color for Navi (when Navi fly around Enemies or Bosses)"); - EnhancementColor3("Navi Enemy Outer", "gNavi_Enemy_Outer_", navi_enemy_o_col, false); + EnhancementColor("Navi Enemy Outer", "gNavi_Enemy_Outer_", navi_enemy_o_col, ImVec4(220,220,0,255), false); Tooltip("Outer color for Navi (when Navi fly around Enemies or Bosses)"); ImGui::Separator(); - EnhancementColor3("Navi Prop Inner", "gNavi_Prop_Inner_", navi_prop_i_col, false); + EnhancementColor("Navi Prop Inner", "gNavi_Prop_Inner_", navi_prop_i_col, ImVec4(0,255,0,255), false); Tooltip("Inner color for Navi (when Navi fly around props (signs etc))"); - EnhancementColor3("Navi Prop Outer", "gNavi_Prop_Outer_", navi_prop_o_col, false); + EnhancementColor("Navi Prop Outer", "gNavi_Prop_Outer_", navi_prop_o_col, ImVec4(0,220,0,255), false); Tooltip("Outer color for Navi (when Navi fly around props (signs etc))"); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Tunics")) { EnhancementCheckbox("Custom colors on tunics", "gUseTunicsCol"); - Tooltip("Enable/Disable custom Link's tunics colors. \nIf disabled you will have original colors for Link's tunics"); - EnhancementColor3("Kokiri Tunic", "gTunic_Kokiri_", kokiri_col, false); + Tooltip("Enable/Disable custom Link's tunics colors. \nIf disabled you will have original colors for Link's tunics."); + EnhancementColor("Kokiri Tunic", "gTunic_Kokiri_", kokiri_col, ImVec4(30,105,27,255)); ImGui::Separator(); - EnhancementColor3("Goron Tunic", "gTunic_Goron_", goron_col, false); + EnhancementColor("Goron Tunic", "gTunic_Goron_", goron_col, ImVec4(100,20,0,255)); ImGui::Separator(); - EnhancementColor3("Zora Tunic", "gTunic_Zora_", zora_col, false); + EnhancementColor("Zora Tunic", "gTunic_Zora_", zora_col, ImVec4(0,60,100,255)); ImGui::EndTabItem(); } ImGui::EndTabBar(); @@ -983,39 +1074,39 @@ namespace SohImGui { ImGui::Begin("Interface Editor", nullptr, ImGuiWindowFlags_NoFocusOnAppearing); if (ImGui::BeginTabBar("Interface Editor", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { if (ImGui::BeginTabItem("Hearts")) { - EnhancementColor3("Hearts inner", "gCCHeartsPrim", hearts_colors, false); + EnhancementColor("Hearts inner", "gCCHeartsPrim", hearts_colors, ImVec4(255,70,50,255)); Tooltip("Hearts inner color (red in original)\nAffect both Normal Hearts and the ones in Double Defense"); - EnhancementColor3("Hearts double def", "gDDCCHeartsPrim", hearts_dd_colors, false); - Tooltip("Hearts outline color (white in original)\nAffect Double Defense outline only"); + EnhancementColor("Hearts double def", "gDDCCHeartsPrim", hearts_dd_colors, ImVec4(255,255,255,255)); + Tooltip("Hearts outline color (white in original)\nAffect Double Defense outline only."); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Buttons")) { - EnhancementColor3("A Buttons", "gCCABtnPrim", a_btn_colors, false); - Tooltip("A Buttons colors (Green in original Gamecube)\nAffect A buttons colors on interface, in shops, messages boxes, ocarina notes and inventory cursors"); - EnhancementColor3("B Buttons", "gCCBBtnPrim", b_btn_colors, false); + EnhancementColor("A Buttons", "gCCABtnPrim", a_btn_colors, ImVec4(90,90,255,255)); + Tooltip("A Buttons colors (Green in original Gamecube)\nAffect A buttons colors on interface, in shops, messages boxes, ocarina notes and inventory cursors."); + EnhancementColor("B Buttons", "gCCBBtnPrim", b_btn_colors, ImVec4(0,150,0,255)); Tooltip("B Button colors (Red in original Gamecube)\nAffect B button colors on interface"); - EnhancementColor3("C Buttons", "gCCCBtnPrim", c_btn_colors, false); + EnhancementColor("C Buttons", "gCCCBtnPrim", c_btn_colors, ImVec4(255,160,0,255)); Tooltip("C Buttons colors (Yellowish / Oranges in originals)\nAffect C buttons colors on interface, in inventory and ocarina notes"); - EnhancementColor3("Start Buttons", "gCCStartBtnPrim", start_btn_colors, false); + EnhancementColor("Start Buttons", "gCCStartBtnPrim", start_btn_colors, ImVec4(120,120,120,255)); Tooltip("Start Button colors (gray in Gamecube)\nAffect Start button colors in inventory"); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Magic Bar")) { - EnhancementColor3("Magic bar borders", "gCCMagicBorderPrim", magic_border_colors, false); - Tooltip("Affect the border of the magic bar when being used\nWhite flash in original game"); - EnhancementColor3("Magic bar main color", "gCCMagicPrim", magic_remaining_colors, false); - Tooltip("Affect the magic bar color\nGreen in original game"); - EnhancementColor3("Magic bar being used", "gCCMagicUsePrim", magic_use_colors, false); - Tooltip("Affect the magic bar when being used\nYellow in original game"); + EnhancementColor("Magic bar borders", "gCCMagicBorderPrim", magic_border_colors, ImVec4(255,255,255,255)); + Tooltip("Affect the border of the magic bar when being used\nWhite flash in original game."); + EnhancementColor("Magic bar main color", "gCCMagicPrim", magic_remaining_colors, ImVec4(0,200,0,255)); + Tooltip("Affect the magic bar color\nGreen in original game."); + EnhancementColor("Magic bar being used", "gCCMagicUsePrim", magic_use_colors, ImVec4(250,250,0,255)); + Tooltip("Affect the magic bar when being used\nYellow in original game."); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Misc")) { - EnhancementColor3("Minimap color", "gCCMinimapPrim", minimap_colors, false); - Tooltip("Affect the Dungeon and Overworld minimaps"); - EnhancementColor3("Rupee icon color", "gCCRupeePrim", rupee_colors, false); - Tooltip("Affect the Rupee icon on interface\nGreen by default"); - EnhancementColor3("Small Keys icon color", "gCCKeysPrim", smolekey_colors, false); - Tooltip("Affect the Small keys icon on interface\nGray by default"); + EnhancementColor("Minimap color", "gCCMinimapPrim", minimap_colors, ImVec4(0,255,255,255)); + Tooltip("Affect the Dungeon and Overworld minimaps."); + EnhancementColor("Rupee icon color", "gCCRupeePrim", rupee_colors, ImVec4(120,120,120,255)); + Tooltip("Affect the Rupee icon on interface\nGreen by default."); + EnhancementColor("Small Keys icon color", "gCCKeysPrim", smolekey_colors, ImVec4(200,230,255,255)); + Tooltip("Affect the Small keys icon on interface\nGray by default."); ImGui::EndTabItem(); } ImGui::EndTabBar(); @@ -1180,6 +1271,8 @@ namespace SohImGui { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); } + //Placed here so it does the rainbow effects even if menu is not on. + LoadRainbowColor(); } void CancelFrame() { diff --git a/libultraship/libultraship/SohImGuiImpl.h b/libultraship/libultraship/SohImGuiImpl.h index 2a192802d..421c71778 100644 --- a/libultraship/libultraship/SohImGuiImpl.h +++ b/libultraship/libultraship/SohImGuiImpl.h @@ -63,16 +63,14 @@ namespace SohImGui { extern bool needs_save; void Init(WindowImpl window_impl); void Update(EventImpl event); - + void Tooltip(const char* text); + void EnhancementRadioButton(const char* text, const char* cvarName, int id); void EnhancementCheckbox(const char* text, const char* cvarName); void EnhancementButton(const char* text, const char* cvarName); void EnhancementSliderInt(const char* text, const char* id, const char* cvarName, int min, int max, const char* format); void EnhancementSliderFloat(const char* text, const char* id, const char* cvarName, float min, float max, const char* format, float defaultValue, bool isPercentage); - - void Tooltip(const char* text); - - void EnhancementColor3(const char* text, const char* cvarName, float defaultColors[3], bool TitleSameLine); + void EnhancementColor(const char* text, const char* cvarName, ImVec4 ColorRGBA, ImVec4 default_colors, bool allow_rainbow = true, bool has_alpha=false, bool TitleSameLine=false); void DrawMainMenuAndCalculateGameSize(void); @@ -83,7 +81,10 @@ namespace SohImGui { void BindCmd(const std::string& cmd, CommandEntry entry); void AddWindow(const std::string& category, const std::string& name, WindowDrawFunc drawFunc); void LoadResource(const std::string& name, const std::string& path, const ImVec4& tint = ImVec4(1, 1, 1, 1)); - void LoadInterfaceEditor(); + void LoadPickersColors(ImVec4& ColorArray, const char* cvarname, const ImVec4& default_colors, bool has_alpha=false); + void RandomizeColor(const char* cvarName, ImVec4* colors); + void RainbowColor(const char* cvarName, ImVec4* colors); + void ResetColor(const char* cvarName, ImVec4* colors, ImVec4 defaultcolors, bool has_alpha); ImTextureID GetTextureByID(int id); ImTextureID GetTextureByName(const std::string& name); } diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c index 30b18683f..e45850a73 100644 --- a/soh/src/code/z_lifemeter.c +++ b/soh/src/code/z_lifemeter.c @@ -128,6 +128,13 @@ void HealthMeter_Init(GlobalContext* globalCtx) { HeartDDOutline[0] = CVar_GetS32("gDDCCHeartsPrimR", 90); HeartDDOutline[1] = CVar_GetS32("gDDCCHeartsPrimG", 90); HeartDDOutline[2] = CVar_GetS32("gDDCCHeartsPrimB", 90); + } else { + HeartInner[0] = HEARTS_PRIM_R; + HeartInner[1] = HEARTS_PRIM_G; + HeartInner[2] = HEARTS_PRIM_B; + HeartDDOutline[0] = HEARTS_DD_PRIM_R; + HeartDDOutline[1] = HEARTS_DD_PRIM_G; + HeartDDOutline[2] = HEARTS_DD_PRIM_B; } interfaceCtx->unk_228 = 0x140; @@ -194,12 +201,12 @@ void HealthMeter_Update(GlobalContext* globalCtx) { HeartDDOutline[1] = CVar_GetS32("gDDCCHeartsPrimG", sHeartsDDPrim[0][1]); HeartDDOutline[2] = CVar_GetS32("gDDCCHeartsPrimB", sHeartsDDPrim[0][2]); } else { - HeartInner[0] = sHeartsPrimColors[0][0]; - HeartInner[1] = sHeartsPrimColors[0][1]; - HeartInner[2] = sHeartsPrimColors[0][2]; - HeartDDOutline[0] = sHeartsDDPrim[0][0]; - HeartDDOutline[1] = sHeartsDDPrim[0][1]; - HeartDDOutline[2] = sHeartsDDPrim[0][2]; + HeartInner[0] = HEARTS_PRIM_R; + HeartInner[1] = HEARTS_PRIM_G; + HeartInner[2] = HEARTS_PRIM_B; + HeartDDOutline[0] = HEARTS_DD_PRIM_R; + HeartDDOutline[1] = HEARTS_DD_PRIM_G; + HeartDDOutline[2] = HEARTS_DD_PRIM_B; } if (interfaceCtx->unk_200 != 0) { @@ -299,33 +306,33 @@ void HealthMeter_Update(GlobalContext* globalCtx) { sBeatingHeartsDDEnv[1] = (u8)(gFactor + HeartDDInner[1]) & 0xFF; sBeatingHeartsDDEnv[2] = (u8)(bFactor + HeartDDInner[2]) & 0xFF; } else { - sHeartsDDPrim[2][0] = HeartInner[0]; - sHeartsDDPrim[2][1] = HeartInner[1]; - sHeartsDDPrim[2][2] = HeartInner[2]; + sHeartsDDPrim[2][0] = HEARTS_PRIM_R; + sHeartsDDPrim[2][1] = HEARTS_PRIM_G; + sHeartsDDPrim[2][2] = HEARTS_PRIM_B; - sHeartsDDPrim[1][0] = sHeartsDDPrimColors[ddType][0]; - sHeartsDDPrim[1][1] = sHeartsDDPrimColors[ddType][1]; - sHeartsDDPrim[1][2] = sHeartsDDPrimColors[ddType][2]; + sHeartsDDPrim[1][0] = HEARTS_DD_PRIM_R; + sHeartsDDPrim[1][1] = HEARTS_DD_PRIM_G; + sHeartsDDPrim[1][2] = HEARTS_DD_PRIM_B; - sHeartsDDEnv[1][0] = sHeartsDDEnvColors[ddType][0]; - sHeartsDDEnv[1][1] = sHeartsDDEnvColors[ddType][1]; - sHeartsDDEnv[1][2] = sHeartsDDEnvColors[ddType][2]; + sHeartsDDEnv[1][0] = HEARTS_PRIM_R; + sHeartsDDEnv[1][1] = HEARTS_PRIM_G; + sHeartsDDEnv[1][2] = HEARTS_PRIM_B; rFactor = sHeartsDDPrimFactors[ddType][0] * ddFactor; gFactor = sHeartsDDPrimFactors[ddType][1] * ddFactor; bFactor = sHeartsDDPrimFactors[ddType][2] * ddFactor; - sBeatingHeartsDDPrim[0] = (u8)(rFactor + HeartDDOutline[0]) & 0xFF; - sBeatingHeartsDDPrim[1] = (u8)(gFactor + HeartDDOutline[1]) & 0xFF; - sBeatingHeartsDDPrim[2] = (u8)(bFactor + HeartDDOutline[2]) & 0xFF; + sBeatingHeartsDDPrim[0] = (u8)(rFactor + HEARTS_DD_PRIM_R) & 0xFF; + sBeatingHeartsDDPrim[1] = (u8)(gFactor + HEARTS_DD_PRIM_G) & 0xFF; + sBeatingHeartsDDPrim[2] = (u8)(bFactor + HEARTS_DD_PRIM_B) & 0xFF; rFactor = sHeartsDDEnvFactors[ddType][0] * ddFactor; gFactor = sHeartsDDEnvFactors[ddType][1] * ddFactor; bFactor = sHeartsDDEnvFactors[ddType][2] * ddFactor; - sBeatingHeartsDDEnv[0] = (u8)(rFactor + HeartDDInner[0]) & 0xFF; - sBeatingHeartsDDEnv[1] = (u8)(gFactor + HeartDDInner[1]) & 0xFF; - sBeatingHeartsDDEnv[2] = (u8)(bFactor + HeartDDInner[2]) & 0xFF; + sBeatingHeartsDDEnv[0] = (u8)(rFactor + HEARTS_PRIM_R) & 0xFF; + sBeatingHeartsDDEnv[1] = (u8)(gFactor + HEARTS_PRIM_G) & 0xFF; + sBeatingHeartsDDEnv[2] = (u8)(bFactor + HEARTS_PRIM_B) & 0xFF; } } diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index af95f26df..83fae7ff1 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -744,26 +744,38 @@ void func_8008F470(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, #else gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sMouthTextures[eyeIndex])); #endif - Color_RGB8 NewColor[3]; - color = &sTunicColors[tunic]; - if (CVar_GetS32("gUseTunicsCol",0) == 1) { - if (tunic == PLAYER_TUNIC_KOKIRI || tunic == PLAYER_TUNIC_GORON || tunic == PLAYER_TUNIC_ZORA) { - NewColor[PLAYER_TUNIC_KOKIRI].r = CVar_GetS32("gTunic_Kokiri_R", sTunicColors[PLAYER_TUNIC_KOKIRI].r); - NewColor[PLAYER_TUNIC_KOKIRI].g = CVar_GetS32("gTunic_Kokiri_G", sTunicColors[PLAYER_TUNIC_KOKIRI].g); - NewColor[PLAYER_TUNIC_KOKIRI].b = CVar_GetS32("gTunic_Kokiri_B", sTunicColors[PLAYER_TUNIC_KOKIRI].b); - NewColor[PLAYER_TUNIC_GORON].r = CVar_GetS32("gTunic_Goron_R", sTunicColors[PLAYER_TUNIC_GORON].r); - NewColor[PLAYER_TUNIC_GORON].g = CVar_GetS32("gTunic_Goron_G", sTunicColors[PLAYER_TUNIC_GORON].g); - NewColor[PLAYER_TUNIC_GORON].b = CVar_GetS32("gTunic_Goron_B", sTunicColors[PLAYER_TUNIC_GORON].b); - NewColor[PLAYER_TUNIC_ZORA].r = CVar_GetS32("gTunic_Zora_R", sTunicColors[PLAYER_TUNIC_ZORA].r); - NewColor[PLAYER_TUNIC_ZORA].g = CVar_GetS32("gTunic_Zora_G", sTunicColors[PLAYER_TUNIC_ZORA].g); - NewColor[PLAYER_TUNIC_ZORA].b = CVar_GetS32("gTunic_Zora_B", sTunicColors[PLAYER_TUNIC_ZORA].b); + + Color_RGB8 sTemp; + Color_RGB8 sOriginalTunicColors[] = { + { 30, 105, 27 }, + { 100, 20, 0 }, + { 0, 60, 100 }, + }; + color = &sTemp; + if (tunic == PLAYER_TUNIC_KOKIRI && CVar_GetS32("gUseTunicsCol",0)) { + color->r = CVar_GetS32("gTunic_Kokiri_R", sTunicColors[PLAYER_TUNIC_KOKIRI].r); + color->g = CVar_GetS32("gTunic_Kokiri_G", sTunicColors[PLAYER_TUNIC_KOKIRI].g); + color->b = CVar_GetS32("gTunic_Kokiri_B", sTunicColors[PLAYER_TUNIC_KOKIRI].b); + } else if (tunic == PLAYER_TUNIC_GORON && CVar_GetS32("gUseTunicsCol",0)) { + color->r = CVar_GetS32("gTunic_Goron_R", sTunicColors[PLAYER_TUNIC_GORON].r); + color->g = CVar_GetS32("gTunic_Goron_G", sTunicColors[PLAYER_TUNIC_GORON].g); + color->b = CVar_GetS32("gTunic_Goron_B", sTunicColors[PLAYER_TUNIC_GORON].b); + } else if (tunic == PLAYER_TUNIC_ZORA && CVar_GetS32("gUseTunicsCol",0)) { + color->r = CVar_GetS32("gTunic_Zora_R", sTunicColors[PLAYER_TUNIC_ZORA].r); + color->g = CVar_GetS32("gTunic_Zora_G", sTunicColors[PLAYER_TUNIC_ZORA].g); + color->b = CVar_GetS32("gTunic_Zora_B", sTunicColors[PLAYER_TUNIC_ZORA].b); + } else if (!CVar_GetS32("gUseTunicsCol",0)){ + if (tunic >= 3) { + color->r = sOriginalTunicColors[0].r; + color->g = sOriginalTunicColors[0].g; + color->b = sOriginalTunicColors[0].b; } else { - NewColor[PLAYER_TUNIC_KOKIRI].r = CVar_GetS32("gTunic_Kokiri_R", sTunicColors[PLAYER_TUNIC_KOKIRI].r); - NewColor[PLAYER_TUNIC_KOKIRI].g = CVar_GetS32("gTunic_Kokiri_G", sTunicColors[PLAYER_TUNIC_KOKIRI].g); - NewColor[PLAYER_TUNIC_KOKIRI].b = CVar_GetS32("gTunic_Kokiri_B", sTunicColors[PLAYER_TUNIC_KOKIRI].b); + color->r = sOriginalTunicColors[tunic].r; + color->g = sOriginalTunicColors[tunic].g; + color->b = sOriginalTunicColors[tunic].b; } - color = NewColor; } + gDPSetEnvColor(POLY_OPA_DISP++, color->r, color->g, color->b, 0); sDListsLodOffset = lod * 2; diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c index d9f038059..43ae671e6 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c @@ -976,14 +976,23 @@ void KaleidoScope_DrawCursor(GlobalContext* globalCtx, u16 pageIndex) { temp = pauseCtx->unk_1E4; if (CVar_GetS32("gHudColors", 1) == 0) { + sCursorColors[1][0] = 255; + sCursorColors[1][1] = 255; + sCursorColors[1][2] = 0; sCursorColors[2][0] = 0; sCursorColors[2][1] = 50; sCursorColors[2][2] = 255; } else if (CVar_GetS32("gHudColors", 1) == 1) { + sCursorColors[1][0] = 255; + sCursorColors[1][1] = 255; + sCursorColors[1][2] = 0; sCursorColors[2][0] = 0; sCursorColors[2][1] = 255; sCursorColors[2][2] = 50; } else if (CVar_GetS32("gHudColors", 1) == 2) { + sCursorColors[1][0] = CVar_GetS32("gCCCBtnPrimR", 255); + sCursorColors[1][1] = CVar_GetS32("gCCCBtnPrimG", 255); + sCursorColors[1][2] = CVar_GetS32("gCCCBtnPrimB", 0); sCursorColors[2][0] = CVar_GetS32("gCCABtnPrimR", 0); sCursorColors[2][1] = CVar_GetS32("gCCABtnPrimG", 255); sCursorColors[2][2] = CVar_GetS32("gCCABtnPrimB", 50); @@ -1060,6 +1069,12 @@ void KaleidoScope_DrawPages(GlobalContext* globalCtx, GraphicsContext* gfxCtx) { { 0, 0, 0 }, { 255, 255, 0 }, { 0, 255, 50 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 255, 50 }, }; if (CVar_GetS32("gHudColors", 1) == 0) { + D_8082ACF4[4][0] = 255; + D_8082ACF4[4][1] = 255; + D_8082ACF4[4][2] = 0; + D_8082ACF4[7][0] = 255; + D_8082ACF4[7][1] = 255; + D_8082ACF4[7][2] = 0; D_8082ACF4[8][0] = 0; D_8082ACF4[8][1] = 50; D_8082ACF4[8][2] = 255; @@ -1067,6 +1082,12 @@ void KaleidoScope_DrawPages(GlobalContext* globalCtx, GraphicsContext* gfxCtx) { D_8082ACF4[11][1] = 50; D_8082ACF4[11][2] = 255; } else if (CVar_GetS32("gHudColors", 1) == 1) { + D_8082ACF4[4][0] = 255; + D_8082ACF4[4][1] = 255; + D_8082ACF4[4][2] = 0; + D_8082ACF4[7][0] = 255; + D_8082ACF4[7][1] = 255; + D_8082ACF4[7][2] = 0; D_8082ACF4[8][0] = 0; D_8082ACF4[8][1] = 255; D_8082ACF4[8][2] = 50; @@ -1074,6 +1095,12 @@ void KaleidoScope_DrawPages(GlobalContext* globalCtx, GraphicsContext* gfxCtx) { D_8082ACF4[11][1] = 255; D_8082ACF4[11][2] = 50; } else if (CVar_GetS32("gHudColors", 1) == 2) { + D_8082ACF4[4][0] = CVar_GetS32("gCCCBtnPrimR", 255); + D_8082ACF4[4][1] = CVar_GetS32("gCCCBtnPrimG", 255); + D_8082ACF4[4][2] = CVar_GetS32("gCCCBtnPrimB", 0); + D_8082ACF4[7][0] = CVar_GetS32("gCCCBtnPrimR", 255); + D_8082ACF4[7][1] = CVar_GetS32("gCCCBtnPrimG", 255); + D_8082ACF4[7][2] = CVar_GetS32("gCCCBtnPrimB", 0); D_8082ACF4[8][0] = CVar_GetS32("gCCABtnPrimR", 0); D_8082ACF4[8][1] = CVar_GetS32("gCCABtnPrimG", 255); D_8082ACF4[8][2] = CVar_GetS32("gCCABtnPrimB", 50); From 29d4cd27c46af4c31b15980518647c9cb47a5eeb Mon Sep 17 00:00:00 2001 From: Baoulettes Date: Sun, 29 May 2022 22:38:21 +0200 Subject: [PATCH 4/8] Fix Radiobutton uniqueness issues (#397) * Fix Radiobox uniqueness issues * C++ ? and build fix I think * To match the edit made in .c --- libultraship/libultraship/SohImGuiImpl.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libultraship/libultraship/SohImGuiImpl.cpp b/libultraship/libultraship/SohImGuiImpl.cpp index c91f31750..c03cd8e78 100644 --- a/libultraship/libultraship/SohImGuiImpl.cpp +++ b/libultraship/libultraship/SohImGuiImpl.cpp @@ -447,11 +447,17 @@ namespace SohImGui { EnhancementRadioButton("German", "gLanguages", 1); EnhancementRadioButton("French", "gLanguages", 2); */ + std::string make_invisible = "##"; + make_invisible += text; + make_invisible += cvarName; + int val = CVar_GetS32(cvarName, 0); - if (ImGui::RadioButton(text, id == val)) { + if (ImGui::RadioButton(make_invisible.c_str(), id == val)) { CVar_SetS32(cvarName, id); needs_save = true; } + ImGui::SameLine(); + ImGui::Text("%s", text); } void EnhancementCheckbox(const char* text, const char* cvarName) From 08c161fd13836b34bb3257d8361add14e967eac8 Mon Sep 17 00:00:00 2001 From: PurpleHato <47987542+PurpleHato@users.noreply.github.com> Date: Sun, 29 May 2022 22:40:50 +0200 Subject: [PATCH 5/8] Enhancement: More options for Kaleido Link (#394) * Enhancement: More options for Kaleido Link ADDED: 14 animations to use ADDED: 2 random mode ADDED: Link rotation on the menu with DPAD-Left and Right ADDED: Reset position with DPAD-Up and Down * ADDED/ Choice to use C-Button or D-pad --- libultraship/libultraship/SohImGuiImpl.cpp | 41 ++++++- soh/src/code/z_player_lib.c | 108 ++++++++++++++++-- .../ovl_kaleido_scope/z_kaleido_equipment.c | 32 +++++- 3 files changed, 168 insertions(+), 13 deletions(-) diff --git a/libultraship/libultraship/SohImGuiImpl.cpp b/libultraship/libultraship/SohImGuiImpl.cpp index c03cd8e78..3c3ca9c6c 100644 --- a/libultraship/libultraship/SohImGuiImpl.cpp +++ b/libultraship/libultraship/SohImGuiImpl.cpp @@ -861,9 +861,48 @@ namespace SohImGui { if (ImGui::BeginMenu("Graphics")) { + if (ImGui::BeginMenu("Animated Link in Pause Menu")) { + ImGui::Text("Rotation"); + EnhancementRadioButton("Disabled", "gPauseLiveRotation", 0); + EnhancementRadioButton("Rotate Link with D-pad", "gPauseLiveRotation", 1); + Tooltip("Allow you to rotate Link on the Equipment menu with the DPAD\nUse DPAD-Up or DPAD-Down to reset Link's rotation"); + EnhancementRadioButton("Rotate Link with C-buttons", "gPauseLiveRotation", 2); + Tooltip("Allow you to rotate Link on the Equipment menu with the C-buttons\nUse C-Up or C-Down to reset Link's rotation"); + + if (CVar_GetS32("gPauseLiveRotation", 0) != 0) { + EnhancementSliderInt("Rotation Speed: %d", "##MinRotationSpeed", "gPauseLiveLinkRotationSpeed", 1, 20, ""); + } + ImGui::Separator(); + ImGui::Text("Static loop"); + EnhancementRadioButton("Disabled", "gPauseLiveLink", 0); + EnhancementRadioButton("Idle (standing)", "gPauseLiveLink", 1); + EnhancementRadioButton("Idle (look around)", "gPauseLiveLink", 2); + EnhancementRadioButton("Idle (belt)", "gPauseLiveLink", 3); + EnhancementRadioButton("Idle (shield)", "gPauseLiveLink", 4); + EnhancementRadioButton("Idle (test sword)", "gPauseLiveLink", 5); + EnhancementRadioButton("Idle (yawn)", "gPauseLiveLink", 6); + EnhancementRadioButton("Battle Stance", "gPauseLiveLink", 7); + EnhancementRadioButton("Walking (no shield)", "gPauseLiveLink", 8); + EnhancementRadioButton("Walking (holding shield)", "gPauseLiveLink", 9); + EnhancementRadioButton("Running (no shield)", "gPauseLiveLink", 10); + EnhancementRadioButton("Running (holding shield)", "gPauseLiveLink", 11); + EnhancementRadioButton("Hand on hip", "gPauseLiveLink", 12); + EnhancementRadioButton("Spin attack charge", "gPauseLiveLink", 13); + EnhancementRadioButton("Look at hand", "gPauseLiveLink", 14); + ImGui::Separator(); + ImGui::Text("Randomize"); + EnhancementRadioButton("Random", "gPauseLiveLink", 15); + Tooltip("Randomize the animation played each time you open the menu"); + EnhancementRadioButton("Random cycle", "gPauseLiveLink", 16); + Tooltip("andomize the animation played on hte menu after a certain time"); + if (CVar_GetS32("gPauseLiveLink", 0) >= 16) { + EnhancementSliderInt("Frame to wait: %d", "##MinFrameCount", "gMinFrameCount", 1, 1000, ""); + } + + ImGui::EndMenu(); + } EnhancementCheckbox("N64 Mode", "gN64Mode"); Tooltip("Sets aspect ratio to 4:3 and lowers resolution to 240p, the N64's native resolution"); - EnhancementCheckbox("Animated Link in Pause Menu", "gPauseLiveLink"); EnhancementCheckbox("Enable 3D Dropped items", "gNewDrops"); EnhancementCheckbox("Dynamic Wallet Icon", "gDynamicWalletIcon"); Tooltip("Changes the rupee in the wallet icon to match the wallet size you currently have"); diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index 83fae7ff1..cf7fbd51c 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -1661,26 +1661,118 @@ void func_80091A24(GlobalContext* globalCtx, void* seg04, void* seg06, SkelAnime CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_player_lib.c", 3288); } +uintptr_t SelectedAnim = 0; // Current Animaiton on the menu +s16 EquipedStance; // Link's current mode (Two handed, One handed...) +s16 FrameCountSinceLastAnim = 0; // Time since last animation +s16 MinFrameCount; // Frame to wait before checking if we need to change the animation + void func_8009214C(GlobalContext* globalCtx, u8* segment, SkelAnime* skelAnime, Vec3f* pos, Vec3s* rot, f32 scale, s32 sword, s32 tunic, s32 shield, s32 boots) { + Input* p1Input = &globalCtx->state.input[0]; Vec3f eye = { 0.0f, 0.0f, -400.0f }; Vec3f at = { 0.0f, 0.0f, 0.0f }; Vec3s* destTable; Vec3s* srcTable; s32 i; + bool canswitchrnd = false; + s16 SelectedMode = CVar_GetS32("gPauseLiveLink", 1); + MinFrameCount = CVar_GetS32("gMinFrameCount", 200); gSegments[4] = VIRTUAL_TO_PHYSICAL(segment + 0x3800); gSegments[6] = VIRTUAL_TO_PHYSICAL(segment + 0x8800); - if (CVar_GetS32("gPauseLiveLink", 0) || CVar_GetS32("gPauseTriforce", 0)) { - uintptr_t anim = gPlayerAnim_003238; // idle + uintptr_t* PauseMenuAnimSet[15][4] = { + { 0, 0, 0, 0 }, // 0 = none + // IDLE // Two Handed // No shield // Kid Hylian Shield + { gPlayerAnim_003238, gPlayerAnim_002BE0, gPlayerAnim_003240, gPlayerAnim_003240 }, // Idle + { gPlayerAnim_003200, gPlayerAnim_003200, gPlayerAnim_003200, gPlayerAnim_003200 }, // Idle look around + { gPlayerAnim_0033E0, gPlayerAnim_0033E0, gPlayerAnim_0033E0, gPlayerAnim_0033E0 }, // Idle Belt + { gPlayerAnim_003418, gPlayerAnim_003418, gPlayerAnim_003418, gPlayerAnim_003418 }, // Idle shield adjust + { gPlayerAnim_003420, gPlayerAnim_003428, gPlayerAnim_003420, gPlayerAnim_003420 }, // Idle test sword + { gPlayerAnim_0033F0, gPlayerAnim_0033F0, gPlayerAnim_0033F0, gPlayerAnim_0033F0 }, // Idle yawn + { gPlayerAnim_0025D0, gPlayerAnim_002BD0, gPlayerAnim_0025D0, gPlayerAnim_0025D0 }, // Battle Stance + { gPlayerAnim_003290, gPlayerAnim_002BF8, gPlayerAnim_003290, gPlayerAnim_003290 }, // Walking (No shield) + { gPlayerAnim_003268, gPlayerAnim_002BF8, gPlayerAnim_003268, gPlayerAnim_003268 }, // Walking (Holding shield) + { gPlayerAnim_003138, gPlayerAnim_002B40, gPlayerAnim_003138, gPlayerAnim_003138 }, // Running (No shield) + { gPlayerAnim_003140, gPlayerAnim_002B40, gPlayerAnim_003140, gPlayerAnim_003140 }, // Running (Holding shield) + { gPlayerAnim_0031A8, gPlayerAnim_0031A8, gPlayerAnim_0031A8, gPlayerAnim_0031A8 }, // Hand on hip + { gPlayerAnim_002AF0, gPlayerAnim_002928, gPlayerAnim_002AF0, gPlayerAnim_002AF0 }, // Spin Charge + { gPlayerAnim_002820, gPlayerAnim_002820, gPlayerAnim_002820, gPlayerAnim_002820 }, // Look at hand + }; + s16 AnimArraySize = ARRAY_COUNT(PauseMenuAnimSet); - if (CUR_EQUIP_VALUE(EQUIP_SWORD) >= 3) - anim = gPlayerAnim_002BE0; // Two Handed Anim - else if (CUR_EQUIP_VALUE(EQUIP_SHIELD) == 0) - anim = gPlayerAnim_003240; - else if (CUR_EQUIP_VALUE(EQUIP_SHIELD) == 2 && LINK_AGE_IN_YEARS == YEARS_CHILD) - anim = gPlayerAnim_003240; + if (CVar_GetS32("gPauseLiveLink", !0) || CVar_GetS32("gPauseTriforce", 0)) { + uintptr_t anim = 0; // Initialise anim + + if (CUR_EQUIP_VALUE(EQUIP_SWORD) >= 3) { + EquipedStance = 1; + } else if (CUR_EQUIP_VALUE(EQUIP_SHIELD) == 0) { + EquipedStance = 2; + } else if (CUR_EQUIP_VALUE(EQUIP_SHIELD) == 2 && LINK_AGE_IN_YEARS == YEARS_CHILD) { + EquipedStance = 3; + } else { + // Link is idle so revert to 0 + EquipedStance = 0; + } + + if (SelectedMode == 16) { + // Apply Random function + s16 SwitchAtFrame = 0; + s16 CurAnimDuration = 0; + if (FrameCountSinceLastAnim == 0) { + // When opening Kaleido this will be passed one time + SelectedAnim = rand() % (AnimArraySize - 0); + if (SelectedAnim == 0) { + // prevent loading 0 that would result to a crash. + SelectedAnim = 1; + } + } else if (FrameCountSinceLastAnim >= 1) { + SwitchAtFrame = Animation_GetLastFrame(PauseMenuAnimSet[SelectedAnim][EquipedStance]); + CurAnimDuration = Animation_GetLastFrame(PauseMenuAnimSet[SelectedAnim][EquipedStance]); + if (SwitchAtFrame < MinFrameCount) { + // Animation frame count is lower than minimal wait time then we wait for another round. + // This will be looped to always add current animation time if that still lower than minimum time + while (SwitchAtFrame < MinFrameCount) { + SwitchAtFrame = SwitchAtFrame + CurAnimDuration; + } + } else if (CurAnimDuration >= MinFrameCount) { + // Since we have more (or same) animation time than min duration we set the wait time to animation + // time. + SwitchAtFrame = CurAnimDuration; + } + if (FrameCountSinceLastAnim >= SwitchAtFrame) { + SelectedAnim = rand() % (AnimArraySize - 0); + if (SelectedAnim == 0) { + // prevent loading 0 that would result to a crash. + SelectedAnim = 1; + } + FrameCountSinceLastAnim = 1; + } + anim = PauseMenuAnimSet[SelectedAnim][EquipedStance]; + } + FrameCountSinceLastAnim++; + } else if (SelectedMode == 15) { + // When opening Kaleido this will be passed one time + if (FrameCountSinceLastAnim < 1) { + SelectedAnim = rand() % (AnimArraySize - 0); + FrameCountSinceLastAnim++; + if (SelectedAnim == 0) { + // prevent loading 0 that would result to a crash. + SelectedAnim = 1; + } + FrameCountSinceLastAnim = 1; + } + if (CHECK_BTN_ALL(p1Input->press.button, BTN_B) || CHECK_BTN_ALL(p1Input->press.button, BTN_START)) { + FrameCountSinceLastAnim = 0; + } + anim = PauseMenuAnimSet[SelectedAnim][EquipedStance]; + } else if (SelectedMode < 16) { + // Not random so we place our CVar as SelectedAnim + SelectedAnim = SelectedMode; + anim = PauseMenuAnimSet[SelectedAnim][EquipedStance]; + } + + anim = PauseMenuAnimSet[SelectedAnim][EquipedStance]; //anim = gPlayerAnim_003428; // Use for biggoron sword? diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c index 719eaa3f3..125981bff 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c @@ -89,11 +89,17 @@ void KaleidoScope_DrawEquipmentImage(GlobalContext* globalCtx, void* source, u32 CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_equipment.c", 122); } +Vec3s link_kaleido_rot = { 0, 32300, 0 }; // Default rotation link face us. + void KaleidoScope_DrawPlayerWork(GlobalContext* globalCtx) { PauseContext* pauseCtx = &globalCtx->pauseCtx; Vec3f pos; - Vec3s rot; + //Vec3s rot; // Removed for not having it use din the function f32 scale; + Input* input = &globalCtx->state.input[0]; + s16 RotationSpeed = 150 * CVar_GetS32("gPauseLiveLinkRotationSpeed", 0); + bool AllowCRotation = (CVar_GetS32("gPauseLiveLinkRotation", 0) == 2) ? true : false; + bool AllowDPadRotation = (CVar_GetS32("gPauseLiveLinkRotation", 0) == 1) ? true : false; if (LINK_AGE_IN_YEARS == YEARS_CHILD) { pos.x = 2.0f; @@ -112,11 +118,29 @@ void KaleidoScope_DrawPlayerWork(GlobalContext* globalCtx) { scale = 0.047f; } - rot.y = 32300; - rot.x = rot.z = 0; + link_kaleido_rot.x = link_kaleido_rot.z = 0; + + if (AllowDPadRotation && CHECK_BTN_ALL(input->cur.button, BTN_DLEFT) || + AllowCRotation && CHECK_BTN_ALL(input->cur.button, BTN_CLEFT)) { + link_kaleido_rot.y = link_kaleido_rot.y - RotationSpeed; + } else if (AllowDPadRotation && CHECK_BTN_ALL(input->cur.button, BTN_DRIGHT) || + AllowCRotation && CHECK_BTN_ALL(input->cur.button, BTN_CRIGHT)) { + link_kaleido_rot.y = link_kaleido_rot.y + RotationSpeed; + } + + if (AllowDPadRotation && CHECK_BTN_ALL(input->press.button, BTN_DUP) || + AllowDPadRotation && CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { + link_kaleido_rot.y = 32300; + } else if (AllowCRotation && CHECK_BTN_ALL(input->press.button, BTN_CUP) || + AllowCRotation && CHECK_BTN_ALL(input->press.button, BTN_CDOWN)) { + link_kaleido_rot.y = 32300; + } + + link_kaleido_rot.x = 0; + extern int fbTest; gsSPSetFB(globalCtx->state.gfxCtx->polyOpa.p++, fbTest); - func_8009214C(globalCtx, pauseCtx->playerSegment, &pauseCtx->playerSkelAnime, &pos, &rot, scale, + func_8009214C(globalCtx, pauseCtx->playerSegment, &pauseCtx->playerSkelAnime, &pos, &link_kaleido_rot, scale, CUR_EQUIP_VALUE(EQUIP_SWORD), CUR_EQUIP_VALUE(EQUIP_TUNIC) - 1, CUR_EQUIP_VALUE(EQUIP_SHIELD), CUR_EQUIP_VALUE(EQUIP_BOOTS) - 1); gsSPResetFB(globalCtx->state.gfxCtx->polyOpa.p++); From daec428cb3844bdb5261f5ddf47b613cce514d05 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya-ai@users.noreply.github.com> Date: Sun, 29 May 2022 16:45:27 -0400 Subject: [PATCH 6/8] call SDL_Quit() before exit(0) (linux window closing issue) (#408) * use _Exit(0) on linux * use SDL_Quit() instead of _Exit(0) * remove unnecessary call, add bandaid comment Co-authored-by: briaguya --- libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp index 94c3ef5ca..061e3a8ee 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp +++ b/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp @@ -249,6 +249,7 @@ static void gfx_sdl_handle_events(void) { } break; case SDL_QUIT: + SDL_Quit(); // bandaid fix for linux window closing issue exit(0); } } From 0bfe2fec674d6f7aa6c3becfa50bc88ec7fd8d88 Mon Sep 17 00:00:00 2001 From: Baoulettes Date: Thu, 2 Jun 2022 04:06:32 +0200 Subject: [PATCH 7/8] fix win build crash (#425) sizeof issue make it crash on windows. Thanks Melon :) --- libultraship/libultraship/SohImGuiImpl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libultraship/libultraship/SohImGuiImpl.cpp b/libultraship/libultraship/SohImGuiImpl.cpp index 3c3ca9c6c..23fa4ffa9 100644 --- a/libultraship/libultraship/SohImGuiImpl.cpp +++ b/libultraship/libultraship/SohImGuiImpl.cpp @@ -259,7 +259,8 @@ namespace SohImGui { } void LoadRainbowColor() { - for (uint16_t s=0; s <= sizeof(RainbowColorCvarList); s++) { + u8 arrayLength = sizeof(RainbowColorCvarList) / sizeof(*RainbowColorCvarList); + for (u8 s = 0; s < arrayLength; s++) { std::string cvarName = RainbowColorCvarList[s]; std::string Cvar_Red = cvarName; Cvar_Red += "R"; From 05340a926ae62acef99e50efe00f59875b13bb17 Mon Sep 17 00:00:00 2001 From: getBrainError Date: Thu, 2 Jun 2022 03:58:50 +0200 Subject: [PATCH 8/8] Add fishing pole as sword in save editor Adds fishing pole as a sword to the save editor under current equipment. --- soh/soh/Enhancements/debugger/debugSaveEditor.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index ca1b67798..a0d27f70f 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -1316,6 +1316,9 @@ void DrawPlayerTab() { case ITEM_SWORD_BGS: curSword = "Biggoron's Sword"; break; + case ITEM_FISHING_POLE: + curSword = "Fishing Pole"; + break; case ITEM_NONE: curSword = "None"; break; @@ -1463,6 +1466,11 @@ void DrawPlayerTab() { Inventory_ChangeEquipment(EQUIP_SWORD, PLAYER_SWORD_BGS); } + if (ImGui::Selectable("Fishing Pole")) { + player->currentSwordItem = ITEM_FISHING_POLE; + gSaveContext.equips.buttonItems[0] = ITEM_FISHING_POLE; + Inventory_ChangeEquipment(EQUIP_SWORD, PLAYER_SWORD_MASTER); + } ImGui::EndCombo(); }