21 Commits

Author SHA1 Message Date
RadiantDerg
c357062de8 Partial ratio text implementation 2025-01-19 04:22:12 -06:00
RadiantDerg
fc51d0e7ae Fix potential memory issue
Text/Line vectors could've been added to even if Reddog was never enabled, thus only ever expanding in size as they couldn't update.
2025-01-17 19:22:04 -06:00
Hyper
f4406cd8d0 Merge branch 'main' into debug-menu 2025-01-18 00:20:05 +00:00
RadiantDerg
2af11c8d2f Fix position text scaling with aspect ratio 2025-01-17 18:17:50 -06:00
Hyper
dd54867e58 Merge branch 'main' into debug-menu 2025-01-17 23:03:02 +00:00
Hyper
4b358d8e43 Rebase branch to main 2025-01-17 20:44:56 +00:00
Hyper
110f341af6 reddog_window: fix variable titlebar button positions 2025-01-17 20:17:59 +00:00
RadiantDerg
00e148b182 Draw position, Move patches to debug_patches.cpp 2025-01-17 20:17:56 +00:00
RadiantDerg
eb3225c2f7 SWA API mappings 2025-01-17 20:17:16 +00:00
Hyper
4ab26a1696 reddog: window improvements, added "welcome" message 2025-01-17 20:17:16 +00:00
Hyper
72a0a17e3a view_window: reordering and clean-up 2025-01-17 20:17:15 +00:00
Hyper
35b9361a91 api: use MmGetHostAddress for globals 2025-01-17 20:17:15 +00:00
Hyper
7788e36704 api: added globals struct 2025-01-17 20:17:15 +00:00
RadiantDerg
023fc0edf7 Added Vector mappings, Added DebugDraw class for drawing text on screen and in a popup
SWA API:
- Added CVector
- Added CVector4
- Combined CVectorX into one header

Reddog:
- Added toggle for visualizing the loaded GI Atlas Mipmap level
- Added toggles for rendering Havok collision
- Added toggles for Light Field
2025-01-17 20:17:12 +00:00
Hyper
d5ca843716 reddog: implemented FPS counter 2025-01-17 20:16:30 +00:00
Hyper
dbdf020218 reddog: improve button accuracy 2025-01-17 20:14:53 +00:00
Hyper
d5e947f520 reddog: improve window list accuracy 2025-01-17 20:14:52 +00:00
Hyper
a7e004fb60 reddog: improve window colour accuracy 2025-01-17 20:14:52 +00:00
Hyper
892e24f71e reddog: implemented custom button 2025-01-17 20:14:51 +00:00
Hyper
72a0507c66 Fix old header paths 2025-01-17 20:14:51 +00:00
Hyper
2f68ee5df2 Implemented mock reddog window manager
Requires micross.ttf font in game root and ENABLE_IM_FONT_ATLAS_SNAPSHOT undefined, not currently in the font atlas.
2025-01-17 20:14:50 +00:00
50 changed files with 2079 additions and 160 deletions

View File

@@ -154,7 +154,8 @@ set(SWA_PATCHES_CXX_SOURCES
"patches/ui/frontend_listener.cpp"
"patches/aspect_ratio_patches.cpp"
"patches/audio_patches.cpp"
"patches/camera_patches.cpp"
"patches/camera_patches.cpp"
"patches/debug_patches.cpp"
"patches/fps_patches.cpp"
"patches/inspire_patches.cpp"
"patches/misc_patches.cpp"
@@ -165,6 +166,15 @@ set(SWA_PATCHES_CXX_SOURCES
)
set(SWA_UI_CXX_SOURCES
"ui/reddog/windows/counter_window.cpp"
"ui/reddog/windows/exports_window.cpp"
"ui/reddog/windows/view_window.cpp"
"ui/reddog/windows/welcome_window.cpp"
"ui/reddog/windows/window_list.cpp"
"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"
@@ -196,22 +206,22 @@ set(SWA_INSTALL_CXX_SOURCES
set(SWA_USER_CXX_SOURCES
"user/achievement_data.cpp"
"user/config.cpp"
)
set(SWA_MOD_CXX_SOURCES
"mod/mod_loader.cpp"
)
set(SWA_MOD_CXX_SOURCES
"mod/mod_loader.cpp"
)
set(SWA_THIRDPARTY_SOURCES
"${SWA_THIRDPARTY_ROOT}/imgui/backends/imgui_impl_sdl2.cpp"
"${SWA_THIRDPARTY_ROOT}/imgui/imgui.cpp"
"${SWA_THIRDPARTY_ROOT}/imgui/imgui.cpp"
"${SWA_THIRDPARTY_ROOT}/imgui/imgui_demo.cpp"
"${SWA_THIRDPARTY_ROOT}/imgui/imgui_draw.cpp"
"${SWA_THIRDPARTY_ROOT}/imgui/imgui_tables.cpp"
"${SWA_THIRDPARTY_ROOT}/imgui/imgui_widgets.cpp"
"${SWA_THIRDPARTY_ROOT}/implot/implot.cpp"
"${SWA_THIRDPARTY_ROOT}/implot/implot_demo.cpp"
"${SWA_THIRDPARTY_ROOT}/implot/implot_items.cpp"
"${SWA_THIRDPARTY_ROOT}/implot/implot.cpp"
"${SWA_THIRDPARTY_ROOT}/implot/implot_demo.cpp"
"${SWA_THIRDPARTY_ROOT}/implot/implot_items.cpp"
"${SWA_THIRDPARTY_ROOT}/libmspack/libmspack/mspack/lzxd.c"
"${SWA_THIRDPARTY_ROOT}/tiny-AES-c/aes.c"
"${SWA_TOOLS_ROOT}/ShaderRecomp/thirdparty/smol-v/source/smolv.cpp"
@@ -220,7 +230,7 @@ set(SWA_THIRDPARTY_SOURCES
set(SWA_THIRDPARTY_INCLUDES
"${SWA_THIRDPARTY_ROOT}/concurrentqueue"
"${SWA_THIRDPARTY_ROOT}/ddspp"
"${SWA_THIRDPARTY_ROOT}/imgui"
"${SWA_THIRDPARTY_ROOT}/imgui"
"${SWA_THIRDPARTY_ROOT}/implot"
"${SWA_THIRDPARTY_ROOT}/libmspack/libmspack/mspack"
"${SWA_THIRDPARTY_ROOT}/magic_enum/include"
@@ -247,7 +257,7 @@ set(SWA_CXX_SOURCES
"exports.cpp"
"main.cpp"
"misc_impl.cpp"
"stdafx.cpp"
"stdafx.cpp"
"version.cpp"
${SWA_KERNEL_CXX_SOURCES}
@@ -260,7 +270,7 @@ set(SWA_CXX_SOURCES
${SWA_PATCHES_CXX_SOURCES}
${SWA_UI_CXX_SOURCES}
${SWA_INSTALL_CXX_SOURCES}
${SWA_USER_CXX_SOURCES}
${SWA_USER_CXX_SOURCES}
${SWA_MOD_CXX_SOURCES}
${SWA_THIRDPARTY_SOURCES}
)
@@ -274,7 +284,7 @@ else()
add_executable(UnleashedRecomp ${SWA_CXX_SOURCES})
endif()
set_target_properties(UnleashedRecomp PROPERTIES OUTPUT_NAME ${TARGET_NAME})
set_target_properties(UnleashedRecomp PROPERTIES OUTPUT_NAME ${TARGET_NAME})
if (SWA_FLATPAK)
target_compile_definitions(UnleashedRecomp PRIVATE "GAME_INSTALL_DIRECTORY=\"/var/data\"")
@@ -380,8 +390,8 @@ function(compile_pixel_shader FILE_PATH)
endfunction()
compile_vertex_shader(copy_vs)
compile_pixel_shader(csd_filter_ps)
compile_vertex_shader(csd_no_tex_vs)
compile_pixel_shader(csd_filter_ps)
compile_vertex_shader(csd_no_tex_vs)
compile_vertex_shader(csd_vs)
compile_pixel_shader(enhanced_motion_blur_ps)
compile_pixel_shader(gaussian_blur_3x3)
@@ -425,21 +435,21 @@ generate_aggregate_header(
"${CMAKE_CURRENT_SOURCE_DIR}/api"
"${CMAKE_CURRENT_SOURCE_DIR}/api/SWA.h"
)
# Only show build type if not Release.
set(IS_BUILD_TYPE_IN_VER_STRING 0)
if (NOT ${CMAKE_BUILD_TYPE} MATCHES "Release")
set(IS_BUILD_TYPE_IN_VER_STRING 1)
endif()
include("version.cmake")
GenerateVersionSources(
OUTPUT_DIR "${PROJECT_SOURCE_DIR}"
VERSION_TXT "${PROJECT_SOURCE_DIR}/res/version.txt"
H_TEMPLATE "${PROJECT_SOURCE_DIR}/res/version.h.template"
CXX_TEMPLATE "${PROJECT_SOURCE_DIR}/res/version.cpp.template"
BUILD_TYPE ${CMAKE_BUILD_TYPE}
IS_BUILD_TYPE_IN_VER_STRING ${IS_BUILD_TYPE_IN_VER_STRING}
# Only show build type if not Release.
set(IS_BUILD_TYPE_IN_VER_STRING 0)
if (NOT ${CMAKE_BUILD_TYPE} MATCHES "Release")
set(IS_BUILD_TYPE_IN_VER_STRING 1)
endif()
include("version.cmake")
GenerateVersionSources(
OUTPUT_DIR "${PROJECT_SOURCE_DIR}"
VERSION_TXT "${PROJECT_SOURCE_DIR}/res/version.txt"
H_TEMPLATE "${PROJECT_SOURCE_DIR}/res/version.h.template"
CXX_TEMPLATE "${PROJECT_SOURCE_DIR}/res/version.cpp.template"
BUILD_TYPE ${CMAKE_BUILD_TYPE}
IS_BUILD_TYPE_IN_VER_STRING ${IS_BUILD_TYPE_IN_VER_STRING}
IS_GIT_REPO 1
)
@@ -456,6 +466,24 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/co
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/kbm.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/kbm.dds" ARRAY_NAME "g_kbm" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fade.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fade.dds" ARRAY_NAME "g_select_fade" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fill.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fill.dds" ARRAY_NAME "g_select_fill" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/button_close_1.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/button_close_1.dds" ARRAY_NAME "g_button_close_1")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/button_close_2.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/button_close_2.dds" ARRAY_NAME "g_button_close_2")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/button_minimum_1.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/button_minimum_1.dds" ARRAY_NAME "g_button_minimum_1")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/button_minimum_2.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/button_minimum_2.dds" ARRAY_NAME "g_button_minimum_2")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/button_pin_1.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/button_pin_1.dds" ARRAY_NAME "g_button_pin_1")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/button_pin_2.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/button_pin_2.dds" ARRAY_NAME "g_button_pin_2")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/checkbox_1.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/checkbox_1.dds" ARRAY_NAME "g_checkbox_1")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/checkbox_2.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/checkbox_2.dds" ARRAY_NAME "g_checkbox_2")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/common_button_1.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/common_button_1.dds" ARRAY_NAME "g_common_button_1")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/common_button_2.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/common_button_2.dds" ARRAY_NAME "g_common_button_2")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/common_icon.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/common_icon.dds" ARRAY_NAME "g_common_icon")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/debug_icon.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/debug_icon.dds" ARRAY_NAME "g_debug_icon")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/mouse_cursor.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/mouse_cursor.bmp" ARRAY_NAME "g_mouse_cursor")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/mouse_cursor_h.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/mouse_cursor_h.bmp" ARRAY_NAME "g_mouse_cursor_h")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/mouse_cursor_slant_l.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/mouse_cursor_slant_l.bmp" ARRAY_NAME "g_mouse_cursor_slant_l")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/mouse_cursor_slant_r.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/mouse_cursor_slant_r.bmp" ARRAY_NAME "g_mouse_cursor_slant_r")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/mouse_cursor_v.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/mouse_cursor_v.bmp" ARRAY_NAME "g_mouse_cursor_v")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/reddog/title_bar.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/reddog/title_bar.dds" ARRAY_NAME "g_title_bar")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/arrow_circle.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/arrow_circle.dds" ARRAY_NAME "g_arrow_circle" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_001.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_001.dds" ARRAY_NAME "g_install_001" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_002.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_002.dds" ARRAY_NAME "g_install_002" COMPRESSION_TYPE "zstd")

