mirror of
				https://github.com/kevinporetti/UnrealImGui.git
				synced 2025-10-31 05:53:17 +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( | ||||
| 			new string[] | ||||
| 			{ | ||||
| 				"CoreUObject", | ||||
| 				"Engine", | ||||
| 				"Slate", | ||||
| 				"SlateCore" | ||||
| 				// ... 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 "ImGuiModuleManager.h" | ||||
| 
 | ||||
| #include <IPluginManager.h> | ||||
| 
 | ||||
| 
 | ||||
| #define LOCTEXT_NAMESPACE "FImGuiModule" | ||||
| 
 | ||||
| 
 | ||||
| static FImGuiModuleManager* ModuleManager = nullptr; | ||||
| 
 | ||||
| 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() | ||||
| { | ||||
| 	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
 | ||||
| 	// we call this function before unloading the module.
 | ||||
| 	checkf(ModuleManager, TEXT("Null Module Manager. Manager instance should be deleted during module shutdown.")); | ||||
| 	 | ||||
| 	// Before we shutdown we need to delete manager that will do all necessary cleanup.
 | ||||
| 	delete ModuleManager; | ||||
| 	ModuleManager = nullptr; | ||||
| } | ||||
| 
 | ||||
| #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
 | ||||
| #include <Core.h> | ||||
| #include <Engine.h> | ||||
| 
 | ||||
| // 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.
 | ||||
|  | ||||
							
								
								
									
										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
	 Sebastian
						Sebastian