diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 3542f12..beb09f5 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -171,6 +171,7 @@ set(SWA_UI_CXX_SOURCES "ui/reddog/reddog_controls.cpp" "ui/reddog/reddog_manager.cpp" "ui/reddog/reddog_window.cpp" + "ui/reddog/debug_draw.cpp" "ui/achievement_menu.cpp" "ui/achievement_overlay.cpp" "ui/installer_wizard.cpp" diff --git a/UnleashedRecomp/patches/misc_patches.cpp b/UnleashedRecomp/patches/misc_patches.cpp index d6bb7d6..b0e64db 100644 --- a/UnleashedRecomp/patches/misc_patches.cpp +++ b/UnleashedRecomp/patches/misc_patches.cpp @@ -2,6 +2,7 @@ #include #include #include +#include void AchievementManagerUnlockMidAsmHook(PPCRegister& id) { @@ -106,3 +107,22 @@ PPC_FUNC(sub_82586698) __imp__sub_82586698(ctx, base); } + +PPC_FUNC_IMPL(__imp__sub_822C9398); +PPC_FUNC(sub_822C9398) +{ + auto a2 = (Hedgehog::Math::CVector*)g_memory.Translate(ctx.r4.u32); + auto a3 = (Hedgehog::Math::CVector*)g_memory.Translate(ctx.r5.u32); + auto a4 = (be*)g_memory.Translate(ctx.r6.u32); + + Reddog::Vector3 start(a2->X, a2->Y, a2->Z); + Reddog::Vector3 end(a3->X, a3->Y, a3->Z); + + const Reddog::SDrawLine line{ + start, end, a4->value + }; + + Reddog::DebugDraw::DrawLine(line); + + __imp__sub_822C9398(ctx, base); +} diff --git a/UnleashedRecomp/ui/imgui_utils.h b/UnleashedRecomp/ui/imgui_utils.h index f7e6359..8904b82 100644 --- a/UnleashedRecomp/ui/imgui_utils.h +++ b/UnleashedRecomp/ui/imgui_utils.h @@ -221,6 +221,17 @@ inline void DrawTextWithShadow(const ImFont* font, float fontSize, const ImVec2& drawList->AddText(font, fontSize, pos, colour, text, nullptr); } +inline void DrawTextWithShadow(ImDrawList* drawList, const ImFont* font, float fontSize, const ImVec2& pos, ImU32 colour, const char* text, float offset = 2.0f, float radius = 1.0f, ImU32 shadowColour = IM_COL32(0, 0, 0, 255)) +{ + offset = Scale(offset); + + SetOutline(radius); + drawList->AddText(font, fontSize, { pos.x + offset, pos.y + offset }, shadowColour, text); + ResetOutline(); + + drawList->AddText(font, fontSize, pos, colour, text, nullptr); +} + inline float CalcWidestTextSize(const ImFont* font, float fontSize, std::span strs) { auto result = 0.0f; diff --git a/UnleashedRecomp/ui/reddog/debug_draw.cpp b/UnleashedRecomp/ui/reddog/debug_draw.cpp new file mode 100644 index 0000000..e47e7c4 --- /dev/null +++ b/UnleashedRecomp/ui/reddog/debug_draw.cpp @@ -0,0 +1,221 @@ +#include +#include +#include "ui/imgui_utils.h" + + +namespace Reddog +{ + bool operator<= (const ImVec2& a, const ImVec2& b) { return a.x <= b.x && a.y <= b.y; } + bool operator>= (const ImVec2& a, const ImVec2& b) { return a.x >= b.x && a.y >= b.y; } + Hedgehog::Math::CVector4 operator /= (const Hedgehog::Math::CVector4& vec4, float divisor) + { + auto result = vec4; + + if (result.X.value != 0.0f) + result.X.value /= divisor; + if (result.Y.value != 0.0f) + result.Y.value /= divisor; + if (result.Z.value != 0.0f) + result.Z.value /= divisor; + if (result.W.value != 0.0f) + result.W.value /= divisor; + + return result; + } + + static ImVec2 GetNDCCoordinate(const Vector3& in_rVec) + { + auto& res = ImGui::GetIO().DisplaySize; + + //const auto camera = SWA::CGameDocument::GetInstance()->GetWorld()->GetCamera(); + //Hedgehog::Math::CVector4 ndc = camera->m_MyCamera.m_View * Hedgehog::Math::CVector4(in_rVec.x, in_rVec.y, in_rVec.z, 1.0f); + //ndc = camera->m_MyCamera.m_Projection * ndc; + + //if (ndc.W > 0.0f) // Check if given position is in front of the camera + //{ + // // normalize + // if (ndc.W != 0.0f) + // ndc /= ndc.W; + + // const ImVec2 screen_pos = { + // res.x / 2 * (1 - -ndc.X), // x + // res.y / 2 * (1 - ndc.Y) // y + // }; + + // return screen_pos; // Return screen coordinates + //} + + return { -1, -1 }; // Return invalid + } + + + void Exec_DrawLines(ImDrawList* drawList, ImVec2 canvasSize, float delta) + { + if (DebugDraw::ms_LineList.empty()) + return; + + for (auto line : DebugDraw::ms_LineList) + { + const auto start = GetNDCCoordinate(line.Start); + const auto end = GetNDCCoordinate(line.End); + + // Prevent wrap around + if (start >= ImVec2(0, 0) && end >= ImVec2(0, 0) + && start <= canvasSize && end <= canvasSize) + { + drawList->AddLine(start, end, line.Colour, 2.5f); + } + } + + DebugDraw::ms_LineList.clear(); + } + + + void Exec_DrawScreenText(ImDrawList* drawList, ImVec2 canvasSize, float delta, ImFont* font) + { + if (DebugDraw::ms_FreeTextList.empty()) + return; + + auto fontSize = Scale(12.0f); + + for (auto freeText = DebugDraw::ms_FreeTextList.begin(); freeText != DebugDraw::ms_FreeTextList.end();) + { + // Draw as overlay + if ((freeText->Flags & eDrawTextFlags_Overlay) == eDrawTextFlags_Overlay) + drawList = ImGui::GetForegroundDrawList(); + else + drawList = ImGui::GetBackgroundDrawList(); + + // Draw only on valid screen coordinates + if (freeText->Position >= ImVec2(0, 0) && freeText->Position <= canvasSize) + { + if ((freeText->Flags & eDrawTextFlags_NoShadow) == eDrawTextFlags_NoShadow) + drawList->AddText(font, fontSize * freeText->Scale, freeText->Position, freeText->Colour, freeText->Text); + else + DrawTextWithShadow(drawList, font, fontSize * freeText->Scale, freeText->Position, freeText->Colour, freeText->Text, 1.0f, 1.0f, IM_COL32(0, 0, 0, 128)); + } + + // Decrement timer + freeText->Time -= delta; + + // Remove if expired + if (freeText->Time < 0.0f) + freeText = DebugDraw::ms_FreeTextList.erase(freeText); + } + } + + + void Exec_DrawLogText(ImDrawList* drawList, ImVec2 canvasSize, float delta, ImFont* font) + { + if (DebugDraw::ms_LogTextList.empty()) + return; + + constexpr ImGuiWindowFlags flags { 0 + | ImGuiWindowFlags_NoTitleBar + | ImGuiWindowFlags_NoResize + | ImGuiWindowFlags_NoMove + | ImGuiWindowFlags_NoCollapse + | ImGuiWindowFlags_NoFocusOnAppearing + | ImGuiWindowFlags_AlwaysAutoResize + }; + + ImGui::SetNextWindowBgAlpha(0.65f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0)); + + ImGui::Begin("Status", nullptr, flags); + ImGui::PushFont(font); + ImGui::SetWindowFontScale(Scale(0.4f)); + + for (auto logText = DebugDraw::ms_LogTextList.begin(); logText != DebugDraw::ms_LogTextList.end();) + { + bool useColor = logText->Colour != (ImU32)-1; + + if (useColor) + ImGui::PushStyleColor(ImGuiCol_Text, logText->Colour); + + ImGui::TextUnformatted(logText->Text); + + if (useColor) + ImGui::PopStyleColor(); + + // Decrement timer + logText->Time -= delta; + + // Remove if expired + if (logText->Time < 0.0f) + logText = DebugDraw::ms_LogTextList.erase(logText); + + if (logText != DebugDraw::ms_LogTextList.end()) + ImGui::Separator(); + } + + ImGui::SetWindowPos({ canvasSize.x - Scale(2) - ImGui::GetWindowSize().x, Scale(2) }); + ImGui::PopStyleVar(1); + ImGui::PopStyleColor(); + ImGui::PopFont(); + ImGui::End(); + } + + + void DebugDraw::DrawLine(const SDrawLine& in_rLine) + { + if (!ms_IsRendering && ms_IsDrawLine) + ms_LineList.push_back(in_rLine); + } + + void DebugDraw::DrawText2D(const SDrawText& in_rText) + { + if (!ms_IsRendering && ms_IsDrawText) + ms_FreeTextList.push_back(in_rText); + } + + void DebugDraw::DrawText2D(const SDrawText& in_rText, const Vector3& in_rVec) + { + if (!ms_IsRendering && ms_IsDrawText) + { + auto txt = in_rText; + txt.Position = GetNDCCoordinate(in_rVec); + ms_FreeTextList.push_back(txt); + } + } + + void DebugDraw::DrawTextLog(const SDrawText& in_rText) + { + if (!ms_IsRendering && ms_IsDrawText) + ms_LogTextList.push_back(in_rText); + } + + void DebugDraw::DrawTextLog(const char* in_Text, float in_Time, ImU32 in_Colour, ImU16 in_Priority) + { + if (!ms_IsRendering && ms_IsDrawText) + ms_LogTextList.push_back({ ImVec2(0,0), in_Text, in_Time, 0, in_Colour, eDrawTextFlags_None, in_Priority}); + } + + void DebugDraw::Render(ImFont* font) + { + auto& io = ImGui::GetIO(); + auto& res = io.DisplaySize; + float deltaTime = io.DeltaTime; + auto drawList = ImGui::GetBackgroundDrawList(); + + /*DrawText2D({ ImVec2(Scale(50), Scale(300)), "TEST1 NO SHADOW", 0, 2, 0xFF37C800, eDrawTextFlags_NoShadow }); + DrawText2D({ ImVec2(Scale(50), Scale(325)), "TEST2 OVERLAY", 0, 2, 0xFF37C800, eDrawTextFlags_Overlay }); + DrawText2D({ ImVec2(Scale(50), Scale(350)), "TEST3 SCALE", 0, 5, 0xFF37C800 }); + + DrawTextLog("TEST1 NORMAL"); + DrawTextLog("TEST2 COLORED", 0, 0xFF37C800); + + auto form = fmt::format("- Stats -\nLines: {}\nTexts: {}\nLogs: {}", ms_LineList.size(), ms_FreeTextList.size(), ms_LogTextList.size()); + SDrawText text = { ImVec2(Scale(40), Scale(75)), form.c_str(), 0, 0.75f }; + DrawText2D(text);*/ + + ms_IsRendering = true; + + Exec_DrawLines(drawList, res, deltaTime); + Exec_DrawScreenText(nullptr, res, deltaTime, font); + Exec_DrawLogText(drawList, res, deltaTime, font); + + ms_IsRendering = false; + } +} diff --git a/UnleashedRecomp/ui/reddog/debug_draw.h b/UnleashedRecomp/ui/reddog/debug_draw.h new file mode 100644 index 0000000..02e10a3 --- /dev/null +++ b/UnleashedRecomp/ui/reddog/debug_draw.h @@ -0,0 +1,64 @@ +#pragma once + + +namespace Reddog +{ + struct Vector3 + { + float x, y, z; + constexpr Vector3() : x(0.0f), y(0.0f), z(0.0f) {} + constexpr Vector3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} + }; + + enum SDrawTextFlags : uint8_t + { + eDrawTextFlags_None = 0, + eDrawTextFlags_Overlay = 1 << 0, + eDrawTextFlags_NoShadow = 1 << 1, + }; + + struct SDrawLine + { + Vector3 Start { 0,0,0 }; + Vector3 End { 0,0,0 }; + ImU32 Colour { IM_COL32(255, 255, 255, 255) }; + float Size { 2 }; + }; + + struct SDrawText + { + ImVec2 Position { 0,0 }; + const char* Text { "" }; + float Time { 0 }; + float Scale { 1 }; + ImU32 Colour { IM_COL32(255, 255, 255, 255) }; + SDrawTextFlags Flags { eDrawTextFlags_None }; + ImU16 Priority { 0xFFFF }; + }; + + + ImVec2 GetNDCCoordinate(const Vector3& in_rPosition); + + + class DebugDraw + { + public: + static inline bool ms_IsRendering = false; + static inline bool ms_IsDrawLine = false; + static inline bool ms_IsDrawText = false; + + static inline std::vector ms_LineList = {}; + static inline std::vector ms_FreeTextList = {}; + static inline std::vector ms_LogTextList = {}; + + static void DrawLine(const SDrawLine& in_Line); + + static void DrawText2D(const SDrawText& in_Text); + static void DrawText2D(const SDrawText& in_Text, const Vector3& in_Position); + + static void DrawTextLog(const SDrawText& in_Text); + static void DrawTextLog(const char* in_Text, float in_Time = 0, ImU32 in_Colour = IM_COL32(255, 255, 255, 255), ImU16 in_Priority = 0xFFFF); + + static void Render(ImFont* font); + }; +} diff --git a/UnleashedRecomp/ui/reddog/reddog_manager.cpp b/UnleashedRecomp/ui/reddog/reddog_manager.cpp index 81f8182..e02235e 100644 --- a/UnleashedRecomp/ui/reddog/reddog_manager.cpp +++ b/UnleashedRecomp/ui/reddog/reddog_manager.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,9 @@ void Reddog::Manager::Draw() Video::DrawFPS(s_font); + // Render our Debug Draw + DebugDraw::Render(s_font); + if (!s_isVisible) return; diff --git a/UnleashedRecomp/ui/reddog/windows/view_window.cpp b/UnleashedRecomp/ui/reddog/windows/view_window.cpp index b8e1277..9acf7ac 100644 --- a/UnleashedRecomp/ui/reddog/windows/view_window.cpp +++ b/UnleashedRecomp/ui/reddog/windows/view_window.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -12,9 +13,52 @@ void ViewWindow::Draw() if (Begin()) { Reddog::Checkbox("Render FPS", &Config::ShowFPS.Value); + Reddog::Checkbox("Render Debug Lines", &Reddog::DebugDraw::ms_IsDrawLine); + Reddog::Checkbox("Render Debug Text", &Reddog::DebugDraw::ms_IsDrawText); Reddog::Checkbox("Render HUD (F8)", (bool*)g_memory.Translate(0x8328BB26)); Reddog::Separator(); + //// デバッグ描画 + //Reddog::Checkbox((const char*)u8"Render Debug Draw", (bool*)g_memory.Translate(0x8328BB23)); + //// デバッグ位置描画 + //Reddog::Checkbox((const char*)u8"Render Debug Position Draw", (bool*)g_memory.Translate(0x8328BB24)); + //// デバッグ文字描画 + //Reddog::Checkbox((const char*)u8"Render Debug Draw Text", (bool*)g_memory.Translate(0x8328BB25)); + + //Reddog::Separator(); + + //// 全 HUD 描画 + //Reddog::Checkbox((const char*)g_memory.Translate(0x82031850), (bool*)g_memory.Translate(0x8328BB26)); + // ゲームメインHUD 描画 + Reddog::Checkbox((const char*)u8"Render Main Game HUD", (bool*)g_memory.Translate(0x8328BB27)); + // ポーズメニュー 描画 + Reddog::Checkbox((const char*)u8"Render Pause HUD", (bool*)g_memory.Translate(0x8328BB28)); + + Reddog::Separator(); + + // 値をデバッグ表示 + Reddog::Checkbox((const char*)u8"Light Field Debug", (bool*)g_memory.Translate(0x83367BCD)); + // サンプリング点をデバッグ表示 + Reddog::Checkbox((const char*)u8"Draw Light Field Sampling Point", (bool*)g_memory.Translate(0x83367BCE)); + // データを無視する + Reddog::Checkbox((const char*)u8"Ignore Light Field Data", (bool*)g_memory.Translate(0x83367BCF)); + + Reddog::Separator(); + + // ミップレベルを視覚化 赤=0, 緑=1, 青=2, 黄=未ロード + Reddog::Checkbox((const char*)u8"Visualize Loaded GI Mip Level", (bool*)g_memory.Translate(0x833678C1)); + + Reddog::Separator(); + + // IsCollisionRender + Reddog::Checkbox((const char*)u8"Render Stage Collision", (bool*)g_memory.Translate(0x833678A6)); + // IsTriggerRender + Reddog::Checkbox((const char*)u8"Render Event Collision", (bool*)g_memory.Translate(0x83367904)); + // IsObjectCollisionRender + Reddog::Checkbox((const char*)u8"Render Rigid Body Collision", (bool*)g_memory.Translate(0x83367905)); + + Reddog::Separator(); + if (Reddog::Button("Reset Window Dimensions (F2)")) { Config::Fullscreen = GameWindow::SetFullscreen(false);