View File

@@ -0,0 +1,19 @@
#pragma once
#include <SWA.inl>
namespace Hedgehog::Math
{
class CAabb // Eigen::AlignedBox3f
{
public:
Hedgehog::Math::CVector min;
Hedgehog::Math::CVector max;
CVector Center() const;
};
SWA_ASSERT_SIZEOF(CAabb, 0x18);
}
#include "Aabb.inl"

View File

@@ -0,0 +1,12 @@
namespace Hedgehog::Math
{
inline CVector Hedgehog::Math::CAabb::Center() const
{
// return (min + max) / 2;
Hedgehog::Math::CVector result;
result.X = (max.X + min.X) * 0.5f;
result.Y = (max.Y + min.Y) * 0.5f;
result.Z = 0.5f * (max.Z + min.Z);
return result;
}
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <SWA.inl>
namespace Hedgehog::Math
{
class CMatrix
{
public:
be<float> data[16]; // Eigen::Affine3f
};
class CMatrix44
{
public:
be<float> data[16]; // Eigen::Matrix4f
};
SWA_ASSERT_SIZEOF(CMatrix, 0x40);
SWA_ASSERT_SIZEOF(CMatrix44, 0x40);
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include <SWA.inl>
namespace Hedgehog::Math
{
class CQuaternion //Eigen::Quaternionf;
{
public:
be<float> X;
be<float> Y;
be<float> Z;
be<float> W;
};
SWA_ASSERT_SIZEOF(CQuaternion, 0x10);
}

View File

@@ -28,3 +28,5 @@ namespace Hedgehog::Math
be<float> W;
};
}
#include "Vector.inl"

View File

@@ -0,0 +1,4 @@
namespace Hedgehog::Math
{
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include <SWA.inl>
namespace Hedgehog::Mirage
{
class CCamera : Base::CObject
{
public:
Math::CMatrix m_View;
Math::CMatrix44 m_Projection;
Math::CVector m_Position;
Math::CVector m_Direction;
be<float> m_AspectRatio;
be<float> m_Near;
be<float> m_Far;
};
}

View File

@@ -27,7 +27,11 @@
#include "Hedgehog/Base/Type/hhSharedString.h"
#include "Hedgehog/Base/hhObject.h"
#include "Hedgehog/Database/System/hhDatabaseData.h"
#include "Hedgehog/Math/Aabb.h"
#include "Hedgehog/Math/Matrix.h"
#include "Hedgehog/Math/Quaternion.h"
#include "Hedgehog/Math/Vector.h"
#include "Hedgehog/MirageCore/Camera/hhCamera.h"
#include "Hedgehog/MirageCore/Misc/hhVertexDeclarationPtr.h"
#include "Hedgehog/MirageCore/RenderData/hhMaterialData.h"
#include "Hedgehog/MirageCore/RenderData/hhMeshData.h"
@@ -57,6 +61,7 @@
#include "SWA/CSD/CsdTexListMirage.h"
#include "SWA/CSD/GameObjectCSD.h"
#include "SWA/Camera/Camera.h"
#include "SWA/Globals.h"
#include "SWA/HUD/GeneralWindow/GeneralWindow.h"
#include "SWA/HUD/Loading/Loading.h"
#include "SWA/HUD/Pause/HudPause.h"
@@ -73,6 +78,8 @@
#include "SWA/Inspire/InspireTextureOverlayInfo.h"
#include "SWA/Movie/MovieDisplayer.h"
#include "SWA/Movie/MovieManager.h"
#include "SWA/Path/PathController.h"
#include "SWA/Path/Animation/PathAnimation.h"
#include "SWA/Player/Character/EvilSonic/EvilSonic.h"
#include "SWA/Player/Character/EvilSonic/EvilSonicContext.h"
#include "SWA/Player/Character/EvilSonic/Hud/EvilHudGuide.h"
@@ -98,6 +105,7 @@
#include "SWA/System/GammaController.h"
#include "SWA/System/InputState.h"
#include "SWA/System/PadState.h"
#include "SWA/System/StageManager.h"
#include "SWA/System/World.h"
#include "boost/smart_ptr/make_shared_object.h"
#include "boost/smart_ptr/shared_ptr.h"

View File

@@ -9,7 +9,8 @@ namespace SWA
{
public:
xpointer<void> m_pVftable;
SWA_INSERT_PADDING(0xC4);
// SWA::CCamera::MyCamera
SWA_INSERT_PADDING(0xC4);
be<float> m_VertAspectRatio;
SWA_INSERT_PADDING(0x48);
be<float> m_HorzAspectRatio;

View File

@@ -0,0 +1,69 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
struct SGlobals
{
// ms_DrawLightFieldSamplingPoint: サンプリング点をデバッグ表示
static inline bool* ms_DrawLightFieldSamplingPoint;
// ms_IgnoreLightFieldData: データを無視する
static inline bool* ms_IgnoreLightFieldData;
// IsCollisionRender
static inline bool* ms_IsCollisionRender;
// N/A
static inline bool* ms_IsLoading;
// IsObjectCollisionRender
static inline bool* ms_IsObjectCollisionRender;
// ms_IsRenderDebugDraw: デバッグ描画
static inline bool* ms_IsRenderDebugDraw;
// ms_IsRenderDebugDrawText: デバッグ文字描画
static inline bool* ms_IsRenderDebugDrawText;
// ms_IsRenderDebugPositionDraw: デバッグ位置描画
static inline bool* ms_IsRenderDebugPositionDraw;
// ms_IsRenderGameMainHud: ゲームメインHUD 描画
static inline bool* ms_IsRenderGameMainHud;
// ms_IsRenderHud: 全 HUD 描画
static inline bool* ms_IsRenderHud;
// ms_IsRenderHudPause: ポーズメニュー 描画
static inline bool* ms_IsRenderHudPause;
// IsTriggerRender
static inline bool* ms_IsTriggerRender;
// ms_LightFieldDebug: 値をデバッグ表示
static inline bool* ms_LightFieldDebug;
// VisualizeLoadedLevel: ミップレベルを視覚化 赤=0, 緑=1, 青=2, 黄=未ロード
static inline bool* ms_VisualizeLoadedLevel;
static void Init()
{
ms_DrawLightFieldSamplingPoint = (bool*)MmGetHostAddress(0x83367BCE);
ms_IgnoreLightFieldData = (bool*)MmGetHostAddress(0x83367BCF);
ms_IsCollisionRender = (bool*)MmGetHostAddress(0x833678A6);
ms_IsLoading = (bool*)MmGetHostAddress(0x83367A4C);
ms_IsObjectCollisionRender = (bool*)MmGetHostAddress(0x83367905);
ms_IsRenderDebugDraw = (bool*)MmGetHostAddress(0x8328BB23);
ms_IsRenderDebugDrawText = (bool*)MmGetHostAddress(0x8328BB25);
ms_IsRenderDebugPositionDraw = (bool*)MmGetHostAddress(0x8328BB24);
ms_IsRenderGameMainHud = (bool*)MmGetHostAddress(0x8328BB27);
ms_IsRenderHud = (bool*)MmGetHostAddress(0x8328BB26);
ms_IsRenderHudPause = (bool*)MmGetHostAddress(0x8328BB28);
ms_IsTriggerRender = (bool*)MmGetHostAddress(0x83367904);
ms_LightFieldDebug = (bool*)MmGetHostAddress(0x83367BCD);
ms_VisualizeLoadedLevel = (bool*)MmGetHostAddress(0x833678C1);
}
};
}

View File

@@ -0,0 +1,22 @@
#pragma once
namespace SWA::PathAnimation
{
class Entity
{
public:
xpointer<void> m_pVftable;
SWA_INSERT_PADDING(0x18);
};
class Controller
{
public:
xpointer<void> m_pVftable;
SWA_INSERT_PADDING(0x1C);
be<float> m_DistanceAlongPath;
};
SWA_ASSERT_OFFSETOF(Controller, m_DistanceAlongPath, 0x20);
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include "Animation/PathAnimation.h"
namespace SWA
{
class CPathController
{
public:
be<uint32_t> m_Field000;
be<uint32_t> m_Field004;
boost::shared_ptr<PathAnimation::Controller> m_spPathAnimationController;
};
}

View File

@@ -29,7 +29,8 @@ namespace SWA
SWA_INSERT_PADDING(0x10);
};
SWA_INSERT_PADDING(0x1C);
SWA_INSERT_PADDING(0x10);
hh::map<Hedgehog::Base::CSharedString, boost::shared_ptr<SWA::CWorld>> m_Worlds;
boost::shared_ptr<Hedgehog::Database::CDatabase> m_spDatabase;
SWA_INSERT_PADDING(0x88);
Hedgehog::Base::CSharedString m_StageName;

View File

@@ -0,0 +1,30 @@
#pragma once
#include "SWA.inl"
#include "Hedgehog/Base/Thread/hhSynchronizedObject.h"
#include "Hedgehog/Universe/Engine/hhMessageActor.h"
#include "SWA/System/GameObject.h"
namespace SWA
{
class CStageManager //: public SWA::CGameObject, public Hedgehog::Base::CSynchronizedObject
{
public:
SWA_INSERT_PADDING(0xC4);
boost::shared_ptr<SWA::CPathController> m_spStageGuidePathController; //xpointer<SWA::CPathController> m_pStageGuidePathController;
SWA_INSERT_PADDING(0x4);
Hedgehog::Math::CVector m_PlayerPosition;
be<float> m_Field0DC; // Sonic's air distance from path??
SWA_INSERT_PADDING(0x3C);
be<float> m_StageGuidePathRatio; // Not updated in retail
be<float> m_StageGuidePathLength;
SWA_INSERT_PADDING(0x9C);
};
SWA_ASSERT_OFFSETOF(CStageManager, m_spStageGuidePathController, 0xC4);
SWA_ASSERT_OFFSETOF(CStageManager, m_PlayerPosition, 0xD0);
SWA_ASSERT_OFFSETOF(CStageManager, m_StageGuidePathRatio, 0x11C);
SWA_ASSERT_OFFSETOF(CStageManager, m_StageGuidePathLength, 0x120);
SWA_ASSERT_SIZEOF(CStageManager, 0x1C0);
}

View File

@@ -12,9 +12,21 @@ namespace SWA
class CMember
{
public:
SWA_INSERT_PADDING(0x80);
//boost::shared_ptr<Hedgehog::Mirage::CRenderScene> m_spRenderScene;
//Hedgehog::Base::CSharedString m_Name;
SWA_INSERT_PADDING(0xC);
boost::shared_ptr<CCamera> m_spCamera;
boost::shared_ptr<CCamera> m_spOverrideCamera;
SWA_INSERT_PADDING(0x64);
};
xpointer<CMember> m_pMember;
boost::shared_ptr<CCamera> GetCamera() const;
};
//SWA_ASSERT_OFFSETOF(CWorld::CMember, m_spCamera, 0xC);
//SWA_ASSERT_SIZEOF(CWorld::CMember, 0x80);
}
#include "World.inl"

View File

@@ -0,0 +1,7 @@
namespace SWA
{
inline boost::shared_ptr<CCamera> CWorld::GetCamera() const
{
return m_pMember->m_spOverrideCamera ? m_pMember->m_spOverrideCamera : m_pMember->m_spCamera;
}
}

