Merge remote-tracking branch 'origin/main' into presets

# Conflicts:
#	files.cmake
#	src/d/actor/d_a_title.cpp
#	src/dusk/config.cpp
#	src/dusk/imgui/ImGuiConsole.cpp
#	src/dusk/imgui/ImGuiEngine.cpp
This commit is contained in:
MelonSpeedruns
2026-04-11 16:53:04 -04:00
15380 changed files with 4960 additions and 1354842 deletions
+6 -1
View File
@@ -17,6 +17,7 @@
#include <memory>
#include "JSystem/JKernel/JKRHeap.h"
#include "common/TracySystem.hpp"
#include "dusk/main.h"
#include "dusk/os.h"
@@ -41,7 +42,7 @@ struct PCThreadData {
bool suspended = false;
~PCThreadData() {
if (dusk::IsShuttingDown) {
if (dusk::IsShuttingDown && nativeThread.joinable()) {
// Don't care about threads if we're shutting down.
nativeThread.detach();
}
@@ -667,6 +668,9 @@ void OSSetCurrentThreadName(const char* name) {
// "Why is this current thread only?", you might ask?
// Because macOS requires that. For some reason.
#if TRACY_ENABLE
tracy::SetThreadName(name);
#else
#if _WIN32
wchar_t buffer[256];
const auto converted = MultiByteToWideChar(
@@ -687,6 +691,7 @@ void OSSetCurrentThreadName(const char* name) {
#elif __APPLE__
pthread_setname_np(name);
#endif
#endif
}
#ifdef __cplusplus
+9
View File
@@ -14,6 +14,7 @@
#include "DuskDsp.hpp"
#include "JSystem/JAudio2/JASAudioThread.h"
#include "JSystem/JAudio2/JASDriverIF.h"
#include "tracy/Tracy.hpp"
using namespace dusk::audio;
@@ -82,18 +83,25 @@ void dusk::audio::SetEnableReverb(const bool value) {
EnableReverb = value;
}
#ifdef TRACY_ENABLE
static auto FrameName = "GetNewAudio";
#endif
void SDLCALL GetNewAudio(
void*,
SDL_AudioStream*,
int needed,
int) {
FrameMarkStart(FrameName);
while (needed > 0) {
const int rendered = RenderNewAudioFrame();
needed -= rendered;
}
FrameMarkEnd(FrameName);
}
int RenderNewAudioFrame() {
ZoneScoped;
JASCriticalSection section;
const u32 countSubframes = JASDriver::getSubFrames();
@@ -120,6 +128,7 @@ static void InterleaveOutputData(const OutputSubframe& data, std::span<f32> targ
}
void RenderAudioSubframe() {
ZoneScoped;
OutBuffer = {};
JASDriver::updateDSP();
+42
View File
@@ -14,6 +14,7 @@
#include "dusk/audio/DuskAudioSystem.h"
#include "dusk/endian.h"
#include "global.h"
#include "tracy/Tracy.hpp"
using namespace dusk::audio;
@@ -116,6 +117,14 @@ static void ResetChannel(JASDsp::TChannel& channel, ChannelAuxData& aux) {
aux.resamplePos = 0.0;
aux.resamplePrev = 0;
aux.prev_lp_out = 0.0f;
aux.prev_lp_in = 0.0f;
aux.biq_in1 = 0.0f;
aux.biq_in2 = 0.0f;
aux.biq_out1 = 0.0f;
aux.biq_out2 = 0.0f;
for (auto& volume : aux.prevVolume) {
volume = NAN;
}
@@ -133,6 +142,7 @@ static void MixSubframe(DspSubframe& dst, const DspSubframe& src) {
}
void dusk::audio::DspRender(OutputSubframe& subframe) {
ZoneScoped;
if (DumpAudio != sDumpWasActive) {
sDumpWasActive = DumpAudio;
if (DumpAudio) {
@@ -519,6 +529,38 @@ static void RenderChannel(
channelAux.resamplePos = pos;
channelAux.resamplePrev = prev;
// IIR FILTER
// IIR part 1, low-pass: out[n] = (in[n] - in[n-1]) * (coeff/128) + out[n-1]
if (s16 coeff = channel.iir_filter_params[4]; coeff != 0) {
for (f32& sample : audioLoadBuffer) {
f32 out = std::clamp(
(sample - channelAux.prev_lp_in) * ((f32)coeff / 128.0f) + channelAux.prev_lp_out, -1.0f, 1.0f
);
channelAux.prev_lp_in = sample; // in[n-1] = in[n]
sample = channelAux.prev_lp_out = out; // out[n-1] = out[n]
}
}
// IIR part 2, biquad: out[n] = (b1*in[n-1] + b2*in[n-2] + a1*out[n-1] + a2*out[n-2]) / 32768
if ((channel.mFilterMode & 0x20) != 0) {
for (f32& sample : audioLoadBuffer) {
f32 out = std::clamp((
channel.iir_filter_params[0] * channelAux.biq_in1 + // b1
channel.iir_filter_params[1] * channelAux.biq_in2 + // b2
channel.iir_filter_params[2] * channelAux.biq_out1 + // a1
channel.iir_filter_params[3] * channelAux.biq_out2 // a2
) / 32768.0f, -1.0f, 1.0f);
// shift history, then store new input and output
channelAux.biq_in2 = channelAux.biq_in1; // in[n-2] = in[n-1]
channelAux.biq_in1 = sample; // in[n-1] = in[n]
channelAux.biq_out2 = channelAux.biq_out1; // out[n-2] = out[n-1]
sample = channelAux.biq_out1 = out; // out[n-1] = out[n]
}
}
// move any remaining samples in the decode buf to the beginning
int remainingDecodeBuf = channelAux.decodeBufCount - srcIdx;
if (remainingDecodeBuf > 0) {
+10
View File
@@ -52,6 +52,16 @@ namespace dusk::audio {
f32 resamplePos;
// last consumed sample from decodeBuf
s16 resamplePrev;
// low pass previous state
f32 prev_lp_out; // out[n-1]
f32 prev_lp_in; // in[n-1]
// biquad state
f32 biq_in1; // in[n-1]
f32 biq_in2; // in[n-2]
f32 biq_out1; // out[n-1]
f32 biq_out2; // out[n-2]
};
extern ChannelAuxData ChannelAux[DSP_CHANNELS];
+3 -1
View File
@@ -2,7 +2,9 @@
#include <mutex>
static std::recursive_mutex gAudioThreadMutex;
#include "tracy/Tracy.hpp"
static TracyLockable(std::recursive_mutex, gAudioThreadMutex);
JASCriticalSection::JASCriticalSection() {
gAudioThreadMutex.lock();
+2 -10
View File
@@ -20,8 +20,7 @@ using json = nlohmann::json;
aurora::Module DuskConfigLog("dusk::config");
static absl::flat_hash_map<std::string_view, ConfigVarBase*> RegisteredConfigVars;
static bool RegistrationDone;
static bool s_configFileMissing = false;
static bool RegistrationDone = false;
static std::string GetConfigJsonPath() {
return fmt::format("{}{}", configPath, ConfigFileName);
@@ -188,7 +187,6 @@ void dusk::config::LoadFromFileName(const char* path) {
} catch (const std::system_error& e) {
if (e.code() == std::errc::no_such_file_or_directory) {
DuskConfigLog.info("Config file did not exist, staying with defaults");
s_configFileMissing = true;
} else {
DuskConfigLog.error("Failed to load from config! {}", e.what());
}
@@ -212,12 +210,6 @@ void dusk::config::Save() {
}
io::FileStream::WriteAllText(configJsonPath.c_str(), j.dump(4));
s_configFileMissing = false;
}
bool dusk::config::IsConfigFileMissing() {
return s_configFileMissing;
}
ConfigVarBase* dusk::config::GetConfigVar(std::string_view name) {
@@ -227,4 +219,4 @@ ConfigVarBase* dusk::config::GetConfigVar(std::string_view name) {
}
return nullptr;
}
}
+398
View File
@@ -0,0 +1,398 @@
#include "dusk/frame_interpolation.h"
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <memory>
#include <unordered_map>
#include <vector>
namespace {
enum class Op : uint8_t {
OpenChild,
FinalMtx,
};
struct Label {
const void* key = nullptr;
int32_t id = 0;
bool operator==(const Label& other) const {
return key == other.key && id == other.id;
}
};
struct Data {
Label child_label{};
size_t child_index = 0;
Mtx matrix{};
const Mtx* dest = nullptr;
};
struct Path;
struct ChildBucket {
Label label{};
std::vector<std::unique_ptr<Path>> nodes;
};
struct OpBucket {
Op op = Op::OpenChild;
std::vector<Data> values;
};
struct Path {
std::vector<ChildBucket> children;
std::vector<OpBucket> ops;
std::vector<std::pair<Op, size_t>> items;
};
struct Recording {
Path root;
};
struct MatrixValue {
Mtx value;
};
using FinalMtxLookup = std::unordered_map<const Mtx*, const Data*>;
bool s_initialized = false;
bool g_enabled = false;
bool g_recording = false;
bool g_interpolating = false;
float g_step = 0.0f;
uint32_t g_pending_presentation_ui_ticks = 0;
uint32_t g_current_presentation_ui_ticks = 0;
Recording g_current_recording;
Recording g_previous_recording;
std::vector<Path*> g_current_path;
std::unordered_map<const Mtx*, MatrixValue> g_replacements;
inline void copy_matrix(const Mtx src, Mtx dst) {
MTXCopy(src, dst);
}
inline void concat_matrix(const Mtx lhs, const Mtx rhs, Mtx out) {
MTXConcat(lhs, rhs, out);
}
inline void lerp_matrix(Mtx out, const Mtx lhs, const Mtx rhs, float step) {
const float old_weight = 1.0f - step;
for (size_t row = 0; row < 3; ++row) {
for (size_t col = 0; col < 4; ++col) {
out[row][col] = lhs[row][col] * old_weight + rhs[row][col] * step;
}
}
}
inline bool matrix_differs(const Mtx lhs, const Mtx rhs, float epsilon = 0.0001f) {
for (size_t row = 0; row < 3; ++row) {
for (size_t col = 0; col < 4; ++col) {
if (std::abs(lhs[row][col] - rhs[row][col]) > epsilon) {
return true;
}
}
}
return false;
}
Data& append_op(Op op) {
auto& items = g_current_path.back()->items;
auto& buckets = g_current_path.back()->ops;
auto it = std::find_if(buckets.begin(), buckets.end(),
[op](const OpBucket& bucket) { return bucket.op == op; });
if (it == buckets.end()) {
buckets.push_back({op, {}});
it = buckets.end() - 1;
}
items.emplace_back(op, it->values.size());
return it->values.emplace_back();
}
const Data* find_matching_data(const Path& path, Op op, size_t index) {
auto it = std::find_if(path.ops.begin(), path.ops.end(),
[op](const OpBucket& bucket) { return bucket.op == op; });
if (it == path.ops.end() || index >= it->values.size()) {
return nullptr;
}
return &it->values[index];
}
const OpBucket* find_op_bucket(const Path& path, Op op) {
auto it = std::find_if(path.ops.begin(), path.ops.end(),
[op](const OpBucket& bucket) { return bucket.op == op; });
if (it == path.ops.end()) {
return nullptr;
}
return &*it;
}
void build_final_mtx_lookup(const Path& path, FinalMtxLookup& lookup) {
lookup.clear();
const OpBucket* bucket = find_op_bucket(path, Op::FinalMtx);
if (bucket == nullptr) {
return;
}
for (const Data& data : bucket->values) {
if (data.dest == nullptr) {
continue;
}
lookup[data.dest] = &data;
}
}
const Data* find_matching_final_mtx(const FinalMtxLookup& lookup, const Data& new_data) {
if (new_data.dest == nullptr) {
return nullptr;
}
auto it = lookup.find(new_data.dest);
if (it == lookup.end()) {
return nullptr;
}
return it->second;
}
ChildBucket& get_child_bucket(Path& path, const Label& label) {
auto it = std::find_if(path.children.begin(), path.children.end(),
[&label](const ChildBucket& bucket) { return bucket.label == label; });
if (it == path.children.end()) {
path.children.push_back({});
it = path.children.end() - 1;
it->label = label;
}
return *it;
}
const ChildBucket* find_child_bucket(const Path& path, const Label& label) {
auto it = std::find_if(path.children.begin(), path.children.end(),
[&label](const ChildBucket& bucket) { return bucket.label == label; });
if (it == path.children.end()) {
return nullptr;
}
return &*it;
}
void store_replacement(const Data& old_data, const Data& new_data, float step) {
if (new_data.dest == nullptr) {
return;
}
auto& replacement = g_replacements[new_data.dest];
lerp_matrix(replacement.value, old_data.matrix, new_data.matrix, step);
}
void interpolate_branch(const Path& old_path, const Path& new_path, float step) {
FinalMtxLookup old_final_mtx_lookup;
build_final_mtx_lookup(old_path, old_final_mtx_lookup);
for (const auto& item : new_path.items) {
const Op op = item.first;
const size_t index = item.second;
const Data* new_data = find_matching_data(new_path, op, index);
if (new_data == nullptr) {
continue;
}
if (op == Op::OpenChild) {
const ChildBucket* new_children = find_child_bucket(new_path, new_data->child_label);
if (new_children == nullptr || new_data->child_index >= new_children->nodes.size())
{
continue;
}
const Path& new_child = *new_children->nodes[new_data->child_index];
const ChildBucket* old_children = find_child_bucket(old_path, new_data->child_label);
if (old_children != nullptr && new_data->child_index < old_children->nodes.size())
{
interpolate_branch(*old_children->nodes[new_data->child_index], new_child, step);
} else {
interpolate_branch(new_child, new_child, step);
}
continue;
}
const Data* indexed_old_data = find_matching_data(old_path, op, index);
const Data* old_data = op == Op::FinalMtx ? find_matching_final_mtx(old_final_mtx_lookup, *new_data) : indexed_old_data;
if (op == Op::FinalMtx) {
store_replacement(old_data != nullptr ? *old_data : *new_data, *new_data, step);
}
}
}
const Mtx* resolve_replacement(const Mtx* source, Mtx* scratch) {
if (!g_interpolating || source == nullptr) {
return source;
}
auto it = g_replacements.find(source);
if (it == g_replacements.end()) {
return source;
}
copy_matrix(it->second.value, *scratch);
return scratch;
}
bool has_recording_data(const Recording& recording) {
return !recording.root.items.empty() || !recording.root.children.empty();
}
void clear_replacements() {
g_replacements.clear();
}
} // namespace
namespace dusk {
namespace frame_interp {
void ensure_initialized() {
g_enabled = getSettings().game.enableFrameInterpolation;
s_initialized = true;
}
void begin_record() {
ensure_initialized();
if (!g_enabled) {
g_interpolating = false;
g_previous_recording = {};
g_current_recording = {};
g_current_path.clear();
clear_replacements();
return;
}
g_previous_recording = std::move(g_current_recording);
g_current_recording = {};
g_current_path.clear();
g_current_path.push_back(&g_current_recording.root);
g_recording = true;
g_interpolating = false;
clear_replacements();
}
void end_record() {
g_recording = false;
}
void interpolate(float step) {
ensure_initialized();
clear_replacements();
g_step = std::clamp(step, 0.0f, 1.0f);
g_interpolating = g_enabled && !g_recording && has_recording_data(g_current_recording);
if (!g_interpolating) {
return;
}
if (!has_recording_data(g_previous_recording)) {
interpolate_branch(g_current_recording.root, g_current_recording.root, g_step);
return;
}
interpolate_branch(g_previous_recording.root, g_current_recording.root, g_step);
}
void notify_sim_tick_complete() {
ensure_initialized();
g_pending_presentation_ui_ticks++;
}
uint32_t begin_presentation_ui_pass() {
ensure_initialized();
g_current_presentation_ui_ticks = g_pending_presentation_ui_ticks;
g_pending_presentation_ui_ticks = 0;
return g_current_presentation_ui_ticks;
}
uint32_t get_presentation_ui_advance_ticks() {
if (!s_initialized) {
return 0;
}
if (!g_enabled) {
return 1;
}
return g_current_presentation_ui_ticks;
}
void end_presentation_ui_pass() {
if (!s_initialized) {
return;
}
g_current_presentation_ui_ticks = 0;
}
void open_child(const void* key, int32_t id) {
if (!s_initialized || !g_recording) {
return;
}
Label label{key, id};
auto& siblings = get_child_bucket(*g_current_path.back(), label).nodes;
Data& data = append_op(Op::OpenChild);
data.child_label = label;
data.child_index = siblings.size();
siblings.emplace_back(std::make_unique<Path>());
g_current_path.push_back(siblings.back().get());
}
void close_child() {
if (!s_initialized || !g_recording || g_current_path.size() <= 1) {
return;
}
g_current_path.pop_back();
}
void record_final_mtx_raw(const Mtx* dest, const Mtx src) {
if (!s_initialized || !g_recording || dest == nullptr) {
return;
}
Data& data = append_op(Op::FinalMtx);
data.dest = dest;
copy_matrix(src, data.matrix);
}
bool lookup_replacement(const void* source, Mtx out) {
if (!s_initialized || !g_interpolating || source == nullptr) {
return false;
}
auto it = g_replacements.find(reinterpret_cast<const Mtx*>(source));
if (it == g_replacements.end()) {
return false;
}
copy_matrix(it->second.value, out);
return true;
}
bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx out) {
if (!s_initialized || !g_interpolating || lhs == nullptr || rhs == nullptr) {
return false;
}
Mtx lhs_scratch;
Mtx rhs_scratch;
const Mtx* resolved_lhs = resolve_replacement(reinterpret_cast<const Mtx*>(lhs), &lhs_scratch);
const Mtx* resolved_rhs = resolve_replacement(reinterpret_cast<const Mtx*>(rhs), &rhs_scratch);
if (resolved_lhs == reinterpret_cast<const Mtx*>(lhs) &&
resolved_rhs == reinterpret_cast<const Mtx*>(rhs))
{
return false;
}
concat_matrix(*resolved_lhs, *resolved_rhs, out);
return true;
}
} // namespace frame_interp
} // namespace dusk
+6
View File
@@ -253,3 +253,9 @@ void dusk::ImGuiMenuTools::ShowAudioDebug() {
ImGui::End();
}
void dusk::ImGuiMenuTools::ShowSaveEditor() {
if (m_showSaveEditor) {
m_saveEditor.draw(m_showSaveEditor);
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ namespace dusk {
windowFlags |= ImGuiWindowFlags_NoMove;
}
ImGui::SetNextWindowBgAlpha(0.65f);
// ImGui::SetNextWindowBgAlpha(0.65f);
if (!ImGui::Begin("Camera Debug", nullptr, windowFlags)) {
ImGui::End();
+126 -40
View File
@@ -14,9 +14,11 @@
#include "JSystem/JUtility/JUTGamePad.h"
#include "SDL3/SDL_mouse.h"
#include "dusk/config.hpp"
#include "dusk/main.h"
#include "dusk/settings.h"
#include "dusk/audio/DuskAudioSystem.h"
#include "dusk/dusk.h"
#include "tracy/Tracy.hpp"
#if _WIN32
#define NOMINMAX
@@ -34,6 +36,21 @@ namespace dusk {
ImGui::TextUnformatted(text.data(), text.data() + text.size());
}
void ImGuiTextCenter(std::string_view text) {
ImGui::NewLine();
float fontSize = ImGui::CalcTextSize(text.data(), text.data() + text.size()).x;
ImGui::SameLine(ImGui::GetWindowSize().x / 2 - fontSize + fontSize / 2);
ImGuiStringViewText(text);
}
bool ImGuiButtonCenter(std::string_view text) {
ImGui::NewLine();
float fontSize = ImGui::CalcTextSize(text.data(), text.data() + text.size()).x;
fontSize += ImGui::GetStyle().FramePadding.x;
ImGui::SameLine(ImGui::GetWindowSize().x / 2 - fontSize + fontSize / 2);
return ImGui::Button(text.data());
}
std::string BytesToString(size_t bytes) {
constexpr std::array suffixes{ "B"sv, "KB"sv, "MB"sv, "GB"sv, "TB"sv, "PB"sv, "EB"sv };
uint32_t s = 0;
@@ -183,37 +200,15 @@ namespace dusk {
ImGuiConsole::ImGuiConsole() {}
void ImGuiConsole::InitSettings() {
bool lockAspect = getSettings().video.lockAspectRatio;
if (lockAspect) {
VILockAspectRatio(defaultAspectRatioW, defaultAspectRatioH);
} else {
VIUnlockAspectRatio();
}
dusk::audio::SetMasterVolume(getSettings().audio.masterVolume / 100.0f);
dusk::audio::SetEnableReverb(getSettings().audio.enableReverb);
}
void ImGuiConsole::UpdateSettings() {
getTransientSettings().skipFrameRateLimit = getSettings().game.enableTurboKeybind && ImGui::IsKeyDown(ImGuiKey_Tab);
}
void ImGuiConsole::PreDraw() {
if (config::IsConfigFileMissing()) {
m_firstRunPreset.draw();
return;
}
if (!m_isLaunchInitialized) {
InitSettings();
m_toasts.emplace_back("Press F1 to toggle menu"s, 5.f);
m_isLaunchInitialized = true;
}
ZoneScoped;
UpdateSettings();
if ((ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl)) &&
ImGui::IsKeyPressed(ImGuiKey_R))
{
@@ -224,24 +219,11 @@ namespace dusk {
ImGuiMenuGame::ToggleFullscreen();
}
if (CheckMenuViewToggle(ImGuiKey_F1, m_isHidden)) {
ShowToasts();
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
SDL_HideCursor();
return;
}
bool showMenu = !dusk::IsGameLaunched || !CheckMenuViewToggle(ImGuiKey_F1, m_isHidden);
ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange;
// Imgui will re-show cursor.
// TODO: we need to be able to render the menu bar & any overlays separately
// The code currently ties them all together, so hiding the menu hides all windows
if (ImGui::BeginMainMenuBar()) {
if (showMenu && ImGui::BeginMainMenuBar()) {
m_menuGame.draw();
m_menuEnhancements.draw();
// Keep always last
m_menuTools.draw();
ImGui::SetCursorPosX(ImGui::GetWindowWidth() - 80.0f * ImGuiScale());
@@ -251,6 +233,46 @@ namespace dusk {
ImGui::EndMainMenuBar();
}
if (!dusk::IsGameLaunched) {
m_preLaunchWindow.draw();
}
if (!m_isLaunchInitialized && !getSettings().backend.wasPresetChosen) {
if (dusk::IsGameLaunched) {
m_firstRunPreset.draw();
}
return;
}
if (!m_isLaunchInitialized) {
m_toasts.emplace_back("Press F1 to toggle menu"s, 5.f);
m_isLaunchInitialized = true;
}
m_menuGame.windowControllerConfig();
m_menuGame.windowInputViewer();
if (dusk::IsGameLaunched) {
m_menuTools.ShowDebugOverlay();
m_menuTools.ShowCameraOverlay();
m_menuTools.ShowProcessManager();
m_menuTools.ShowHeapOverlay();
m_menuTools.ShowStubLog();
m_menuTools.ShowMapLoader();
m_menuTools.ShowPlayerInfo();
m_menuTools.ShowAudioDebug();
m_menuTools.ShowSaveEditor();
}
DuskDebugPad(); // temporary, remove later
// Only show cursor when menu or any windows are open
if (showMenu || ImGui::GetIO().MetricsRenderWindows > 0) {
ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange;
// Imgui will re-show cursor.
} else {
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
SDL_HideCursor();
}
ShowToasts();
}
@@ -290,6 +312,70 @@ namespace dusk {
}
}
std::string_view backend_id(AuroraBackend backend) {
switch (backend) {
default:
return "auto"sv;
case BACKEND_D3D12:
return "d3d12"sv;
case BACKEND_D3D11:
return "d3d11"sv;
case BACKEND_METAL:
return "metal"sv;
case BACKEND_VULKAN:
return "vulkan"sv;
case BACKEND_OPENGL:
return "opengl"sv;
case BACKEND_OPENGLES:
return "opengles"sv;
case BACKEND_WEBGPU:
return "webgpu"sv;
case BACKEND_NULL:
return "null"sv;
}
}
bool try_parse_backend(std::string_view backend, AuroraBackend& outBackend) {
if (backend == "auto") {
outBackend = BACKEND_AUTO;
return true;
}
if (backend == "d3d11") {
outBackend = BACKEND_D3D11;
return true;
}
if (backend == "d3d12") {
outBackend = BACKEND_D3D12;
return true;
}
if (backend == "metal") {
outBackend = BACKEND_METAL;
return true;
}
if (backend == "vulkan") {
outBackend = BACKEND_VULKAN;
return true;
}
if (backend == "opengl") {
outBackend = BACKEND_OPENGL;
return true;
}
if (backend == "opengles") {
outBackend = BACKEND_OPENGLES;
return true;
}
if (backend == "webgpu") {
outBackend = BACKEND_WEBGPU;
return true;
}
if (backend == "null") {
outBackend = BACKEND_NULL;
return true;
}
return false;
}
void ImGuiConsole::ShowToasts() {
if (m_toasts.empty()) {
return;
@@ -333,7 +419,7 @@ namespace dusk {
void ImGuiConsole::ShowPipelineProgress() {
const auto* stats = aurora_get_stats();
const u32 queuedPipelines = stats->queuedPipelines;
if (queuedPipelines == 0) {
if (queuedPipelines == 0 || !getSettings().backend.showPipelineCompilation) {
return;
}
const u32 createdPipelines = stats->createdPipelines;
+7 -1
View File
@@ -4,18 +4,19 @@
#include <aurora/aurora.h>
#include <deque>
#include <string>
#include <string_view>
#include "ImGuiFirstRunPreset.hpp"
#include "ImGuiMenuEnhancements.hpp"
#include "ImGuiMenuGame.hpp"
#include "ImGuiMenuTools.hpp"
#include "ImGuiPreLaunchWindow.hpp"
#include "imgui.h"
namespace dusk {
class ImGuiConsole {
public:
ImGuiConsole();
void InitSettings();
void UpdateSettings();
void PreDraw();
void PostDraw();
@@ -38,6 +39,7 @@ private:
ImGuiFirstRunPreset m_firstRunPreset;
ImGuiMenuGame m_menuGame;
ImGuiMenuEnhancements m_menuEnhancements;
ImGuiPreLaunchWindow m_preLaunchWindow;
// Keep always last
ImGuiMenuTools m_menuTools;
@@ -49,12 +51,16 @@ private:
extern ImGuiConsole g_imguiConsole;
std::string_view backend_name(AuroraBackend backend);
std::string_view backend_id(AuroraBackend backend);
bool try_parse_backend(std::string_view backend, AuroraBackend& outBackend);
std::string BytesToString(size_t bytes);
void SetOverlayWindowLocation(int corner);
bool ShowCornerContextMenu(int& corner, int avoidCorner);
void ImGuiStringViewText(std::string_view text);
void ImGuiBeginGroupPanel(const char* name, const ImVec2& size);
void ImGuiEndGroupPanel();
void ImGuiTextCenter(std::string_view text);
bool ImGuiButtonCenter(std::string_view text);
float ImGuiScale();
} // namespace dusk
+1 -8
View File
@@ -6,13 +6,6 @@
#include "ImGuiMenuGame.hpp"
namespace dusk {
void TextCenter(const std::string& text) {
float font_size = ImGui::GetFontSize() * text.size() / 2;
ImGui::SameLine(ImGui::GetWindowSize().x / 2 - font_size + (font_size / 2));
ImGui::TextUnformatted(text.c_str());
}
void ImGuiMenuGame::windowInputViewer() {
if (!m_showInputViewer) {
return;
@@ -35,7 +28,7 @@ namespace dusk {
if (ImGui::Begin("Input Viewer", nullptr, windowFlags)) {
float scale = ImGuiScale();
if (!m_controllerName.empty()) {
TextCenter(m_controllerName);
ImGuiTextCenter(m_controllerName);
ImGui::Separator();
}
+52 -54
View File
@@ -6,6 +6,7 @@
#include <aurora/imgui.h>
#include <cmath>
#include <cstring>
#include <fmt/format.h>
#include <string>
#include "dusk/logging.h"
@@ -32,66 +33,64 @@ bool AssetExists(const std::string& path) {
ImFont* ImGuiEngine::fontNormal;
ImFont* ImGuiEngine::fontLarge;
ImTextureID ImGuiEngine::duskIcon;
ImFont* ImGuiEngine::fontExtraLarge;
ImFont* ImGuiEngine::fontMono;
ImTextureID ImGuiEngine::duskIcon = 0;
inline ImFont* CreateFont(float size, const std::string& fontPath, std::string_view fontName) {
bool fontFileExists = !fontPath.empty() && AssetExists(fontPath);
ImFontConfig fontConfig{};
fontConfig.SizePixels = size;
auto name = fmt::format_to_n(fontConfig.Name, sizeof(fontConfig.Name) - 1, "{}, {}px", fontName,
static_cast<int>(fontConfig.SizePixels));
*name.out = '\0';
const ImGuiIO& io = ImGui::GetIO();
ImFont* outFont =
fontFileExists ?
io.Fonts->AddFontFromFileTTF(fontPath.c_str(), fontConfig.SizePixels, &fontConfig) :
nullptr;
if (outFont == nullptr) {
if (fontFileExists) {
DuskLog.warn("Failed to load font '{}': {}", fontPath, SDL_GetError());
}
outFont = io.Fonts->AddFontDefault(&fontConfig);
}
return outFont;
}
void ImGuiEngine_Initialize(float scale) {
// Round font scale to nearest integer
scale = std::ceilf(scale);
ImGui::GetCurrentContext();
ImGuiIO& io = ImGui::GetIO();
io.Fonts->Clear();
io.FontGlobalScale = scale > 0.0f ? 1.0f / scale : 1.0f;
const std::string fontPath = GetAssetPath("NotoMono-Regular.ttf");
const bool hasFontFile = AssetExists(fontPath);
ImFontConfig fontConfig{};
fontConfig.SizePixels = std::floor(15.f * scale);
snprintf(static_cast<char*>(fontConfig.Name), sizeof(fontConfig.Name),
"Noto Mono Regular, %dpx", static_cast<int>(fontConfig.SizePixels));
ImGuiEngine::fontNormal =
hasFontFile ?
io.Fonts->AddFontFromFileTTF(fontPath.c_str(), fontConfig.SizePixels, &fontConfig) :
nullptr;
if (ImGuiEngine::fontNormal == nullptr) {
if (hasFontFile) {
DuskLog.warn("Failed to load font '{}': {}", fontPath, SDL_GetError());
}
ImGuiEngine::fontNormal = io.Fonts->AddFontDefault(&fontConfig);
}
fontConfig.SizePixels = std::floor(26.f * scale);
#ifdef IMGUI_ENABLE_FREETYPE
fontConfig.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Bold;
snprintf(static_cast<char*>(fontConfig.Name), sizeof(fontConfig.Name), "Noto Mono Bold, %dpx",
static_cast<int>(fontConfig.SizePixels));
#else
snprintf(static_cast<char*>(fontConfig.Name), sizeof(fontConfig.Name),
"Noto Mono Regular, %dpx", static_cast<int>(fontConfig.SizePixels));
#endif
CreateFont(std::floor(18.f * scale), GetAssetPath("Inter-Regular.ttf"), "Inter Regular");
ImGuiEngine::fontLarge =
hasFontFile ?
io.Fonts->AddFontFromFileTTF(fontPath.c_str(), fontConfig.SizePixels, &fontConfig) :
nullptr;
if (ImGuiEngine::fontLarge == nullptr) {
if (hasFontFile) {
DuskLog.warn("Failed to load font '{}': {}", fontPath, SDL_GetError());
}
ImGuiEngine::fontLarge = io.Fonts->AddFontDefault(&fontConfig);
}
CreateFont(std::floor(26.f * scale), GetAssetPath("Inter-Regular.ttf"), "Inter Regular");
ImGuiEngine::fontExtraLarge =
CreateFont(std::floor(40.f * scale), GetAssetPath("Inter-Bold.ttf"), "Inter Bold");
ImGuiEngine::fontMono =
CreateFont(std::floor(16.f * scale), GetAssetPath("NotoMono-Regular.ttf"),
"Noto Mono Regular");
auto& style = ImGui::GetStyle();
style = {}; // Reset sizes
style.WindowPadding = ImVec2(15, 15);
style.WindowRounding = 5.0f;
style.FrameBorderSize = 1.f;
style.FramePadding = ImVec2(5, 5);
style.FramePadding = ImVec2(8, 5);
style.FrameRounding = 4.0f;
style.ItemSpacing = ImVec2(12, 8);
style.ItemInnerSpacing = ImVec2(8, 6);
style.IndentSpacing = 25.0f;
style.ScrollbarSize = 15.0f;
style.ScrollbarRounding = 9.0f;
style.GrabMinSize = 5.0f;
style.GrabRounding = 3.0f;
style.GrabMinSize = 20.0f;
style.GrabRounding = 5.0f;
style.PopupBorderSize = 1.f;
style.PopupRounding = 7.0;
style.TabBorderSize = 1.f;
@@ -145,26 +144,24 @@ void ImGuiEngine_Initialize(float scale) {
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.60f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
}
Icon GetIcon() {
const std::string iconPath = GetAssetPath("icon.png");
if (!AssetExists(iconPath)) {
Image GetImage(const std::string& path) {
if (!AssetExists(path)) {
return {};
}
SDL_Surface* loadedSurface = SDL_LoadPNG(iconPath.c_str());
SDL_Surface* loadedSurface = SDL_LoadPNG(path.c_str());
if (loadedSurface == nullptr) {
DuskLog.warn("Failed to load icon '{}': {}", iconPath, SDL_GetError());
DuskLog.warn("Failed to load image '{}': {}", path, SDL_GetError());
return {};
}
SDL_Surface* rgbaSurface = SDL_ConvertSurface(loadedSurface, SDL_PIXELFORMAT_RGBA32);
SDL_DestroySurface(loadedSurface);
if (rgbaSurface == nullptr) {
DuskLog.warn("Failed to convert icon '{}': {}", iconPath, SDL_GetError());
DuskLog.warn("Failed to convert image '{}': {}", path, SDL_GetError());
return {};
}
@@ -181,7 +178,7 @@ Icon GetIcon() {
}
SDL_DestroySurface(rgbaSurface);
return Icon{
return Image{
std::move(ptr),
size,
iconWidth,
@@ -190,12 +187,13 @@ Icon GetIcon() {
}
void ImGuiEngine_AddTextures() {
auto icon = GetIcon();
if (icon.data == nullptr || icon.width == 0 || icon.height == 0) {
ImGuiEngine::duskIcon = 0;
return;
if (ImGuiEngine::duskIcon == 0) {
auto icon = GetImage(GetAssetPath("icon.png"));
if (icon.data == nullptr || icon.width == 0 || icon.height == 0) {
ImGuiEngine::duskIcon = 0;
return;
}
ImGuiEngine::duskIcon = aurora_imgui_add_texture(icon.width, icon.height, icon.data.get());
}
ImGuiEngine::duskIcon = aurora_imgui_add_texture(icon.width, icon.height, icon.data.get());
}
} // namespace dusk
+4 -2
View File
@@ -9,17 +9,19 @@ class ImGuiEngine {
public:
static ImFont* fontNormal;
static ImFont* fontLarge;
static ImFont* fontExtraLarge;
static ImFont* fontMono;
static ImTextureID duskIcon;
};
void ImGuiEngine_Initialize(float scale);
void ImGuiEngine_AddTextures();
struct Icon {
struct Image {
std::unique_ptr<uint8_t[]> data;
size_t size;
uint32_t width;
uint32_t height;
};
Icon GetIcon();
Image GetImage(std::string_view path);
} // namespace dusk
+4
View File
@@ -118,8 +118,12 @@ void ImGuiFirstRunPreset::draw() {
if (chosen == 0) ApplyPresetVanilla();
if (chosen == 1) ApplyPresetDefault();
if (chosen == 2) ApplyPresetQoL();
getSettings().backend.wasPresetChosen.setValue(true);
config::Save();
m_done = true;
ImGui::CloseCurrentPopup();
}
+1 -1
View File
@@ -16,7 +16,7 @@ namespace dusk {
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_AlwaysAutoResize |
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
ImGui::SetNextWindowBgAlpha(0.65f);
// ImGui::SetNextWindowBgAlpha(0.65f);
if (!ImGui::Begin("Map Loader", &m_showMapLoader, windowFlags)) {
ImGui::End();
+29 -21
View File
@@ -14,48 +14,48 @@ namespace dusk {
config::ImGuiCheckbox("Bigger Wallets", getSettings().game.biggerWallets);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Wallet sizes are like in the HD version (500, 1000, 2000)");
ImGui::SetTooltip("Wallet sizes are like in the HD version. (500, 1000, 2000)");
}
config::ImGuiCheckbox("No Rupee Returns", getSettings().game.noReturnRupees);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full");
ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full.");
}
config::ImGuiCheckbox("Disable Rupee Cutscenes", getSettings().game.disableRupeeCutscenes);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Rupees won't play cutscenes after you've collected them the first time");
ImGui::SetTooltip("Rupees won't play cutscenes after you've collected them the first time.");
}
config::ImGuiCheckbox("No Sword Recoil", getSettings().game.noSwordRecoil);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Link won't recoil when his sword hits walls");
ImGui::SetTooltip("Link won't recoil when his sword hits walls.");
}
config::ImGuiCheckbox("Faster Climbing", getSettings().game.fastClimbing);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Quicker climbing on ladders and vines like the HD version");
ImGui::SetTooltip("Quicker climbing on ladders and vines like the HD version.");
}
config::ImGuiCheckbox("No Climbing Miss Animation", getSettings().game.noMissClimbing);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Prevents Link from playing a struggle animation\n"
"when using the Clawshot on vines at a weird angle");
"when grabbing ledges or climbing on vines.");
}
config::ImGuiCheckbox("Faster Tears of Light", getSettings().game.fastTears);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Tears of Light dropped by Shadow Insects pop out faster like the HD version");
ImGui::SetTooltip("Tears of Light dropped by Shadow Insects pop out faster like the HD version.");
}
config::ImGuiCheckbox("Hide TV Settings Screen", getSettings().game.hideTvSettingsScreen);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hides the TV calibration screen shown when loading a save");
ImGui::SetTooltip("Hides the TV calibration screen shown when loading a save.");
}
config::ImGuiCheckbox("Instant Saves", getSettings().game.instantSaves);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Skip the delay when writing to the Memory Card");
ImGui::SetTooltip("Skip the delay when writing to the Memory Card.");
}
ImGui::EndMenu();
@@ -64,7 +64,7 @@ namespace dusk {
if (ImGui::BeginMenu("Preferences")) {
config::ImGuiCheckbox("Mirror Mode", getSettings().game.enableMirrorMode);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Mirrors the world, matching the Wii version of the game");
ImGui::SetTooltip("Mirrors the world, matching the Wii version of the game.");
}
config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis);
@@ -73,12 +73,16 @@ namespace dusk {
}
if (ImGui::BeginMenu("Graphics")) {
config::ImGuiCheckbox("Native Bloom", getSettings().game.enableBloom);
config::ImGuiCheckbox("Unlock Framerate", getSettings().game.enableFrameInterpolation);
const bool frameInterpolationHovered = ImGui::IsItemHovered();
config::ImGuiCheckbox("Water Projection Offset", getSettings().game.useWaterProjectionOffset);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Adds GC-specific -0.01 transS offset\n"
"that causes ~6px ghost artifacts in water reflections");
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.72f, 0.2f, 1.0f));
ImGui::TextUnformatted("[EXPERIMENTAL]");
ImGui::PopStyleColor();
if (frameInterpolationHovered || ImGui::IsItemHovered()) {
ImGui::SetTooltip("Uses inter-frame interpolation to enable higher frame rates.\nVisual artifacts, animation glitches, or instability may occur.");
}
ImGui::EndMenu();
@@ -87,12 +91,12 @@ namespace dusk {
if (ImGui::BeginMenu("Audio")) {
config::ImGuiCheckbox("No Low HP Sound", getSettings().game.noLowHpSound);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Disable the beeping sound when having low health");
ImGui::SetTooltip("Disable the beeping sound when having low health.");
}
config::ImGuiCheckbox("Non-Stop Midna's Lament", getSettings().game.midnasLamentNonStop);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Prevents enemy music while Midna's Lament is playing");
ImGui::SetTooltip("Prevents enemy music while Midna's Lament is playing.");
}
ImGui::EndMenu();
@@ -103,7 +107,7 @@ namespace dusk {
config::ImGuiCheckbox("Can Transform Anywhere", getSettings().game.canTransformAnywhere);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Allows you to transform even if NPCs are looking");
ImGui::SetTooltip("Allows you to transform even if NPCs are looking.");
}
config::ImGuiCheckbox("Fast Spinner", getSettings().game.fastSpinner);
@@ -124,7 +128,7 @@ namespace dusk {
config::ImGuiCheckbox("Instant Death", getSettings().game.instantDeath);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Any hit will instantly kill you");
ImGui::SetTooltip("Any hit will instantly kill you.");
}
ImGui::EndMenu();
@@ -134,7 +138,7 @@ namespace dusk {
config::ImGuiCheckbox("Restore Wii 1.0 Glitches", getSettings().game.restoreWiiGlitches);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Restores patched glitches from Wii USA 1.0,\n"
"the first released version");
"the first released version.");
}
ImGui::EndMenu();
@@ -142,6 +146,10 @@ namespace dusk {
if (ImGui::BeginMenu("Tools")) {
config::ImGuiCheckbox("Enable Turbo Key", getSettings().game.enableTurboKeybind);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Holding TAB will speed up the game.\n"
"This will not work with the \"Unlock Framerate\" enhancement.");
}
ImGui::EndMenu();
}
+38 -15
View File
@@ -17,6 +17,8 @@
#include <aurora/gfx.h>
#include "dusk/main.h"
namespace dusk {
void ImGuiMenuGame::ToggleFullscreen() {
getSettings().video.enableFullscreen.setValue(!getSettings().video.enableFullscreen);
@@ -28,12 +30,6 @@ namespace dusk {
void ImGuiMenuGame::draw() {
if (ImGui::BeginMenu("Game")) {
if (ImGui::MenuItem("Reset", hotkeys::DO_RESET)) {
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
}
ImGui::Separator();
if (ImGui::BeginMenu("Graphics")) {
if (ImGui::MenuItem("Toggle Fullscreen", hotkeys::TOGGLE_FULLSCREEN)) {
ToggleFullscreen();
@@ -66,6 +62,14 @@ namespace dusk {
config::Save();
}
config::ImGuiCheckbox("Native Bloom", getSettings().game.enableBloom);
config::ImGuiCheckbox("Water Projection Offset", getSettings().game.useWaterProjectionOffset);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Adds GC-specific -0.01 transS offset\n"
"that causes ~6px ghost artifacts in water reflections.");
}
ImGui::EndMenu();
}
@@ -107,11 +111,25 @@ namespace dusk {
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Interface")) {
config::ImGuiCheckbox("Skip Pre-Launch UI", getSettings().backend.skipPreLaunchUI);
config::ImGuiCheckbox("Show Pipeline Compilation", getSettings().backend.showPipelineCompilation);
ImGui::EndMenu();
}
ImGui::Separator();
if (ImGui::MenuItem("Reset", hotkeys::DO_RESET)) {
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
}
if (ImGui::MenuItem("Exit")) {
dusk::IsRunning = false;
}
ImGui::EndMenu();
}
windowInputViewer();
windowControllerConfig();
}
static void drawVirtualStick(const char* id, const ImVec2& stick) {
@@ -176,7 +194,7 @@ namespace dusk {
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_AlwaysAutoResize;
ImGui::SetNextWindowBgAlpha(0.65f);
// ImGui::SetNextWindowBgAlpha(0.65f);
if (!ImGui::Begin("Controller Config", &m_showControllerConfig, windowFlags)) {
ImGui::End();
@@ -259,6 +277,7 @@ namespace dusk {
// buttons panel
const float uiButtonSize = 40 * scale;
ImVec2 btnSize(110.0f * scale, 30.0f * scale);
ImGuiBeginGroupPanel("Buttons", ImVec2(150 * scale, 20 * scale));
@@ -281,10 +300,14 @@ namespace dusk {
if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingButtonMapping == &btnMappingList[i]) {
dispName = fmt::format("Press a Key...##{}", btnName);
} else {
dispName = fmt::format("{0}##-{1}", PADGetNativeButtonName(btnMappingList[i].nativeButton), i);
const char* nativeName = PADGetNativeButtonName(btnMappingList[i].nativeButton);
if (nativeName == nullptr) {
nativeName = "[unbound]";
}
dispName = fmt::format("{0}##-{1}", nativeName, i);
}
bool pressed = ImGui::Button(dispName.c_str(),
ImVec2(100.0f * scale, 20.0f * scale));
btnSize);
if (pressed) {
m_controllerConfig.m_isReading = true;
@@ -324,7 +347,7 @@ namespace dusk {
dispName = fmt::format("{0}##-{1}", PADGetNativeAxisName(axisMappingList[trigger].nativeAxis), trigger);
}
bool pressed = ImGui::Button(dispName.c_str(),
ImVec2(100.0f * scale, 20.0f * scale));
btnSize);
if (pressed) {
m_controllerConfig.m_isReading = true;
@@ -401,7 +424,7 @@ namespace dusk {
dispName = fmt::format("{0}##-{1}", PADGetNativeButtonName(axisMappingList[axis].nativeButton), axis);
}
}
bool pressed = ImGui::Button(dispName.c_str(), ImVec2(100.0f * scale, 20.0f * scale));
bool pressed = ImGui::Button(dispName.c_str(), btnSize);
if (pressed) {
m_controllerConfig.m_isReading = true;
@@ -463,7 +486,7 @@ namespace dusk {
dispName = fmt::format("{0}##-{1}", PADGetNativeButtonName(axisMappingList[axis].nativeButton), axis);
}
}
bool pressed = ImGui::Button(fmt::format("{0}##sub{1}", dispName, label).c_str(), ImVec2(100.0f * scale, 20.0f * scale));
bool pressed = ImGui::Button(fmt::format("{0}##sub{1}", dispName, label).c_str(), btnSize);
if (pressed) {
m_controllerConfig.m_isReading = true;
+34 -41
View File
@@ -7,21 +7,22 @@
#include "ImGuiConsole.hpp"
#include "ImGuiMenuTools.hpp"
#include "ImGuiEngine.hpp"
#include "d/actor/d_a_alink.h"
#include "d/actor/d_a_horse.h"
#include "d/d_com_inf_game.h"
#include "dusk/dusk.h"
#include "dusk/main.h"
#include "m_Do/m_Do_main.h"
namespace dusk {
ImGuiMenuTools::ImGuiMenuTools() {}
void ImGuiMenuTools::draw() {
bool isToggleDevelopmentMode = false;
if (ImGui::BeginMenu("Debug")) {
if (ImGui::Checkbox("Development Mode", &m_isDevelopmentMode)) {
isToggleDevelopmentMode = true;
bool developmentMode = mDoMain::developmentMode == 1;
if (ImGui::Checkbox("Development Mode", &developmentMode)) {
mDoMain::developmentMode = developmentMode ? 1 : -1;
}
ImGui::Separator();
@@ -40,6 +41,10 @@ namespace dusk {
ImGui::EndMenu();
}
if (!dusk::IsGameLaunched) {
ImGui::BeginDisabled();
}
ImGui::MenuItem("Process Management", hotkeys::SHOW_PROCESS_MANAGEMENT, &m_showProcessManagement);
ImGui::MenuItem("Debug Overlay", hotkeys::SHOW_DEBUG_OVERLAY, &m_showDebugOverlay);
ImGui::MenuItem("Heap Viewer", hotkeys::SHOW_HEAP_VIEWER, &m_showHeapOverlay);
@@ -49,28 +54,14 @@ namespace dusk {
ImGui::MenuItem("Player Info", nullptr, &m_showPlayerInfo);
ImGui::MenuItem("Save Editor", nullptr, &m_showSaveEditor);
ImGui::MenuItem("Audio Debug", hotkeys::SHOW_AUDIO_DEBUG, &m_showAudioDebug);
if (!dusk::IsGameLaunched) {
ImGui::EndDisabled();
}
ImGui::MenuItem("OSReport Force", nullptr, &OSReportReallyForceEnable);
ImGui::EndMenu();
}
if (isToggleDevelopmentMode) {
mDoMain::developmentMode = m_isDevelopmentMode ? 1 : -1;
}
ShowDebugOverlay();
ShowCameraOverlay();
ShowProcessManager();
ShowHeapOverlay();
ShowStubLog();
ShowMapLoader();
ShowPlayerInfo();
ShowAudioDebug();
if (m_showSaveEditor) {
m_saveEditor.draw(m_showSaveEditor);
}
DuskDebugPad(); // temporary, remove later
}
void ImGuiMenuTools::ShowDebugOverlay() {
@@ -78,6 +69,8 @@ namespace dusk {
return;
}
ImGui::PushFont(ImGuiEngine::fontMono);
ImGuiIO& io = ImGui::GetIO();
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_AlwaysAutoResize |
@@ -90,26 +83,14 @@ namespace dusk {
ImGui::SetNextWindowBgAlpha(0.65f);
if (ImGui::Begin("Debug Overlay", nullptr, windowFlags)) {
bool hasPrevious = false;
if (hasPrevious) {
ImGui::Separator();
}
hasPrevious = true;
ImGuiStringViewText(fmt::format(FMT_STRING("FPS: {:.2f}\n"), io.Framerate));
ImGuiStringViewText(fmt::format(FMT_STRING("Frame usage: {:.1f}%\n"), frameUsagePct));
if (hasPrevious) {
ImGui::Separator();
}
hasPrevious = true;
ImGui::Separator();
ImGuiStringViewText(fmt::format(FMT_STRING("Backend: {}\n"), backend_name(aurora_get_backend())));
if (hasPrevious) {
ImGui::Separator();
}
hasPrevious = true;
ImGui::Separator();
const auto& stats = lastFrameAuroraStats;
@@ -141,6 +122,8 @@ namespace dusk {
ShowCornerContextMenu(m_debugOverlayCorner, m_cameraOverlayCorner);
}
ImGui::End();
ImGui::PopFont();
}
void ImGuiMenuTools::ShowPlayerInfo() {
@@ -148,13 +131,20 @@ namespace dusk {
return;
}
ImGuiIO& io = ImGui::GetIO();
ImGuiWindowFlags windowFlags =
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize;
ImGui::PushFont(ImGuiEngine::fontMono);
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_AlwaysAutoResize |
ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoNav;
if (m_playerInfoOverlayCorner != -1) {
SetOverlayWindowLocation(m_playerInfoOverlayCorner);
windowFlags |= ImGuiWindowFlags_NoMove;
}
ImGui::SetNextWindowBgAlpha(0.65f);
if (ImGui::Begin("Player Info", &m_showPlayerInfo, windowFlags)) {
if (ImGui::Begin("Player Info", nullptr, windowFlags)) {
daAlink_c* player = (daAlink_c*)dComIfGp_getPlayer(0);
daHorse_c* horse = dComIfGp_getHorseActor();
@@ -196,8 +186,11 @@ namespace dusk {
? fmt::format("Speed: {0}\n", horse->speedF)
: "Speed: ?\n"
);
ShowCornerContextMenu(m_playerInfoOverlayCorner, m_debugOverlayCorner);
}
ImGui::End();
ImGui::PopFont();
}
}
+2 -1
View File
@@ -22,6 +22,7 @@ namespace dusk {
void ShowMapLoader();
void ShowPlayerInfo();
void ShowAudioDebug();
void ShowSaveEditor();
private:
bool m_showDebugOverlay = false;
@@ -51,8 +52,8 @@ namespace dusk {
bool showInternalNames = false;
} m_mapLoaderInfo;
bool m_isDevelopmentMode = false;
bool m_showPlayerInfo = false;
int m_playerInfoOverlayCorner = 1; // top-right
bool m_showSaveEditor = false;
ImGuiSaveEditor m_saveEditor;
+194
View File
@@ -0,0 +1,194 @@
#include "imgui.h"
#include "ImGuiConfig.hpp"
#include "ImGuiEngine.hpp"
#include "ImGuiPreLaunchWindow.hpp"
#include "ImGuiConsole.hpp"
#include "dusk/main.h"
#include "dusk/settings.h"
#include <SDL3/SDL_dialog.h>
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_filesystem.h>
#include "aurora/lib/internal.hpp"
#include "aurora/lib/window.hpp"
namespace dusk {
typedef void (ImGuiPreLaunchWindow::*drawFunc)();
drawFunc drawTable[2] = {&ImGuiPreLaunchWindow::drawMainMenu, &ImGuiPreLaunchWindow::drawOptions};
static constexpr std::array<SDL_DialogFileFilter, 2> skGameDiscFileFilters{{
{"Game Disc Images", "iso;gcm;ciso;gcz;nfs;rvz;wbfs;wia;tgc"},
{"All Files", "*"},
}};
void fileDialogCallback(void* userdata, const char* const* filelist, [[maybe_unused]] int filter) {
auto* self = static_cast<ImGuiPreLaunchWindow*>(userdata);
if (filelist != nullptr) {
if (filelist[0] == nullptr) {
// Cancelled
self->m_selectedIsoPath.clear();
} else {
self->m_selectedIsoPath = filelist[0];
getSettings().backend.isoPath.setValue(self->m_selectedIsoPath);
config::Save();
}
} else {
// Error occurred
self->m_selectedIsoPath.clear();
self->m_errorString = fmt::format("File dialog error: {}", SDL_GetError());
}
}
ImGuiPreLaunchWindow::ImGuiPreLaunchWindow() = default;
bool ImGuiPreLaunchWindow::isSelectedPathValid() const {
return !m_selectedIsoPath.empty() && SDL_GetPathInfo(m_selectedIsoPath.c_str(), nullptr);
}
void ImGuiPreLaunchWindow::draw() {
if (m_IsFirstDraw) {
m_selectedIsoPath = getSettings().backend.isoPath;
m_initialGraphicsBackend = getSettings().backend.graphicsBackend;
m_IsFirstDraw = false;
}
if (isSelectedPathValid() && getSettings().backend.skipPreLaunchUI) {
dusk::IsGameLaunched = true;
return;
}
auto& io = ImGui::GetIO();
ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x, io.DisplaySize.y));
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowBgAlpha(0.65f);
ImGui::Begin("Pre Launch Window", nullptr,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoBringToFrontOnFocus);
const auto& windowSize = ImGui::GetWindowSize();
for (int i = 0; i < 5; i++)
ImGui::NewLine();
float iconSize = 150.f;
ImGui::SameLine(windowSize.x / 2 - iconSize + (iconSize / 2));
if (ImGuiEngine::duskIcon != 0)
ImGui::Image(ImGuiEngine::duskIcon, ImVec2{iconSize, iconSize});
ImGuiTextCenter("Twilit Realm presents");
ImGui::PushFont(ImGuiEngine::fontExtraLarge);
ImGuiTextCenter("Dusk");
ImGui::PopFont();
(this->*drawTable[m_CurMenu])();
ImGui::End();
}
void ImGuiPreLaunchWindow::drawMainMenu() {
const auto& windowSize = ImGui::GetWindowSize();
ImGui::SetCursorPosY(windowSize.y - 200);
ImGui::PushFont(ImGuiEngine::fontLarge);
if (m_selectedIsoPath.empty() || !SDL_GetPathInfo(m_selectedIsoPath.c_str(), nullptr)) {
if (ImGuiButtonCenter("Select disc image...")) {
SDL_ShowOpenFileDialog(&fileDialogCallback, this, aurora::window::get_sdl_window(),
skGameDiscFileFilters.data(), int(skGameDiscFileFilters.size()),
nullptr, false);
}
} else {
if (ImGuiButtonCenter("Start game")) {
dusk::IsGameLaunched = true;
}
}
if (ImGuiButtonCenter("Options")) {
m_CurMenu = 1;
}
ImGui::PopFont();
}
void ImGuiPreLaunchWindow::drawOptions() {
const auto& windowSize = ImGui::GetWindowSize();
ImGui::NewLine();
ImGui::PushFont(ImGuiEngine::fontLarge);
ImGuiTextCenter("Options");
ImGui::Separator();
ImGui::PopFont();
auto cursorY = ImGui::GetCursorPosY();
float endCursorY = windowSize.y - 100;
float childWidth = windowSize.x - 400;
ImGui::SetCursorPosX(windowSize.x / 2 - (childWidth / 2));
if (ImGui::BeginChild("OptionsChild", ImVec2(childWidth, endCursorY - cursorY),
ImGuiChildFlags_None, ImGuiWindowFlags_NoBackground))
{
ImGui::InputText("Game ISO Path", &m_selectedIsoPath, ImGuiInputTextFlags_ReadOnly);
ImGui::SameLine();
if (ImGui::Button("Set")) {
SDL_ShowOpenFileDialog(&fileDialogCallback, this, aurora::window::get_sdl_window(),
skGameDiscFileFilters.data(), int(skGameDiscFileFilters.size()),
nullptr, false);
}
AuroraBackend configuredBackend = BACKEND_AUTO;
const std::string& configuredBackendId = getSettings().backend.graphicsBackend;
if (!try_parse_backend(configuredBackendId, configuredBackend)) {
configuredBackend = BACKEND_AUTO;
}
if (ImGui::BeginCombo("Graphics Backend", backend_name(configuredBackend).data())) {
if (ImGui::Selectable("Auto", configuredBackend == BACKEND_AUTO)) {
getSettings().backend.graphicsBackend.setValue("auto");
config::Save();
}
size_t backendCount = 0;
const AuroraBackend* availableBackends = aurora_get_available_backends(&backendCount);
for (size_t i = 0; i < backendCount; ++i) {
const AuroraBackend backend = availableBackends[i];
const bool isSelected = configuredBackend == backend;
if (ImGui::Selectable(backend_name(backend).data(), isSelected)) {
getSettings().backend.graphicsBackend.setValue(
std::string(backend_id(backend)));
config::Save();
}
if (isSelected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
if (configuredBackendId != m_initialGraphicsBackend) {
ImGui::TextDisabled("Restart Required");
}
ImGui::EndChild();
}
ImGui::SetCursorPosY(endCursorY);
ImGui::NewLine();
ImGui::Separator();
ImGui::PushFont(ImGuiEngine::fontLarge);
if (ImGuiButtonCenter("Back")) {
m_CurMenu = 0;
}
ImGui::PopFont();
}
} // namespace dusk
+22
View File
@@ -0,0 +1,22 @@
#pragma once
namespace dusk {
class ImGuiPreLaunchWindow {
private:
int m_CurMenu = 0;
bool m_IsFirstDraw = true;
std::string m_initialGraphicsBackend;
bool isSelectedPathValid() const;
public:
ImGuiPreLaunchWindow();
void draw();
void drawMainMenu();
void drawOptions();
std::string m_selectedIsoPath;
std::string m_errorString;
};
} // namespace dusk
+1 -1
View File
@@ -287,7 +287,7 @@ namespace dusk {
ImGuiWindowFlags windowFlags =
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize;
ImGui::SetNextWindowBgAlpha(0.65f);
// ImGui::SetNextWindowBgAlpha(0.65f);
if (ImGui::Begin("Save Editor", &open, windowFlags)) {
if (ImGui::BeginTabBar("SaveEditorTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) {
+3
View File
@@ -2,6 +2,8 @@
#include <cstdio>
#include <cstdlib>
#include "tracy/Tracy.hpp"
bool StubLogEnabled = true;
using namespace std::literals::string_view_literals;
@@ -32,6 +34,7 @@ static bool IsForStubLog(const char* message) {
void aurora_log_callback(AuroraLogLevel level, const char* module, const char* message,
unsigned int len) {
ZoneScoped;
if (StubLogEnabled && level != LOG_FATAL && IsForStubLog(message)) {
dusk::SendToStubLog(level, module, message);
return;
+16
View File
@@ -41,6 +41,7 @@ UserSettings g_userSettings = {
// Graphics
.enableBloom {"game.enableBloom", true},
.useWaterProjectionOffset {"game.useWaterProjectionOffset", false},
.enableFrameInterpolation = {"game.enableFrameInterpolation", false},
// Audio
.noLowHpSound {"game.noLowHpSound", false},
@@ -58,6 +59,14 @@ UserSettings g_userSettings = {
// Controls
.enableTurboKeybind {"game.enableTurboKeybind", false},
},
.backend = {
.isoPath {"backend.isoPath", ""},
.graphicsBackend {"backend.graphicsBackend", "auto"},
.skipPreLaunchUI {"backend.skipPreLaunchUI", false},
.showPipelineCompilation{"backend.showPipelineCompilation", false},
.wasPresetChosen{"backend.wasPresetChosen", false}
}
};
UserSettings& getSettings() {
@@ -103,6 +112,13 @@ void registerSettings() {
Register(g_userSettings.game.midnasLamentNonStop);
Register(g_userSettings.game.enableTurboKeybind);
Register(g_userSettings.game.fastSpinner);
Register(g_userSettings.game.enableFrameInterpolation);
Register(g_userSettings.backend.isoPath);
Register(g_userSettings.backend.graphicsBackend);
Register(g_userSettings.backend.skipPreLaunchUI);
Register(g_userSettings.backend.showPipelineCompilation);
Register(g_userSettings.backend.wasPresetChosen);
}
// Transient settings
+3
View File
@@ -13,6 +13,8 @@
#include <dusk/logging.h>
#include <dusk/main.h>
#include "tracy/Tracy.hpp"
#ifndef _WIN32
#include <sys/time.h>
#include <time.h>
@@ -355,6 +357,7 @@ void VISetNextFrameBuffer(void* fb) {
}
void VIWaitForRetrace() {
ZoneScoped;
sRetraceCount++;
if (sVIPreRetraceCallback) {
sVIPreRetraceCallback(sRetraceCount);