2017-03-26 20:32:57 +00:00
|
|
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
|
|
|
|
|
|
|
#include "ImGuiPrivatePCH.h"
|
|
|
|
|
|
|
|
#include "ImGuiContextProxy.h"
|
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
#include "ImGuiImplementation.h"
|
|
|
|
#include "ImGuiInteroperability.h"
|
|
|
|
|
2017-11-05 22:36:39 +00:00
|
|
|
#include <Runtime/Launch/Resources/Version.h>
|
|
|
|
|
2017-03-26 20:32:57 +00:00
|
|
|
|
|
|
|
static constexpr float DEFAULT_CANVAS_WIDTH = 3840.f;
|
|
|
|
static constexpr float DEFAULT_CANVAS_HEIGHT = 2160.f;
|
|
|
|
|
2017-09-27 20:16:54 +00:00
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
FString GetSaveDirectory()
|
|
|
|
{
|
2017-11-05 22:36:39 +00:00
|
|
|
#if (ENGINE_MAJOR_VERSION > 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 18))
|
|
|
|
const FString SavedDir = FPaths::ProjectSavedDir();
|
|
|
|
#else
|
|
|
|
const FString SavedDir = FPaths::GameSavedDir();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
FString Directory = FPaths::Combine(*SavedDir, TEXT("ImGui"));
|
2017-09-27 20:16:54 +00:00
|
|
|
|
|
|
|
// Make sure that directory is created.
|
|
|
|
IPlatformFile::GetPlatformPhysical().CreateDirectory(*Directory);
|
|
|
|
|
|
|
|
return Directory;
|
|
|
|
}
|
|
|
|
|
|
|
|
FString GetIniFile(const FString& Name)
|
|
|
|
{
|
|
|
|
static FString SaveDirectory = GetSaveDirectory();
|
|
|
|
return FPaths::Combine(SaveDirectory, Name + TEXT(".ini"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-18 19:45:08 +00:00
|
|
|
FImGuiContextProxy::FImGuiContextProxy(const FString& InName, FSimpleMulticastDelegate* InSharedDrawEvent)
|
2017-09-27 20:16:54 +00:00
|
|
|
: Name(InName)
|
2018-03-18 19:45:08 +00:00
|
|
|
, SharedDrawEvent(InSharedDrawEvent)
|
2017-09-27 20:16:54 +00:00
|
|
|
, IniFilename(TCHAR_TO_ANSI(*GetIniFile(InName)))
|
2017-03-26 20:32:57 +00:00
|
|
|
{
|
2017-08-28 19:29:07 +00:00
|
|
|
// Create context.
|
2018-03-18 19:45:08 +00:00
|
|
|
Context = TUniquePtr<ImGuiContext>(ImGui::CreateContext());
|
2017-08-28 19:29:07 +00:00
|
|
|
|
|
|
|
// Set this context in ImGui for initialization (any allocations will be tracked in this context).
|
|
|
|
SetAsCurrent();
|
|
|
|
|
|
|
|
// Start initialization.
|
2017-03-26 20:32:57 +00:00
|
|
|
ImGuiIO& IO = ImGui::GetIO();
|
|
|
|
|
2017-09-27 20:16:54 +00:00
|
|
|
// Set session data storage.
|
|
|
|
IO.IniFilename = IniFilename.c_str();
|
|
|
|
|
2017-03-26 20:32:57 +00:00
|
|
|
// Use pre-defined canvas size.
|
2017-08-28 19:29:07 +00:00
|
|
|
IO.DisplaySize = { DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT };
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2017-09-21 21:09:03 +00:00
|
|
|
// When GetTexData is called for the first time it builds atlas texture and copies mouse cursor data to context.
|
|
|
|
// When multiple contexts share atlas then only the first one will get mouse data. A simple workaround is to use
|
|
|
|
// a temporary atlas if shared one is already built.
|
2017-03-26 20:32:57 +00:00
|
|
|
unsigned char* Pixels;
|
2017-09-21 21:09:03 +00:00
|
|
|
const bool bIsAltasBuilt = IO.Fonts->TexPixelsAlpha8 != nullptr;
|
|
|
|
if (bIsAltasBuilt)
|
|
|
|
{
|
|
|
|
ImFontAtlas().GetTexDataAsRGBA32(&Pixels, nullptr, nullptr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
IO.Fonts->GetTexDataAsRGBA32(&Pixels, nullptr, nullptr);
|
|
|
|
}
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2017-04-22 15:38:04 +00:00
|
|
|
// Initialize key mapping, so context can correctly interpret input state.
|
|
|
|
ImGuiInterops::SetUnrealKeyMap(IO);
|
|
|
|
|
2017-03-26 20:32:57 +00:00
|
|
|
// Begin frame to complete context initialization (this is to avoid problems with other systems calling to ImGui
|
|
|
|
// during startup).
|
|
|
|
BeginFrame();
|
|
|
|
}
|
|
|
|
|
|
|
|
FImGuiContextProxy::~FImGuiContextProxy()
|
|
|
|
{
|
2017-08-28 19:29:07 +00:00
|
|
|
if (Context)
|
|
|
|
{
|
|
|
|
// Set this context in ImGui for de-initialization (any de-allocations will be tracked in this context).
|
|
|
|
SetAsCurrent();
|
|
|
|
|
2017-09-27 20:16:54 +00:00
|
|
|
// Save context data and destroy.
|
|
|
|
ImGuiImplementation::SaveCurrentContextIniSettings(IniFilename.c_str());
|
2018-03-18 19:45:08 +00:00
|
|
|
ImGui::DestroyContext(Context.Release());
|
2017-08-28 19:29:07 +00:00
|
|
|
|
|
|
|
// Set default context in ImGui to keep global context pointer valid.
|
2017-09-27 20:16:54 +00:00
|
|
|
ImGui::SetCurrentContext(&ImGuiImplementation::GetDefaultContext());
|
2017-08-28 19:29:07 +00:00
|
|
|
}
|
2017-03-26 20:32:57 +00:00
|
|
|
}
|
|
|
|
|
2018-03-18 19:45:08 +00:00
|
|
|
void FImGuiContextProxy::Draw()
|
2017-03-26 20:32:57 +00:00
|
|
|
{
|
2018-03-18 19:45:08 +00:00
|
|
|
// Comparing to LastTickFrameNumber rather than GFrameNumber to make sure that this is driven by our own update cycle.
|
|
|
|
if (LastDrawFrameNumber < LastTickFrameNumber)
|
2017-03-26 20:32:57 +00:00
|
|
|
{
|
2018-03-18 19:45:08 +00:00
|
|
|
LastDrawFrameNumber = LastTickFrameNumber;
|
|
|
|
|
|
|
|
if (bIsFrameStarted)
|
2017-03-26 20:32:57 +00:00
|
|
|
{
|
2018-03-18 19:45:08 +00:00
|
|
|
SetAsCurrent();
|
|
|
|
|
|
|
|
// Broadcast draw event to allow listeners to draw their controls to this context.
|
|
|
|
if (DrawEvent.IsBound())
|
|
|
|
{
|
|
|
|
DrawEvent.Broadcast();
|
|
|
|
}
|
|
|
|
if (SharedDrawEvent && SharedDrawEvent->IsBound())
|
|
|
|
{
|
|
|
|
SharedDrawEvent->Broadcast();
|
|
|
|
}
|
2017-03-26 20:32:57 +00:00
|
|
|
}
|
2018-03-18 19:45:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FImGuiContextProxy::Tick(float DeltaSeconds)
|
|
|
|
{
|
|
|
|
// Making sure that we tick only once per frame.
|
|
|
|
if (LastTickFrameNumber < GFrameNumber)
|
|
|
|
{
|
|
|
|
LastTickFrameNumber = GFrameNumber;
|
|
|
|
|
|
|
|
SetAsCurrent();
|
|
|
|
|
|
|
|
if (bIsFrameStarted)
|
2017-10-03 20:16:17 +00:00
|
|
|
{
|
2018-03-18 19:45:08 +00:00
|
|
|
// Make sure that draw events are called before the end of the frame.
|
|
|
|
Draw();
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2018-03-18 19:45:08 +00:00
|
|
|
// 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();
|
|
|
|
}
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2018-03-18 19:45:08 +00:00
|
|
|
// Update context information (some data, like mouse cursor, may be cleaned in new frame, so we should collect it
|
|
|
|
// beforehand).
|
|
|
|
bHasActiveItem = ImGui::IsAnyItemActive();
|
|
|
|
MouseCursor = ImGuiInterops::ToSlateMouseCursor(ImGui::GetMouseCursor());
|
2018-03-13 23:42:37 +00:00
|
|
|
|
2018-03-18 19:45:08 +00:00
|
|
|
// Begin a new frame and set the context back to a state in which it allows to draw controls.
|
|
|
|
BeginFrame(DeltaSeconds);
|
|
|
|
}
|
2017-03-26 20:32:57 +00:00
|
|
|
}
|
|
|
|
|
2017-08-28 19:29:07 +00:00
|
|
|
void FImGuiContextProxy::BeginFrame(float DeltaTime)
|
2017-03-26 20:32:57 +00:00
|
|
|
{
|
|
|
|
if (!bIsFrameStarted)
|
|
|
|
{
|
|
|
|
ImGuiIO& IO = ImGui::GetIO();
|
|
|
|
IO.DeltaTime = DeltaTime;
|
|
|
|
|
2017-04-22 15:38:04 +00:00
|
|
|
if (InputState)
|
|
|
|
{
|
|
|
|
ImGuiInterops::CopyInput(IO, *InputState);
|
|
|
|
}
|
|
|
|
|
2017-03-26 20:32:57 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|