View File

@@ -1,4 +1,5 @@
#include <app.h>
#include <api/SWA.h>
#include <gpu/video.h>
#include <install/installer.h>
#include <kernel/function.h>
@@ -33,6 +34,8 @@ PPC_FUNC(sub_824EB490)
App::s_isMissingDLC = !Installer::checkAllDLC(GetGamePath());
App::s_language = Config::Language;
SWA::SGlobals::Init();
__imp__sub_824EB490(ctx, base);
}

View File

@@ -17,6 +17,7 @@
#include <res/font/im_font_atlas.dds.h>
#include <shader/shader_cache.h>
#include <SWA.h>
#include <ui/reddog/reddog_manager.h>
#include <ui/achievement_menu.h>
#include <ui/achievement_overlay.h>
#include <ui/button_guide.h>
@@ -26,6 +27,7 @@
#include <ui/options_menu.h>
#include <ui/sdl_listener.h>
#include <ui/game_window.h>
#include <ui/imgui_utils.h>
#include <patches/aspect_ratio_patches.h>
#include <user/config.h>
#include <xxHashMap.h>
@@ -1192,9 +1194,16 @@ static void CreateImGuiBackend()
AchievementMenu::Init();
AchievementOverlay::Init();
ButtonGuide::Init();
InstallerWizard::Init();
MessageWindow::Init();
OptionsMenu::Init();
InstallerWizard::Init();
#if !_DEBUG
if (Config::Debug)
#endif
{
Reddog::Manager::Init();
}
ImGui_ImplSDL2_InitForOther(GameWindow::s_pWindow);
@@ -1965,82 +1974,77 @@ static double g_applicationValues[PROFILER_VALUE_COUNT];
static Profiler g_presentProfiler;
static Profiler g_renderDirectorProfiler;
static bool g_profilerVisible;
static bool g_profilerWasToggled;
static void DrawProfiler()
void Video::DrawCounter()
{
bool toggleProfiler = SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_F1] != 0;
g_applicationValues[g_profilerValueIndex] = App::s_deltaTime * 1000.0;
if (!g_profilerWasToggled && toggleProfiler)
g_profilerVisible = !g_profilerVisible;
const double applicationAvg = std::accumulate(g_applicationValues, g_applicationValues + PROFILER_VALUE_COUNT, 0.0) / PROFILER_VALUE_COUNT;
double presentAvg = g_presentProfiler.UpdateAndReturnAverage();
double renderDirectorAvg = g_renderDirectorProfiler.UpdateAndReturnAverage();
g_profilerWasToggled = toggleProfiler;
if (ImPlot::BeginPlot("Frame Time"))
{
ImPlot::SetupAxisLimits(ImAxis_Y1, 0.0, 20.0);
ImPlot::SetupAxis(ImAxis_Y1, "ms", ImPlotAxisFlags_None);
ImPlot::PlotLine<double>("Application", g_applicationValues, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex);
ImPlot::PlotLine<double>("Present", g_presentProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex);
ImPlot::PlotLine<double>("Render Director", g_renderDirectorProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex);
ImPlot::EndPlot();
}
if (!g_profilerVisible)
g_profilerValueIndex = (g_profilerValueIndex + 1) % PROFILER_VALUE_COUNT;
ImGui::Text("Current Application: %g ms (%g FPS)", App::s_deltaTime * 1000.0, 1.0 / App::s_deltaTime);
ImGui::Text("Current Present: %g ms (%g FPS)", g_presentProfiler.value.load(), 1000.0 / g_presentProfiler.value.load());
ImGui::Text("Current Render Director: %g ms (%g FPS)", g_renderDirectorProfiler.value.load(), 1000.0 / g_renderDirectorProfiler.value.load());
ImGui::NewLine();
ImGui::Text("Average Application: %g ms (%g FPS)", applicationAvg, 1000.0 / applicationAvg);
ImGui::Text("Average Present: %g ms (%g FPS)", presentAvg, 1000.0 / presentAvg);
ImGui::Text("Average Render Director: %g ms (%g FPS)", renderDirectorAvg, 1000.0 / renderDirectorAvg);
ImGui::NewLine();
O1HeapDiagnostics diagnostics, physicalDiagnostics;
{
std::lock_guard lock(g_userHeap.mutex);
diagnostics = o1heapGetDiagnostics(g_userHeap.heap);
}
{
std::lock_guard lock(g_userHeap.physicalMutex);
physicalDiagnostics = o1heapGetDiagnostics(g_userHeap.physicalHeap);
}
ImGui::Text("Heap Allocated: %d MB", int32_t(diagnostics.allocated / (1024 * 1024)));
ImGui::Text("Physical Heap Allocated: %d MB", int32_t(physicalDiagnostics.allocated / (1024 * 1024)));
ImGui::NewLine();
auto capabilities = g_device->getCapabilities();
ImGui::Text("Present Wait: %s", capabilities.presentWait ? "Supported" : "Unsupported");
ImGui::Text("Triangle Fan: %s", capabilities.triangleFan ? "Supported" : "Unsupported");
ImGui::NewLine();
const char* sdlVideoDriver = SDL_GetCurrentVideoDriver();
if (sdlVideoDriver != nullptr)
ImGui::Text("SDL Video Driver: %s", sdlVideoDriver);
}
void Video::DrawFPS(ImFont* font)
{
if (!Config::ShowFPS)
return;
ImFont* font = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf");
float defaultScale = font->Scale;
font->Scale = ImGui::GetDefaultFont()->FontSize / font->FontSize;
ImGui::PushFont(font);
auto drawList = ImGui::GetBackgroundDrawList();
if (ImGui::Begin("Profiler", &g_profilerVisible))
{
g_applicationValues[g_profilerValueIndex] = App::s_deltaTime * 1000.0;
auto fmt = fmt::format("FPS: {:.2f}", 1000.0 / g_presentProfiler.value.load());
auto fontSize = Scale(12.0f);
auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, fmt.c_str());
const double applicationAvg = std::accumulate(g_applicationValues, g_applicationValues + PROFILER_VALUE_COUNT, 0.0) / PROFILER_VALUE_COUNT;
double presentAvg = g_presentProfiler.UpdateAndReturnAverage();
double renderDirectorAvg = g_renderDirectorProfiler.UpdateAndReturnAverage();
ImVec2 min = { Scale(40), Scale(30) };
ImVec2 max = { min.x + std::max(Scale(75), textSize.x + Scale(10)), min.y + Scale(15) };
ImVec2 textPos = { min.x + Scale(2), CENTRE_TEXT_VERT(min, max, textSize) - Scale(0.5f) };
if (ImPlot::BeginPlot("Frame Time"))
{
ImPlot::SetupAxisLimits(ImAxis_Y1, 0.0, 20.0);
ImPlot::SetupAxis(ImAxis_Y1, "ms", ImPlotAxisFlags_None);
ImPlot::PlotLine<double>("Application", g_applicationValues, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex);
ImPlot::PlotLine<double>("Present", g_presentProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex);
ImPlot::PlotLine<double>("Render Director", g_renderDirectorProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex);
ImPlot::EndPlot();
}
g_profilerValueIndex = (g_profilerValueIndex + 1) % PROFILER_VALUE_COUNT;
ImGui::Text("Current Application: %g ms (%g FPS)", App::s_deltaTime * 1000.0, 1.0 / App::s_deltaTime);
ImGui::Text("Current Present: %g ms (%g FPS)", g_presentProfiler.value.load(), 1000.0 / g_presentProfiler.value.load());
ImGui::Text("Current Render Director: %g ms (%g FPS)", g_renderDirectorProfiler.value.load(), 1000.0 / g_renderDirectorProfiler.value.load());
ImGui::NewLine();
ImGui::Text("Average Application: %g ms (%g FPS)", applicationAvg, 1000.0 / applicationAvg);
ImGui::Text("Average Present: %g ms (%g FPS)", presentAvg, 1000.0 / presentAvg);
ImGui::Text("Average Render Director: %g ms (%g FPS)", renderDirectorAvg, 1000.0 / renderDirectorAvg);
ImGui::NewLine();
O1HeapDiagnostics diagnostics, physicalDiagnostics;
{
std::lock_guard lock(g_userHeap.mutex);
diagnostics = o1heapGetDiagnostics(g_userHeap.heap);
}
{
std::lock_guard lock(g_userHeap.physicalMutex);
physicalDiagnostics = o1heapGetDiagnostics(g_userHeap.physicalHeap);
}
ImGui::Text("Heap Allocated: %d MB", int32_t(diagnostics.allocated / (1024 * 1024)));
ImGui::Text("Physical Heap Allocated: %d MB", int32_t(physicalDiagnostics.allocated / (1024 * 1024)));
ImGui::NewLine();
ImGui::Text("Present Wait: %s", g_capabilities.presentWait ? "Supported" : "Unsupported");
ImGui::Text("Triangle Fan: %s", g_capabilities.triangleFan ? "Supported" : "Unsupported");
ImGui::NewLine();
const char* sdlVideoDriver = SDL_GetCurrentVideoDriver();
if (sdlVideoDriver != nullptr)
ImGui::Text("SDL Video Driver: %s", sdlVideoDriver);
}
ImGui::End();
ImGui::PopFont();
font->Scale = defaultScale;
drawList->AddRectFilled(min, max, IM_COL32(0, 0, 0, 255));
drawList->AddText(font, fontSize, textPos, IM_COL32_WHITE, fmt.c_str());
}
static void DrawImGui()
@@ -2076,7 +2080,12 @@ static void DrawImGui()
ButtonGuide::Draw();
Fader::Draw();
DrawProfiler();
#if !_DEBUG
if (Config::Debug)
#endif
{
Reddog::Manager::Draw();
}
ImGui::Render();
@@ -3395,7 +3404,7 @@ static RenderPipeline* CreateGraphicsPipelineInRenderThread(PipelineState pipeli
pipeline = CreateGraphicsPipeline(pipelineState);
#ifdef ASYNC_PSO_DEBUG
bool loading = *reinterpret_cast<bool*>(g_memory.Translate(0x83367A4C));
bool loading = *SWA::SGlobals::ms_IsLoading;
if (loading)
++g_pipelinesCreatedAsynchronously;

View File

@@ -23,6 +23,8 @@ struct Video
static void Present();
static void StartPipelinePrecompilation();
static void WaitForGPU();
static void DrawCounter();
static void DrawFPS(ImFont* font);
static struct GuestSurface* GetBackBuffer();
static void ComputeViewportDimensions();
};

View File

