mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-30 16:02:18 -04:00
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:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -253,3 +253,9 @@ void dusk::ImGuiMenuTools::ShowAudioDebug() {
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void dusk::ImGuiMenuTools::ShowSaveEditor() {
|
||||
if (m_showSaveEditor) {
|
||||
m_saveEditor.draw(m_showSaveEditor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user