From 36fe68637d0a357fa54af68106e7abd4481dddb0 Mon Sep 17 00:00:00 2001 From: praydog Date: Sun, 28 Apr 2024 12:50:39 -0700 Subject: [PATCH 01/10] Lua: Fix hook storage not working correctly with multiple hooks --- src/mods/ScriptRunner.cpp | 16 ++++++++++------ src/mods/ScriptRunner.hpp | 27 +++++++++++++++++++-------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/mods/ScriptRunner.cpp b/src/mods/ScriptRunner.cpp index 87afdf638..261987eda 100644 --- a/src/mods/ScriptRunner.cpp +++ b/src/mods/ScriptRunner.cpp @@ -13,6 +13,7 @@ #include "sdk/SF6Utility.hpp" #include "utility/String.hpp" +#include #include "Mods.hpp" @@ -63,16 +64,16 @@ uint32_t get_id() { return std::this_thread::get_id()._Get_underlying_id(); } -sol::object get_hook_storage(sol::this_state s, size_t hash) { +sol::object get_hook_storage(sol::this_state s) { auto sol_state = sol::state_view{s}; auto state = sol_state.registry()["state"].get(); - auto result = state->get_hook_storage(get_hash()); + auto result = state->get_hook_storage(); - if (!result.has_value()) { + if (!result.valid()) { return sol::make_object(s, sol::lua_nil); } - return sol::make_object(s, result.value()); + return sol::make_object(s, result); } } @@ -667,9 +668,13 @@ void ScriptState::install_hooks() { return; } + const auto thash = std::hash{}(std::this_thread::get_id()); + utility::ScopeGuard sg{[state, thash] { state->pop_hook_storage(thash); }}; + try { + state->m_current_hook_storage = state->get_hook_storage_internal(thash); + if (post_cb.is()) { - state->pop_hook_storage(std::hash{}(std::this_thread::get_id())); return; } @@ -680,7 +685,6 @@ void ScriptState::install_hooks() { } ret_val = (uintptr_t)script_result.get(); - state->pop_hook_storage(std::hash{}(std::this_thread::get_id())); } catch (const std::exception& e) { ScriptRunner::get()->spew_error(e.what()); } catch (...) { diff --git a/src/mods/ScriptRunner.hpp b/src/mods/ScriptRunner.hpp index 719022056..68cfeff3e 100644 --- a/src/mods/ScriptRunner.hpp +++ b/src/mods/ScriptRunner.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -166,33 +167,42 @@ class ScriptState { void push_hook_storage(size_t thread_hash) { auto it = m_hook_storage.find(thread_hash); if (it == m_hook_storage.end()) { - it = m_hook_storage.emplace(thread_hash, std::stack{}).first; + it = m_hook_storage.emplace(thread_hash, std::deque{}).first; } - it->second.push(m_lua.create_table()); + it->second.push_back(m_lua.create_table()); + m_current_hook_storage = it->second.back(); } void pop_hook_storage(size_t thread_hash) { auto it = m_hook_storage.find(thread_hash); if (it != m_hook_storage.end()) { if (!it->second.empty()) { - it->second.pop(); + it->second.pop_front(); } } + + m_current_hook_storage = sol::nil; + } + + sol::reference get_hook_storage() { + return m_current_hook_storage; } - std::optional get_hook_storage(size_t thread_hash) { +private: + sol::reference get_hook_storage_internal(size_t thread_hash) { + //return m_current_hook_storage; + auto it = m_hook_storage.find(thread_hash); if (it != m_hook_storage.end()) { if (!it->second.empty()) { - return it->second.top(); + return it->second.front(); } } - return std::nullopt; + return sol::nil; } -private: sol::state m_lua{}; GarbageCollectionData m_gc_data{}; @@ -221,7 +231,8 @@ class ScriptState { std::deque m_hooks_to_add{}; std::unordered_map> m_hooks{}; - std::unordered_map> m_hook_storage{}; + std::unordered_map> m_hook_storage{}; + sol::reference m_current_hook_storage{}; }; class ScriptRunner : public Mod { From e1f53fdda79d535c491f88557e156e47040ac402 Mon Sep 17 00:00:00 2001 From: praydog Date: Mon, 29 Apr 2024 11:22:09 -0700 Subject: [PATCH 02/10] Graphics: Add ray tracing tweaks --- shared/sdk/intrusive_ptr.hpp | 7 ++ src/Mods.cpp | 2 +- src/mods/Graphics.cpp | 235 +++++++++++++++++++++++++++++++++++ src/mods/Graphics.hpp | 75 +++++++++++ 4 files changed, 318 insertions(+), 1 deletion(-) diff --git a/shared/sdk/intrusive_ptr.hpp b/shared/sdk/intrusive_ptr.hpp index f0a8893fe..d171cff8c 100644 --- a/shared/sdk/intrusive_ptr.hpp +++ b/shared/sdk/intrusive_ptr.hpp @@ -61,6 +61,13 @@ class intrusive_ptr { return m_ptr != nullptr; } + void reset() { + if (m_ptr != nullptr) { + m_ptr->release(); + m_ptr = nullptr; + } + } + private: T* m_ptr{nullptr}; }; diff --git a/src/Mods.cpp b/src/Mods.cpp index 25fa78a3f..ca01065b4 100644 --- a/src/Mods.cpp +++ b/src/Mods.cpp @@ -44,7 +44,7 @@ Mods::Mods() { // All games!!!! m_mods.emplace_back(std::make_unique()); - m_mods.emplace_back(std::make_unique()); + m_mods.emplace_back(Graphics::get()); #if defined(RE2) || defined(RE3) || defined(RE8) m_mods.emplace_back(std::make_unique()); diff --git a/src/mods/Graphics.cpp b/src/mods/Graphics.cpp index 2c342ee33..9cec21649 100644 --- a/src/mods/Graphics.cpp +++ b/src/mods/Graphics.cpp @@ -1,3 +1,6 @@ +#include +#include + #include #include "VR.hpp" @@ -31,6 +34,11 @@ #endif #endif +std::shared_ptr& Graphics::get() { + static auto mod = std::make_shared(); + return mod; +} + void Graphics::on_config_load(const utility::Config& cfg) { for (IModValue& option : m_options) { option.config_load(cfg); @@ -47,6 +55,11 @@ void Graphics::on_frame() { if (m_disable_gui_key->is_key_down_once()) { m_disable_gui->toggle(); } + + if (m_ray_tracing_tweaks->value()) { + setup_path_trace_hook(); + apply_ray_tracing_tweaks(); + } } void Graphics::on_draw_ui() { @@ -92,6 +105,38 @@ void Graphics::on_draw_ui() { m_disable_gui_key->draw("Hide GUI key"); ImGui::TreePop(); } + +#if TDB_VER >= 69 + ImGui::SetNextItemOpen(true, ImGuiCond_::ImGuiCond_Once); + if (ImGui::TreeNode("Ray Tracing Tweaks")) { + m_ray_tracing_tweaks->draw("Enable Ray Tracing Tweaks"); + + if (m_ray_tracing_tweaks->value()) { + m_ray_trace_type->draw("Ray Trace Type"); + m_ray_trace_clone_type_pre->draw("Ray Trace Clone Type Pre"); + const auto clone_tooltip = + "Can draw another RT pass over the main RT pass. Useful for hybrid rendering.\n" + "Example: Set Ray Trace Type to Pure and Ray Trace Clone Type to ASVGF. This adds RTGI to the path traced image.\n" + "Path Space Filter is also another good alternative for RTGI but it costs more performance.\n"; + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip(clone_tooltip); + } + + m_ray_trace_clone_type_post->draw("Ray Trace Clone Type Post"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip(clone_tooltip); + } + + // Hybrid/pure + if (m_ray_trace_type->value() == (int32_t)RayTraceType::Hybrid || m_ray_trace_type->value() == (int32_t)RayTraceType::Pure) { + m_bounce_count->draw("Bounce Count"); + m_samples_per_pixel->draw("Samples Per Pixel"); + } + } + + ImGui::TreePop(); + } +#endif } void Graphics::on_present() { @@ -569,3 +614,193 @@ void Graphics::set_ultrawide_fov(bool use_vertical_fov) { } } } + +#if TDB_VER >= 69 +void Graphics::setup_path_trace_hook() { + if (m_rt_draw_hook != nullptr || m_attempted_path_trace_hook) { + return; + } + + m_attempted_path_trace_hook = true; + + spdlog::info("[Graphics] Setting up path trace hook"); + + const auto game = utility::get_executable(); + const auto ref = utility::find_function_from_string_ref(game, "RayTraceSettings", true); + + if (!ref.has_value()) { + spdlog::error("[Graphics] Failed to find function with RayTraceSettings string reference"); + return; + } + + // gets us the actual function start + const auto fn = utility::find_function_start_with_call(ref.value()); + + if (!fn.has_value()) { + spdlog::error("[Graphics] Failed to find RayTraceSettings function"); + return; + } + + spdlog::info("[Graphics] Found RayTraceSettings function @ {:x}", *fn); + + std::unordered_map offset_reference_counts{}; + + // Locate RT type offset being referenced within the function + // We'll need to find the most referenced offset being used with a "cmp" instruction + utility::exhaustive_decode((uint8_t*)*fn, 1000, [&](utility::ExhaustionContext& ctx) -> utility::ExhaustionResult { + const auto mnem = std::string_view{ctx.instrux.Mnemonic}; + // Do not care about calls + if (mnem.starts_with("CALL")) { + return utility::ExhaustionResult::STEP_OVER; + } + + if (!mnem.starts_with("CMP")) { + return utility::ExhaustionResult::CONTINUE; + } + + // We're looking for cmp dword ptr [reg+offset], imm + if (ctx.instrux.Operands[0].Type != ND_OP_MEM || ctx.instrux.Operands[1].Type != ND_OP_IMM) { + return utility::ExhaustionResult::CONTINUE; + } + + if (!ctx.instrux.Operands[0].Info.Memory.HasBase) { + return utility::ExhaustionResult::CONTINUE; + } + + const auto offset = ctx.instrux.Operands[0].Info.Memory.Disp; + + if (offset_reference_counts.contains(offset)) { + offset_reference_counts[offset]++; + } else { + offset_reference_counts[offset] = 1; + } + + spdlog::info("[Graphics] Encountered a CMP offset @ {:x}", offset); + + return utility::ExhaustionResult::CONTINUE; + }); + + if (offset_reference_counts.empty()) { + spdlog::error("[Graphics] Failed to find any RT type offsets"); + return; + } + + const auto max_offset = std::max_element(offset_reference_counts.begin(), offset_reference_counts.end(), [](const auto& a, const auto& b) { + return a.second < b.second; + }); + + if (max_offset == offset_reference_counts.end()) { + spdlog::error("[Graphics] Failed to find most referenced RT type offset"); + return; + } + + m_rt_type_offset = max_offset->first; + spdlog::info("[Graphics] Found RT type offset @ {:x}", *m_rt_type_offset); + + m_rt_draw_hook = std::make_unique(*fn, (uintptr_t)rt_draw_impl_hook); + if (!m_rt_draw_hook->create()) { + spdlog::error("[Graphics] Failed to create path trace hook"); + return; + } + + spdlog::info("[Graphics] Path trace hook set up"); +} + +void Graphics::setup_rt_component() { + static const auto rt_t = sdk::find_type_definition("via.render.ExperimentalRayTrace"); + + if (rt_t == nullptr) { + return; + } + + // Means our reference is still valid + if (m_rt_component != nullptr && m_rt_component->referenceCount > 1) { + return; + } + + m_rt_component.reset(); + + const auto camera = sdk::get_primary_camera(); + + if (camera == nullptr) { + return; + } + + const auto game_object = utility::re_component::get_game_object(camera); + + if (game_object == nullptr || game_object->transform == nullptr) { + return; + } + + auto rt_component = utility::re_component::find(game_object->transform, rt_t->get_type()); + + // Attempt to create the component if it doesn't exist + if (rt_component == nullptr) { + rt_component = sdk::call_object_func_easy(game_object, "createComponent(System.Type)", rt_t->get_runtime_type()); + } + + if (rt_component == nullptr) { + return; + } + + m_rt_component = (sdk::ManagedObject*)rt_component; +} + +void Graphics::apply_ray_tracing_tweaks() { + setup_rt_component(); + + if (m_rt_component == nullptr) { + return; + } + + static const auto rt_t = sdk::find_type_definition("via.render.ExperimentalRayTrace"); + + if (rt_t == nullptr) { + return; + } + + static const auto set_RaytracingMode = rt_t->get_method("set_RaytracingMode"); + static const auto setBounce = rt_t->get_method("setBounce"); + static const auto setSpp = rt_t->get_method("setSpp"); + + const auto context = sdk::get_thread_context(); + + if (set_RaytracingMode != nullptr && m_ray_trace_type->value() > (int32_t)RayTraceType::Disabled) { + set_RaytracingMode->call(context, m_rt_component.get(), m_ray_trace_type->value() - 1); + } + + if (m_ray_trace_type->value() == (int32_t)RayTraceType::Hybrid || m_ray_trace_type->value() == (int32_t)RayTraceType::Pure) { + if (setBounce != nullptr) { + setBounce->call(context, m_rt_component.get(), m_bounce_count->value()); + } + + if (setSpp != nullptr) { + setSpp->call(context, m_rt_component.get(), m_samples_per_pixel->value()); + } + } +} + +void* Graphics::rt_draw_impl_hook(void* rt_impl, void* draw_context, void* r8, void* r9, void* unk) { + auto& graphics = Graphics::get(); + + uint8_t& ray_tracing_mode = *(uint8_t*)((uintptr_t)rt_impl + graphics->m_rt_type_offset.value()); + const auto old_mode = ray_tracing_mode; + const auto og = graphics->m_rt_draw_hook->get_original(); + + if (graphics->m_ray_tracing_tweaks->value() && graphics->m_ray_trace_clone_type_pre->value() > (int32_t)RayTraceType::Disabled) { + ray_tracing_mode = graphics->m_ray_trace_clone_type_pre->value() - 1; + og(rt_impl, draw_context, r8, r9, unk); + ray_tracing_mode = old_mode; + } + + const auto result = og(rt_impl, draw_context, r8, r9, unk); + + if (graphics->m_ray_tracing_tweaks->value() && graphics->m_ray_trace_clone_type_post->value() > (int32_t)RayTraceType::Disabled) { + ray_tracing_mode = graphics->m_ray_trace_clone_type_post->value() - 1; + og(rt_impl, draw_context, r8, r9, unk); + ray_tracing_mode = old_mode; + } + + return result; +} +#endif \ No newline at end of file diff --git a/src/mods/Graphics.hpp b/src/mods/Graphics.hpp index f0d07778c..079395bff 100644 --- a/src/mods/Graphics.hpp +++ b/src/mods/Graphics.hpp @@ -3,12 +3,18 @@ #include #include +#include +#include + #include "Mod.hpp" class REManagedObject; class REComponent; class Graphics : public Mod { +public: + static std::shared_ptr& get(); + public: std::string_view get_name() const override { return "Graphics"; }; @@ -34,6 +40,19 @@ class Graphics : public Mod { void do_ultrawide_fov_restore(bool force = false); void set_ultrawide_fov(bool enable); +#if TDB_VER >= 69 + void setup_path_trace_hook(); + void setup_rt_component(); + void apply_ray_tracing_tweaks(); + + static void* rt_draw_impl_hook(void* rt, void* draw_context, void* r8, void* r9, void* unk); + std::unique_ptr m_rt_draw_hook{}; + bool m_attempted_path_trace_hook{ false }; + + std::optional m_rt_type_offset{}; + sdk::intrusive_ptr m_rt_component{}; +#endif + std::recursive_mutex m_fov_mutex{}; std::unordered_map<::REManagedObject*, float> m_fov_map{}; std::unordered_map<::REManagedObject*, bool> m_vertical_fov_map{}; @@ -51,6 +70,53 @@ class Graphics : public Mod { const ModToggle::Ptr m_force_render_res_to_window{ ModToggle::create(generate_name("ForceRenderResToWindow"), false) }; const ModKey::Ptr m_disable_gui_key{ ModKey::create(generate_name("DisableGUIKey")) }; +#if TDB_VER >= 69 + const ModToggle::Ptr m_ray_tracing_tweaks { ModToggle::create(generate_name("RayTracingTweaks"), false) }; + + enum class RayTraceType : uint8_t { + Disabled = 0, + AmbientOcclusion = 1, + Hybrid = 2, + Pure = 3, + PathSpaceFilter = 4, + ScreenSpacePhotonMapping = 5, + Debug = 6, + ASVGF = 7, + }; + + static const inline std::vector s_ray_trace_type { + "Disabled", + "Ambient Occlusion", + "Hybrid Path Tracing", + "Pure Path Tracing", + "Path Space Filter", + "Screen Space Photon Mapping", + "Debug", + "ASVGF", + }; + + static const inline std::vector s_bounce_count { + "0", + "1", + "2", + "3", + "7" + }; + + static const inline std::vector s_samples_per_pixel { + "1", + "2", + "4" + }; + + const ModCombo::Ptr m_ray_trace_type{ ModCombo::create(generate_name("RayTraceType"), s_ray_trace_type) }; + const ModCombo::Ptr m_ray_trace_clone_type_pre{ ModCombo::create(generate_name("RayTraceCloneTypePre"), s_ray_trace_type) }; + const ModCombo::Ptr m_ray_trace_clone_type_post{ ModCombo::create(generate_name("RayTraceCloneTypePost"), s_ray_trace_type) }; + + const ModCombo::Ptr m_bounce_count{ ModCombo::create(generate_name("BounceCount"), s_bounce_count, 1) }; + const ModCombo::Ptr m_samples_per_pixel{ ModCombo::create(generate_name("SamplesPerPixel"), s_samples_per_pixel, 1) }; +#endif + #ifdef RE4 const ModToggle::Ptr m_scope_tweaks{ ModToggle::create(generate_name("ScopeTweaks"), false) }; const ModToggle::Ptr m_scope_interlaced_rendering{ ModToggle::create(generate_name("ScopeInterlacedRendering"), false) }; @@ -68,6 +134,15 @@ class Graphics : public Mod { *m_force_render_res_to_window, *m_disable_gui_key, +#if TDB_VER >= 69 + *m_ray_tracing_tweaks, + *m_ray_trace_type, + *m_ray_trace_clone_type_pre, + *m_ray_trace_clone_type_post, + *m_bounce_count, + *m_samples_per_pixel, +#endif + #ifdef RE4 *m_scope_tweaks, *m_scope_interlaced_rendering, From 30ec83158ffb58bb0ba77fe018a3bf64da418fe1 Mon Sep 17 00:00:00 2001 From: praydog Date: Mon, 29 Apr 2024 11:58:28 -0700 Subject: [PATCH 03/10] RT: Fix some bugs in other games --- src/mods/Graphics.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/mods/Graphics.cpp b/src/mods/Graphics.cpp index 9cec21649..a9105bb2a 100644 --- a/src/mods/Graphics.cpp +++ b/src/mods/Graphics.cpp @@ -669,6 +669,11 @@ void Graphics::setup_path_trace_hook() { const auto offset = ctx.instrux.Operands[0].Info.Memory.Disp; + // ones that are comparing to 6 (ASVGF) are the right one. + if (ctx.instrux.Operands[0].Info.Memory.DispSize != 4 || (ctx.instrux.Operands[1].Info.Immediate.Imm & 0xFFFFFFFF) != 6) { + return utility::ExhaustionResult::CONTINUE; + } + if (offset_reference_counts.contains(offset)) { offset_reference_counts[offset]++; } else { @@ -712,13 +717,6 @@ void Graphics::setup_rt_component() { if (rt_t == nullptr) { return; } - - // Means our reference is still valid - if (m_rt_component != nullptr && m_rt_component->referenceCount > 1) { - return; - } - - m_rt_component.reset(); const auto camera = sdk::get_primary_camera(); @@ -740,10 +738,13 @@ void Graphics::setup_rt_component() { } if (rt_component == nullptr) { + m_rt_component.reset(); return; } - m_rt_component = (sdk::ManagedObject*)rt_component; + if (m_rt_component.get() != (sdk::ManagedObject*)rt_component) { + m_rt_component = (sdk::ManagedObject*)rt_component; + } } void Graphics::apply_ray_tracing_tweaks() { From e4413544ce52e9523a08d394ac756314e3670593 Mon Sep 17 00:00:00 2001 From: praydog Date: Mon, 29 Apr 2024 12:17:08 -0700 Subject: [PATCH 04/10] Fix other games failing to compile --- src/mods/Graphics.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mods/Graphics.cpp b/src/mods/Graphics.cpp index a9105bb2a..81e2e8484 100644 --- a/src/mods/Graphics.cpp +++ b/src/mods/Graphics.cpp @@ -56,10 +56,12 @@ void Graphics::on_frame() { m_disable_gui->toggle(); } +#if TDB_VER >= 69 if (m_ray_tracing_tweaks->value()) { setup_path_trace_hook(); apply_ray_tracing_tweaks(); } +#endif } void Graphics::on_draw_ui() { From 1dccb382de94359cdbcbe63c1eb99f4b8f62b1bb Mon Sep 17 00:00:00 2001 From: praydog Date: Mon, 29 Apr 2024 17:22:59 -0700 Subject: [PATCH 05/10] RT: Fixes + True clone option --- shared/sdk/REComponent.hpp | 11 ++++ src/mods/Graphics.cpp | 126 ++++++++++++++++++++++++++++++++----- src/mods/Graphics.hpp | 8 ++- 3 files changed, 129 insertions(+), 16 deletions(-) diff --git a/shared/sdk/REComponent.hpp b/shared/sdk/REComponent.hpp index e91856298..15344b952 100644 --- a/shared/sdk/REComponent.hpp +++ b/shared/sdk/REComponent.hpp @@ -42,6 +42,17 @@ namespace utility::re_component { return nullptr; } + template + static T** find_replaceable(::REComponent* comp, REType* t) { + for (auto* child = &comp->childComponent; *child != nullptr && *child != comp; child = &(*child)->childComponent) { + if (utility::re_managed_object::is_a(*child, t)) { + return (T**)child; + } + } + + return nullptr; + } + // Find a component using the getComponent method /*template static T *find_using_method(::REComponent *comp, std::string_view name) { diff --git a/src/mods/Graphics.cpp b/src/mods/Graphics.cpp index 81e2e8484..94eea7153 100644 --- a/src/mods/Graphics.cpp +++ b/src/mods/Graphics.cpp @@ -115,11 +115,13 @@ void Graphics::on_draw_ui() { if (m_ray_tracing_tweaks->value()) { m_ray_trace_type->draw("Ray Trace Type"); - m_ray_trace_clone_type_pre->draw("Ray Trace Clone Type Pre"); + const auto clone_tooltip = "Can draw another RT pass over the main RT pass. Useful for hybrid rendering.\n" "Example: Set Ray Trace Type to Pure and Ray Trace Clone Type to ASVGF. This adds RTGI to the path traced image.\n" "Path Space Filter is also another good alternative for RTGI but it costs more performance.\n"; + + m_ray_trace_clone_type_pre->draw("Ray Trace Clone Type Pre"); if (ImGui::IsItemHovered()) { ImGui::SetTooltip(clone_tooltip); } @@ -128,9 +130,19 @@ void Graphics::on_draw_ui() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip(clone_tooltip); } + + m_ray_trace_clone_type_true->draw("Ray Trace Clone Type True"); + if (ImGui::IsItemHovered()) { + const auto true_tooltip = + "Uses a completely separate RT component instead of re-using the main RT component.\n" + "Might crash or have other issues. Use with caution.\n"; + ImGui::SetTooltip(true_tooltip); + } // Hybrid/pure - if (m_ray_trace_type->value() == (int32_t)RayTraceType::Hybrid || m_ray_trace_type->value() == (int32_t)RayTraceType::Pure) { + if (m_ray_trace_type->value() == (int32_t)RayTraceType::Hybrid || m_ray_trace_type->value() == (int32_t)RayTraceType::Pure + || m_ray_trace_clone_type_true->value() == (int32_t)RayTraceType::Hybrid || m_ray_trace_clone_type_true->value() == (int32_t)RayTraceType::Pure) + { m_bounce_count->draw("Bounce Count"); m_samples_per_pixel->draw("Samples Per Pixel"); } @@ -619,7 +631,7 @@ void Graphics::set_ultrawide_fov(bool use_vertical_fov) { #if TDB_VER >= 69 void Graphics::setup_path_trace_hook() { - if (m_rt_draw_hook != nullptr || m_attempted_path_trace_hook) { + if (m_attempted_path_trace_hook) { return; } @@ -704,9 +716,30 @@ void Graphics::setup_path_trace_hook() { m_rt_type_offset = max_offset->first; spdlog::info("[Graphics] Found RT type offset @ {:x}", *m_rt_type_offset); - m_rt_draw_hook = std::make_unique(*fn, (uintptr_t)rt_draw_impl_hook); + m_rt_draw_impl_hook = std::make_unique(*fn, (uintptr_t)rt_draw_impl_hook); + if (!m_rt_draw_impl_hook->create()) { + spdlog::error("[Graphics] Failed to create path trace draw impl hook"); + return; + } + + const auto draw_ref = utility::find_function_from_string_ref(game, "Bounce2", true); + + if (!draw_ref.has_value()) { + spdlog::error("[Graphics] Failed to find function with Bounce2 string reference"); + return; + } + + const auto draw_fn = utility::find_virtual_function_start(draw_ref.value()); + + if (!draw_fn.has_value()) { + spdlog::error("[Graphics] Failed to find Bounce2 function"); + return; + } + + m_rt_draw_hook = std::make_unique(*draw_fn, (uintptr_t)rt_draw_hook); + if (!m_rt_draw_hook->create()) { - spdlog::error("[Graphics] Failed to create path trace hook"); + spdlog::error("[Graphics] Failed to create path trace draw hook"); return; } @@ -732,11 +765,21 @@ void Graphics::setup_rt_component() { return; } - auto rt_component = utility::re_component::find(game_object->transform, rt_t->get_type()); + const auto go_name = utility::re_string::get_view(game_object->name); + if ((!go_name.starts_with(L"Main") && !go_name.starts_with(L"main"))) { + return; + } + + auto rt_component = utility::re_component::find(game_object->transform, rt_t->get_type()); + // Attempt to create the component if it doesn't exist if (rt_component == nullptr) { rt_component = sdk::call_object_func_easy(game_object, "createComponent(System.Type)", rt_t->get_runtime_type()); + + if (rt_component != nullptr) { + spdlog::info("[Graphics] Successfully created new RT component @ {:x}", (uintptr_t)rt_component); + } } if (rt_component == nullptr) { @@ -747,6 +790,14 @@ void Graphics::setup_rt_component() { if (m_rt_component.get() != (sdk::ManagedObject*)rt_component) { m_rt_component = (sdk::ManagedObject*)rt_component; } + + if (m_rt_cloned_component.get() == nullptr && m_ray_trace_clone_type_true->value() > (int32_t)RayTraceType::Disabled) { + m_rt_cloned_component = (sdk::ManagedObject*)rt_t->create_instance_full(false); + + if (m_rt_cloned_component.get() != nullptr) { + spdlog::info("[Graphics] Successfully cloned RT component @ {:x}", (uintptr_t)m_rt_cloned_component.get()); + } + } } void Graphics::apply_ray_tracing_tweaks() { @@ -766,21 +817,66 @@ void Graphics::apply_ray_tracing_tweaks() { static const auto setBounce = rt_t->get_method("setBounce"); static const auto setSpp = rt_t->get_method("setSpp"); - const auto context = sdk::get_thread_context(); + auto fix = [this](sdk::ManagedObject* target, int32_t rt_type) { + if (target == nullptr) { + return; + } + + const auto context = sdk::get_thread_context(); + + if (set_RaytracingMode != nullptr && rt_type > (int32_t)RayTraceType::Disabled) { + set_RaytracingMode->call(context, target, rt_type - 1); + } + + if (rt_type == (int32_t)RayTraceType::Hybrid || rt_type == (int32_t)RayTraceType::Pure) { + if (setBounce != nullptr) { + setBounce->call(context, target, m_bounce_count->value()); + } + + if (setSpp != nullptr) { + setSpp->call(context, target, m_samples_per_pixel->value()); + } + } + }; + + fix(m_rt_component.get(), m_ray_trace_type->value()); + fix(m_rt_cloned_component.get(), m_ray_trace_clone_type_true->value()); +} - if (set_RaytracingMode != nullptr && m_ray_trace_type->value() > (int32_t)RayTraceType::Disabled) { - set_RaytracingMode->call(context, m_rt_component.get(), m_ray_trace_type->value() - 1); +void* Graphics::rt_draw_hook(REComponent* rt, void* draw_context, void* r8, void* r9) { + auto& graphics = Graphics::get(); + + const auto og = graphics->m_rt_draw_hook->get_original(); + const auto result = og(rt, draw_context, r8, r9); + + if (graphics->m_rt_cloned_component.get() == nullptr) { + return result; } - if (m_ray_trace_type->value() == (int32_t)RayTraceType::Hybrid || m_ray_trace_type->value() == (int32_t)RayTraceType::Pure) { - if (setBounce != nullptr) { - setBounce->call(context, m_rt_component.get(), m_bounce_count->value()); + if (graphics->m_ray_tracing_tweaks->value() && graphics->m_ray_trace_clone_type_true->value() > (int32_t)RayTraceType::Disabled) { + auto go = utility::re_component::get_game_object(rt); + + if (go == nullptr || go->transform == nullptr) { + return result; } - if (setSpp != nullptr) { - setSpp->call(context, m_rt_component.get(), m_samples_per_pixel->value()); + static auto rt_t = sdk::find_type_definition("via.render.ExperimentalRayTrace"); + + if (rt_t == nullptr) { + return result; + } + + auto replaceable_rt = utility::re_component::find_replaceable(go->transform, rt_t->get_type()); + + // The cursed part of the code + if (replaceable_rt != nullptr) { + *replaceable_rt = (REComponent*)graphics->m_rt_cloned_component.get(); + og((REComponent*)graphics->m_rt_cloned_component.get(), draw_context, r8, r9); + *replaceable_rt = rt; } } + + return result; } void* Graphics::rt_draw_impl_hook(void* rt_impl, void* draw_context, void* r8, void* r9, void* unk) { @@ -788,7 +884,7 @@ void* Graphics::rt_draw_impl_hook(void* rt_impl, void* draw_context, void* r8, v uint8_t& ray_tracing_mode = *(uint8_t*)((uintptr_t)rt_impl + graphics->m_rt_type_offset.value()); const auto old_mode = ray_tracing_mode; - const auto og = graphics->m_rt_draw_hook->get_original(); + const auto og = graphics->m_rt_draw_impl_hook->get_original(); if (graphics->m_ray_tracing_tweaks->value() && graphics->m_ray_trace_clone_type_pre->value() > (int32_t)RayTraceType::Disabled) { ray_tracing_mode = graphics->m_ray_trace_clone_type_pre->value() - 1; diff --git a/src/mods/Graphics.hpp b/src/mods/Graphics.hpp index 079395bff..3f3e4a709 100644 --- a/src/mods/Graphics.hpp +++ b/src/mods/Graphics.hpp @@ -45,12 +45,16 @@ class Graphics : public Mod { void setup_rt_component(); void apply_ray_tracing_tweaks(); - static void* rt_draw_impl_hook(void* rt, void* draw_context, void* r8, void* r9, void* unk); + static void* rt_draw_hook(REComponent* rt, void* draw_context, void* r8, void* r9); + static void* rt_draw_impl_hook(void* rt_impl, void* draw_context, void* r8, void* r9, void* unk); + std::unique_ptr m_rt_draw_hook{}; + std::unique_ptr m_rt_draw_impl_hook{}; bool m_attempted_path_trace_hook{ false }; std::optional m_rt_type_offset{}; sdk::intrusive_ptr m_rt_component{}; + sdk::intrusive_ptr m_rt_cloned_component{}; #endif std::recursive_mutex m_fov_mutex{}; @@ -110,6 +114,7 @@ class Graphics : public Mod { }; const ModCombo::Ptr m_ray_trace_type{ ModCombo::create(generate_name("RayTraceType"), s_ray_trace_type) }; + const ModCombo::Ptr m_ray_trace_clone_type_true{ ModCombo::create(generate_name("RayTraceTrueCloneType"), s_ray_trace_type) }; const ModCombo::Ptr m_ray_trace_clone_type_pre{ ModCombo::create(generate_name("RayTraceCloneTypePre"), s_ray_trace_type) }; const ModCombo::Ptr m_ray_trace_clone_type_post{ ModCombo::create(generate_name("RayTraceCloneTypePost"), s_ray_trace_type) }; @@ -137,6 +142,7 @@ class Graphics : public Mod { #if TDB_VER >= 69 *m_ray_tracing_tweaks, *m_ray_trace_type, + *m_ray_trace_clone_type_true, *m_ray_trace_clone_type_pre, *m_ray_trace_clone_type_post, *m_bounce_count, From 3ff3531f2df10c3bd434bc38b37321518f2dd4c2 Mon Sep 17 00:00:00 2001 From: praydog Date: Mon, 29 Apr 2024 18:53:38 -0700 Subject: [PATCH 06/10] RT: Fix in SF6 --- src/mods/Graphics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mods/Graphics.cpp b/src/mods/Graphics.cpp index 94eea7153..2f086be44 100644 --- a/src/mods/Graphics.cpp +++ b/src/mods/Graphics.cpp @@ -767,7 +767,7 @@ void Graphics::setup_rt_component() { const auto go_name = utility::re_string::get_view(game_object->name); - if ((!go_name.starts_with(L"Main") && !go_name.starts_with(L"main"))) { + if ((!go_name.starts_with(L"Main") && !go_name.starts_with(L"main")) && !go_name.contains(L"DefaultCamera")) { return; } From fdc846e81a89f6b883ff818c97e06c029b293180 Mon Sep 17 00:00:00 2001 From: praydog Date: Tue, 30 Apr 2024 14:15:26 -0700 Subject: [PATCH 07/10] FreeCam: Disable mouse movement when UI is focused --- src/REFramework.hpp | 1 + src/mods/FreeCam.cpp | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/REFramework.hpp b/src/REFramework.hpp index c11f4c4d6..c51dc8131 100644 --- a/src/REFramework.hpp +++ b/src/REFramework.hpp @@ -45,6 +45,7 @@ class REFramework { Address get_module() const { return m_game_module; } bool is_ready() const { return m_initialized && m_game_data_initialized; } + bool is_ui_focused() const { return m_is_ui_focused; } void run_imgui_frame(bool from_present); diff --git a/src/mods/FreeCam.cpp b/src/mods/FreeCam.cpp index afb7cb3ec..e3b29a8ab 100644 --- a/src/mods/FreeCam.cpp +++ b/src/mods/FreeCam.cpp @@ -123,7 +123,6 @@ void FreeCam::on_update_transform(RETransform* transform) { return; } - #if defined(RE2) || defined(RE3) static auto get_player_condition_method = sdk::find_method_definition(game_namespace("SurvivorManager"), "get_Player"); static auto get_action_orderer_method = sdk::find_method_definition(game_namespace("survivor.SurvivorCondition"), "get_ActionOrderer"); @@ -252,11 +251,13 @@ void FreeCam::on_update_transform(RETransform* transform) { dir_speed *= dir_speed_mod_slow; } - const auto& mouse_delta = g_framework->get_mouse_delta(); + if (!g_framework->is_ui_focused()) { + const auto& mouse_delta = g_framework->get_mouse_delta(); - m_custom_angles[0] -= mouse_delta[1] * rotation_speed_kbm * delta * timescale_mult; - m_custom_angles[1] -= mouse_delta[0] * rotation_speed_kbm * delta * timescale_mult; - m_custom_angles[2] = 0.0f; + m_custom_angles[0] -= mouse_delta[1] * rotation_speed_kbm * delta * timescale_mult; + m_custom_angles[1] -= mouse_delta[0] * rotation_speed_kbm * delta * timescale_mult; + m_custom_angles[2] = 0.0f; + } math::fix_angles(m_custom_angles); From 9de94eac3f049da0324bd738174557642abc8d43 Mon Sep 17 00:00:00 2001 From: praydog Date: Tue, 30 Apr 2024 15:55:54 -0700 Subject: [PATCH 08/10] FreeCam: Add twist modifier (RT/RButton) and Up/Down modifier (LT) --- src/mods/FreeCam.cpp | 76 +++++++++++++++++++++++++++++++++++++------- src/mods/FreeCam.hpp | 3 +- 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/src/mods/FreeCam.cpp b/src/mods/FreeCam.cpp index e3b29a8ab..698d06cba 100644 --- a/src/mods/FreeCam.cpp +++ b/src/mods/FreeCam.cpp @@ -166,7 +166,8 @@ void FreeCam::on_update_transform(RETransform* transform) { m_first_time = false; - m_custom_angles = math::euler_angles(glm::extractMatrixRotation(m_last_camera_matrix)); + m_custom_rotation = glm::extractMatrixRotation(m_last_camera_matrix); + m_custom_angles = {}; // per-frame rotation gets reset. //m_custom_angles[1] *= -1.0f; //m_custom_angles[1] += glm::radians(180.0f); @@ -212,17 +213,63 @@ void FreeCam::on_update_transform(RETransform* transform) { // Controller support if (pad != nullptr) { + static const auto gamepad_device_t = sdk::find_type_definition("via.hid.GamePadDevice"); + static const auto is_down = gamepad_device_t != nullptr ? gamepad_device_t->get_method("isDown(via.hid.GamePadButton)") : nullptr; + // Move direction // It's not a Vector2f because via.vec2 is not actually 8 bytes, we don't want stack corruption to occur. const auto axis_l = *re_managed_object::get_field(pad, "AxisL"); const auto axis_r = *re_managed_object::get_field(pad, "AxisR"); - m_custom_angles[0] += axis_r.y * rotation_speed * delta * timescale_mult; - m_custom_angles[1] -= axis_r.x * rotation_speed * delta * timescale_mult; - m_custom_angles[2] = 0.0f; + bool is_using_up_down_modifier = false; + bool is_using_twist_modifier = false; + + if (is_down != nullptr) { + const auto dpad_up_is_down = is_down->call_safe(sdk::get_thread_context(), pad, via::hid::GamePadButton::LUp); + const auto dpad_down_is_down = is_down->call_safe(sdk::get_thread_context(), pad, via::hid::GamePadButton::LDown); + + if (dpad_up_is_down) { + dir.y = 1.0f; + } else if (dpad_down_is_down) { + dir.y = -1.0f; + } + + const auto dpad_left_is_down = is_down->call_safe(sdk::get_thread_context(), pad, via::hid::GamePadButton::LLeft); + const auto dpad_right_is_down = is_down->call_safe(sdk::get_thread_context(), pad, via::hid::GamePadButton::LRight); + + if (dpad_left_is_down) { + dir.x -= 1.0f; + } else if (dpad_right_is_down) { + dir.x += 1.0f; + } + + const auto l_trigger_is_down = is_down->call_safe(sdk::get_thread_context(), pad, via::hid::GamePadButton::LTrigBottom); + + if (l_trigger_is_down) { + if (glm::length(axis_r) > 0.0f) { + dir += Vector4f{ 0.0, axis_r.y, 0.0, 0.0f }; + is_using_up_down_modifier = true; + } + } + + const auto r_trigger_is_down = is_down->call_safe(sdk::get_thread_context(), pad, via::hid::GamePadButton::RTrigBottom); + + if (r_trigger_is_down) { + if (glm::length(axis_r) > 0.0f) { + m_custom_angles[2] -= axis_r.x * rotation_speed * delta * timescale_mult; + is_using_twist_modifier = true; + } + } + } + + if (!is_using_up_down_modifier && !is_using_twist_modifier) { + m_custom_angles[0] += axis_r.y * rotation_speed * delta * timescale_mult; + m_custom_angles[1] -= axis_r.x * rotation_speed * delta * timescale_mult; + //m_custom_angles[2] = 0.0f; + } if (glm::length(axis_l) > 0.0f) { - dir = Vector4f{ axis_l.x, 0.0f, axis_l.y * -1.0f, 0.0f }; + dir += Vector4f{ axis_l.x, 0.0f, axis_l.y * -1.0f, 0.0f }; } } @@ -254,18 +301,25 @@ void FreeCam::on_update_transform(RETransform* transform) { if (!g_framework->is_ui_focused()) { const auto& mouse_delta = g_framework->get_mouse_delta(); - m_custom_angles[0] -= mouse_delta[1] * rotation_speed_kbm * delta * timescale_mult; - m_custom_angles[1] -= mouse_delta[0] * rotation_speed_kbm * delta * timescale_mult; - m_custom_angles[2] = 0.0f; + if (keyboard_state[VK_RBUTTON]) { + m_custom_angles[2] -= mouse_delta[0] * rotation_speed_kbm * delta * timescale_mult; + } else { + m_custom_angles[0] -= mouse_delta[1] * rotation_speed_kbm * delta * timescale_mult; + m_custom_angles[1] -= mouse_delta[0] * rotation_speed_kbm * delta * timescale_mult; + } } math::fix_angles(m_custom_angles); - const auto new_rotation = Matrix4x4f{ glm::quat{ m_custom_angles } }; - const auto new_pos = m_last_camera_matrix[3] + new_rotation * dir * (dir_speed * delta * timescale_mult); + if (glm::length(m_custom_angles) > 0.0f) { + m_custom_rotation *= glm::quat{ m_custom_angles }; + m_custom_angles = {}; + } + + const auto new_pos = m_last_camera_matrix[3] + m_custom_rotation * dir * (dir_speed * delta * timescale_mult); // Keep track of the rotation if we want to lock the camera - m_last_camera_matrix = new_rotation; + m_last_camera_matrix = glm::mat4{m_custom_rotation}; m_last_camera_matrix[3] = new_pos; } diff --git a/src/mods/FreeCam.hpp b/src/mods/FreeCam.hpp index df2ea2ce2..344acdd36 100644 --- a/src/mods/FreeCam.hpp +++ b/src/mods/FreeCam.hpp @@ -36,7 +36,7 @@ class FreeCam : public Mod { const ModSlider::Ptr m_speed{ ModSlider::create(generate_name("Speed"), 0.0f, 1.0f, 0.1f) }; const ModSlider::Ptr m_speed_modifier{ ModSlider::create(generate_name("SpeedModifier"), 1.f, 50.f, 4.f) }; - const ModSlider::Ptr m_rotation_speed{ ModSlider::create(generate_name("RotationSpeed"), 0.0f, 1.0f, 1.0f) }; + const ModSlider::Ptr m_rotation_speed{ ModSlider::create(generate_name("RotationSpeed"), 0.0f, 1.0f, 0.1f) }; ValueList m_options{ *m_enabled, @@ -71,6 +71,7 @@ class FreeCam : public Mod { bool m_was_disabled{ false }; Vector3f m_custom_angles{}; + glm::quat m_custom_rotation{}; RECamera* m_camera{nullptr}; From 1ec771cfedb8991634fdbf449d22d2d01b00b90d Mon Sep 17 00:00:00 2001 From: praydog Date: Wed, 1 May 2024 09:58:12 -0700 Subject: [PATCH 09/10] murmur: add calc32_as_utf8 --- shared/sdk/MurmurHash.cpp | 6 ++++++ shared/sdk/MurmurHash.hpp | 1 + 2 files changed, 7 insertions(+) diff --git a/shared/sdk/MurmurHash.cpp b/shared/sdk/MurmurHash.cpp index 26584d0cb..d34659520 100644 --- a/shared/sdk/MurmurHash.cpp +++ b/shared/sdk/MurmurHash.cpp @@ -20,4 +20,10 @@ uint32_t calc32(std::wstring_view str) { uint32_t calc32(std::string_view str) { return calc32(utility::widen(str)); } + +uint32_t calc32_as_utf8(std::string_view str) { + static auto calc_method = type()->get_method("calc32AsUTF8"); + + return calc_method->call(sdk::get_thread_context(), sdk::VM::create_managed_string(utility::widen(str)), str.length()); +} } diff --git a/shared/sdk/MurmurHash.hpp b/shared/sdk/MurmurHash.hpp index d6ecced93..b3f76550c 100644 --- a/shared/sdk/MurmurHash.hpp +++ b/shared/sdk/MurmurHash.hpp @@ -11,5 +11,6 @@ namespace murmur_hash { sdk::RETypeDefinition* type(); uint32_t calc32(std::wstring_view str); uint32_t calc32(std::string_view str); +uint32_t calc32_as_utf8(std::string_view str); } } \ No newline at end of file From 6cd87ef4031285a514a1801f06eb8cdb002a673c Mon Sep 17 00:00:00 2001 From: praydog Date: Wed, 1 May 2024 09:58:20 -0700 Subject: [PATCH 10/10] Lua: Add debug library --- src/mods/ScriptRunner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mods/ScriptRunner.cpp b/src/mods/ScriptRunner.cpp index 261987eda..da768d5f4 100644 --- a/src/mods/ScriptRunner.cpp +++ b/src/mods/ScriptRunner.cpp @@ -120,7 +120,7 @@ ScriptState::ScriptState(const ScriptState::GarbageCollectionData& gc_data,bool m_is_main_state = is_main_state; m_lua.registry()["state"] = this; m_lua.open_libraries(sol::lib::base, sol::lib::package, sol::lib::string, sol::lib::math, sol::lib::table, sol::lib::bit32, - sol::lib::utf8, sol::lib::os, sol::lib::coroutine); + sol::lib::utf8, sol::lib::os, sol::lib::coroutine, sol::lib::debug); // Disable garbage collection. We will manually do it at the end of each frame. gc_data_changed(gc_data);