@@ -0,0 +1,97 @@
#include <api/SWA.h>
#include <ui/game_window.h>
#include <user/config.h>
#include <ui/reddog/debug_draw.h>
#include <ui/imgui_utils.h>
#include <patches/aspect_ratio_patches.h>
// boost::~::SWA::CDebugDraw::CMember::SDrawLine
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<unsigned int>*)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);
}
// SWA::CStageManager::UpdateParallel
//PPC_FUNC_IMPL(__imp__sub_82521C68);
//PPC_FUNC(sub_82521C68)
//{
// __imp__sub_82521C68(ctx, base);
//}
// SWA::CStageManager::UpdateSerial
PPC_FUNC_IMPL(__imp__sub_82522040);
PPC_FUNC(sub_82522040)
{
auto a1 = static_cast<SWA::CStageManager*>(g_memory.Translate(ctx.r3.u32));
__imp__sub_82522040(ctx, base);
// Draw player position
if (Reddog::DebugDraw::GetIsDrawPosition())
{
// TODO (RadiantDerg): Reimplement SWA::CStageManager ability to draw progress ratio
// NOTE: Currently does not work, is the API mapping horribly misaligned/misunderstood?
if (a1->m_spStageGuidePathController)
{
if (a1->m_spStageGuidePathController->m_spPathAnimationController)
{
auto PAC = a1->m_spStageGuidePathController->m_spPathAnimationController;
//Reddog::DebugDraw::DrawTextLog(fmt::format("m_DistanceAlongPath = {:.2f}", PAC->m_DistanceAlongPath.get()).c_str());
}
// Ratio
if (a1->m_StageGuidePathLength.get() > 0.0f)
{
//float distance = 0; // GetDistanceAlongPath(a1->m_StageGuidePathController)
//a1->m_StageGuidePathRatio = distance / a1->m_StageGuidePathLength;
const Reddog::SDrawText ratioText{
{Scale(g_aspectRatioOffsetX + 720), Scale(g_aspectRatioOffsetY + 36)},
fmt::format("{:.1f}m [{:.1f}/100.0]", a1->m_StageGuidePathLength.get(), a1->m_StageGuidePathRatio.get()),
0,
3.25f,
0xFFFFFFFF,
Reddog::eDrawTextFlags_NoShadow
};
Reddog::DebugDraw::DrawText2D(ratioText);
}
}
// Position
const Reddog::SDrawText positionText{
{Scale(g_aspectRatioOffsetX + 750), Scale(g_aspectRatioOffsetY + 120)},
fmt::format("( {:.2f}, {:.2f}, {:.2f} )", a1->m_PlayerPosition.X.get(), a1->m_PlayerPosition.Y.get(), a1->m_PlayerPosition.Z.get()),
0,
2.0f,
0xFFFFFFFF,
Reddog::eDrawTextFlags_NoShadow
};
Reddog::DebugDraw::DrawText2D(positionText);
}
}
// GetIsDebugRenderForGameObject()
//PPC_FUNC(sub_82512BF8)
//{
// ctx.r3.u8 = 1; // Always return true
//}

View File

@@ -94,8 +94,7 @@ bool LoadingUpdateMidAsmHook(PPCRegister& r31)
g_ppcContext->f1.f64 = deltaTime;
g_memory.FindFunction(update)(*g_ppcContext, base);
bool loading = PPC_LOAD_U8(0x83367A4C);
if (loading)
if (*SWA::SGlobals::ms_IsLoading)
{
now = std::chrono::steady_clock::now();
constexpr auto INTERVAL = 1000000000ns / 30;
@@ -104,7 +103,7 @@ bool LoadingUpdateMidAsmHook(PPCRegister& r31)
std::this_thread::sleep_until(next);
}
return loading;
return *SWA::SGlobals::ms_IsLoading;
}
// ADXM_WaitVsync

View File

@@ -1,4 +1,5 @@
#include <api/SWA.h>
#include <ui/reddog/debug_draw.h>
#include <ui/game_window.h>
#include <user/achievement_data.h>
#include <user/config.h>

View File

@@ -159,9 +159,7 @@ PPC_FUNC(sub_824B0930)
{
g_achievementMenuIntroTime = 0;
const auto ms_IsRenderHud = (bool*)g_memory.Translate(0x8328BB26);
if (*ms_IsRenderHud && pHudPause->m_IsShown && pHudPause->m_Transition == SWA::eTransitionType_Undefined)
if (*SWA::SGlobals::ms_IsRenderHud && pHudPause->m_IsShown && pHudPause->m_Transition == SWA::eTransitionType_Undefined)
{
ButtonGuide::Open(Button(Localise("Achievements_Name"), EButtonIcon::Back, EButtonAlignment::Left, EFontQuality::Low));
g_isClosed = false;

View File

@@ -1,3 +1,34 @@
#include "frontend_listener.h"
#include <api/SWA.h>
#include <kernel/memory.h>
#include <os/logger.h>
#include <ui/options_menu.h>
FrontendListener m_frontendListener;
FrontendListener g_frontendListener;
void FrontendListener::OnSDLEvent(SDL_Event* event)
{
if (OptionsMenu::s_isVisible)
return;
switch (event->type)
{
case SDL_KEYDOWN:
{
if (event->key.keysym.sym != SDLK_F8 || m_isF8KeyDown)
break;
*SWA::SGlobals::ms_IsRenderHud = !*SWA::SGlobals::ms_IsRenderHud;
LOGFN("HUD {}", *SWA::SGlobals::ms_IsRenderHud ? "ON" : "OFF");
m_isF8KeyDown = true;
break;
}
case SDL_KEYUP:
m_isF8KeyDown = event->key.keysym.sym != SDLK_F8;
break;
}
}

View File

@@ -1,42 +1,11 @@
#pragma once
#include <kernel/memory.h>
#include <ui/sdl_listener.h>
#include <ui/options_menu.h>
#include <os/logger.h>
class FrontendListener : public SDLEventListener
{
bool m_isF8KeyDown = false;
public:
void OnSDLEvent(SDL_Event* event) override
{
if (OptionsMenu::s_isVisible)
return;
switch (event->type)
{
case SDL_KEYDOWN:
{
if (event->key.keysym.sym != SDLK_F8 || m_isF8KeyDown)
break;
// アプリケーション設定 / 開発用 / デバッグ / HUD / 全 HUD 描画
const auto ms_IsRenderHud = (bool*)g_memory.Translate(0x8328BB26);
*ms_IsRenderHud = !*ms_IsRenderHud;
LOGFN("HUD {}", *ms_IsRenderHud ? "ON" : "OFF");
m_isF8KeyDown = true;
break;
}
case SDL_KEYUP:
m_isF8KeyDown = event->key.keysym.sym != SDLK_F8;
break;
}
}
void OnSDLEvent(SDL_Event* event) override;
};

View File

@@ -12,6 +12,11 @@
#pragma comment(lib, "dwmapi.lib")
#endif
#include <res/images/reddog/mouse_cursor.bmp.h>
#include <res/images/reddog/mouse_cursor_h.bmp.h>
#include <res/images/reddog/mouse_cursor_slant_l.bmp.h>
#include <res/images/reddog/mouse_cursor_slant_r.bmp.h>
#include <res/images/reddog/mouse_cursor_v.bmp.h>
#include <res/images/game_icon.bmp.h>
#include <res/images/game_icon_night.bmp.h>
@@ -105,7 +110,7 @@ int Window_OnSDLEvent(void*, SDL_Event* event)
GameWindow::s_isFocused = true;
if (GameWindow::IsFullscreen())
SDL_ShowCursor(GameWindow::s_isFullscreenCursorVisible ? SDL_ENABLE : SDL_DISABLE);
SDL_ShowCursor(GameWindow::s_isCursorVisible ? SDL_ENABLE : SDL_DISABLE);
break;
}
@@ -146,7 +151,7 @@ int Window_OnSDLEvent(void*, SDL_Event* event)
if (event->type == SDL_CONTROLLERBUTTONDOWN || event->type == SDL_CONTROLLERBUTTONUP || event->type == SDL_CONTROLLERAXISMOTION)
{
// Hide mouse cursor when controller input is detected.
SDL_ShowCursor(SDL_DISABLE);
SDL_ShowCursor(GameWindow::s_isCursorVisible ? SDL_ENABLE : SDL_DISABLE);
}
else if (event->type == SDL_MOUSEMOTION)
{
@@ -264,23 +269,23 @@ void GameWindow::Update()
s_isChangingDisplay = false;
}
SDL_Surface* GameWindow::GetIconSurface(void* pIconBmp, size_t iconSize)
SDL_Surface* GameWindow::CreateSurface(void* pBitmapData, size_t bitmapSize)
{
auto rw = SDL_RWFromMem(pIconBmp, iconSize);
auto rw = SDL_RWFromMem(pBitmapData, bitmapSize);
auto surface = SDL_LoadBMP_RW(rw, 1);
if (!surface)
LOGF_ERROR("Failed to load icon: {}", SDL_GetError());
LOGF_ERROR("Failed to create surface: {}", SDL_GetError());
return surface;
}
void GameWindow::SetIcon(void* pIconBmp, size_t iconSize)
{
if (auto icon = GetIconSurface(pIconBmp, iconSize))
if (auto surface = CreateSurface(pIconBmp, iconSize))
{
SDL_SetWindowIcon(s_pWindow, icon);
SDL_FreeSurface(icon);
SDL_SetWindowIcon(s_pWindow, surface);
SDL_FreeSurface(surface);
}
}
@@ -296,6 +301,69 @@ void GameWindow::SetIcon(bool isNight)
}
}
void GameWindow::SetCursor(ECursorType cursorType)
{
if (s_currentCursor == cursorType)
return;
s_currentCursor = cursorType;
uint8_t* pBitmapData;
size_t bitmapSize;
int x, y;
switch (cursorType)
{
case ECursorType::DebugDefault:
pBitmapData = g_mouse_cursor;
bitmapSize = sizeof(g_mouse_cursor);
x = 0;
y = 0;
break;
case ECursorType::DebugHorizontal:
pBitmapData = g_mouse_cursor_h;
bitmapSize = sizeof(g_mouse_cursor_h);
x = 15;
y = 15;
break;
case ECursorType::DebugVertical:
pBitmapData = g_mouse_cursor_v;
bitmapSize = sizeof(g_mouse_cursor_v);
x = 15;
y = 15;
break;
case ECursorType::DebugLeftDiagonal:
pBitmapData = g_mouse_cursor_slant_l;
bitmapSize = sizeof(g_mouse_cursor_slant_l);
x = 16;
y = 15;
break;
case ECursorType::DebugRightDiagonal:
pBitmapData = g_mouse_cursor_slant_r;
bitmapSize = sizeof(g_mouse_cursor_slant_r);
x = 16;
y = 15;
break;
default:
SDL_SetCursor(SDL_GetDefaultCursor());
return;
}
if (auto surface = CreateSurface(pBitmapData, bitmapSize))
{
if (auto cursor = SDL_CreateColorCursor(surface, x, y))
{
SDL_SetCursor(cursor);
SDL_FreeSurface(surface);
}
}
}
const char* GameWindow::GetTitle()
{
return Config::Language == ELanguage::Japanese
@@ -338,7 +406,7 @@ bool GameWindow::SetFullscreen(bool isEnabled)
if (isEnabled)
{
SDL_SetWindowFullscreen(s_pWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
SDL_ShowCursor(s_isFullscreenCursorVisible ? SDL_ENABLE : SDL_DISABLE);
SDL_ShowCursor(s_isCursorVisible ? SDL_ENABLE : SDL_DISABLE);
}
else
{
@@ -352,13 +420,13 @@ bool GameWindow::SetFullscreen(bool isEnabled)
return isEnabled;
}
void GameWindow::SetFullscreenCursorVisibility(bool isVisible)
void GameWindow::SetCursorVisibility(bool isVisible)
{
s_isFullscreenCursorVisible = isVisible;
s_isCursorVisible = isVisible;
if (IsFullscreen())
{
SDL_ShowCursor(s_isFullscreenCursorVisible ? SDL_ENABLE : SDL_DISABLE);
SDL_ShowCursor(s_isCursorVisible ? SDL_ENABLE : SDL_DISABLE);
}
else
{
@@ -422,6 +490,8 @@ void GameWindow::ResetDimensions()
Config::WindowY = s_y;
Config::WindowWidth = s_width;
Config::WindowHeight = s_height;
SetDimensions(s_width, s_height, s_x, s_y);
}
uint32_t GameWindow::GetWindowFlags()

View File

@@ -9,6 +9,16 @@
#define MIN_WIDTH 640
#define MIN_HEIGHT 480
enum class ECursorType
{
Default,
DebugDefault,
DebugHorizontal,
DebugVertical,
DebugLeftDiagonal,
DebugRightDiagonal
};
class GameWindow
{
public:
@@ -22,18 +32,21 @@ public:
static inline bool s_isFocused;
static inline bool s_isIconNight;
static inline bool s_isFullscreenCursorVisible;
static inline bool s_isCursorVisible;
static inline bool s_isChangingDisplay;
static SDL_Surface* GetIconSurface(void* pIconBmp, size_t iconSize);
static inline ECursorType s_currentCursor;
static SDL_Surface* CreateSurface(void* pBitmapData, size_t bitmapSize);
static void SetIcon(void* pIconBmp, size_t iconSize);
static void SetIcon(bool isNight = false);
static void SetCursor(ECursorType cursorType = ECursorType::Default);
static const char* GetTitle();
static void SetTitle(const char* title = nullptr);
static void SetTitleBarColour();
static bool IsFullscreen();
static bool SetFullscreen(bool isEnabled);
static void SetFullscreenCursorVisibility(bool isVisible);
static void SetCursorVisibility(bool isVisible);
static bool IsMaximised();
static EWindowState SetMaximised(bool isEnabled);
static SDL_Rect GetDimensions();

View File

@@ -219,6 +219,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<std::string> strs)
{
auto result = 0.0f;

View File

@@ -1556,7 +1556,7 @@ bool InstallerWizard::Run(std::filesystem::path installPath, bool skipGame)
g_currentPage = g_firstPage;
}
GameWindow::SetFullscreenCursorVisibility(true);
GameWindow::SetCursorVisibility(true);
s_isVisible = true;
while (s_isVisible)
@@ -1568,7 +1568,7 @@ bool InstallerWizard::Run(std::filesystem::path installPath, bool skipGame)
Video::Present();
}
GameWindow::SetFullscreenCursorVisibility(false);
GameWindow::SetCursorVisibility(false);
NFD_Quit();
InstallerWizard::Shutdown();

View File

@@ -0,0 +1,236 @@
#include <api/SWA.h>
#include <ui/reddog/debug_draw.h>
#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_rPosition)
{
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.c_str());
else
DrawTextWithShadow(drawList, font, fontSize * freeText->Scale, freeText->Position, freeText->Colour, freeText->Text.c_str(), 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.c_str());
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 (Config::Debug && !ms_IsRendering && GetIsDrawDebug())
ms_LineList.push_back(in_rLine);
}
void DebugDraw::DrawText2D(const SDrawText& in_rText)
{
if (Config::Debug && !ms_IsRendering && GetIsDrawText())
ms_FreeTextList.push_back(in_rText);
}
void DebugDraw::DrawText2D(const SDrawText& in_rText, const Vector3& in_rPosition)
{
if (Config::Debug && !ms_IsRendering && GetIsDrawText())
{
auto txt = in_rText;
txt.Position = GetNDCCoordinate(in_rPosition);
ms_FreeTextList.push_back(txt);
}
}
void DebugDraw::DrawTextLog(const SDrawText& in_rText)
{
if (Config::Debug && !ms_IsRendering && GetIsDrawText())
ms_LogTextList.push_back(in_rText);
}
void DebugDraw::DrawTextLog(const char* in_Text, float in_Time, ImU32 in_Colour, ImU16 in_Priority)
{
if (Config::Debug && !ms_IsRendering && GetIsDrawText())
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 stats = fmt::format("== Stats ==\nLines: {}\nTexts: {}\nLogs: {}", ms_LineList.size(), ms_FreeTextList.size(), ms_LogTextList.size());
SDrawText text = { ImVec2(Scale(40), Scale(75)), stats, 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;
}
bool DebugDraw::GetIsDrawDebug()
{
return *SWA::SGlobals::ms_IsRenderDebugDraw;
}
bool DebugDraw::GetIsDrawText()
{
return *SWA::SGlobals::ms_IsRenderDebugDrawText;
}
bool DebugDraw::GetIsDrawPosition()
{
return *SWA::SGlobals::ms_IsRenderDebugPositionDraw;
}
}

View File

@@ -0,0 +1,66 @@
#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 };
std::string Text;
float Time { 0 };
float Scale { 1 };
ImU32 Colour { IM_COL32(255, 255, 255, 255) };
SDrawTextFlags Flags { eDrawTextFlags_None };
ImU16 Priority { 0 };
};
ImVec2 GetNDCCoordinate(const Vector3& in_rPosition);
class DebugDraw
{
public:
static inline bool ms_IsRendering = false;
static inline std::vector<SDrawLine> ms_LineList = {};
static inline std::vector<SDrawText> ms_FreeTextList = {};
static inline std::vector<SDrawText> ms_LogTextList = {};
static void DrawLine(const SDrawLine& in_rLine);
static void DrawText2D(const SDrawText& in_rText);
static void DrawText2D(const SDrawText& in_rText, const Vector3& in_rPosition);
static void DrawTextLog(const SDrawText& in_rText);
static void DrawTextLog(const char* in_Text, float in_Time = 0, ImU32 in_Colour = IM_COL32(255, 255, 255, 255), ImU16 in_Priority = 0);
static void Render(ImFont* font);
static bool GetIsDrawDebug();
static bool GetIsDrawText();
static bool GetIsDrawPosition();
};
}

