mirror of
https://github.com/kevinporetti/UnrealImGui.git
synced 2025-01-18 08:20:32 +00:00
Added support for ImGui context update and rendering:
- Added ImGui Module Manager that that implements module logic and manages other module resources. - Added Texture Manager to manage texture resources and maps them to index that can be passed to ImGui context. - Added Context Proxy that represents and manages a single ImGui context. - Added Slate ImGui Widget to render ImGui output.
This commit is contained in:
parent
e74e3475d9
commit
35f2d342a0
@ -39,6 +39,10 @@ public class ImGui : ModuleRules
|
|||||||
PrivateDependencyModuleNames.AddRange(
|
PrivateDependencyModuleNames.AddRange(
|
||||||
new string[]
|
new string[]
|
||||||
{
|
{
|
||||||
|
"CoreUObject",
|
||||||
|
"Engine",
|
||||||
|
"Slate",
|
||||||
|
"SlateCore"
|
||||||
// ... add private dependencies that you statically link with here ...
|
// ... add private dependencies that you statically link with here ...
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
96
Source/ImGui/Private/ImGuiContextProxy.cpp
Normal file
96
Source/ImGui/Private/ImGuiContextProxy.cpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#include "ImGuiPrivatePCH.h"
|
||||||
|
|
||||||
|
#include "ImGuiContextProxy.h"
|
||||||
|
|
||||||
|
|
||||||
|
static constexpr float DEFAULT_CANVAS_WIDTH = 3840.f;
|
||||||
|
static constexpr float DEFAULT_CANVAS_HEIGHT = 2160.f;
|
||||||
|
|
||||||
|
FImGuiContextProxy::FImGuiContextProxy()
|
||||||
|
{
|
||||||
|
ImGuiIO& IO = ImGui::GetIO();
|
||||||
|
|
||||||
|
// Use pre-defined canvas size.
|
||||||
|
IO.DisplaySize.x = DEFAULT_CANVAS_WIDTH;
|
||||||
|
IO.DisplaySize.y = DEFAULT_CANVAS_HEIGHT;
|
||||||
|
|
||||||
|
// Load texture atlas.
|
||||||
|
unsigned char* Pixels;
|
||||||
|
IO.Fonts->GetTexDataAsRGBA32(&Pixels, nullptr, nullptr);
|
||||||
|
|
||||||
|
// Begin frame to complete context initialization (this is to avoid problems with other systems calling to ImGui
|
||||||
|
// during startup).
|
||||||
|
BeginFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
FImGuiContextProxy::~FImGuiContextProxy()
|
||||||
|
{
|
||||||
|
ImGui::Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiContextProxy::Tick(float DeltaSeconds)
|
||||||
|
{
|
||||||
|
if (bIsFrameStarted)
|
||||||
|
{
|
||||||
|
// Broadcast draw event to allow listeners to draw their controls to this context.
|
||||||
|
if (DrawEvent.IsBound())
|
||||||
|
{
|
||||||
|
DrawEvent.Broadcast();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ending frame will produce render output that we capture and store for later use. This also puts context to
|
||||||
|
// state in which it does not allow to draw controls, so we want to immediately start a new frame.
|
||||||
|
EndFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin a new frame and set the context back to a state in which it allows to draw controls.
|
||||||
|
BeginFrame(DeltaSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiContextProxy::BeginFrame(float DeltaTime)
|
||||||
|
{
|
||||||
|
if (!bIsFrameStarted)
|
||||||
|
{
|
||||||
|
ImGuiIO& IO = ImGui::GetIO();
|
||||||
|
IO.DeltaTime = DeltaTime;
|
||||||
|
|
||||||
|
ImGui::NewFrame();
|
||||||
|
|
||||||
|
bIsFrameStarted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiContextProxy::EndFrame()
|
||||||
|
{
|
||||||
|
if (bIsFrameStarted)
|
||||||
|
{
|
||||||
|
// Prepare draw data (after this call we cannot draw to this context until we start a new frame).
|
||||||
|
ImGui::Render();
|
||||||
|
|
||||||
|
// Update our draw data, so we can use them later during Slate rendering while ImGui is in the middle of the
|
||||||
|
// next frame.
|
||||||
|
UpdateDrawData(ImGui::GetDrawData());
|
||||||
|
|
||||||
|
bIsFrameStarted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiContextProxy::UpdateDrawData(ImDrawData* DrawData)
|
||||||
|
{
|
||||||
|
if (DrawData && DrawData->CmdListsCount > 0)
|
||||||
|
{
|
||||||
|
DrawLists.SetNum(DrawData->CmdListsCount, false);
|
||||||
|
|
||||||
|
for (int Index = 0; Index < DrawData->CmdListsCount; Index++)
|
||||||
|
{
|
||||||
|
DrawLists[Index].TransferDrawData(*DrawData->CmdLists[Index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If we are not rendering then this might be a good moment to empty the array.
|
||||||
|
DrawLists.Empty();
|
||||||
|
}
|
||||||
|
}
|
47
Source/ImGui/Private/ImGuiContextProxy.h
Normal file
47
Source/ImGui/Private/ImGuiContextProxy.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ImGuiDrawData.h"
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Represents a single ImGui context. All the context updates should be done through this proxy. During update it
|
||||||
|
// broadcasts draw events to allow listeners draw their controls. After update it stores produced draw data.
|
||||||
|
// TODO: Add dynamically created contexts, so we can have a better support for multi-PIE.
|
||||||
|
class FImGuiContextProxy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
FImGuiContextProxy();
|
||||||
|
~FImGuiContextProxy();
|
||||||
|
|
||||||
|
FImGuiContextProxy(const FImGuiContextProxy&) = delete;
|
||||||
|
FImGuiContextProxy& operator=(const FImGuiContextProxy&) = delete;
|
||||||
|
|
||||||
|
FImGuiContextProxy(FImGuiContextProxy&&) = delete;
|
||||||
|
FImGuiContextProxy& operator=(FImGuiContextProxy&&) = delete;
|
||||||
|
|
||||||
|
// Get draw data from the last frame.
|
||||||
|
const TArray<FImGuiDrawList>& GetDrawData() const { return DrawLists; }
|
||||||
|
|
||||||
|
// Delegate called right before ending the frame to allows listeners draw their controls.
|
||||||
|
FSimpleMulticastDelegate& OnDraw() { return DrawEvent; }
|
||||||
|
|
||||||
|
// Tick to advance context to the next frame.
|
||||||
|
void Tick(float DeltaSeconds);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void BeginFrame(float DeltaTime = 1.f / 60.f);
|
||||||
|
void EndFrame();
|
||||||
|
|
||||||
|
void UpdateDrawData(ImDrawData* DrawData);
|
||||||
|
|
||||||
|
TArray<FImGuiDrawList> DrawLists;
|
||||||
|
|
||||||
|
FSimpleMulticastDelegate DrawEvent;
|
||||||
|
|
||||||
|
bool bIsFrameStarted = false;
|
||||||
|
};
|
39
Source/ImGui/Private/ImGuiDemo.cpp
Normal file
39
Source/ImGui/Private/ImGuiDemo.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#include "ImGuiPrivatePCH.h"
|
||||||
|
|
||||||
|
#include "ImGuiDemo.h"
|
||||||
|
#include "ImGuiModuleManager.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Demo copied from ImGui examples. See https://github.com/ocornut/imgui.
|
||||||
|
void FImGuiDemo::DrawControls()
|
||||||
|
{
|
||||||
|
// 1. Show a simple window
|
||||||
|
// Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets appears in a window automatically called "Debug"
|
||||||
|
{
|
||||||
|
static float f = 0.0f;
|
||||||
|
ImGui::Text("Hello, world!");
|
||||||
|
ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
|
||||||
|
ImGui::ColorEdit3("clear color", (float*)&ClearColor);
|
||||||
|
if (ImGui::Button("Test Window")) bDemoShowTestWindow = !bDemoShowTestWindow;
|
||||||
|
if (ImGui::Button("Another Window")) bDemoShowAnotherTestWindow = !bDemoShowAnotherTestWindow;
|
||||||
|
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Show another simple window, this time using an explicit Begin/End pair
|
||||||
|
if (bDemoShowAnotherTestWindow)
|
||||||
|
{
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(200, 100), ImGuiSetCond_FirstUseEver);
|
||||||
|
ImGui::Begin("Another Window", &bDemoShowAnotherTestWindow);
|
||||||
|
ImGui::Text("Hello");
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowTestWindow()
|
||||||
|
if (bDemoShowTestWindow)
|
||||||
|
{
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiSetCond_FirstUseEver);
|
||||||
|
ImGui::ShowTestWindow(&bDemoShowTestWindow);
|
||||||
|
}
|
||||||
|
}
|
22
Source/ImGui/Private/ImGuiDemo.h
Normal file
22
Source/ImGui/Private/ImGuiDemo.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Widget drawing ImGui demo.
|
||||||
|
class FImGuiDemo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
void DrawControls();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
ImVec4 ClearColor = ImColor{ 114, 144, 154 };
|
||||||
|
|
||||||
|
bool bShowDemo = false;
|
||||||
|
bool bDemoShowTestWindow = true;
|
||||||
|
bool bDemoShowAnotherTestWindow = false;
|
||||||
|
};
|
59
Source/ImGui/Private/ImGuiDrawData.cpp
Normal file
59
Source/ImGui/Private/ImGuiDrawData.cpp
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#include "ImGuiPrivatePCH.h"
|
||||||
|
|
||||||
|
#include "ImGuiDrawData.h"
|
||||||
|
|
||||||
|
|
||||||
|
void FImGuiDrawList::CopyVertexData(TArray<FSlateVertex>& OutVertexBuffer, const FVector2D VertexPositionOffset, const FSlateRotatedRect& VertexClippingRect) const
|
||||||
|
{
|
||||||
|
// Reset and reserve space in destination buffer.
|
||||||
|
OutVertexBuffer.SetNumUninitialized(ImGuiVertexBuffer.Size, false);
|
||||||
|
|
||||||
|
// Transform and copy vertex data.
|
||||||
|
for (int Idx = 0; Idx < ImGuiVertexBuffer.Size; Idx++)
|
||||||
|
{
|
||||||
|
const ImDrawVert& ImGuiVertex = ImGuiVertexBuffer[Idx];
|
||||||
|
FSlateVertex& SlateVertex = OutVertexBuffer[Idx];
|
||||||
|
|
||||||
|
// Final UV is calculated in shader as XY * ZW, so we need set all components.
|
||||||
|
SlateVertex.TexCoords[0] = ImGuiVertex.uv.x;
|
||||||
|
SlateVertex.TexCoords[1] = ImGuiVertex.uv.y;
|
||||||
|
SlateVertex.TexCoords[2] = SlateVertex.TexCoords[3] = 1.f;
|
||||||
|
|
||||||
|
// Copy ImGui position and add offset.
|
||||||
|
SlateVertex.Position[0] = ImGuiVertex.pos.x + VertexPositionOffset.X;
|
||||||
|
SlateVertex.Position[1] = ImGuiVertex.pos.y + VertexPositionOffset.Y;
|
||||||
|
|
||||||
|
// Set clipping rectangle.
|
||||||
|
SlateVertex.ClipRect = VertexClippingRect;
|
||||||
|
|
||||||
|
// Unpack ImU32 color.
|
||||||
|
SlateVertex.Color = ImGuiInterops::UnpackImU32Color(ImGuiVertex.col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiDrawList::CopyIndexData(TArray<SlateIndex>& OutIndexBuffer, const int32 StartIndex, const int32 NumElements) const
|
||||||
|
{
|
||||||
|
// Reset buffer.
|
||||||
|
OutIndexBuffer.SetNumUninitialized(NumElements, false);
|
||||||
|
|
||||||
|
// Copy elements (slow copy because of different sizes of ImDrawIdx and SlateIndex and because SlateIndex can
|
||||||
|
// have different size on different platforms).
|
||||||
|
for (int i = 0; i < NumElements; i++)
|
||||||
|
{
|
||||||
|
OutIndexBuffer[i] = ImGuiIndexBuffer[StartIndex + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiDrawList::TransferDrawData(ImDrawList& Src)
|
||||||
|
{
|
||||||
|
// Move data from source to this list.
|
||||||
|
Src.CmdBuffer.swap(ImGuiCommandBuffer);
|
||||||
|
Src.IdxBuffer.swap(ImGuiIndexBuffer);
|
||||||
|
Src.VtxBuffer.swap(ImGuiVertexBuffer);
|
||||||
|
|
||||||
|
// ImGui seems to clear draw lists in every frame, but since source list can contain pointers to buffers that
|
||||||
|
// we just swapped, it is better to clear explicitly here.
|
||||||
|
Src.Clear();
|
||||||
|
}
|
58
Source/ImGui/Private/ImGuiDrawData.h
Normal file
58
Source/ImGui/Private/ImGuiDrawData.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ImGuiInteroperability.h"
|
||||||
|
|
||||||
|
#include <SlateCore.h>
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
|
|
||||||
|
// ImGui draw command data transformed for Slate.
|
||||||
|
struct FImGuiDrawCommand
|
||||||
|
{
|
||||||
|
uint32 NumElements;
|
||||||
|
FSlateRect ClippingRect;
|
||||||
|
TextureIndex TextureId;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wraps raw ImGui draw list data in utilities that transform them for Slate.
|
||||||
|
class FImGuiDrawList
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Get the number of draw commands in this list.
|
||||||
|
FORCEINLINE int NumCommands() const { return ImGuiCommandBuffer.Size; }
|
||||||
|
|
||||||
|
// Get the draw command by number.
|
||||||
|
// @param CommandNb - Number of draw command
|
||||||
|
// @returns Draw command data
|
||||||
|
FORCEINLINE FImGuiDrawCommand GetCommand(int CommandNb) const
|
||||||
|
{
|
||||||
|
const ImDrawCmd& ImGuiCommand = ImGuiCommandBuffer[CommandNb];
|
||||||
|
return{ ImGuiCommand.ElemCount, ImGuiInterops::ToSlateRect(ImGuiCommand.ClipRect), ImGuiInterops::ToTextureIndex(ImGuiCommand.TextureId) };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform and copy vertex data to target buffer (old data in the target buffer are replaced).
|
||||||
|
// @param OutVertexBuffer - Destination buffer
|
||||||
|
// @param VertexPositionOffset - Position offset added to every vertex to transform it to different space
|
||||||
|
// @param VertexClippingRect - Clipping rectangle for Slate vertices
|
||||||
|
void CopyVertexData(TArray<FSlateVertex>& OutVertexBuffer, const FVector2D VertexPositionOffset, const FSlateRotatedRect& VertexClippingRect) const;
|
||||||
|
|
||||||
|
// Transform and copy index data to target buffer (old data in the target buffer are replaced).
|
||||||
|
// Internal index buffer contains enough data to match the sum of NumElements from all draw commands.
|
||||||
|
// @param OutIndexBuffer - Destination buffer
|
||||||
|
// @param StartIndex - Start copying source data starting from this index
|
||||||
|
// @param NumElements - How many elements we want to copy
|
||||||
|
void CopyIndexData(TArray<SlateIndex>& OutIndexBuffer, const int32 StartIndex, const int32 NumElements) const;
|
||||||
|
|
||||||
|
// Transfers data from ImGui source list to this object. Leaves source cleared.
|
||||||
|
void TransferDrawData(ImDrawList& Src);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
ImVector<ImDrawCmd> ImGuiCommandBuffer;
|
||||||
|
ImVector<ImDrawIdx> ImGuiIndexBuffer;
|
||||||
|
ImVector<ImDrawVert> ImGuiVertexBuffer;
|
||||||
|
};
|
42
Source/ImGui/Private/ImGuiInteroperability.h
Normal file
42
Source/ImGui/Private/ImGuiInteroperability.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "TextureManager.h"
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Utilities to help standardise operations between Unreal and ImGui.
|
||||||
|
namespace ImGuiInterops
|
||||||
|
{
|
||||||
|
//====================================================================================================
|
||||||
|
// Conversions
|
||||||
|
//====================================================================================================
|
||||||
|
|
||||||
|
// Convert from ImGui packed color to FColor.
|
||||||
|
FORCEINLINE FColor UnpackImU32Color(ImU32 Color)
|
||||||
|
{
|
||||||
|
// We use IM_COL32_R/G/B/A_SHIFT macros to support different ImGui configurations.
|
||||||
|
return FColor{ ((Color >> IM_COL32_R_SHIFT) & 0xFF), ((Color >> IM_COL32_G_SHIFT) & 0xFF),
|
||||||
|
((Color >> IM_COL32_B_SHIFT) & 0xFF), ((Color >> IM_COL32_A_SHIFT) & 0xFF) };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from ImVec4 rectangle to FSlateRect.
|
||||||
|
FORCEINLINE FSlateRect ToSlateRect(const ImVec4& ImGuiRect)
|
||||||
|
{
|
||||||
|
return FSlateRect{ ImGuiRect.x, ImGuiRect.y, ImGuiRect.z, ImGuiRect.w };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from ImGui Texture Id to Texture Index that we use for texture resources.
|
||||||
|
FORCEINLINE TextureIndex ToTextureIndex(ImTextureID Index)
|
||||||
|
{
|
||||||
|
return static_cast<TextureIndex>(reinterpret_cast<intptr_t>(Index));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from Texture Index to ImGui Texture Id that we pass to ImGui.
|
||||||
|
FORCEINLINE ImTextureID ToImTextureID(TextureIndex Index)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<ImTextureID>(static_cast<intptr_t>(Index));
|
||||||
|
}
|
||||||
|
}
|
@ -2,20 +2,31 @@
|
|||||||
|
|
||||||
#include "ImGuiPrivatePCH.h"
|
#include "ImGuiPrivatePCH.h"
|
||||||
|
|
||||||
|
#include "ImGuiModuleManager.h"
|
||||||
|
|
||||||
#include <IPluginManager.h>
|
#include <IPluginManager.h>
|
||||||
|
|
||||||
|
|
||||||
#define LOCTEXT_NAMESPACE "FImGuiModule"
|
#define LOCTEXT_NAMESPACE "FImGuiModule"
|
||||||
|
|
||||||
|
|
||||||
|
static FImGuiModuleManager* ModuleManager = nullptr;
|
||||||
|
|
||||||
void FImGuiModule::StartupModule()
|
void FImGuiModule::StartupModule()
|
||||||
{
|
{
|
||||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
checkf(!ModuleManager, TEXT("Instance of Module Manager already exists. Instance should be created only during module startup."));
|
||||||
|
|
||||||
|
// Create module manager that implements modules logic.
|
||||||
|
ModuleManager = new FImGuiModuleManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FImGuiModule::ShutdownModule()
|
void FImGuiModule::ShutdownModule()
|
||||||
{
|
{
|
||||||
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
|
checkf(ModuleManager, TEXT("Null Module Manager. Manager instance should be deleted during module shutdown."));
|
||||||
// we call this function before unloading the module.
|
|
||||||
|
// Before we shutdown we need to delete manager that will do all necessary cleanup.
|
||||||
|
delete ModuleManager;
|
||||||
|
ModuleManager = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef LOCTEXT_NAMESPACE
|
#undef LOCTEXT_NAMESPACE
|
||||||
|
172
Source/ImGui/Private/ImGuiModuleManager.cpp
Normal file
172
Source/ImGui/Private/ImGuiModuleManager.cpp
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#include "ImGuiPrivatePCH.h"
|
||||||
|
|
||||||
|
#include "ImGuiModuleManager.h"
|
||||||
|
|
||||||
|
#include "ImGuiInteroperability.h"
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
|
|
||||||
|
FImGuiModuleManager::FImGuiModuleManager()
|
||||||
|
{
|
||||||
|
// Bind ImGui demo to proxy, so it can draw controls in its context.
|
||||||
|
ContextProxy.OnDraw().AddRaw(&ImGuiDemo, &FImGuiDemo::DrawControls);
|
||||||
|
|
||||||
|
// Typically we will use viewport created events to add widget to new game viewports.
|
||||||
|
ViewportCreatedHandle = UGameViewportClient::OnViewportCreated().AddRaw(this, &FImGuiModuleManager::OnViewportCreated);
|
||||||
|
|
||||||
|
// Initialize resources and start ticking. Depending on loading phase, this may fail if Slate is not yet ready.
|
||||||
|
Initialize();
|
||||||
|
|
||||||
|
// We need to add widgets to active game viewports as they won't generate on-created events. This is especially
|
||||||
|
// important during hot-reloading.
|
||||||
|
if (bInitialized)
|
||||||
|
{
|
||||||
|
AddWidgetToAllViewports();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FImGuiModuleManager::~FImGuiModuleManager()
|
||||||
|
{
|
||||||
|
// We are no longer interested with adding widgets to viewports.
|
||||||
|
if (ViewportCreatedHandle.IsValid())
|
||||||
|
{
|
||||||
|
UGameViewportClient::OnViewportCreated().Remove(ViewportCreatedHandle);
|
||||||
|
ViewportCreatedHandle.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deactivate this manager.
|
||||||
|
Uninitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiModuleManager::Initialize()
|
||||||
|
{
|
||||||
|
// We rely on Slate, so we can only continue if it is already initialized.
|
||||||
|
if (!bInitialized && FSlateApplication::IsInitialized())
|
||||||
|
{
|
||||||
|
bInitialized = true;
|
||||||
|
LoadTextures();
|
||||||
|
RegisterTick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiModuleManager::Uninitialize()
|
||||||
|
{
|
||||||
|
if (bInitialized)
|
||||||
|
{
|
||||||
|
bInitialized = false;
|
||||||
|
UnregisterTick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiModuleManager::LoadTextures()
|
||||||
|
{
|
||||||
|
checkf(FSlateApplication::IsInitialized(), TEXT("Slate should be initialized before we can create textures."));
|
||||||
|
|
||||||
|
// Create an empty texture at index 0. We will use it for ImGui outputs with null texture id.
|
||||||
|
TextureManager.CreatePlainTexture(FName{ "ImGuiModule_Null" }, 2, 2, FColor::White);
|
||||||
|
|
||||||
|
// Create a font atlas texture.
|
||||||
|
ImFontAtlas* Fonts = ImGui::GetIO().Fonts;
|
||||||
|
checkf(Fonts, TEXT("Invalid font atlas."));
|
||||||
|
|
||||||
|
unsigned char* Pixels;
|
||||||
|
int Width, Height, Bpp;
|
||||||
|
Fonts->GetTexDataAsRGBA32(&Pixels, &Width, &Height, &Bpp);
|
||||||
|
|
||||||
|
TextureIndex FontsTexureIndex = TextureManager.CreateTexture(FName{ "ImGuiModule_FontAtlas" }, Width, Height, Bpp, Pixels, false);
|
||||||
|
|
||||||
|
// Set font texture index in ImGui.
|
||||||
|
Fonts->TexID = ImGuiInterops::ToImTextureID(FontsTexureIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiModuleManager::RegisterTick()
|
||||||
|
{
|
||||||
|
checkf(FSlateApplication::IsInitialized(), TEXT("Slate should be initialized before we can register tick listener."));
|
||||||
|
|
||||||
|
// We will tick on Slate Post-Tick events. They are quite convenient as they happen at the very end of the frame,
|
||||||
|
// what helps to minimise tearing.
|
||||||
|
if (!TickDelegateHandle.IsValid())
|
||||||
|
{
|
||||||
|
TickDelegateHandle = FSlateApplication::Get().OnPostTick().AddRaw(this, &FImGuiModuleManager::Tick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiModuleManager::UnregisterTick()
|
||||||
|
{
|
||||||
|
if (TickDelegateHandle.IsValid())
|
||||||
|
{
|
||||||
|
if (FSlateApplication::IsInitialized())
|
||||||
|
{
|
||||||
|
FSlateApplication::Get().OnPostTick().Remove(TickDelegateHandle);
|
||||||
|
}
|
||||||
|
TickDelegateHandle.Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FImGuiModuleManager::IsInUpdateThread()
|
||||||
|
{
|
||||||
|
// We can get ticks from the Game thread and Slate loading thread. In both cases IsInGameThread() is true, so we
|
||||||
|
// need to make additional test to filter out loading thread.
|
||||||
|
return IsInGameThread() && !IsInSlateThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiModuleManager::Tick(float DeltaSeconds)
|
||||||
|
{
|
||||||
|
if (IsInUpdateThread())
|
||||||
|
{
|
||||||
|
// Update context proxy to advance to next frame.
|
||||||
|
ContextProxy.Tick(DeltaSeconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiModuleManager::OnViewportCreated()
|
||||||
|
{
|
||||||
|
checkf(FSlateApplication::IsInitialized(), TEXT("We expect Slate to be initialized when game viewport is created."));
|
||||||
|
|
||||||
|
// Make sure that all resources are initialized to handle configurations where this module is loaded before Slate.
|
||||||
|
Initialize();
|
||||||
|
|
||||||
|
// Create widget to viewport responsible for this event.
|
||||||
|
AddWidgetToViewport(GEngine->GameViewport);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiModuleManager::AddWidgetToViewport(UGameViewportClient* GameViewport)
|
||||||
|
{
|
||||||
|
checkf(GameViewport, TEXT("Null game viewport."));
|
||||||
|
checkf(FSlateApplication::IsInitialized(), TEXT("Slate should be initialized before we can add widget to game viewports."));
|
||||||
|
|
||||||
|
if (!ViewportWidget.IsValid())
|
||||||
|
{
|
||||||
|
SAssignNew(ViewportWidget, SImGuiWidget).ModuleManager(this);
|
||||||
|
checkf(ViewportWidget.IsValid(), TEXT("Failed to create SImGuiWidget."));
|
||||||
|
}
|
||||||
|
|
||||||
|
// High enough z-order guarantees that ImGui output is rendered on top of the game UI.
|
||||||
|
constexpr int32 IMGUI_WIDGET_Z_ORDER = 10000;
|
||||||
|
|
||||||
|
GameViewport->AddViewportWidgetContent(SNew(SWeakWidget).PossiblyNullContent(ViewportWidget), IMGUI_WIDGET_Z_ORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiModuleManager::AddWidgetToAllViewports()
|
||||||
|
{
|
||||||
|
checkf(FSlateApplication::IsInitialized(), TEXT("Slate should be initialized before we can add widget to game viewports."));
|
||||||
|
|
||||||
|
if (GEngine)
|
||||||
|
{
|
||||||
|
// Loop as long as we have a valid viewport or until we detect a cycle.
|
||||||
|
UGameViewportClient* GameViewport = GEngine->GameViewport;
|
||||||
|
while (GameViewport)
|
||||||
|
{
|
||||||
|
AddWidgetToViewport(GameViewport);
|
||||||
|
|
||||||
|
GameViewport = GEngine->GetNextPIEViewport(GameViewport);
|
||||||
|
if (GameViewport == GEngine->GameViewport)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
70
Source/ImGui/Private/ImGuiModuleManager.h
Normal file
70
Source/ImGui/Private/ImGuiModuleManager.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ImGuiContextProxy.h"
|
||||||
|
#include "ImGuiDemo.h"
|
||||||
|
#include "SImGuiWidget.h"
|
||||||
|
#include "TextureManager.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Central manager that implements module logic. It initializes and controls remaining module components.
|
||||||
|
class FImGuiModuleManager
|
||||||
|
{
|
||||||
|
// Allow module to control life-cycle of this class.
|
||||||
|
friend class FImGuiModule;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Get ImGui context proxy.
|
||||||
|
FImGuiContextProxy& GetContextProxy() { return ContextProxy; }
|
||||||
|
|
||||||
|
// Get texture resources manager.
|
||||||
|
FTextureManager& GetTextureManager() { return TextureManager; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
FImGuiModuleManager();
|
||||||
|
~FImGuiModuleManager();
|
||||||
|
|
||||||
|
FImGuiModuleManager(const FImGuiModuleManager&) = delete;
|
||||||
|
FImGuiModuleManager& operator=(const FImGuiModuleManager&) = delete;
|
||||||
|
|
||||||
|
FImGuiModuleManager(FImGuiModuleManager&&) = delete;
|
||||||
|
FImGuiModuleManager& operator=(FImGuiModuleManager&&) = delete;
|
||||||
|
|
||||||
|
void Initialize();
|
||||||
|
void Uninitialize();
|
||||||
|
|
||||||
|
void LoadTextures();
|
||||||
|
|
||||||
|
void RegisterTick();
|
||||||
|
void UnregisterTick();
|
||||||
|
|
||||||
|
bool IsInUpdateThread();
|
||||||
|
|
||||||
|
void Tick(float DeltaSeconds);
|
||||||
|
|
||||||
|
void OnViewportCreated();
|
||||||
|
|
||||||
|
void AddWidgetToViewport(UGameViewportClient* GameViewport);
|
||||||
|
void AddWidgetToAllViewports();
|
||||||
|
|
||||||
|
// Proxy controlling ImGui context.
|
||||||
|
FImGuiContextProxy ContextProxy;
|
||||||
|
|
||||||
|
// ImWidget that draws ImGui demo.
|
||||||
|
FImGuiDemo ImGuiDemo;
|
||||||
|
|
||||||
|
// Manager for textures resources.
|
||||||
|
FTextureManager TextureManager;
|
||||||
|
|
||||||
|
// Slate widget that we attach to created game viewports (widget without per-viewport state can be attached to
|
||||||
|
// multiple viewports).
|
||||||
|
TSharedPtr<SImGuiWidget> ViewportWidget;
|
||||||
|
|
||||||
|
FDelegateHandle TickDelegateHandle;
|
||||||
|
FDelegateHandle ViewportCreatedHandle;
|
||||||
|
|
||||||
|
bool bInitialized = false;
|
||||||
|
};
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
// Engine
|
// Engine
|
||||||
#include <Core.h>
|
#include <Core.h>
|
||||||
|
#include <Engine.h>
|
||||||
|
|
||||||
// You should place include statements to your module's private header files here. You only need to
|
// You should place include statements to your module's private header files here. You only need to
|
||||||
// add includes for headers that are used in most of your module's source files though.
|
// add includes for headers that are used in most of your module's source files though.
|
||||||
|
65
Source/ImGui/Private/SImGuiWidget.cpp
Normal file
65
Source/ImGui/Private/SImGuiWidget.cpp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#include "ImGuiPrivatePCH.h"
|
||||||
|
|
||||||
|
#include "SImGuiWidget.h"
|
||||||
|
|
||||||
|
#include "ImGuiModuleManager.h"
|
||||||
|
#include "TextureManager.h"
|
||||||
|
#include "Utilities/ScopeGuards.h"
|
||||||
|
|
||||||
|
|
||||||
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||||
|
void SImGuiWidget::Construct(const FArguments& InArgs)
|
||||||
|
{
|
||||||
|
checkf(InArgs._ModuleManager, TEXT("Null Module Manager argument"));
|
||||||
|
ModuleManager = InArgs._ModuleManager;
|
||||||
|
}
|
||||||
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||||
|
|
||||||
|
int32 SImGuiWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect,
|
||||||
|
FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& WidgetStyle, bool bParentEnabled) const
|
||||||
|
{
|
||||||
|
// Calculate offset that will transform vertex positions to screen space - rounded to avoid half pixel offsets.
|
||||||
|
const FVector2D VertexPositionOffset{ FMath::RoundToFloat(MyClippingRect.Left), FMath::RoundToFloat(MyClippingRect.Top) };
|
||||||
|
|
||||||
|
// Convert clipping rectangle to format required by Slate vertex.
|
||||||
|
const FSlateRotatedRect VertexClippingRect{ MyClippingRect };
|
||||||
|
|
||||||
|
for (const auto& DrawList : ModuleManager->GetContextProxy().GetDrawData())
|
||||||
|
{
|
||||||
|
DrawList.CopyVertexData(VertexBuffer, VertexPositionOffset, VertexClippingRect);
|
||||||
|
|
||||||
|
// Get access to the Slate scissor rectangle defined in Slate Core API, so we can customize elements drawing.
|
||||||
|
extern SLATECORE_API TOptional<FShortRect> GSlateScissorRect;
|
||||||
|
|
||||||
|
auto GSlateScissorRectSaver = ScopeGuards::MakeStateSaver(GSlateScissorRect);
|
||||||
|
|
||||||
|
int IndexBufferOffset = 0;
|
||||||
|
for (int CommandNb = 0; CommandNb < DrawList.NumCommands(); CommandNb++)
|
||||||
|
{
|
||||||
|
const auto& DrawCommand = DrawList.GetCommand(CommandNb);
|
||||||
|
|
||||||
|
DrawList.CopyIndexData(IndexBuffer, IndexBufferOffset, DrawCommand.NumElements);
|
||||||
|
|
||||||
|
// Advance offset by number of copied elements to position it for the next command.
|
||||||
|
IndexBufferOffset += DrawCommand.NumElements;
|
||||||
|
|
||||||
|
// Get texture resource handle for this draw command (null index will be also mapped to a valid texture).
|
||||||
|
const FSlateResourceHandle& Handle = ModuleManager->GetTextureManager().GetTextureHandle(DrawCommand.TextureId);
|
||||||
|
|
||||||
|
// Transform clipping rectangle to screen space and set in Slate, to apply it to elements that we draw.
|
||||||
|
GSlateScissorRect = FShortRect{ DrawCommand.ClippingRect.OffsetBy(MyClippingRect.GetTopLeft()).IntersectionWith(MyClippingRect) };
|
||||||
|
|
||||||
|
// Add elements to the list.
|
||||||
|
FSlateDrawElement::MakeCustomVerts(OutDrawElements, LayerId, Handle, VertexBuffer, IndexBuffer, nullptr, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return LayerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
FVector2D SImGuiWidget::ComputeDesiredSize(float) const
|
||||||
|
{
|
||||||
|
return FVector2D{ 3840.f, 2160.f };
|
||||||
|
}
|
31
Source/ImGui/Private/SImGuiWidget.h
Normal file
31
Source/ImGui/Private/SImGuiWidget.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Widgets/SLeafWidget.h>
|
||||||
|
|
||||||
|
class FImGuiModuleManager;
|
||||||
|
|
||||||
|
// Slate widget for rendering ImGui output.
|
||||||
|
class SImGuiWidget : public SLeafWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
SLATE_BEGIN_ARGS(SImGuiWidget)
|
||||||
|
{}
|
||||||
|
SLATE_ARGUMENT(FImGuiModuleManager*, ModuleManager)
|
||||||
|
SLATE_END_ARGS()
|
||||||
|
|
||||||
|
void Construct(const FArguments& InArgs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& WidgetStyle, bool bParentEnabled) const override;
|
||||||
|
|
||||||
|
virtual FVector2D ComputeDesiredSize(float) const override;
|
||||||
|
|
||||||
|
FImGuiModuleManager* ModuleManager = nullptr;
|
||||||
|
|
||||||
|
mutable TArray<FSlateVertex> VertexBuffer;
|
||||||
|
mutable TArray<SlateIndex> IndexBuffer;
|
||||||
|
};
|
76
Source/ImGui/Private/TextureManager.cpp
Normal file
76
Source/ImGui/Private/TextureManager.cpp
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#include "ImGuiPrivatePCH.h"
|
||||||
|
|
||||||
|
#include "TextureManager.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
||||||
|
TextureIndex FTextureManager::CreateTexture(const FName& Name, int32 Width, int32 Height, uint32 SrcBpp, uint8* SrcData, bool bDeleteSrcData)
|
||||||
|
{
|
||||||
|
checkf(FindTextureIndex(Name) == INDEX_NONE, TEXT("Trying to create texture using resource name '%s' that is already registered."), *Name.ToString());
|
||||||
|
|
||||||
|
// Create a texture.
|
||||||
|
UTexture2D* Texture = UTexture2D::CreateTransient(Width, Height);
|
||||||
|
|
||||||
|
// Create a new resource for that texture.
|
||||||
|
Texture->UpdateResource();
|
||||||
|
|
||||||
|
// Update texture data.
|
||||||
|
FUpdateTextureRegion2D* TextureRegion = new FUpdateTextureRegion2D(0, 0, 0, 0, Width, Height);
|
||||||
|
auto DataCleanup = [bDeleteSrcData](uint8* Data, const FUpdateTextureRegion2D* UpdateRegion)
|
||||||
|
{
|
||||||
|
if (bDeleteSrcData)
|
||||||
|
{
|
||||||
|
delete Data;
|
||||||
|
}
|
||||||
|
delete UpdateRegion;
|
||||||
|
};
|
||||||
|
Texture->UpdateTextureRegions(0, 1u, TextureRegion, SrcBpp * Width, SrcBpp, SrcData, DataCleanup);
|
||||||
|
|
||||||
|
// Create a new entry for the texture.
|
||||||
|
return TextureResources.Emplace(Name, Texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureIndex FTextureManager::CreatePlainTexture(const FName& Name, int32 Width, int32 Height, FColor Color)
|
||||||
|
{
|
||||||
|
// Create buffer with raw data.
|
||||||
|
const uint32 ColorPacked = Color.ToPackedARGB();
|
||||||
|
const uint32 Bpp = sizeof(ColorPacked);
|
||||||
|
const uint32 SizeInPixels = Width * Height;
|
||||||
|
const uint32 SizeInBytes = SizeInPixels * Bpp;
|
||||||
|
uint8* SrcData = new uint8[SizeInBytes];
|
||||||
|
std::fill(reinterpret_cast<uint32*>(SrcData), reinterpret_cast<uint32*>(SrcData) + SizeInPixels, ColorPacked);
|
||||||
|
|
||||||
|
// Create new texture from raw data (we created the buffer, so mark it for delete).
|
||||||
|
return CreateTexture(Name, Width, Height, Bpp, SrcData, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
FTextureManager::FTextureEntry::FTextureEntry(const FName& InName, UTexture2D* InTexture)
|
||||||
|
: Name{ InName }
|
||||||
|
, Texture{ InTexture }
|
||||||
|
{
|
||||||
|
// Add texture to root to prevent garbage collection.
|
||||||
|
Texture->AddToRoot();
|
||||||
|
|
||||||
|
// Create brush and resource handle for input texture.
|
||||||
|
Brush.SetResourceObject(Texture);
|
||||||
|
ResourceHandle = FSlateApplication::Get().GetRenderer()->GetResourceHandle(Brush);
|
||||||
|
}
|
||||||
|
|
||||||
|
FTextureManager::FTextureEntry::~FTextureEntry()
|
||||||
|
{
|
||||||
|
// Release brush.
|
||||||
|
if (Brush.HasUObject() && FSlateApplication::IsInitialized())
|
||||||
|
{
|
||||||
|
FSlateApplication::Get().GetRenderer()->ReleaseDynamicResource(Brush);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove texture from root to allow for garbage collection (it might be already invalid if this is application
|
||||||
|
// shutdown).
|
||||||
|
if (Texture && Texture->IsValidLowLevel())
|
||||||
|
{
|
||||||
|
Texture->RemoveFromRoot();
|
||||||
|
}
|
||||||
|
}
|
95
Source/ImGui/Private/TextureManager.h
Normal file
95
Source/ImGui/Private/TextureManager.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Core.h>
|
||||||
|
#include <Styling/SlateBrush.h>
|
||||||
|
#include <Textures/SlateShaderResource.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Index type to be used as a texture handle.
|
||||||
|
using TextureIndex = int32;
|
||||||
|
|
||||||
|
// Manager for textures resources which can be referenced by a unique name or index.
|
||||||
|
// Name is primarily for lookup and index provides a direct access to resources.
|
||||||
|
class FTextureManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Creates an empty manager.
|
||||||
|
FTextureManager() = default;
|
||||||
|
|
||||||
|
// Copying is disabled to protected resource ownership.
|
||||||
|
FTextureManager(const FTextureManager&) = delete;
|
||||||
|
FTextureManager& operator=(const FTextureManager&) = delete;
|
||||||
|
|
||||||
|
// Moving transfers ownership and leaves source empty.
|
||||||
|
FTextureManager(FTextureManager&&) = default;
|
||||||
|
FTextureManager& operator=(FTextureManager&&) = default;
|
||||||
|
|
||||||
|
// Find texture index by name.
|
||||||
|
// @param Name - The name of a texture to find
|
||||||
|
// @returns The index of a texture with given name or INDEX_NONE if there is no such texture
|
||||||
|
TextureIndex FindTextureIndex(const FName& Name) const
|
||||||
|
{
|
||||||
|
return TextureResources.IndexOfByPredicate([&](const auto& Entry) { return Entry.Name == Name; });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the name of a texture at given index. Throws exception if index is out of range.
|
||||||
|
// @param Index - Index of a texture
|
||||||
|
// @returns The name of a texture at given index
|
||||||
|
FORCEINLINE FName GetTextureName(TextureIndex Index) const
|
||||||
|
{
|
||||||
|
return TextureResources[Index].Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the Slate Resource Handle to a texture at given index. Throws exception if index is out of range.
|
||||||
|
// @param Index - Index of a texture
|
||||||
|
// @returns The Slate Resource Handle for a texture at given index
|
||||||
|
FORCEINLINE const FSlateResourceHandle& GetTextureHandle(TextureIndex Index) const
|
||||||
|
{
|
||||||
|
return TextureResources[Index].ResourceHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a texture from raw data. Throws exception if there is already a texture with that name.
|
||||||
|
// @param Name - The texture name
|
||||||
|
// @param Width - The texture width
|
||||||
|
// @param Height - The texture height
|
||||||
|
// @param SrcBpp - The size in bytes of one pixel
|
||||||
|
// @param SrcData - The source data
|
||||||
|
// @param bDeleteSrcData - If true, we should delete source data after creating a texture
|
||||||
|
// @returns The index of a texture that was created
|
||||||
|
TextureIndex CreateTexture(const FName& Name, int32 Width, int32 Height, uint32 SrcBpp, uint8* SrcData, bool bDeleteSrc = false);
|
||||||
|
|
||||||
|
// Create a plain texture. Throws exception if there is already a texture with that name.
|
||||||
|
// @param Name - The texture name
|
||||||
|
// @param Width - The texture width
|
||||||
|
// @param Height - The texture height
|
||||||
|
// @param Color - The texture color
|
||||||
|
// @returns The index of a texture that was created
|
||||||
|
TextureIndex CreatePlainTexture(const FName& Name, int32 Width, int32 Height, FColor Color);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Entry for texture resources. Only supports explicit construction.
|
||||||
|
struct FTextureEntry
|
||||||
|
{
|
||||||
|
FTextureEntry(const FName& InName, UTexture2D* InTexture);
|
||||||
|
~FTextureEntry();
|
||||||
|
|
||||||
|
// Copying is not supported.
|
||||||
|
FTextureEntry(const FTextureEntry&) = delete;
|
||||||
|
FTextureEntry& operator=(const FTextureEntry&) = delete;
|
||||||
|
|
||||||
|
// We rely on TArray and don't implement custom move semantics.
|
||||||
|
FTextureEntry(FTextureEntry&&) = delete;
|
||||||
|
FTextureEntry& operator=(FTextureEntry&&) = delete;
|
||||||
|
|
||||||
|
FName Name = NAME_None;
|
||||||
|
UTexture2D* Texture = nullptr;
|
||||||
|
FSlateBrush Brush;
|
||||||
|
FSlateResourceHandle ResourceHandle;
|
||||||
|
};
|
||||||
|
|
||||||
|
TArray<FTextureEntry> TextureResources;
|
||||||
|
};
|
58
Source/ImGui/Private/Utilities/ScopeGuards.h
Normal file
58
Source/ImGui/Private/Utilities/ScopeGuards.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace ScopeGuards
|
||||||
|
{
|
||||||
|
// Saves snapshot of the object state and restores it during destruction.
|
||||||
|
template<typename T>
|
||||||
|
class TStateSaver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Constructor taking target object in state that we want to save.
|
||||||
|
TStateSaver(T& Target)
|
||||||
|
: Ptr(&Target)
|
||||||
|
, Value(Target)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move constructor allowing to transfer state out of scope.
|
||||||
|
TStateSaver(TStateSaver&& Other)
|
||||||
|
: Ptr(Other.Ptr)
|
||||||
|
, Value(MoveTemp(Other.Value))
|
||||||
|
{
|
||||||
|
// Release responsibility from the other object (std::exchange currently not supported by all platforms).
|
||||||
|
Other.Ptr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-assignable to enforce acquisition on construction.
|
||||||
|
TStateSaver& operator=(TStateSaver&&) = delete;
|
||||||
|
|
||||||
|
// Non-copyable.
|
||||||
|
TStateSaver(const TStateSaver&) = delete;
|
||||||
|
TStateSaver& operator=(const TStateSaver&) = delete;
|
||||||
|
|
||||||
|
~TStateSaver()
|
||||||
|
{
|
||||||
|
if (Ptr)
|
||||||
|
{
|
||||||
|
*Ptr = Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
T* Ptr;
|
||||||
|
T Value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a state saver for target object. Unless saver is moved, state will be restored at the end of scope.
|
||||||
|
// @param Target - Target object in state that we want to save
|
||||||
|
// @returns State saver that unless moved, will restore target's state during scope exit
|
||||||
|
template<typename T>
|
||||||
|
TStateSaver<T> MakeStateSaver(T& Target)
|
||||||
|
{
|
||||||
|
return TStateSaver<T>{ Target };
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user