From b58f73da2894b905601145423f21effefccc95d4 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sun, 18 May 2025 22:16:00 -0400 Subject: [PATCH] Got Cog jailbroken from the game viewport (a UCogEditorSubsystem instance can be within the editor's main frame now) --- Plugins/Cog/Private/CogHelper.cpp | 100 ++++++++++ Plugins/Cog/Public/CogConsoleCommandManager.h | 36 ++++ Plugins/Cog/Source/Cog/Cog.Build.cs | 2 + .../Cog/Source/Cog/Private/CogSubsystem.cpp | 49 ++--- Plugins/Cog/Source/Cog/Private/CogWindow.cpp | 2 +- .../Source/Cog/Private/CogWindow_Layouts.cpp | 11 +- .../Source/Cog/Private/CogWindow_Settings.cpp | 9 +- Plugins/Cog/Source/Cog/Public/CogHelper.h | 1 + Plugins/Cog/Source/Cog/Public/CogSubsystem.h | 11 +- .../Source/Cog/Public/CogWindow_Settings.h | 2 +- .../Cog/Source/CogCommon/CogCommon.Build.cs | 2 + Plugins/Cog/Source/CogImgui/CogImgui.Build.cs | 2 + .../CogImgui/Private/CogImguiContext.cpp | 14 +- .../CogImgui/Private/CogImguiWidget.cpp | 25 ++- .../Source/CogImgui/Public/CogImguiContext.h | 132 ++++++++----- .../Public/CogImguiInputCatcherWidget.h | 6 +- .../Source/CogImgui/Public/CogImguiWidget.h | 6 +- Plugins/CogEditor/CogEditor.uplugin | 31 +++ .../Source/CogEditor/CogEditor.Build.cs | 64 ++++++ .../CogEditor/CogEditorImguiContext.cpp | 113 +++++++++++ .../Source/CogEditor/CogEditorImguiContext.h | 11 ++ .../Source/CogEditor/CogEditorMinimal.cpp | 3 + .../Source/CogEditor/CogEditorMinimal.h | 130 +++++++++++++ .../Source/CogEditor/CogEditorModule.cpp | 17 ++ .../Source/CogEditor/CogEditorModule.h | 16 ++ .../Source/CogEditor/CogEditorSubsystem.cpp | 182 ++++++++++++++++++ .../Source/CogEditor/CogEditorSubsystem.h | 38 ++++ 27 files changed, 914 insertions(+), 101 deletions(-) create mode 100644 Plugins/Cog/Private/CogHelper.cpp create mode 100644 Plugins/Cog/Public/CogConsoleCommandManager.h create mode 100644 Plugins/CogEditor/CogEditor.uplugin create mode 100644 Plugins/CogEditor/Source/CogEditor/CogEditor.Build.cs create mode 100644 Plugins/CogEditor/Source/CogEditor/CogEditorImguiContext.cpp create mode 100644 Plugins/CogEditor/Source/CogEditor/CogEditorImguiContext.h create mode 100644 Plugins/CogEditor/Source/CogEditor/CogEditorMinimal.cpp create mode 100644 Plugins/CogEditor/Source/CogEditor/CogEditorMinimal.h create mode 100644 Plugins/CogEditor/Source/CogEditor/CogEditorModule.cpp create mode 100644 Plugins/CogEditor/Source/CogEditor/CogEditorModule.h create mode 100644 Plugins/CogEditor/Source/CogEditor/CogEditorSubsystem.cpp create mode 100644 Plugins/CogEditor/Source/CogEditor/CogEditorSubsystem.h diff --git a/Plugins/Cog/Private/CogHelper.cpp b/Plugins/Cog/Private/CogHelper.cpp new file mode 100644 index 0000000..0c1e722 --- /dev/null +++ b/Plugins/Cog/Private/CogHelper.cpp @@ -0,0 +1,100 @@ +#include "CogHelper.h" + +#include "AssetRegistry/AssetRegistryModule.h" +#include "AssetRegistry/IAssetRegistry.h" +#include "CogDebug.h" +#include "GameFramework/PlayerController.h" +#include "imgui.h" + +//---------------------------------------------------------------------------------------------------------------------- +const UObject* FCogHelper::GetFirstAssetByClass(const TSubclassOf& AssetClass) +{ + const IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")).Get(); + + TArray Assets; + AssetRegistry.GetAssetsByClass(AssetClass->GetClassPathName(), Assets, true); + if (Assets.Num() == 0) + { + return nullptr; + } + + const UObject* Asset = Assets[0].GetAsset(); + return Asset; +} + +//-------------------------------------------------------------------------------------------------------------------------- +FString FCogHelper::GetActorName(const AActor* Actor) +{ + if (Actor == nullptr) + { + return FString("none"); + } + + return GetActorName(*Actor); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FString FCogHelper::GetActorName(const AActor& Actor) +{ +#if WITH_EDITOR + + const FCogDebugSettings& Settings = FCogDebug::Settings; + return Settings.ActorNameUseLabel ? Actor.GetActorLabel() : Actor.GetName(); + +#else //WITH_EDITOR + + return Actor.GetName(); + +#endif //WITH_EDITOR +} + + +//----------------------------------------------------------------------------------------- +bool FCogHelper::ComputeBoundingBoxScreenPosition(const APlayerController* PlayerController, const FVector& Origin, const FVector& Extent, FVector2D& Min, FVector2D& Max) +{ + FVector Corners[8]; + Corners[0].Set(-Extent.X, -Extent.Y, -Extent.Z); // - - - + Corners[1].Set(Extent.X, -Extent.Y, -Extent.Z); // + - - + Corners[2].Set(-Extent.X, Extent.Y, -Extent.Z); // - + - + Corners[3].Set(-Extent.X, -Extent.Y, Extent.Z); // - - + + Corners[4].Set(Extent.X, Extent.Y, -Extent.Z); // + + - + Corners[5].Set(Extent.X, -Extent.Y, Extent.Z); // + - + + Corners[6].Set(-Extent.X, Extent.Y, Extent.Z); // - + + + Corners[7].Set(Extent.X, Extent.Y, Extent.Z); // + + + + + Min.X = FLT_MAX; + Min.Y = FLT_MAX; + Max.X = -FLT_MAX; + Max.Y = -FLT_MAX; + + for (int i = 0; i < 8; ++i) + { + FVector2D ScreenLocation; + if (PlayerController->ProjectWorldLocationToScreen(Origin + Corners[i], ScreenLocation, false) == false) + { + return false; + } + + Min.X = FMath::Min(ScreenLocation.X, Min.X); + Min.Y = FMath::Min(ScreenLocation.Y, Min.Y); + Max.X = FMath::Max(ScreenLocation.X, Max.X); + Max.Y = FMath::Max(ScreenLocation.Y, Max.Y); + } + + // Prevent getting large values when the camera get close to the target + ImVec2 DisplaySize = ImGui::GetIO().DisplaySize; + Min.X = FMath::Max(-DisplaySize.x, Min.X); + Min.Y = FMath::Max(-DisplaySize.y, Min.Y); + Max.X = FMath::Min(DisplaySize.x * 2, Max.X); + Max.Y = FMath::Min(DisplaySize.y * 2, Max.Y); + + return true; +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool FCogHelper::IsTraceChannelHidden(const UCollisionProfile& InCollisionProfile, const ECollisionChannel InCollisionChannel) +{ + + + return false; +} diff --git a/Plugins/Cog/Public/CogConsoleCommandManager.h b/Plugins/Cog/Public/CogConsoleCommandManager.h new file mode 100644 index 0000000..a6d2736 --- /dev/null +++ b/Plugins/Cog/Public/CogConsoleCommandManager.h @@ -0,0 +1,36 @@ +#pragma once + +#include "CoreMinimal.h" +#include "HAL/IConsoleManager.h" + +class UWorld; + +DECLARE_DELEGATE_TwoParams(FCogWindowConsoleCommandDelegate, const TArray&, UWorld*); + +//-------------------------------------------------------------------------------------------------------------------------- +struct FCogCommandReceiver +{ + int32 PIEInstance = INDEX_NONE; + + FCogWindowConsoleCommandDelegate Delegate; +}; + +//-------------------------------------------------------------------------------------------------------------------------- +struct FCogCommandInfo +{ + IConsoleObject* ConsoleObject = nullptr; + + TArray Receivers; +}; + +//-------------------------------------------------------------------------------------------------------------------------- +struct COG_API FCogConsoleCommandManager +{ + static void RegisterWorldConsoleCommand(const TCHAR* InName, const TCHAR* InHelp, UWorld* InWorld, const FCogWindowConsoleCommandDelegate& InDelegate); + + static void UnregisterAllWorldConsoleCommands(const UWorld* InWorld); + +protected: + + static TMap CommandMap; +}; diff --git a/Plugins/Cog/Source/Cog/Cog.Build.cs b/Plugins/Cog/Source/Cog/Cog.Build.cs index 128f9ab..06ae560 100644 --- a/Plugins/Cog/Source/Cog/Cog.Build.cs +++ b/Plugins/Cog/Source/Cog/Cog.Build.cs @@ -6,6 +6,8 @@ public class Cog : ModuleRules { PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + OptimizeCode = CodeOptimization.Never; + PublicIncludePaths.AddRange( new string[] { } diff --git a/Plugins/Cog/Source/Cog/Private/CogSubsystem.cpp b/Plugins/Cog/Source/Cog/Private/CogSubsystem.cpp index d38a487..0bbf869 100644 --- a/Plugins/Cog/Source/Cog/Private/CogSubsystem.cpp +++ b/Plugins/Cog/Source/Cog/Private/CogSubsystem.cpp @@ -70,10 +70,12 @@ void UCogSubsystem::TryInitialize(UWorld& World) { return; } UE_LOG(LogCogImGui, Verbose, TEXT("UCogSubsystem::TryInitialize | World:%s %p"), *World.GetName(), &World); - - Context.Initialize(WorldContext->GameViewport.Get()); - FCogImGuiContextScope ImGuiContextScope(Context); + Context = MakeUnique(); + FCogImguiContext& ImguiCtx = * (FCogImguiContext*)(Context.Get()); + ImguiCtx.Initialize(WorldContext->GameViewport.Get()); + + FCogImGuiContextScope ImGuiContextScope(* Context); ImGuiSettingsHandler IniHandler; IniHandler.TypeName = "Cog"; @@ -174,11 +176,16 @@ void UCogSubsystem::TryInitialize(UWorld& World) //-------------------------------------------------------------------------------------------------------------------------- void UCogSubsystem::Shutdown() { - FCogImGuiContextScope ImGuiContextScope(Context); + FCogConsoleCommandManager::UnregisterAllWorldConsoleCommands(GetWorld()); + + if (Context.IsValid() == false) { + return; + } + FCogImGuiContextScope ImGuiContextScope(* Context); if (bIsInitialized) { - Context.SaveSettings(); + Context->SaveSettings(); } for (FCogWindow* Window : Windows) @@ -190,10 +197,8 @@ void UCogSubsystem::Shutdown() if (bIsInitialized) { - Context.Shutdown(); + Context->Shutdown(); } - - FCogConsoleCommandManager::UnregisterAllWorldConsoleCommands(GetWorld()); } //-------------------------------------------------------------------------------------------------------------------------- @@ -246,7 +251,7 @@ void UCogSubsystem::Tick(float InDeltaTime) return; } - FCogImGuiContextScope ImGuiContextScope(Context); + FCogImGuiContextScope ImGuiContextScope(GetContext()); UpdatePlayerControllers(*World); @@ -275,12 +280,12 @@ void UCogSubsystem::Tick(float InDeltaTime) } const bool ShouldSkipRendering = NetImgui::IsConnected() && bIsSelectionModeActive == false; - Context.SetSkipRendering(ShouldSkipRendering); + GetContext().SetSkipRendering(ShouldSkipRendering); - if (Context.BeginFrame(InDeltaTime)) + if (GetContext().BeginFrame(InDeltaTime)) { Render(InDeltaTime); - Context.EndFrame(); + Context->EndFrame(); } } @@ -360,7 +365,7 @@ void UCogSubsystem::UpdatePlayerControllers(UWorld& World) //-------------------------------------------------------------------------------------------------------------------------- void UCogSubsystem::Render(float DeltaTime) { - FCogImGuiContextScope ImGuiContextScope(Context); + FCogImGuiContextScope ImGuiContextScope(GetContext()); ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0)); ImGui::DockSpaceOverViewport(0, nullptr, ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_NoDockingOverCentralNode | ImGuiDockNodeFlags_AutoHideTabBar); @@ -376,12 +381,12 @@ void UCogSubsystem::Render(float DeltaTime) // There is no need to have Imgui input enabled if the imgui rendering // is only done on the NetImgui server. So we disable imgui input. //---------------------------------------------------------------------- - if (Context.GetEnableInput() && NetImgui::IsConnected() && bIsSelectionModeActive == false) + if (Context->GetEnableInput() && NetImgui::IsConnected() && bIsSelectionModeActive == false) { - Context.SetEnableInput(false); + Context->SetEnableInput(false); } - if ((Context.GetEnableInput() || NetImgui::IsConnected()) && bIsSelectionModeActive == false) + if ((Context->GetEnableInput() || NetImgui::IsConnected()) && bIsSelectionModeActive == false) { RenderMainMenu(); } @@ -459,7 +464,7 @@ FCogWindow* UCogSubsystem::FindWindowByID(const ImGuiID ID) //-------------------------------------------------------------------------------------------------------------------------- void UCogSubsystem::ResetLayout() { - FCogImGuiContextScope ImGuiContextScope(Context); + FCogImGuiContextScope ImGuiContextScope(* Context); for (const FCogWindow* Window : Windows) { @@ -492,7 +497,7 @@ void UCogSubsystem::LoadLayout(const int32 LayoutIndex) //-------------------------------------------------------------------------------------------------------------------------- void UCogSubsystem::SaveLayout(const int32 LayoutIndex) { - FCogImGuiContextScope ImGuiContextScope(Context); + FCogImGuiContextScope ImGuiContextScope(* Context); const FString Filename = *FCogImguiHelper::GetIniFilePath(FString::Printf(TEXT("imgui_layout_%d"), LayoutIndex)); ImGui::SaveIniSettingsToDisk(TCHAR_TO_ANSI(*Filename)); @@ -1010,14 +1015,14 @@ const UObject* UCogSubsystem::GetAsset(const TSubclassOf& AssetClass) c void UCogSubsystem::ToggleInputMode() { UE_LOG(LogCogImGui, Verbose, TEXT("UCogSubsystem::ToggleInputMode")); - Context.SetEnableInput(!Context.GetEnableInput()); + Context->SetEnableInput(! Context->GetEnableInput()); } //-------------------------------------------------------------------------------------------------------------------------- void UCogSubsystem::DisableInputMode() { UE_LOG(LogCogImGui, Verbose, TEXT("UCogSubsystem::DisableInputMode")); - Context.SetEnableInput(false); + Context->SetEnableInput(false); } //-------------------------------------------------------------------------------------------------------------------------- @@ -1028,9 +1033,9 @@ void UCogSubsystem::SetActivateSelectionMode(const bool Value) if (bIsSelectionModeActive) { - bIsInputEnabledBeforeEnteringSelectionMode = GetContext().GetEnableInput(); + bIsInputEnabledBeforeEnteringSelectionMode = Context->GetEnableInput(); - Context.SetEnableInput(true); + Context->SetEnableInput(true); } else { diff --git a/Plugins/Cog/Source/Cog/Private/CogWindow.cpp b/Plugins/Cog/Source/Cog/Private/CogWindow.cpp index 7ffe71f..ff355e3 100644 --- a/Plugins/Cog/Source/Cog/Private/CogWindow.cpp +++ b/Plugins/Cog/Source/Cog/Private/CogWindow.cpp @@ -253,7 +253,7 @@ bool FCogWindow::IsWindowRenderedInMainMenu() //-------------------------------------------------------------------------------------------------------------------------- float FCogWindow::GetDpiScale() const { - return GetOwner()->GetContext().GetDpiScale(); + return GetOwner()->GetContext().GetDPIScale(); } //-------------------------------------------------------------------------------------------------------------------------- diff --git a/Plugins/Cog/Source/Cog/Private/CogWindow_Layouts.cpp b/Plugins/Cog/Source/Cog/Private/CogWindow_Layouts.cpp index 8650952..5bd0261 100644 --- a/Plugins/Cog/Source/Cog/Private/CogWindow_Layouts.cpp +++ b/Plugins/Cog/Source/Cog/Private/CogWindow_Layouts.cpp @@ -15,11 +15,12 @@ FCogWindow_Layouts::FCogWindow_Layouts() //-------------------------------------------------------------------------------------------------------------------------- void FCogWindow_Layouts::RenderContent() { - const UPlayerInput* PlayerInput = FCogImguiInputHelper::GetPlayerInput(*GetWorld()); - if (PlayerInput == nullptr) - { - return; - } + bool bIsEditor = GetWorld()->IsEditorWorld(); + const UPlayerInput* PlayerInput = FCogImguiInputHelper::GetPlayerInput(*GetWorld()); + if (bIsEditor == false && PlayerInput == nullptr) + { + return; + } if (ImGui::MenuItem("Reset Window Layout")) { diff --git a/Plugins/Cog/Source/Cog/Private/CogWindow_Settings.cpp b/Plugins/Cog/Source/Cog/Private/CogWindow_Settings.cpp index 3940697..a867395 100644 --- a/Plugins/Cog/Source/Cog/Private/CogWindow_Settings.cpp +++ b/Plugins/Cog/Source/Cog/Private/CogWindow_Settings.cpp @@ -31,7 +31,7 @@ void FCogWindow_Settings::Initialize() GetOwner()->GetContext().SetDPIScale(Config->DPIScale); - FCogImguiContext& Context = GetOwner()->GetContext(); + ICogImguiContext& Context = GetOwner()->GetContext(); Context.SetEnableInput(Config->bEnableInput); Context.SetShareKeyboard(Config->bShareKeyboard); Context.SetShareMouse(Config->bShareMouse); @@ -52,7 +52,7 @@ void FCogWindow_Settings::PreSaveConfig() //Config->bNavEnableGamepad = IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad; //Config->bNavNoCaptureInput = IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard; - const FCogImguiContext& Context = GetOwner()->GetContext(); + const ICogImguiContext& Context = GetOwner()->GetContext(); Config->bEnableInput = Context.GetEnableInput(); Config->bShareKeyboard = Context.GetShareKeyboard(); Config->bShareMouse = Context.GetShareMouse(); @@ -62,14 +62,15 @@ void FCogWindow_Settings::PreSaveConfig() //-------------------------------------------------------------------------------------------------------------------------- void FCogWindow_Settings::RenderContent() { + bool bIsEditor = GetWorld()->IsEditorWorld(); const UPlayerInput* PlayerInput = FCogImguiInputHelper::GetPlayerInput(*GetWorld()); - if (PlayerInput == nullptr) + if (bIsEditor == false && PlayerInput == nullptr) { return; } ImGuiIO& IO = ImGui::GetIO(); - FCogImguiContext& Context = GetOwner()->GetContext(); + ICogImguiContext& Context = GetOwner()->GetContext(); //------------------------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Input", ImGuiTreeNodeFlags_DefaultOpen)) diff --git a/Plugins/Cog/Source/Cog/Public/CogHelper.h b/Plugins/Cog/Source/Cog/Public/CogHelper.h index 4e9a5b2..f299184 100644 --- a/Plugins/Cog/Source/Cog/Public/CogHelper.h +++ b/Plugins/Cog/Source/Cog/Public/CogHelper.h @@ -2,6 +2,7 @@ #include "AssetRegistry/AssetData.h" #include "CoreMinimal.h" +#include "Engine/EngineTypes.h" #include "Templates/SubclassOf.h" class UCollisionProfile; diff --git a/Plugins/Cog/Source/Cog/Public/CogSubsystem.h b/Plugins/Cog/Source/Cog/Public/CogSubsystem.h index 8fc9631..b293bbe 100644 --- a/Plugins/Cog/Source/Cog/Public/CogSubsystem.h +++ b/Plugins/Cog/Source/Cog/Public/CogSubsystem.h @@ -23,6 +23,7 @@ struct ImGuiSettingsHandler; struct ImGuiTextBuffer; struct FKey; + //-------------------------------------------------------------------------------------------------------------------------- UCLASS() class COG_API UCogSubsystem : public UTickableWorldSubsystem @@ -82,9 +83,11 @@ public: void RebindShortcut(const UCogCommonConfig& InConfig, const FProperty& InProperty); - const FCogImguiContext& GetContext() const { return Context; } - - FCogImguiContext& GetContext() { return Context; } + ICogImguiContext const& GetContext() const { return * Context; } + ICogImguiContext& GetContext() { return * Context; } + + //const FCogImguiContext& GetContext() const { return Context; } + //FCogImguiContext& GetContext() { return Context; } bool IsRenderingMainMenu() const { return bIsRenderingInMainMenu; } @@ -192,7 +195,7 @@ protected: TWeakObjectPtr InputComponent; - FCogImguiContext Context; + TUniquePtr Context; TArray Windows; diff --git a/Plugins/Cog/Source/Cog/Public/CogWindow_Settings.h b/Plugins/Cog/Source/Cog/Public/CogWindow_Settings.h index a08de74..1d987ab 100644 --- a/Plugins/Cog/Source/Cog/Public/CogWindow_Settings.h +++ b/Plugins/Cog/Source/Cog/Public/CogWindow_Settings.h @@ -46,7 +46,7 @@ enum class ECogWidgetAlignment //-------------------------------------------------------------------------------------------------------------------------- UCLASS(Config = Cog) -class UCogWindowConfig_Settings : public UCogCommonConfig +class COG_API UCogWindowConfig_Settings : public UCogCommonConfig { GENERATED_BODY() diff --git a/Plugins/Cog/Source/CogCommon/CogCommon.Build.cs b/Plugins/Cog/Source/CogCommon/CogCommon.Build.cs index d462300..1d86c8d 100644 --- a/Plugins/Cog/Source/CogCommon/CogCommon.Build.cs +++ b/Plugins/Cog/Source/CogCommon/CogCommon.Build.cs @@ -6,6 +6,8 @@ public class CogCommon : ModuleRules { bUseUnity = false; PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + OptimizeCode = CodeOptimization.Never; PublicIncludePaths.AddRange( new string[] { diff --git a/Plugins/Cog/Source/CogImgui/CogImgui.Build.cs b/Plugins/Cog/Source/CogImgui/CogImgui.Build.cs index 69ac734..b7ae087 100644 --- a/Plugins/Cog/Source/CogImgui/CogImgui.Build.cs +++ b/Plugins/Cog/Source/CogImgui/CogImgui.Build.cs @@ -6,6 +6,8 @@ public class CogImgui : ModuleRules public CogImgui(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + + OptimizeCode = CodeOptimization.Never; PublicDependencyModuleNames.AddRange(new[] { diff --git a/Plugins/Cog/Source/CogImgui/Private/CogImguiContext.cpp b/Plugins/Cog/Source/CogImgui/Private/CogImguiContext.cpp index 78558d6..561ce42 100644 --- a/Plugins/Cog/Source/CogImgui/Private/CogImguiContext.cpp +++ b/Plugins/Cog/Source/CogImgui/Private/CogImguiContext.cpp @@ -23,6 +23,16 @@ #include "Widgets/SViewport.h" #include "Widgets/SWindow.h" +//-------------------------------------------------------------------------------------------------------------------------- +FCogImGuiContextScope::FCogImGuiContextScope(ICogImguiContext& CogImguiContext) +{ + PrevContext = ImGui::GetCurrentContext(); + PrevPlotContext = ImPlot::GetCurrentContext(); + + ImGui::SetCurrentContext(CogImguiContext.GetImGuiContext()); + ImPlot::SetCurrentContext(CogImguiContext.GetImPlotContext()); +} + //-------------------------------------------------------------------------------------------------------------------------- FCogImGuiContextScope::FCogImGuiContextScope(const FCogImguiContext& CogImguiContext) { @@ -455,7 +465,7 @@ void FCogImguiContext::ImGui_CreateWindow(ImGuiViewport* Viewport) return; } - FCogImguiContext* Context = ParentViewportData->Context; + FCogImguiContext* Context = (FCogImguiContext*)ParentViewportData->Context; const bool bTooltipWindow = (Viewport->Flags & ImGuiViewportFlags_TopMost); const bool bPopupWindow = (Viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon); @@ -512,7 +522,7 @@ void FCogImguiContext::ImGui_CreateWindow(ImGuiViewport* Viewport) ViewportData->Widget = Widget; ViewportData->Window = Window; - ParentViewportData->Context->WindowToViewportMap.Add(Window, Viewport->ID); + Context->WindowToViewportMap.Add(Window, Viewport->ID); Viewport->PlatformRequestResize = false; } diff --git a/Plugins/Cog/Source/CogImgui/Private/CogImguiWidget.cpp b/Plugins/Cog/Source/CogImgui/Private/CogImguiWidget.cpp index 8ad7315..595f65c 100644 --- a/Plugins/Cog/Source/CogImgui/Private/CogImguiWidget.cpp +++ b/Plugins/Cog/Source/CogImgui/Private/CogImguiWidget.cpp @@ -153,16 +153,21 @@ FReply SCogImguiWidget::HandleKeyEvent(const FKeyEvent& KeyEvent, bool Down) con return FReply::Unhandled(); } - if (const UWorld* World = Context->GetGameViewport()->GetWorld()) - { - if (const UPlayerInput* PlayerInput = FCogImguiInputHelper::GetPlayerInput(*World)) - { - if (FCogImguiInputHelper::IsTopPriorityKeyEvent(*PlayerInput, KeyEvent)) - { - return FReply::Unhandled(); - } - } - } + // TODO(Ed): Do we need a better way to know when the context is definitely an Cog Editor? + bool bIsEditor = Context->GetGameViewport() == nullptr; + if (bIsEditor == false) + { + if (const UWorld* World = Context->GetGameViewport()->GetWorld()) + { + if (const UPlayerInput* PlayerInput = FCogImguiInputHelper::GetPlayerInput(*World)) + { + if (FCogImguiInputHelper::IsTopPriorityKeyEvent(*PlayerInput, KeyEvent)) + { + return FReply::Unhandled(); + } + } + } + } ImGuiIO& IO = ImGui::GetIO(); IO.AddKeyEvent(FCogImguiInputHelper::ToImKey(KeyEvent.GetKey()), Down); diff --git a/Plugins/Cog/Source/CogImgui/Public/CogImguiContext.h b/Plugins/Cog/Source/CogImgui/Public/CogImguiContext.h index 30b896e..a0575b8 100644 --- a/Plugins/Cog/Source/CogImgui/Public/CogImguiContext.h +++ b/Plugins/Cog/Source/CogImgui/Public/CogImguiContext.h @@ -6,6 +6,7 @@ #include "Templates/SharedPointer.h" #include "UObject/StrongObjectPtr.h" +class ICogImguiContext; class FCogImguiContext; class IInputProcessor; class SCogImguiWidget; @@ -25,69 +26,109 @@ struct COGIMGUI_API FCogImGuiViewportData TWeakPtr Widget = nullptr; }; +class COGIMGUI_API ICogImguiContext +{ +public: + virtual ~ICogImguiContext() {}; + + virtual TObjectPtr GetGameViewport() const PURE_VIRTUAL(GetGameViewport, return(nullptr); ); + virtual TSharedPtr GetMainWidget() const PURE_VIRTUAL(GetMainWidget, return(nullptr); ); + + virtual ImGuiContext* GetImGuiContext() PURE_VIRTUAL(GetImGuiContext, return nullptr; ); + virtual ImPlotContext* GetImPlotContext() PURE_VIRTUAL(GetImPlotContext, return nullptr; ); + + virtual bool GetEnableInput() const PURE_VIRTUAL(GetEnableInput, return false; ); + virtual void SetEnableInput(bool InValue) PURE_VIRTUAL(SetEnableInput); + + virtual bool GetShareKeyboard() const PURE_VIRTUAL(GetEnableKeyboard, return false; ); + virtual void SetShareKeyboard(bool InValue) PURE_VIRTUAL(SetShareKeyboard); + + virtual bool GetShareMouse() const PURE_VIRTUAL(GetShareMouse, return false; ); + virtual void SetShareMouse(bool InValue) PURE_VIRTUAL(SetShareMouse); + + virtual bool GetShareMouseWithGameplay() const PURE_VIRTUAL(GetShareMouseWithGameplay, return false; ); + virtual void SetShareMouseWithGameplay(bool InValue) PURE_VIRTUAL(SetShareMouseWithGameplay); + + virtual bool GetWantCaptureMouse() const PURE_VIRTUAL(GetWantCaptureMouse, return(false); ); + + virtual float GetDPIScale() const PURE_VIRTUAL(GetDPIScale, return(0.0f); ); + virtual void SetDPIScale(float Value) PURE_VIRTUAL(SetDPIScale); + + virtual bool BeginFrame(float InDeltaTime) PURE_VIRTUAL(BeginFrame, return false; ); + virtual void EndFrame() PURE_VIRTUAL(EndFrame); + + virtual ImVec2 GetImguiMousePos() const PURE_VIRTUAL(GetImguiMousePos, return ImVec2{}; ); + + virtual bool GetSkipRendering() const PURE_VIRTUAL(GetSkipRendering, return(false); ); + virtual void SetSkipRendering(bool skip) PURE_VIRTUAL(SkipRendering); + + virtual void SaveSettings() const PURE_VIRTUAL(SaveSettings); + virtual void Shutdown() PURE_VIRTUAL(Shutdown); + + virtual void OnImGuiWidgetFocusLost() PURE_VIRTUAL(OnImGuiWidgetFocusLost); +}; struct COGIMGUI_API FCogImGuiContextScope { + UE_NODISCARD_CTOR explicit FCogImGuiContextScope(ICogImguiContext& CogImguiContext); UE_NODISCARD_CTOR explicit FCogImGuiContextScope(const FCogImguiContext& CogImguiContext); UE_NODISCARD_CTOR explicit FCogImGuiContextScope(ImGuiContext* GuiCtx, ImPlotContext* PlotCtx); ~FCogImGuiContextScope(); -private: +protected: ImGuiContext* PrevContext = nullptr; ImPlotContext* PrevPlotContext = nullptr; }; -class COGIMGUI_API FCogImguiContext : public TSharedFromThis +class COGIMGUI_API FCogImguiContext + : public TSharedFromThis + , public ICogImguiContext { public: +#pragma region ICogImguiContext + bool BeginFrame(float InDeltaTime) override; + void EndFrame() override; + + ImGuiContext* GetImGuiContext() override { return Context; } + ImPlotContext* GetImPlotContext() override { return PlotContext; } + + TObjectPtr GetGameViewport() const override { return GameViewport; } + TSharedPtr GetMainWidget() const override { return MainWidget; } + + bool GetEnableInput() const override { return bEnableInput; } + void SetEnableInput(bool InValue) override; + + bool GetShareKeyboard() const override { return bShareKeyboard; } + void SetShareKeyboard(bool Value) override { bShareKeyboard = Value; } + + bool GetShareMouse() const override { return bShareMouse; } + void SetShareMouse(bool Value) override; + + void SetShareMouseWithGameplay(bool Value) override; + bool GetShareMouseWithGameplay() const override { return bShareMouseWithGameplay; } + + ImVec2 GetImguiMousePos() const override; + + bool GetWantCaptureMouse() const override { return bWantCaptureMouse; } + + float GetDPIScale() const override { return DpiScale; } + void SetDPIScale(float Value) override; + + bool GetSkipRendering() const override; + void SetSkipRendering(bool Value) override; + + void Shutdown() override; + void SaveSettings() const override; + + void OnImGuiWidgetFocusLost() override; +#pragma endregion ICogImguiContext + void Initialize(UGameViewportClient* InGameViewport); - void Shutdown(); - - void SaveSettings() const; - - bool GetEnableInput() const { return bEnableInput; } - - void SetEnableInput(bool InValue); - - bool GetWantCaptureMouse() const { return bWantCaptureMouse; } - - bool GetShareMouse() const { return bShareMouse; } - - void SetShareMouse(bool Value); - - bool GetShareMouseWithGameplay() const { return bShareMouseWithGameplay; } - - void SetShareMouseWithGameplay(bool Value); - - bool GetShareKeyboard() const { return bShareKeyboard; } - - void SetShareKeyboard(bool Value) { bShareKeyboard = Value; } - - bool BeginFrame(float InDeltaTime); - - void EndFrame(); - - float GetDpiScale() const { return DpiScale; } - - void SetDPIScale(float Value); - - bool GetSkipRendering() const; - - void SetSkipRendering(bool Value); - - ImVec2 GetImguiMousePos() const; - - TObjectPtr GetGameViewport() const { return GameViewport; } - - TSharedPtr GetMainWidget() const { return MainWidget; } - - void OnImGuiWidgetFocusLost(); - static bool GetIsNetImguiInitialized() { return bIsNetImGuiInitialized; } -private: +protected: friend struct FCogImGuiContextScope; @@ -186,5 +227,4 @@ private: bool bRetakeFocus = false; static bool bIsNetImGuiInitialized; - }; diff --git a/Plugins/Cog/Source/CogImgui/Public/CogImguiInputCatcherWidget.h b/Plugins/Cog/Source/CogImgui/Public/CogImguiInputCatcherWidget.h index 21d7d5e..fc2079b 100644 --- a/Plugins/Cog/Source/CogImgui/Public/CogImguiInputCatcherWidget.h +++ b/Plugins/Cog/Source/CogImgui/Public/CogImguiInputCatcherWidget.h @@ -6,7 +6,7 @@ #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SLeafWidget.h" -class FCogImguiContext; +class ICogImguiContext; class SWindow; class UGameViewportClient; @@ -18,7 +18,7 @@ class COGIMGUI_API SCogImguiInputCatcherWidget : public SLeafWidget public: SLATE_BEGIN_ARGS(SCogImguiInputCatcherWidget) {} - SLATE_ARGUMENT(FCogImguiContext*, Context) + SLATE_ARGUMENT(ICogImguiContext*, Context) SLATE_END_ARGS() void Construct(const FArguments& InArgs); @@ -59,7 +59,7 @@ protected: void RefreshVisibility(); - FCogImguiContext* Context = nullptr; + ICogImguiContext* Context = nullptr; TSharedPtr Window = nullptr; }; diff --git a/Plugins/Cog/Source/CogImgui/Public/CogImguiWidget.h b/Plugins/Cog/Source/CogImgui/Public/CogImguiWidget.h index a67a9ef..b910b72 100644 --- a/Plugins/Cog/Source/CogImgui/Public/CogImguiWidget.h +++ b/Plugins/Cog/Source/CogImgui/Public/CogImguiWidget.h @@ -7,7 +7,7 @@ #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SLeafWidget.h" -class FCogImguiContext; +class ICogImguiContext; class SWindow; class UGameViewportClient; @@ -19,7 +19,7 @@ class COGIMGUI_API SCogImguiWidget : public SLeafWidget public: SLATE_BEGIN_ARGS(SCogImguiWidget) {} - SLATE_ARGUMENT(FCogImguiContext*, Context) + SLATE_ARGUMENT(ICogImguiContext*, Context) SLATE_END_ARGS() void Construct(const FArguments& InArgs); @@ -68,7 +68,7 @@ protected: void RefreshVisibility(); - FCogImguiContext* Context = nullptr; + ICogImguiContext* Context = nullptr; TSharedPtr Window = nullptr; diff --git a/Plugins/CogEditor/CogEditor.uplugin b/Plugins/CogEditor/CogEditor.uplugin new file mode 100644 index 0000000..7610e1a --- /dev/null +++ b/Plugins/CogEditor/CogEditor.uplugin @@ -0,0 +1,31 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "Cog Editor", + "Description": "Cog: Editor Plugin for usage outside of the game viewport", + "Category": "Other", + "CreatedBy": "Edward R. Gonzalez", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": false, + "IsBetaVersion": false, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": + [ + { + "Name": "CogEditor", + "Type": "Editor", + "LoadingPhase": "Default" + } + ], + "Plugins": [ + { + "Name": "Cog", + "Enabled": true + } + ] +} \ No newline at end of file diff --git a/Plugins/CogEditor/Source/CogEditor/CogEditor.Build.cs b/Plugins/CogEditor/Source/CogEditor/CogEditor.Build.cs new file mode 100644 index 0000000..209d91e --- /dev/null +++ b/Plugins/CogEditor/Source/CogEditor/CogEditor.Build.cs @@ -0,0 +1,64 @@ +using UnrealBuildTool; + +public class CogEditor : ModuleRules +{ + public CogEditor(ReadOnlyTargetRules Target) : base(Target) + { + bUseUnity = false; + bMergeUnityFiles = false; + IWYUSupport = IWYUSupport.None; + PCHUsage = PCHUsageMode.NoPCHs; + OptimizeCode = CodeOptimization.Never; + MinCpuArchX64 = MinimumCpuArchitectureX64.AVX512; + IncludeOrderVersion = EngineIncludeOrderVersion.Latest; + + bCodeCoverage = false; + bDisableStaticAnalysis = true; + bValidateCircularDependencies = true; + bValidateFormatStrings = false; + bValidateInternalApi = false; + bEnableExceptions = false; + bEnableBufferSecurityChecks = false; + bEnableNonInlinedGenCppWarnings = false; + bIgnoreUnresolvedSymbols = false; + // bEnableUndefinedIdentifierWarnings = false; + + bWarningsAsErrors = false; + ShadowVariableWarningLevel = UnrealBuildTool.WarningLevel.Off; + UndefinedIdentifierWarningLevel = UnrealBuildTool.WarningLevel.Off; + + UndefinedIdentifierWarningLevel = WarningLevel.Off; + + bEnableObjCAutomaticReferenceCounting = false; + bEnableObjCExceptions = false; + + var Kilobyte = 1024; + NumIncludedBytesPerUnityCPPOverride = Kilobyte * 32; + MinFilesUsingPrecompiledHeaderOverride = 1; + + #region Engine + PrivateDependencyModuleNames.AddRange(new string[] + { + "CoreUObject", + "Engine", + + "Slate", + "SlateCore", + + "UnrealEd" + }); + #endregion Engine + + + #region Cog + PublicDependencyModuleNames.AddRange(new string[] + { + "Core", + "Cog", + "CogCommon", + "CogImgui", + "CogDebug", + }); + #endregion Cog + } +} \ No newline at end of file diff --git a/Plugins/CogEditor/Source/CogEditor/CogEditorImguiContext.cpp b/Plugins/CogEditor/Source/CogEditor/CogEditorImguiContext.cpp new file mode 100644 index 0000000..6143c19 --- /dev/null +++ b/Plugins/CogEditor/Source/CogEditor/CogEditorImguiContext.cpp @@ -0,0 +1,113 @@ +#include "CogEditorImguiContext.h" + +#include "CogImguiHelper.h" +#include "CogImguiInputCatcherWidget.h" +#include "CogImguiWidget.h" +#include "imgui_internal.h" +#include "Interfaces/IMainFrameModule.h" +#include "Widgets/DeclarativeSyntaxSupport.h" + +#include "implot.h" +#include "Framework/Application/SlateApplication.h" + +void FCogEditorImguiContext::Initialize() +{ + IMGUI_CHECKVERSION(); + + IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked("MainFrame"); + TSharedPtr MainWindow = MainFrameModule.GetParentWindow(); + + // ImGui Context must be created before creating widgets as widgets can receive events that uses the ImGui context right away. + Context = ImGui::CreateContext(); + PlotContext = ImPlot::CreateContext(); + ImGui::SetCurrentContext(Context); + ImPlot::SetImGuiContext(Context); + ImPlot::SetCurrentContext(PlotContext); + + SAssignNew(MainWidget, SCogImguiWidget).Context(this); + SAssignNew(InputCatcherWidget, SCogImguiInputCatcherWidget).Context(this); + MainWindow->AddOverlaySlot() [ + MainWidget.ToSharedRef() + ]; + MainWindow->AddOverlaySlot() [ + InputCatcherWidget.ToSharedRef() + ]; + + ImGuiIO& IO = ImGui::GetIO(); + IO.UserData = this; + + + IO.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + IO.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; + IO.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + IO.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; + + IO.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; + IO.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; + IO.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; + IO.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; + IO.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; + IO.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; + + //-------------------------------------------------------------------- + // + //-------------------------------------------------------------------- + ImGuiViewport* MainViewport = ImGui::GetMainViewport(); + FCogImGuiViewportData* ViewportData = new FCogImGuiViewportData(); + MainViewport->PlatformUserData = ViewportData; + ViewportData->Window = FSlateApplication::IsInitialized() ? FSlateApplication::Get().GetActiveTopLevelWindow() : nullptr; + ViewportData->Context = this; + ViewportData->Widget = MainWidget; + + const auto InitFilenameTemp = StringCast(*FCogImguiHelper::GetIniFilePath("imgui")); + ImStrncpy(IniFilename, InitFilenameTemp.Get(), IM_ARRAYSIZE(IniFilename)); + IO.IniFilename = IniFilename; + + ImGuiPlatformIO& PlatformIO = ImGui::GetPlatformIO(); + PlatformIO.Platform_CreateWindow = ImGui_CreateWindow; + PlatformIO.Platform_DestroyWindow = ImGui_DestroyWindow; + PlatformIO.Platform_ShowWindow = ImGui_ShowWindow; + PlatformIO.Platform_SetWindowPos = ImGui_SetWindowPos; + PlatformIO.Platform_GetWindowPos = ImGui_GetWindowPos; + PlatformIO.Platform_SetWindowSize = ImGui_SetWindowSize; + PlatformIO.Platform_GetWindowSize = ImGui_GetWindowSize; + PlatformIO.Platform_SetWindowFocus = ImGui_SetWindowFocus; + PlatformIO.Platform_GetWindowFocus = ImGui_GetWindowFocus; + PlatformIO.Platform_GetWindowMinimized = ImGui_GetWindowMinimized; + PlatformIO.Platform_SetWindowTitle = ImGui_SetWindowTitle; + PlatformIO.Platform_SetWindowAlpha = ImGui_SetWindowAlpha; + PlatformIO.Platform_RenderWindow = ImGui_RenderWindow; + + PlatformIO.Platform_ClipboardUserData = &ClipboardBuffer; + PlatformIO.Platform_GetClipboardTextFn = ImGui_GetClipboardTextFn; + PlatformIO.Platform_SetClipboardTextFn = ImGui_SetClipboardTextFn; + PlatformIO.Platform_OpenInShellFn = ImGui_OpenInShell; + + if (FSlateApplication::IsInitialized()) + { + if (const TSharedPtr PlatformApplication = FSlateApplication::Get().GetPlatformApplication()) + { + FDisplayMetrics DisplayMetrics; + PlatformApplication->GetInitialDisplayMetrics(DisplayMetrics); + PlatformApplication->OnDisplayMetricsChanged().AddRaw(this, &FCogEditorImguiContext::OnDisplayMetricsChanged); + OnDisplayMetricsChanged(DisplayMetrics); + } + } + else + { + FMonitorInfo monitorInfo; + monitorInfo.bIsPrimary = true; + monitorInfo.DisplayRect = FPlatformRect(0, 0, 1080, 720); + FDisplayMetrics DisplayMetrics; + DisplayMetrics.MonitorInfo.Add(monitorInfo); + OnDisplayMetricsChanged(DisplayMetrics); + } + +#if NETIMGUI_ENABLED + if (bIsNetImGuiInitialized == false) + { + NetImgui::Startup(); + bIsNetImGuiInitialized = true; + } +#endif +} diff --git a/Plugins/CogEditor/Source/CogEditor/CogEditorImguiContext.h b/Plugins/CogEditor/Source/CogEditor/CogEditorImguiContext.h new file mode 100644 index 0000000..773d3a6 --- /dev/null +++ b/Plugins/CogEditor/Source/CogEditor/CogEditorImguiContext.h @@ -0,0 +1,11 @@ +#pragma once + +#include "CogImguiContext.h" + +class COGEDITOR_API FCogEditorImguiContext : public FCogImguiContext +{ +public: + + void Initialize(UGameViewportClient* viewport) = delete; + void Initialize(); +}; diff --git a/Plugins/CogEditor/Source/CogEditor/CogEditorMinimal.cpp b/Plugins/CogEditor/Source/CogEditor/CogEditorMinimal.cpp new file mode 100644 index 0000000..607d9eb --- /dev/null +++ b/Plugins/CogEditor/Source/CogEditor/CogEditorMinimal.cpp @@ -0,0 +1,3 @@ +#include "CogEditorMinimal.h" + +DEFINE_LOG_CATEGORY(LogCogEditor); diff --git a/Plugins/CogEditor/Source/CogEditor/CogEditorMinimal.h b/Plugins/CogEditor/Source/CogEditor/CogEditorMinimal.h new file mode 100644 index 0000000..aab49ca --- /dev/null +++ b/Plugins/CogEditor/Source/CogEditor/CogEditorMinimal.h @@ -0,0 +1,130 @@ +#pragma once + +/*---------------------------------------------------------------------------- + Low level includes. +----------------------------------------------------------------------------*/ + +#include "CoreTypes.h" + +/*---------------------------------------------------------------------------- + Forward declarations +----------------------------------------------------------------------------*/ + +#include "CoreFwd.h" +#include "UObject/UObjectHierarchyFwd.h" +#include "Containers/ContainersFwd.h" + +/*---------------------------------------------------------------------------- + Commonly used headers +----------------------------------------------------------------------------*/ + +#include "Misc/VarArgs.h" +#include "Logging/LogVerbosity.h" +#include "UObject/ObjectMacros.h" +#include "Delegates/Delegate.h" +#include "Delegates/DelegateCombinations.h" + +/*----------------------------------------------------------------------------*/ + +#include "CogEditorMinimal.generated.h" + +#pragma region Engine Forwards +#pragma endregion Engine Forwards + +#pragma region Cog Forwards +class UCogEditorModule; +class UCogEditorSubsystem; +#pragma endregion Cog Forwards + + +#pragma region Logging + +// Straight from the Engine +UENUM(BlueprintType) +enum class ECogEditorVerbosity : uint8 +{ + /** Not used */ + NoLogging = 0, + + /** Always prints a fatal error to console (and log file) and crashes (even if logging is disabled) */ + // Fatal, + // Just use GASA_Fatal... + + /** + * Prints an error to console (and log file). + * Commandlets and the editor collect and report errors. Error messages result in commandlet failure. + */ + Error = ELogVerbosity::Error, + + /** + * Prints a warning to console (and log file). + * Commandlets and the editor collect and report warnings. Warnings can be treated as an error. + */ + Warning, + + /** Prints a message to console (and log file) */ + Display, + + /** Prints a message to a log file (does not print to console) */ + Log, + + /** + * Prints a verbose message to a log file (if Verbose logging is enabled for the given category, + * usually used for detailed logging) + */ + Verbose, + + /** + * Prints a verbose message to a log file (if VeryVerbose logging is enabled, + * usually used for detailed logging that would otherwise spam output) + */ + VeryVerbose, +}; + +DECLARE_LOG_CATEGORY_EXTERN(LogCogEditor, Log, All); + +namespace CogEditor +{ + using ELogV = ECogEditorVerbosity; + + // Works for Unreal 5.4, Win64 MSVC (untested in other scenarios, for now) + inline + void Log( FString Message + , ELogV Verbosity = ELogV::Log + , FLogCategoryBase& Category = LogCogEditor + , bool DumpStack = false + , int32 Line = __builtin_LINE() + , const ANSICHAR* File = __builtin_FILE() + , const ANSICHAR* Func = __builtin_FUNCTION() ) + { + #if !UE_BUILD_SHIPPING && !NO_LOGGING + ELogVerbosity::Type EngineVerbosity = (ELogVerbosity::Type) Verbosity; + + static UE::Logging::Private::FStaticBasicLogDynamicData LOG_Dynamic; + static UE::Logging::Private::FStaticBasicLogRecord + LOG_Static(TEXT("%s -- %hs %hs(%d)"), File, Line, EngineVerbosity, LOG_Dynamic); + + if ((EngineVerbosity & ELogVerbosity::VerbosityMask) <= ELogVerbosity::COMPILED_IN_MINIMUM_VERBOSITY) + { + if ((EngineVerbosity & ELogVerbosity::VerbosityMask) <= Category.GetVerbosity()) + { + if ( ! Category.IsSuppressed(EngineVerbosity)) + { + if (DumpStack) + FDebug::DumpStackTraceToLog(EngineVerbosity); + BasicLog(Category, &LOG_Static, *Message, File, Func, Line); + } + } + } + #endif + } +} + +#define CogEditor_Fatal(Message) UE_LOG( LogCogEditor, Fatal, TEXT("%s -- %hs %hs(%d)"), *Message, __builtin_FILE(), __func__, __builtin_LINE() ); +#define CogEditor_Error(Message) UE_LOG( LogCogEditor, Error, TEXT("%s -- %hs %hs(%d)"), *Message, __builtin_File(), __func__, __builtin_LINE() ); +#define CogEditor_Warning(Message) UE_LOG( LogCogEditor, Warning, TEXT("%s -- %hs %hs(%d)"), *Message, __builtin_File(), __func__, __builtin_LINE() ); +#define CogEditor_Display(Message) UE_LOG( LogCogEditor, Display, TEXT("%s -- %hs %hs(%d)"), *Message, __builtin_File(), __func__, __builtin_LINE() ); +#define CogEditor_Log(Message) UE_LOG( LogCogEditor, Log, TEXT("%s -- %hs %hs(%d)"), *Message, __builtin_File(), __func__, __builtin_LINE() ); +#define CogEditor_Verbose(Message) UE_LOG( LogCogEditor, Verbose, TEXT("%s -- %hs %hs(%d)"), *Message, __builtin_File(), __func__, __builtin_LINE() ); +#define CogEditor_VeryVerbose(Message) UE_LOG( LogCogEditor, VeryVerbose, TEXT("%s -- %hs %hs(%d)"), *Message, __builtin_File(), __func__, __builtin_LINE() ); +#pragma endregion Logging diff --git a/Plugins/CogEditor/Source/CogEditor/CogEditorModule.cpp b/Plugins/CogEditor/Source/CogEditor/CogEditorModule.cpp new file mode 100644 index 0000000..d724a82 --- /dev/null +++ b/Plugins/CogEditor/Source/CogEditor/CogEditorModule.cpp @@ -0,0 +1,17 @@ +#include "CogEditorModule.h" + +IMPLEMENT_MODULE(FCogEditorModule, CogEditor) + +#define LOCTEXT_NAMESPACE "FCogEditorModule" + +void FCogEditorModule::StartupModule() +{ + +} + +void FCogEditorModule::ShutdownModule() +{ + +} + +#undef LOCTEXT_NAMESPACE diff --git a/Plugins/CogEditor/Source/CogEditor/CogEditorModule.h b/Plugins/CogEditor/Source/CogEditor/CogEditorModule.h new file mode 100644 index 0000000..04cd938 --- /dev/null +++ b/Plugins/CogEditor/Source/CogEditor/CogEditorModule.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Modules/ModuleInterface.h" +#include "Modules/ModuleManager.h" + +#include "CogEditorMinimal.h" + +class COGEDITOR_API FCogEditorModule : public IModuleInterface +{ +public: + static FCogEditorModule& Get() { return FModuleManager::LoadModuleChecked("CogEditor"); } + + void StartupModule() override; + void ShutdownModule() override; +}; + diff --git a/Plugins/CogEditor/Source/CogEditor/CogEditorSubsystem.cpp b/Plugins/CogEditor/Source/CogEditor/CogEditorSubsystem.cpp new file mode 100644 index 0000000..139b0d3 --- /dev/null +++ b/Plugins/CogEditor/Source/CogEditor/CogEditorSubsystem.cpp @@ -0,0 +1,182 @@ +#include "CogEditorSubsystem.h" + +#include "Editor.h" + +#include "imgui_internal.h" + +#include "CogConsoleCommandManager.h" +#include "CogWindow_Spacing.h" +#include "CogWindow_Layouts.h" +#include "CogWindow_Settings.h" + +FString UCogEditorSubsystem::ToggleInputCommand = TEXT("CogEditor.ToggleInput"); +FString UCogEditorSubsystem::DisableInputCommand = TEXT("CogEditor.DisableInput"); +FString UCogEditorSubsystem::LoadLayoutCommand = TEXT("CogEditor.LoadLayout"); +FString UCogEditorSubsystem::SaveLayoutCommand = TEXT("CogEditor.SaveLayout"); +FString UCogEditorSubsystem::ResetLayoutCommand = TEXT("CogEditor.ResetLayout"); + +UE_DISABLE_OPTIMIZATION +ETickableTickType UCogEditorSubsystem::GetTickableTickType() const +{ + return ETickableTickType::Never; +} +UE_ENABLE_OPTIMIZATION + +void UCogEditorSubsystem::TryInitialize(UWorld& World) +{ + // Super::TryInitialize(World); + + if (bIsInitialized) { + return; + } + + // FWorldContext* WorldContext = GEngine->GetWorldContextFromWorld(&World); + // if (WorldContext == nullptr) { + // return; + // } + + // if (WorldContext->GameViewport == nullptr && IsRunningDedicatedServer() == false) { + // return; + // } + + FWorldContext& WorldContext = GEditor->GetEditorWorldContext(false); + + using namespace CogEditor; + + Log(FString::Printf(TEXT("UCogSubsystem::TryInitialize | World:%s %p"), *World.GetName(), &World), ELogV::Verbose); + + Context = MakeUnique(); + FCogEditorImguiContext& EditorCtx = *(FCogEditorImguiContext*)(Context.Get()); + EditorCtx.Initialize(); + + FCogImGuiContextScope ImGuiContextScope(* Context); + + ImGuiSettingsHandler IniHandler; + IniHandler.TypeName = "CogEditor"; + IniHandler.TypeHash = ImHashStr("CogEditor"); + IniHandler.ClearAllFn = SettingsHandler_ClearAll; + IniHandler.ReadOpenFn = SettingsHandler_ReadOpen; + IniHandler.ReadLineFn = SettingsHandler_ReadLine; + IniHandler.ApplyAllFn = SettingsHandler_ApplyAll; + IniHandler.WriteAllFn = SettingsHandler_WriteAll; + IniHandler.UserData = this; + ImGui::AddSettingsHandler(&IniHandler); + + SpaceWindows.Add(AddWindow("Spacing 1")); + SpaceWindows.Add(AddWindow("Spacing 2")); + SpaceWindows.Add(AddWindow("Spacing 3")); + SpaceWindows.Add(AddWindow("Spacing 4")); + + Settings = GetConfig(); + + UCogWindowConfig_Settings* SettingsPtr = Settings.Get(); + AddShortcut(SettingsPtr, &UCogWindowConfig_Settings::Shortcut_ToggleImguiInput).BindLambda([this] () { ToggleInputMode(); }); + AddShortcut(SettingsPtr, &UCogWindowConfig_Settings::Shortcut_LoadLayout1).BindLambda([this] (){ LoadLayout(1); }); + AddShortcut(SettingsPtr, &UCogWindowConfig_Settings::Shortcut_LoadLayout2).BindLambda([this] (){ LoadLayout(2); }); + AddShortcut(SettingsPtr, &UCogWindowConfig_Settings::Shortcut_LoadLayout3).BindLambda([this] (){ LoadLayout(3); }); + AddShortcut(SettingsPtr, &UCogWindowConfig_Settings::Shortcut_LoadLayout4).BindLambda([this] (){ LoadLayout(4); }); + AddShortcut(SettingsPtr, &UCogWindowConfig_Settings::Shortcut_SaveLayout1).BindLambda([this] (){ SaveLayout(1); }); + AddShortcut(SettingsPtr, &UCogWindowConfig_Settings::Shortcut_SaveLayout2).BindLambda([this] (){ SaveLayout(2); }); + AddShortcut(SettingsPtr, &UCogWindowConfig_Settings::Shortcut_SaveLayout3).BindLambda([this] (){ SaveLayout(3); }); + AddShortcut(SettingsPtr, &UCogWindowConfig_Settings::Shortcut_SaveLayout4).BindLambda([this] (){ SaveLayout(4); }); + AddShortcut(SettingsPtr, &UCogWindowConfig_Settings::Shortcut_ResetLayout).BindLambda([this] (){ ResetLayout(); }); + + LayoutsWindow = AddWindow("Window.Layouts"); + SettingsWindow = AddWindow("Window.Settings"); + + for (FCogWindow* Window : Windows) + { + InitializeWindow(Window); + } + + FCogConsoleCommandManager::RegisterWorldConsoleCommand( + *ToggleInputCommand, + TEXT("Toggle the input focus between the Game and ImGui"), + GetWorld(), + FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray& InArgs, UWorld* InWorld) + { + ToggleInputMode(); + })); + + FCogConsoleCommandManager::RegisterWorldConsoleCommand( + *DisableInputCommand, + TEXT("Disable ImGui input"), + GetWorld(), + FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray& InArgs, UWorld* InWorld) + { + DisableInputMode(); + })); + + FCogConsoleCommandManager::RegisterWorldConsoleCommand( + *ResetLayoutCommand, + TEXT("Reset the layout."), + GetWorld(), + FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray& InArgs, UWorld* InWorld) + { + if (InArgs.Num() > 0) + { + ResetLayout(); + } + })); + + FCogConsoleCommandManager::RegisterWorldConsoleCommand( + *LoadLayoutCommand, + TEXT("Load the layout. CogEditor.LoadLayout "), + GetWorld(), + FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray& InArgs, UWorld* InWorld) + { + if (InArgs.Num() > 0) + { + LoadLayout(FCString::Atoi(*InArgs[0])); + } + })); + + FCogConsoleCommandManager::RegisterWorldConsoleCommand( + *SaveLayoutCommand, + TEXT("Save the layout. CogEditor.SaveLayout "), + GetWorld(), + FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray& InArgs, UWorld* InWorld) + { + if (InArgs.Num() > 0) + { + SaveLayout(FCString::Atoi(*InArgs[0])); + } + })); + + + bIsInitialized = true; +} + +UE_DISABLE_OPTIMIZATION +bool UCogEditorSubsystem::ShouldCreateSubsystem(UObject* Outer) const +{ + bool bIsEditorWorld = GetWorld() == GEditor->EditorWorld; + bool bParentCreate = Super::ShouldCreateSubsystem(Outer); + return bParentCreate && bIsEditorWorld; +} +UE_ENABLE_OPTIMIZATION + +bool UCogEditorSubsystem::DoesSupportWorldType(const EWorldType::Type WorldType) const +{ + return WorldType == EWorldType::Editor; +} + +bool UCogEditorSubsystem::EditorTick(float DeltaTime) +{ + Super::Tick(DeltaTime); + return true; +} + +void UCogEditorSubsystem::PostInitialize() +{ + Super::PostInitialize(); + + TickerDelegate = FTickerDelegate::CreateUObject(this, &UCogEditorSubsystem::EditorTick); + TickerDelegateHandle = FTSTicker::GetCoreTicker().AddTicker(TickerDelegate); +} + +void UCogEditorSubsystem::Deinitialize() +{ + Super::Deinitialize(); + FTSTicker::GetCoreTicker().RemoveTicker(TickerDelegateHandle); +} diff --git a/Plugins/CogEditor/Source/CogEditor/CogEditorSubsystem.h b/Plugins/CogEditor/Source/CogEditor/CogEditorSubsystem.h new file mode 100644 index 0000000..54078a0 --- /dev/null +++ b/Plugins/CogEditor/Source/CogEditor/CogEditorSubsystem.h @@ -0,0 +1,38 @@ +#pragma once + +#include "CogEditorImguiContext.h" +#include "CogEditorMinimal.h" +#include "CogSubsystem.h" +#include "Containers/Ticker.h" + +#include "CogEditorSubsystem.generated.h" + +UCLASS() +class COGEDITOR_API UCogEditorSubsystem : public UCogSubsystem +{ + GENERATED_BODY() +public: + + ETickableTickType GetTickableTickType() const override; + + void TryInitialize(UWorld& World) override; + + bool ShouldCreateSubsystem(UObject* Outer) const override; + + bool DoesSupportWorldType(const EWorldType::Type WorldType) const override; + + bool EditorTick(float DeltaTime); + + void PostInitialize() override; + void Deinitialize() override; + + FTickerDelegate TickerDelegate; + FTSTicker::FDelegateHandle TickerDelegateHandle; + + static FString EnableCommand; + static FString ToggleInputCommand; + static FString DisableInputCommand; + static FString LoadLayoutCommand; + static FString SaveLayoutCommand; + static FString ResetLayoutCommand; +};