View File

@@ -0,0 +1,189 @@
#include "reddog_controls.h"
#include <gpu/video.h>
#include <ui/imgui_utils.h>
#include <res/images/reddog/checkbox_1.dds.h>
#include <res/images/reddog/checkbox_2.dds.h>
#include <res/images/reddog/common_button_1.dds.h>
#include <res/images/reddog/common_button_2.dds.h>
constexpr float BUTTON_SIZE = 32.0f;
constexpr float CHECKBOX_SIZE = 16.0f;
static std::unique_ptr<GuestTexture> g_upCheckbox1;
static std::unique_ptr<GuestTexture> g_upCheckbox2;
static std::unique_ptr<GuestTexture> g_upCommonButton1;
static std::unique_ptr<GuestTexture> g_upCommonButton2;
void Reddog::InitControlsResources()
{
g_upCheckbox1 = LoadTexture(g_checkbox_1, sizeof(g_checkbox_1));
g_upCheckbox2 = LoadTexture(g_checkbox_2, sizeof(g_checkbox_2));
g_upCommonButton1 = LoadTexture(g_common_button_1, sizeof(g_common_button_1));
g_upCommonButton2 = LoadTexture(g_common_button_2, sizeof(g_common_button_2));
}
bool Reddog::Button(const char* label)
{
auto window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
auto& ctx = *GImGui;
auto& style = ctx.Style;
auto id = window->GetID(label);
auto pos = window->DC.CursorPos;
ImVec2 labelSize = { 0, 0 };
if (label && label[0] != '\0')
labelSize = ImGui::CalcTextSize(label, nullptr, true);
ImVec2 size = { BUTTON_SIZE / 2 + labelSize.x, BUTTON_SIZE / 2 };
ImRect rect(pos, { pos.x + size.x, pos.y + size.y });
ImRect paddedRect({ rect.Min.x, rect.Min.y - 1 }, { rect.Max.x, rect.Max.y + 1 });
ImGui::ItemSize(paddedRect, style.FramePadding.y);
if (!ImGui::ItemAdd(rect, id))
return false;
bool isHovered, isHeld;
bool isPressed = ImGui::ButtonBehavior(rect, id, &isHovered, &isHeld);
auto texture = isHeld && isHovered
? g_upCommonButton2.get()
: g_upCommonButton1.get();
constexpr float uvSize = 6.0f / 2.0f;
auto tl = PIXELS_TO_UV_COORDS(32, 32, 0, 0, 8, 8);
auto tc = PIXELS_TO_UV_COORDS(32, 32, 8, 0, 16, 8);
auto tr = PIXELS_TO_UV_COORDS(32, 32, 24, 0, 8, 8);
auto cl = PIXELS_TO_UV_COORDS(32, 32, 0, 8, 8, 16);
auto cc = PIXELS_TO_UV_COORDS(32, 32, 8, 8, 16, 16);
auto cr = PIXELS_TO_UV_COORDS(32, 32, 24, 8, 8, 16);
auto bl = PIXELS_TO_UV_COORDS(32, 32, 0, 24, 8, 8);
auto bc = PIXELS_TO_UV_COORDS(32, 32, 8, 24, 16, 8);
auto br = PIXELS_TO_UV_COORDS(32, 32, 24, 24, 8, 8);
window->DrawList->AddImage(texture, rect.Min, { rect.Min.x + uvSize, rect.Min.y + uvSize }, GET_UV_COORDS(tl));
window->DrawList->AddImage(texture, { rect.Min.x + uvSize, rect.Min.y }, { rect.Max.x - uvSize, rect.Min.y + uvSize }, GET_UV_COORDS(tc));
window->DrawList->AddImage(texture, { rect.Max.x - uvSize, rect.Min.y }, { rect.Max.x, rect.Min.y + uvSize }, GET_UV_COORDS(tr));
window->DrawList->AddImage(texture, { rect.Min.x, rect.Min.y + uvSize }, { rect.Min.x + uvSize, rect.Max.y - uvSize }, GET_UV_COORDS(cl));
window->DrawList->AddImage(texture, { rect.Min.x + uvSize, rect.Min.y + uvSize }, { rect.Max.x - uvSize, rect.Max.y - uvSize }, GET_UV_COORDS(cc));
window->DrawList->AddImage(texture, { rect.Max.x - uvSize, rect.Min.y + uvSize }, { rect.Max.x, rect.Max.y - uvSize }, GET_UV_COORDS(cr));
window->DrawList->AddImage(texture, { rect.Min.x, rect.Max.y - uvSize }, { rect.Min.x + uvSize, rect.Max.y }, GET_UV_COORDS(bl));
window->DrawList->AddImage(texture, { rect.Min.x + uvSize, rect.Max.y - uvSize }, { rect.Max.x - uvSize, rect.Max.y }, GET_UV_COORDS(bc));
window->DrawList->AddImage(texture, { rect.Max.x - uvSize, rect.Max.y - uvSize }, rect.Max, GET_UV_COORDS(br));
float textOffset = isHeld && isPressed ? 1.0f : 0.0f;
if (label && label[0] != '\0')
ImGui::RenderText({ textOffset + rect.Min.x + (size.x - labelSize.x) * 0.5f, textOffset + rect.Min.y + (size.y - labelSize.y) * 0.5f }, label);
return isPressed;
}
bool Reddog::Checkbox(const char* label, bool* v)
{
auto window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
auto& ctx = *GImGui;
auto& style = ctx.Style;
auto id = window->GetID(label);
auto labelSize = ImGui::CalcTextSize(label, nullptr, true);
auto pos = window->DC.CursorPos;
ImVec2 size = { CHECKBOX_SIZE, CHECKBOX_SIZE };
ImRect testRect(pos, { pos.x + size.x, pos.y + size.y });
ImRect totalRect(pos, { pos.x + size.x + (labelSize.x > 0.0f ? style.ItemInnerSpacing.x + labelSize.x : 0.0f), pos.y + size.y });
ImGui::ItemSize(totalRect, style.FramePadding.y);
if (!ImGui::ItemAdd(totalRect, id))
return false;
bool isHovered, isHeld;
bool isPressed = ImGui::ButtonBehavior(testRect, id, &isHovered, &isHeld);
if (isPressed)
{
*v = !*v;
ImGui::MarkItemEdited(id);
}
auto texture = *v
? g_upCheckbox2.get()
: g_upCheckbox1.get();
window->DrawList->AddImage(texture, testRect.Min, testRect.Max);
if (labelSize.x > 0.0f)
ImGui::RenderText({ testRect.Max.x + style.ItemInnerSpacing.x, testRect.Min.y + style.FramePadding.y }, label);
return isPressed;
}
bool Reddog::ExplicitButton(const char* label, EButtonTextAlignment textAlignment, const ImVec2& size, float fontScale)
{
auto window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
auto font = ImGui::GetFont();
font->Scale = fontScale;
ImGui::PushFont(font);
ImVec2 textSize = ImGui::CalcTextSize(label);
ImVec2 pos = ImGui::GetCursorScreenPos();
ImVec2 buttonMax = { pos.x + size.x, pos.y + size.y };
ImGui::InvisibleButton(label, size);
bool isHovered = ImGui::IsItemHovered();
bool isActive = ImGui::IsItemActive();
ImVec4 colBg = isHovered ? ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered) : ImGui::GetStyleColorVec4(ImGuiCol_Button);
ImVec4 colBorder = ImGui::GetStyleColorVec4(ImGuiCol_Border);
ImVec4 colText = ImGui::GetStyleColorVec4(ImGuiCol_Text);
window->DrawList->AddRectFilled(pos, buttonMax, ImGui::ColorConvertFloat4ToU32(colBg));
window->DrawList->AddRect(pos, buttonMax, ImGui::ColorConvertFloat4ToU32(colBorder));
auto colTextU32 = ImGui::ColorConvertFloat4ToU32(colText);
auto framePadding = ImGui::GetStyle().FramePadding;
auto marginX = 8.0f;
auto textY = (pos.y + (size.y - textSize.y) * 0.5f) - 1.0f;
switch (textAlignment)
{
case EButtonTextAlignment::Left:
window->DrawList->AddText({ pos.x + framePadding.x + marginX, textY }, colTextU32, label);
break;
case EButtonTextAlignment::Centre:
window->DrawList->AddText({ pos.x + (size.x - textSize.x) * 0.5f, textY }, colTextU32, label);
break;
case EButtonTextAlignment::Right:
window->DrawList->AddText({ size.x - framePadding.x - textSize.x - marginX, textY }, colTextU32, label);
break;
}
ImGui::PopFont();
return isActive;
}
void Reddog::Separator(float upperPadding, float lowerPadding)
{
ImGui::Dummy(ImVec2(0.0f, upperPadding));
ImGui::Separator();
ImGui::Dummy(ImVec2(0.0f, lowerPadding));
}

