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 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/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/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..698d06cba 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"); @@ -167,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); @@ -213,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 }; } } @@ -252,19 +298,28 @@ 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; + 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}; diff --git a/src/mods/Graphics.cpp b/src/mods/Graphics.cpp index 2c342ee33..2f086be44 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,13 @@ void Graphics::on_frame() { if (m_disable_gui_key->is_key_down_once()) { 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() { @@ -92,6 +107,50 @@ 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"); + + 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); + } + + m_ray_trace_clone_type_post->draw("Ray Trace Clone Type Post"); + 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 + || 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"); + } + } + + ImGui::TreePop(); + } +#endif } void Graphics::on_present() { @@ -569,3 +628,278 @@ void Graphics::set_ultrawide_fov(bool use_vertical_fov) { } } } + +#if TDB_VER >= 69 +void Graphics::setup_path_trace_hook() { + if (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; + + // 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 { + 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_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 draw 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; + } + + 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; + } + + 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")) && !go_name.contains(L"DefaultCamera")) { + 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) { + m_rt_component.reset(); + return; + } + + 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() { + 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"); + + 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()); +} + +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 (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; + } + + 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) { + 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_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; + 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..3f3e4a709 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,23 @@ 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_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{}; std::unordered_map<::REManagedObject*, float> m_fov_map{}; std::unordered_map<::REManagedObject*, bool> m_vertical_fov_map{}; @@ -51,6 +74,54 @@ 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_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) }; + + 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 +139,16 @@ 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_true, + *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, diff --git a/src/mods/ScriptRunner.cpp b/src/mods/ScriptRunner.cpp index 87afdf638..da768d5f4 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); } } @@ -119,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); @@ -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 {