View File

@@ -0,0 +1,17 @@
#pragma once
namespace Reddog
{
enum class EButtonTextAlignment
{
Left,
Centre,
Right
};
void InitControlsResources();
bool Button(const char* label);
bool Checkbox(const char* label, bool* v);
bool ExplicitButton(const char* label, EButtonTextAlignment textAlignment = EButtonTextAlignment::Centre, const ImVec2& size = { 0, 0 }, float fontScale = 1);
void Separator(float upperPadding = 0, float lowerPadding = 0);
}

View File

@@ -0,0 +1,154 @@
#include "reddog_manager.h"
#include <gpu/imgui/imgui_snapshot.h>
#include <gpu/video.h>
#include <ui/reddog/windows/window_list.h>
#include <ui/reddog/reddog_controls.h>
#include <ui/reddog/debug_draw.h>
#include <ui/game_window.h>
#include <ui/imgui_utils.h>
#include <res/images/reddog/debug_icon.dds.h>
static std::unique_ptr<GuestTexture> g_upDebugIcon;
static ImVec2 g_debugIconPosOrig;
static ImVec2 g_debugIconPos = { 0, 0 };
static bool g_isDebugIconClicked = false;
static bool g_isDraggingDebugIcon = false;
static bool g_isReddogToggled = false;
static bool g_isWindowListVisible = false;
void Reddog::Manager::Init()
{
s_font = ImFontAtlasSnapshot::GetFont("micross.ttf");
g_upDebugIcon = LoadTexture(g_debug_icon, sizeof(g_debug_icon));
Reddog::InitControlsResources();
Reddog::InitWindowResources();
}
void Reddog::Manager::Draw()
{
bool isReddogToggled = SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_F1] != 0;
if (!g_isReddogToggled && isReddogToggled)
{
s_isVisible = !s_isVisible;
if (!s_isVisible)
{
g_debugIconPos = { 0, 0 };
GameWindow::SetCursor(ECursorType::Default);
GameWindow::SetCursorVisibility(false);
}
}
g_isReddogToggled = isReddogToggled;
Video::DrawFPS(s_font);
// Render our Debug Draw
DebugDraw::Render(s_font);
if (!s_isVisible)
return;
if (GameWindow::s_currentCursor == ECursorType::Default)
{
GameWindow::SetCursor(ECursorType::DebugDefault);
GameWindow::SetCursorVisibility(true);
}
auto drawList = ImGui::GetForegroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
auto debugIconSize = Scale(32);
ImVec2 debugIconMin = g_debugIconPos;
ImVec2 debugIconMax = { g_debugIconPos.x + debugIconSize, g_debugIconPos.y + debugIconSize };
drawList->AddImage(g_upDebugIcon.get(), debugIconMin, debugIconMax);
if (ImGui::IsMouseHoveringRect(debugIconMin, debugIconMax, false) && ImGui::IsMouseClicked(0))
{
g_debugIconPosOrig = g_debugIconPos;
g_isDraggingDebugIcon = true;
}
g_isDebugIconClicked = false;
if (g_isDraggingDebugIcon && ImGui::IsMouseDown(0))
{
auto& delta = ImGui::GetIO().MouseDelta;
g_debugIconPos.x += delta.x;
g_debugIconPos.y += delta.y;
}
else
{
if (g_isDraggingDebugIcon &&
g_debugIconPos.x == g_debugIconPosOrig.x &&
g_debugIconPos.y == g_debugIconPosOrig.y)
{
g_isDebugIconClicked = true;
g_isWindowListVisible = !g_isWindowListVisible;
}
g_isDraggingDebugIcon = false;
}
if (auto pWindowList = GetWindow<WindowList>(WINDOW_LIST_NAME))
{
if (!g_isDebugIconClicked && !pWindowList->IsFocused)
pWindowList->IsVisible = g_isWindowListVisible = false;
pWindowList->IsVisible = g_isWindowListVisible;
if (pWindowList->ImWindow)
{
if (debugIconMin.x > res.x / 2)
{
pWindowList->ImWindow->Pos.x = debugIconMax.x - pWindowList->ImWindow->Size.x;
}
else
{
pWindowList->ImWindow->Pos.x = debugIconMin.x;
}
if (debugIconMin.y > res.y / 2)
{
pWindowList->ImWindow->Pos.y = debugIconMin.y - pWindowList->ImWindow->Size.y;
}
else
{
pWindowList->ImWindow->Pos.y = debugIconMin.y + debugIconSize;
}
}
}
for (auto& pWindow : Reddog::GetWindows())
{
auto pTrueWindow = (Reddog::Window*)pWindow;
if (!pTrueWindow->IsVisible)
continue;
pWindow->Draw();
}
}
Reddog::Window* Reddog::Manager::GetWindow(const char* pName)
{
for (auto pWindow : Reddog::GetWindows())
{
if (strcmp(pWindow->GetName(), pName) == 0)
return (Reddog::Window*)pWindow;
}
return nullptr;
}
template<typename T>
T* Reddog::Manager::GetWindow(const char* pName)
{
return (T*)Reddog::Manager::GetWindow(pName);
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include <ui/reddog/reddog_window.h>
namespace Reddog
{
class Manager
{
public:
static inline bool s_isVisible = false;
static inline ImFont* s_font{ nullptr };
static void Init();
static void Draw();
static Window* GetWindow(const char* pName);
template<typename T>
static T* GetWindow(const char* pName);
};
}

View File

@@ -0,0 +1,428 @@
#include "reddog_window.h"
#include <gpu/video.h>
#include <ui/reddog/reddog_manager.h>
#include <ui/game_window.h>
#include <ui/imgui_utils.h>
#include <res/images/reddog/button_close_1.dds.h>
#include <res/images/reddog/button_close_2.dds.h>
#include <res/images/reddog/button_minimum_1.dds.h>
#include <res/images/reddog/button_minimum_2.dds.h>
#include <res/images/reddog/button_pin_1.dds.h>
#include <res/images/reddog/button_pin_2.dds.h>
#include <res/images/reddog/common_icon.dds.h>
#include <res/images/reddog/title_bar.dds.h>
constexpr float MIN_WINDOW_SIZE = 21.0f;
static std::unique_ptr<GuestTexture> g_upButtonClose1;
static std::unique_ptr<GuestTexture> g_upButtonClose2;
static std::unique_ptr<GuestTexture> g_upButtonMinimum1;
static std::unique_ptr<GuestTexture> g_upButtonMinimum2;
static std::unique_ptr<GuestTexture> g_upButtonPin1;
static std::unique_ptr<GuestTexture> g_upButtonPin2;
static std::unique_ptr<GuestTexture> g_upCommonIcon;
static std::unique_ptr<GuestTexture> g_upTitleBar;
static std::unique_ptr<GuestTexture> g_upWindowFrame;
float g_defaultFontScale;
ImGuiStyle g_defaultStyle;
static std::vector<Reddog::IWindow*>& Reddog::GetWindows()
{
static std::vector<Reddog::IWindow*> g_windows;
return g_windows;
}
void Reddog::InitWindowResources()
{
g_defaultFontScale = Reddog::Manager::s_font->Scale;
auto& style = ImGui::GetStyle();
g_defaultStyle = style;
g_upButtonClose1 = LoadTexture(g_button_close_1, sizeof(g_button_close_1));
g_upButtonClose2 = LoadTexture(g_button_close_2, sizeof(g_button_close_2));
g_upButtonMinimum1 = LoadTexture(g_button_minimum_1, sizeof(g_button_minimum_1));
g_upButtonMinimum2 = LoadTexture(g_button_minimum_2, sizeof(g_button_minimum_2));
g_upButtonPin1 = LoadTexture(g_button_pin_1, sizeof(g_button_pin_1));
g_upButtonPin2 = LoadTexture(g_button_pin_2, sizeof(g_button_pin_2));
g_upCommonIcon = LoadTexture(g_common_icon, sizeof(g_common_icon));
g_upTitleBar = LoadTexture(g_title_bar, sizeof(g_title_bar));
}
void Reddog::Window::BeginStyle()
{
Reddog::Manager::s_font->Scale = ImGui::GetDefaultFont()->FontSize / Reddog::Manager::s_font->FontSize;
ImGui::PushFont(Reddog::Manager::s_font);
UpdateStyle();
}
void Reddog::Window::UpdateStyle()
{
auto& style = ImGui::GetStyle();
style.WindowMinSize = { 0, MIN_WINDOW_SIZE };
auto colBg = IsFocused
? ImGui::ColorConvertU32ToFloat4(ActiveColour)
: ImGui::ColorConvertU32ToFloat4(InactiveColour);
auto colBlack = ImVec4(0, 0, 0, 1);
auto colTrans = ImVec4(0, 0, 0, 0);
auto colWhite = ImVec4(1, 1, 1, 1);
style.Colors[ImGuiCol_Border] = colTrans;
style.Colors[ImGuiCol_Button] = colTrans;
style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.67f, 0.67f, 0.67f, 1);
style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.675f, 0.675f, 0.675f, 1);
style.Colors[ImGuiCol_CheckMark] = colBlack;
style.Colors[ImGuiCol_ChildBg] = colBg;
style.Colors[ImGuiCol_FrameBg] = colBg;
style.Colors[ImGuiCol_PopupBg] = colBg;
style.Colors[ImGuiCol_ResizeGrip] = colTrans;
style.Colors[ImGuiCol_ResizeGripActive] = colTrans;
style.Colors[ImGuiCol_ResizeGripHovered] = colTrans;
style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.6f, 0.6f, 0.6f, 1);
style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(1, 0.6f, 0, 1);
style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(1, 0.6f, 0, 1);
style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(1, 0.6f, 0, 1);
style.Colors[ImGuiCol_SeparatorActive] = colTrans;
style.Colors[ImGuiCol_SeparatorHovered] = colTrans;
style.Colors[ImGuiCol_SliderGrab] = colWhite;
style.Colors[ImGuiCol_Tab] = ImVec4(0.467f, 0.467f, 0.467f, 1);
style.Colors[ImGuiCol_TabActive] = ImVec4(0.6f, 0.6f, 0.6f, 1);
style.Colors[ImGuiCol_TabHovered] = ImVec4(0.667f, 0.667f, 0.667f, 1);
style.Colors[ImGuiCol_Text] = colBlack;
style.Colors[ImGuiCol_WindowBg] = colBg;
}
void Reddog::Window::EndStyle()
{
ImGui::PopFont();
Reddog::Manager::s_font->Scale = g_defaultFontScale;
ImGui::GetStyle() = g_defaultStyle;
}
void Reddog::Window::CreateWindowGrips()
{
if (!ImWindow || !IsFocused)
return;
auto& io = ImGui::GetIO();
auto windowPos = ImWindow->Pos;
auto windowSize = ImWindow->Size;
constexpr float resizeMargin = 5.0f;
ImRect grips[6] =
{
// Corners
ImRect(windowPos.x, windowPos.y + windowSize.y - resizeMargin, windowPos.x + resizeMargin, windowPos.y + windowSize.y), // Bottom left
ImRect(windowPos.x + windowSize.x - resizeMargin, windowPos.y + windowSize.y - resizeMargin, windowPos.x + windowSize.x, windowPos.y + windowSize.y), // Bottom right
// Edges
ImRect(windowPos.x + resizeMargin, windowPos.y, windowPos.x + windowSize.x - resizeMargin, windowPos.y + resizeMargin), // Top
ImRect(windowPos.x + resizeMargin, windowPos.y + windowSize.y - resizeMargin, windowPos.x + windowSize.x - resizeMargin, windowPos.y + windowSize.y), // Bottom
ImRect(windowPos.x, windowPos.y + resizeMargin, windowPos.x + resizeMargin, windowPos.y + windowSize.y - resizeMargin), // Left
ImRect(windowPos.x + windowSize.x - resizeMargin, windowPos.y + resizeMargin, windowPos.x + windowSize.x, windowPos.y + windowSize.y - resizeMargin) // Right
};
auto hoveredGrip = -1;
for (int i = 0; i < IM_ARRAYSIZE(grips); i++)
{
auto& grip = grips[i];
if (ImGui::IsMouseHoveringRect(grip.Min, grip.Max))
hoveredGrip = i;
}
if (hoveredGrip < 0)
{
GameWindow::SetCursor(ECursorType::DebugDefault);
}
else
{
if (hoveredGrip == 0)
{
GameWindow::SetCursor(ECursorType::DebugRightDiagonal);
}
else if (hoveredGrip == 1)
{
GameWindow::SetCursor(ECursorType::DebugLeftDiagonal);
}
else if (hoveredGrip == 2 || hoveredGrip == 3)
{
GameWindow::SetCursor(ECursorType::DebugVertical);
}
else
{
GameWindow::SetCursor(ECursorType::DebugHorizontal);
}
}
}
void Reddog::Window::DrawFrame()
{
if (!ImWindow)
return;
if ((Flags & eWindowFlags_NoResize) == 0)
CreateWindowGrips();
UpdateStyle();
auto drawList = ImGui::GetWindowDrawList();
auto windowPos = ImWindow->Pos;
auto windowSize = ImWindow->Size;
auto isFocused = ImGui::IsWindowFocused();
auto hasTitleBar = (Flags & eWindowFlags_NoTitleBar) == 0;
float titleBarHeight = hasTitleBar ? 20.0f : 0.0f;
ImVec2 windowMin = { windowPos.x, windowPos.y + titleBarHeight };
ImVec2 windowMax = { windowPos.x + windowSize.x, windowPos.y + windowSize.y };
drawList->AddRectFilled(windowMin, windowMax, isFocused ? ActiveColour : InactiveColour);
drawList->AddLine(windowMin, { windowMax.x, windowMin.y }, IM_COL32(170, 170, 170, 255)); // Outer top line.
drawList->AddLine({ windowMin.x + 1, windowMin.y + 1 }, { windowMin.x + 1, windowMax.y }, IM_COL32(153, 153, 153, 255)); // Outer left line.
drawList->AddLine({ windowMin.x, windowMax.y - 2 }, { windowMax.x, windowMax.y - 2 }, IM_COL32(51, 51, 51, 255)); // Outer bottom line.
drawList->AddLine({ windowMax.x - 2, windowMin.y + 1 }, { windowMax.x - 2, windowMax.y }, IM_COL32(85, 85, 85, 255)); // Outer right line.
drawList->AddLine({ windowMin.x + 2, windowMin.y + 1 }, { windowMax.x - 2, windowMin.y + 1 }, IM_COL32(51, 51, 51, 255)); // Inner top line.
drawList->AddLine({ windowMin.x + 2, windowMin.y + 2 }, { windowMin.x + 2, windowMax.y - 2 }, IM_COL32(85, 85, 85, 255)); // Inner left line.
drawList->AddLine({ windowMin.x + 3, windowMax.y - 3 }, { windowMax.x - 2, windowMax.y - 3 }, IM_COL32(170, 170, 170, 255)); // Inner bottom line.
drawList->AddLine({ windowMax.x - 3, windowMin.y + 2 }, { windowMax.x - 3, windowMax.y - 3 }, IM_COL32(153, 153, 153, 255)); // Inner right line.
// Close window if unfocused whilst unpinned (yes it works like this in SU preview).
if (!IsPinned && !isFocused && m_pIsVisible)
*m_pIsVisible = !*m_pIsVisible;
if (hasTitleBar)
{
auto height = titleBarHeight;
auto left = PIXELS_TO_UV_COORDS(32, 32, 0, 0, 3, 32);
auto centre = PIXELS_TO_UV_COORDS(32, 32, 3, 0, 26, 32);
auto right = PIXELS_TO_UV_COORDS(32, 32, 29, 0, 3, 32);
auto colTitleBar = isFocused
? IM_COL32(220, 220, 247, 255)
: IM_COL32(176, 176, 176, 255);
drawList->AddImage(g_upTitleBar.get(), { windowPos.x, windowPos.y }, { windowPos.x + 3, windowPos.y + height }, GET_UV_COORDS(left), colTitleBar);
drawList->AddImage(g_upTitleBar.get(), { windowPos.x + 3, windowPos.y }, { windowPos.x + windowSize.x - 3, windowPos.y + height }, GET_UV_COORDS(centre), colTitleBar);
drawList->AddImage(g_upTitleBar.get(), { windowPos.x + windowSize.x - 3, windowPos.y }, { windowPos.x + windowSize.x, windowPos.y + height }, GET_UV_COORDS(right), colTitleBar);
auto cmnSize = 16.0f;
ImVec2 cmnIconMin = { windowPos.x + 9, windowPos.y + 2 };
ImVec2 cmnIconMax = { cmnIconMin.x + cmnSize, cmnIconMin.y + cmnSize };
drawList->AddImage(g_upCommonIcon.get(), cmnIconMin, cmnIconMax, { 0, 0 }, { 1, 1 }, IM_COL32_WHITE);
auto colTitleText = isFocused
? IM_COL32_WHITE
: IM_COL32(190, 190, 190, 255);
drawList->AddText({ cmnIconMax.x + 5, windowPos.y + 4 }, colTitleText, Name);
float controlsWidth = 0;
float controlsMargin = 4;
ImVec2 closeButtonMin = { windowPos.x + windowSize.x - cmnSize - controlsMargin, windowPos.y + 3 };
ImVec2 closeButtonMax = { closeButtonMin.x + cmnSize, closeButtonMin.y + cmnSize };
if (IsCloseVisible)
{
controlsWidth += closeButtonMax.x - closeButtonMin.x + controlsMargin;
ImGui::SetCursorScreenPos(closeButtonMin);
ImGui::InvisibleButton("##CloseButton", { cmnSize, cmnSize });
bool isCloseButtonActive = ImGui::IsItemActive();
if (isCloseButtonActive)
{
drawList->AddImage(g_upButtonClose2.get(), closeButtonMin, closeButtonMax);
m_isCloseButtonPressed = true;
}
else
{
drawList->AddImage(g_upButtonClose1.get(), closeButtonMin, closeButtonMax);
if (m_isCloseButtonPressed && m_pIsVisible)
{
*m_pIsVisible = !*m_pIsVisible;
m_isCloseButtonPressed = false;
}
}
}
ImVec2 minimumButtonMin = { windowPos.x + windowSize.x - cmnSize - controlsWidth - controlsMargin, windowPos.y + 3 };
ImVec2 minimumButtonMax = { minimumButtonMin.x + cmnSize, minimumButtonMin.y + cmnSize };
if (IsMinimumVisible)
{
controlsWidth += minimumButtonMax.x - minimumButtonMin.x + controlsMargin;
ImGui::SetCursorScreenPos(minimumButtonMin);
ImGui::InvisibleButton("##MinimumButton", { cmnSize, cmnSize });
bool isMinimumButtonActive = ImGui::IsItemActive();
if (isMinimumButtonActive)
{
drawList->AddImage(g_upButtonMinimum2.get(), minimumButtonMin, minimumButtonMax);
m_isMinimumButtonPressed = true;
}
else
{
drawList->AddImage(g_upButtonMinimum1.get(), minimumButtonMin, minimumButtonMax);
if (m_isMinimumButtonPressed)
{
SetMinimum(!IsMinimum);
m_isMinimumButtonPressed = false;
}
}
}
ImVec2 pinButtonMin = { windowPos.x + windowSize.x - cmnSize - controlsWidth - controlsMargin, windowPos.y + 3 };
ImVec2 pinButtonMax = { pinButtonMin.x + cmnSize, pinButtonMin.y + cmnSize };
if (IsPinVisible)
{
controlsWidth += pinButtonMax.x - pinButtonMin.x + controlsMargin;
ImGui::SetCursorScreenPos(pinButtonMin);
ImGui::InvisibleButton("##PinButton", { cmnSize, cmnSize });
bool isPinButtonActive = ImGui::IsItemActive();
if (isPinButtonActive)
{
drawList->AddImage(g_upButtonPin2.get(), pinButtonMin, pinButtonMax);
m_isPinButtonPressed = true;
}
else
{
drawList->AddImage(g_upButtonPin1.get(), pinButtonMin, pinButtonMax);
if (m_isPinButtonPressed)
{
IsPinned = !IsPinned;
m_isPinButtonPressed = false;
}
}
}
ImVec2 titleBarMin = { windowPos.x, windowPos.y };
ImVec2 titleBarMax = { windowPos.x + windowSize.x - controlsWidth, windowPos.y + titleBarHeight };
if (ImGui::IsMouseHoveringRect(titleBarMin, titleBarMax, false))
{
if (IsMinimumVisible && ImGui::IsMouseDoubleClicked(0))
SetMinimum(!IsMinimum);
}
}
}
void Reddog::Window::SetMinimum(bool isMinimum)
{
IsMinimum = isMinimum;
if (!ImWindow)
return;
auto windowSize = ImWindow->Size;
if (IsMinimum)
{
m_preMinimumWindowSize = windowSize;
ImGui::SetWindowSize(ImWindow, { windowSize.x, MIN_WINDOW_SIZE });
}
else
{
ImGui::SetWindowSize(ImWindow, { windowSize.x, m_preMinimumWindowSize.y });
}
}
void Reddog::Window::SetVisible(bool isVisible)
{
IsVisible = m_isSetVisible = isVisible;
}
bool Reddog::Window::Begin(bool* pIsVisible)
{
if (pIsVisible)
{
m_pIsVisible = pIsVisible;
}
else
{
m_pIsVisible = &IsVisible;
}
BeginStyle();
ImGui::SetNextWindowSizeConstraints({ m_minWidth, m_minHeight }, { m_maxWidth, m_maxHeight });
auto flags = REDDOG_IMGUI_FLAGS;
if ((Flags & eWindowFlags_NoResize) != 0)
flags |= ImGuiWindowFlags_NoResize;
if (m_preBegin)
m_preBegin();
bool result = ImGui::Begin(Name, m_pIsVisible, flags);
if (m_postBegin)
m_postBegin();
if (result && *m_pIsVisible)
{
ImWindow = ImGui::GetCurrentWindow();
IsFocused = ImGui::IsWindowFocused();
if ((Flags & eWindowFlags_Centre) != 0 && !m_isSetCentre)
{
auto viewport = ImGui::GetMainViewport();
auto windowPos = ImGui::GetWindowPos();
auto pos = ImVec2((viewport->Size.x / 2) - (ImWindow->Size.x / 2), (viewport->Size.y / 2) - (ImWindow->Size.y / 2));
ImGui::SetWindowPos(pos);
}
// Keep centring window until it has been moved.
if (ImGui::IsMouseDragging(0) && ImGui::IsWindowHovered())
m_isSetCentre = true;
if (m_isSetVisible)
{
ImGui::SetWindowFocus();
m_isSetVisible = false;
}
DrawFrame();
return true;
}
return false;
}
void Reddog::Window::End()
{
if (m_preEnd)
m_preEnd();
ImGui::End();
if (m_postEnd)
m_postEnd();
EndStyle();
}

View File

@@ -0,0 +1,99 @@
#pragma once
#define REDDOG_IMGUI_FLAGS ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoBackground
namespace Reddog
{
enum EWindowFlags : uint32_t
{
eWindowFlags_None = 0,
eWindowFlags_NoTitleBar = 1 << 0,
eWindowFlags_NoResize = 1 << 1,
eWindowFlags_NoListEntry = 1 << 2,
eWindowFlags_Centre = 1 << 3
};
class IWindow
{
public:
virtual ~IWindow() = default;
virtual const char* GetName() = 0;
virtual void Draw() = 0;
};
extern std::vector<IWindow*>& GetWindows();
class Window : public IWindow
{
private:
bool* m_pIsVisible{ nullptr };
bool m_isSetCentre{};
bool m_isSetVisible{};
bool m_isCloseButtonPressed{};
bool m_isMinimumButtonPressed{};
bool m_isPinButtonPressed{};
ImVec2 m_preMinimumWindowSize{};
void BeginStyle();
void UpdateStyle();
void EndStyle();
void CreateWindowGrips();
void DrawFrame();
protected:
float m_minWidth{ 175.0f };
float m_minHeight{};
float m_maxWidth{ FLT_MAX };
float m_maxHeight{ FLT_MAX };
std::function<void()> m_preBegin{ nullptr };
std::function<void()> m_postBegin{ nullptr };
std::function<void()> m_preEnd{ nullptr };
std::function<void()> m_postEnd{ nullptr };
public:
const char* Name{ "(null)" };
EWindowFlags Flags{ eWindowFlags_None };
bool IsFocused{ true };
bool IsCloseVisible{ true };
bool IsMinimumVisible{ true };
bool IsPinVisible{ true };
bool IsMinimum{};
bool IsPinned{ true };
bool IsVisible{};
ImGuiWindow* ImWindow{ nullptr };
ImU32 ActiveColour{ IM_COL32(227, 227, 255, 255) };
ImU32 InactiveColour{ IM_COL32(182, 182, 182, 255) };
Window()
{
GetWindows().push_back(this);
}
Window(const char* pName, EWindowFlags flags = eWindowFlags_None) : Name(pName), Flags(flags)
{
GetWindows().push_back(this);
}
const char* GetName() override
{
return Name;
}
void Draw() override
{
Begin();
End();
}
void SetMinimum(bool isMinimum);
void SetVisible(bool isVisible = true);
bool Begin(bool* pIsVisible = nullptr);
void End();
};
void InitWindowResources();
}

View File

@@ -0,0 +1,13 @@
#include "counter_window.h"
#include <gpu/video.h>
static CounterWindow g_window;
void CounterWindow::Draw()
{
if (Begin())
{
Video::DrawCounter();
}
End();
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include <ui/reddog/reddog_window.h>
class CounterWindow : public Reddog::Window
{
public:
CounterWindow() : Window()
{
Name = "Counter";
m_minWidth = 215;
m_minHeight = 328;
}
void Draw() override;
};

View File

@@ -0,0 +1,21 @@
#include "exports_window.h"
#include <ui/reddog/reddog_controls.h>
#include <user/config.h>
static ExportsWindow g_window;
void ExportsWindow::Draw()
{
if (Begin())
{
ImGui::TextColored({ 1, 0, 0, 1 }, "For testing purposes only, use Hedge Mod Manager to toggle these.");
Reddog::Checkbox("Allow Cancelling Unleash", &Config::AllowCancellingUnleash.Value);
Reddog::Checkbox("Disable Auto Save Warning", &Config::DisableAutoSaveWarning.Value);
Reddog::Checkbox("Disable DLC Icon", &Config::DisableDLCIcon.Value);
Reddog::Checkbox("Fix Unleash Out of Control Drain", &Config::FixUnleashOutOfControlDrain.Value);
Reddog::Checkbox("Homing Attack on Boost", &Config::HomingAttackOnBoost.Value);
Reddog::Checkbox("Save Score at Checkpoints", &Config::SaveScoreAtCheckpoints.Value);
Reddog::Checkbox("Skip Intro Logos", &Config::SkipIntroLogos.Value);
}
End();
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include <ui/reddog/reddog_window.h>
class ExportsWindow : public Reddog::Window
{
public:
ExportsWindow() : Window()
{
Name = "Exports";
Flags = Reddog::eWindowFlags_NoResize;
}
void Draw() override;
};

View File

@@ -0,0 +1,62 @@
#include "view_window.h"
#include <api/SWA.h>
#include <gpu/video.h>
#include <kernel/memory.h>
#include <ui/reddog/debug_draw.h>
#include <ui/reddog/reddog_controls.h>
#include <ui/game_window.h>
#include <user/config.h>
static ViewWindow g_window;
void ViewWindow::Draw()
{
if (Begin())
{
Reddog::Checkbox("Render FPS", &Config::ShowFPS.Value);
Reddog::Separator();
Reddog::Checkbox("Render HUD (F8)", SWA::SGlobals::ms_IsRenderHud);
Reddog::Checkbox("Render Main Game HUD", SWA::SGlobals::ms_IsRenderGameMainHud);
Reddog::Checkbox("Render Pause HUD", SWA::SGlobals::ms_IsRenderHudPause);
Reddog::Separator();
Reddog::Checkbox("Render Debug Draw", SWA::SGlobals::ms_IsRenderDebugDraw);
Reddog::Checkbox("Render Debug Draw Text", SWA::SGlobals::ms_IsRenderDebugDrawText);
Reddog::Checkbox("Render Debug Position Draw", SWA::SGlobals::ms_IsRenderDebugPositionDraw);
Reddog::Separator();
Reddog::Checkbox("Draw Light Field Sampling Point", SWA::SGlobals::ms_DrawLightFieldSamplingPoint);
Reddog::Checkbox("Ignore Light Field Data", SWA::SGlobals::ms_IgnoreLightFieldData);
Reddog::Checkbox("Light Field Debug", SWA::SGlobals::ms_LightFieldDebug);
Reddog::Separator();
Reddog::Checkbox("Visualize Loaded GI Mip Level", SWA::SGlobals::ms_VisualizeLoadedLevel);
Reddog::Separator();
Reddog::Checkbox("Render Event Collision", SWA::SGlobals::ms_IsTriggerRender);
Reddog::Checkbox("Render Object Collision", SWA::SGlobals::ms_IsObjectCollisionRender);
Reddog::Checkbox("Render Stage Collision", SWA::SGlobals::ms_IsCollisionRender);
ImGui::TextColored({ 1, 0, 0, 1 }, "* Requires stage restart.");
Reddog::Separator();
if (Reddog::Button("Reset Window Dimensions (F2)"))
{
Config::Fullscreen = GameWindow::SetFullscreen(false);
GameWindow::ResetDimensions();
}
if (Reddog::Button("Recentre Window (F3)"))
{
if (!GameWindow::IsFullscreen())
GameWindow::SetDimensions(GameWindow::s_width, GameWindow::s_height);
}
}
End();
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include <ui/reddog/reddog_window.h>
class ViewWindow : public Reddog::Window
{
public:
ViewWindow() : Window()
{
Name = "View";
Flags = Reddog::eWindowFlags_NoResize;
}
void Draw() override;
};

View File

@@ -0,0 +1,13 @@
#include "welcome_window.h"
static WelcomeWindow g_window;
void WelcomeWindow::Draw()
{
if (Begin())
{
ImGui::TextColored({ 1, 0, 0, 1 }, "!!! ATTENTION !!!");
ImGui::Text("For development use only.\nAny changes made here may cause unexpected behaviour.\nUse at your own risk!");
}
End();
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include <ui/game_window.h>
#include <ui/reddog/reddog_window.h>
class WelcomeWindow : public Reddog::Window
{
public:
WelcomeWindow() : Window()
{
Name = "Welcome";
Flags = (Reddog::EWindowFlags)(Reddog::eWindowFlags_NoResize | Reddog::eWindowFlags_NoListEntry | Reddog::eWindowFlags_Centre);
IsMinimumVisible = false;
IsPinVisible = false;
IsPinned = false;
IsVisible = true;
}
void Draw() override;
};

View File

@@ -0,0 +1,22 @@
#include "window_list.h"
#include <ui/reddog/reddog_controls.h>
static WindowList g_window;
void WindowList::Draw()
{
if (Begin())
{
for (auto& pWindow : Reddog::GetWindows())
{
auto pTrueWindow = (Reddog::Window*)pWindow;
if ((pTrueWindow->Flags & Reddog::eWindowFlags_NoListEntry) != 0)
continue;
if (Reddog::ExplicitButton(pTrueWindow->Name, Reddog::EButtonTextAlignment::Left, { 190, 32 }, 1.1f))
pTrueWindow->SetVisible();
}
}
End();
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include <ui/reddog/reddog_window.h>
#define WINDOW_LIST_NAME "##WindowList"
class WindowList : public Reddog::Window
{
public:
WindowList() : Window()
{
Name = WINDOW_LIST_NAME;
Flags = (Reddog::EWindowFlags)(Reddog::eWindowFlags_NoTitleBar | Reddog::eWindowFlags_NoResize | Reddog::eWindowFlags_NoListEntry);
ActiveColour = InactiveColour = IM_COL32_WHITE;
m_minWidth = m_minHeight = 0;
}
void Draw() override;
};

View File

@@ -608,6 +608,7 @@ public:
CONFIG_DEFINE_LOCALISED("System", bool, ControlTutorial, true);
CONFIG_DEFINE_LOCALISED("System", bool, AchievementNotifications, true);
CONFIG_DEFINE_ENUM_LOCALISED("System", ETimeOfDayTransition, TimeOfDayTransition, ETimeOfDayTransition::Xbox);
CONFIG_DEFINE_HIDDEN("System", bool, Debug, false);
CONFIG_DEFINE_LOCALISED("Input", bool, InvertCameraX, false);
CONFIG_DEFINE_LOCALISED("Input", bool, InvertCameraY, false);
@@ -676,6 +677,8 @@ public:
CONFIG_DEFINE_ENUM_LOCALISED("Video", ECutsceneAspectRatio, CutsceneAspectRatio, ECutsceneAspectRatio::Original);
CONFIG_DEFINE_ENUM_LOCALISED("Video", EUIScaleMode, UIScaleMode, EUIScaleMode::Edge);
CONFIG_DEFINE_HIDDEN("Debug", bool, ShowFPS, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, AllowCancellingUnleash, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, DisableAutoSaveWarning, false);
CONFIG_DEFINE_HIDDEN("Exports", bool, DisableDLCIcon, false);