Skip to content

Commit

Permalink
Merge pull request #13 from sammyfreg/dev
Browse files Browse the repository at this point in the history
Main branch update
  • Loading branch information
sammyfreg authored Aug 9, 2021
2 parents b3c0383 + 2ffeeb1 commit fc122db
Show file tree
Hide file tree
Showing 15 changed files with 818 additions and 38 deletions.
4 changes: 2 additions & 2 deletions NetImgui.uplugin
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"FileVersion": 1,
"Version": 3,
"VersionName": "1.3",
"Version": 4,
"VersionName": "1.4",
"FriendlyName": "NetImgui",
"Description": "Plugin exposing Dear ImGui library for drawing 2D menus. These menus are displayed and controlled from an external application but processed from this engine code.",
"Category": "2D",
Expand Down
Binary file modified NetImguiServer/netImguiServer.exe
Binary file not shown.
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,29 @@
</p>

# Summary
### [Unreal Engine 4's](https://github.com/EpicGames) support of [NetImgui 1.5](https://github.com/sammyfreg/netImgui "NetImgui").
### [Unreal Engine 4's](https://github.com/EpicGames) support of [NetImgui 1.6](https://github.com/sammyfreg/netImgui "NetImgui")

**NetImgui** is a library to remotely display and control **Dear ImGui** menus with a connected NetImgui server application.

This plugin allows **UE4** users to remotely connect to their game and display [**Dear ImGui**](https://github.com/ocornut/imgui "Dear ImGui")'s generated menus in a separate window. The game can be running on a different computer or even a different platform such as console, cellpone, etc...

![NetImgui](https://raw.githubusercontent.com/wiki/sammyfreg/netImgui/Web/img/netImgui.png)
![NetImgui](https://raw.githubusercontent.com/wiki/sammyfreg/netImgui/Web/img/UnrealCommands.gif)

> **Note 1:** Allows a very simple use of **NetImgui** in **Unreal Engine 4**. To support more complex usage, with **Dear ImGui** content displayed locally on the game screen, please take a look at the excellent [**UnrealImGui**](https://github.com/segross/UnrealImGui/tree/net_imgui) plugin. It also has NetImgui support integrated.
> **Note 2:** This is a useful plugin when **Dear ImGui** is not already supported in your UE4 engine codebase. Otherwise, it is possible to ignore this plugin and directly add [**NetImgui's**](https://github.com/sammyfreg/netImgui "NetImgui") client code alongside your **Dear ImGui's** code.
# Unreal Commands
Integrated in the plugin, is the ***Imgui Unreal Commands*** functionalities. Allows user to quickly browse and execute the various Unreal Commands that are already available in the Console, but with a nicer interface.

![NetImgui](https://raw.githubusercontent.com/wiki/sammyfreg/netImgui/Web/img/UnrealCommandsFull.gif)
**[[Demonstration Video]](https://raw.githubusercontent.com/wiki/sammyfreg/netImgui/Web/img/UnrealCommands.mp4 "[Demonstration Video]")**

- **Note :**
- The *Imgui Unreal Commands* functionality can easily be added in other projects (without **UnrealNetImgui** dependency).
-Copy `Source\Private\ImguiUnrealCommand.cpp + .h` to your own project
-Follow usage found in `Source\Private\NetImguiModule.cpp` (inside IMGUI_UNREAL_COMMAND_ENABLED defines)

# Integration
1. Download and copy the **UnrealNetImgui** folder to **Unreal Engine**'s Plugin directory (`.\Engine\Plugins`)
1. Regenerate your project solution to have the new plugin included *(right-click [ProjectName].uproject-> Generate Visual Studio Project Files)*
Expand Down Expand Up @@ -73,12 +84,17 @@ void AMyImGuiActor::Tick(float DeltaTime)
}
```
# Release notes
# Release notes 1.4
- Upgraded to [**NetImgui 1.6**](https://github.com/sammyfreg/netImgui "NetImgui") *(more details in link)*
- NetImgui Server keyboard Input fixes
- Added ***Imgui Unreal Commands*** support (browse and execute Unreal Commands)
# Release notes (older)
- Upgraded to **Dear Imgui 1.83** *(docking branch)*
- Upgraded to [**NetImgui 1.5**](https://github.com/sammyfreg/netImgui "NetImgui") *(more details in link)*
- Tested on **Unreal 4.26** *(other versions should be supported without issues)*
- **NetImgui Server** now requires less CPU/GPU
# Credits
Sincere thanks to [Omar Cornut](https://github.com/ocornut/imgui/commits?author=ocornut) for the incredible work on [**Dear ImGui**](https://github.com/ocornut/imgui).
Expand Down
658 changes: 658 additions & 0 deletions Source/Private/ImguiUnrealCommand.cpp

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions Source/Private/ImguiUnrealCommand.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
//
// @author : Sammy Fatnassi
// @date : 2021/08/08
// @version : 0.1
// @brief : Support for 'Unreal Commands' through 'Dear ImGui UI'.
// @note : Used 'SOutputLog.cpp' and 'FConsoleCommandExecutor.cpp' as reference
// @note : This '.h/.cpp pair' is part of the 'UnrealNetImgui' library, but can be used standalone in your own Dear ImGui Unreal integration
// @usage : Search for 'IMGUI_UNREAL_COMMAND_ENABLED' in 'https://github.com/sammyfreg/UnrealNetImgui/blob/master/Source/Private/NetImguiModule.cpp'
// 1- Call 'Create()' once
// 2- Toggle visibility by changing 'IsVisible()' returned value
// 3- Call 'Show()' every frame
// 4- Call 'Destroy()' once, when program is closing
#pragma once

#ifndef IMGUI_UNREAL_COMMAND_ENABLED
#define IMGUI_UNREAL_COMMAND_ENABLED 1 // Default value is 'Active'. Define to 0 to remove 'Dear ImGui Unreal Command' support
#endif

#if IMGUI_UNREAL_COMMAND_ENABLED

#include "CoreMinimal.h"

namespace UECommandImgui
{
struct CommandContext;

// @brief Initialize the Context
// @oaram [addDefaultPresets] Fill the Preset list, with commonly used value
CommandContext* Create(bool addDefaultPresets = true);

// @brief Release the created Context
void Destroy(CommandContext*& pCmdContext);

// @brief Display the 'UE Command' Window, using Dear ImGui
void Show(CommandContext* pCmdContext);

// @brief True if the Window should be displayed
// @note Returned value can be modified to change the Window visibility
bool& IsVisible(CommandContext* pCmdContext);

// @brief Add new 'Commands' to the specified Preset commands list.
// Commanda are executed as is
void AddPresetCommands(CommandContext* pCmdContext, const FString& presetName, const TArray<FString>& commands);

// @brief Add new 'Filters' to the specified Preset commands list.
// Filters are used to find all 'Unreal Commands' starting with this string, and add them to the Preset
void AddPresetFilters(CommandContext* pCmdContext, const FString& presetName, const TArray<FString>& filters);
}

#endif // IMGUI_UNREAL_COMMAND_ENABLED
41 changes: 39 additions & 2 deletions Source/Private/NetImguiModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@

#if NETIMGUI_ENABLED
#include "ThirdParty/NetImgui/NetImgui_Api.h"
//----------------------------------------------------------------------------
#include "ImguiUnrealCommand.h"
#if IMGUI_UNREAL_COMMAND_ENABLED
static UECommandImgui::CommandContext* spUECommandContext = nullptr;
#endif
//----------------------------------------------------------------------------
#endif

#define LOCTEXT_NAMESPACE "FNetImguiModule"
Expand Down Expand Up @@ -79,7 +85,18 @@ void FNetImguiModule::StartupModule()
NetImgui::ConnectFromApp(TCHAR_TO_ANSI(sessionName.GetCharArray().GetData()), GetListeningPort());
//---------------------------------------------------------------------------------------------

mUpdateCallback = FCoreDelegates::OnEndFrame.AddRaw(this, &FNetImguiModule::Update);
mUpdateCallback = FCoreDelegates::OnEndFrame.AddRaw(this, &FNetImguiModule::Update);

//----------------------------------------------------------------------------
#if IMGUI_UNREAL_COMMAND_ENABLED

spUECommandContext = UECommandImgui::Create(); // Create a new Imgui Command Window
// Commented code demonstrating how to add/modify Presets
// Could also modify the list of 'Default Presets' directly (UECommandImgui::sDefaultPresets)
//UECommandImgui::AddPresetFilters(spUECommandContext, TEXT("ExamplePreset"), {"ai.Debug", "fx.Dump"});
//UECommandImgui::AddPresetCommands(spUECommandContext, TEXT("ExamplePreset"), {"Stat Unit", "Stat Fps"});
#endif
//----------------------------------------------------------------------------
#endif
}

Expand All @@ -94,6 +111,11 @@ void FNetImguiModule::ShutdownModule()

ImGui::DestroyContext(mpContext);
mpContext = nullptr;
//----------------------------------------------------------------------------
#if IMGUI_UNREAL_COMMAND_ENABLED
UECommandImgui::Destroy(spUECommandContext);
#endif
//----------------------------------------------------------------------------
#endif
}

Expand All @@ -103,11 +125,26 @@ void FNetImguiModule::Update()
if( NetImgui::IsDrawing() )
NetImgui::EndFrame();

#if NETIMGUI_FRAMESKIP_ENABLED //Not interested in drawing menu until connection established
#if NETIMGUI_FRAMESKIP_ENABLED //Not interested in drawing Dear ImGui Content, until connection established
if( NetImgui::IsConnected() )
#endif
{
NetImgui::NewFrame(NETIMGUI_FRAMESKIP_ENABLED);
if (NetImgui::IsDrawingRemote()) {
//----------------------------------------------------------------------------
#if IMGUI_UNREAL_COMMAND_ENABLED
// Add Main Menu entry to toggle Unreal Command Window visibility
if (ImGui::BeginMainMenuBar()) {
ImGui::MenuItem("Unreal-Commands", nullptr, &UECommandImgui::IsVisible(spUECommandContext) );
ImGui::EndMainMenuBar();
}

// Always try displaying the 'Unreal Command Imgui' Window (handle Window visibily internally)
UECommandImgui::Show(spUECommandContext);
#endif
//----------------------------------------------------------------------------
}

}
#endif
}
Expand Down
8 changes: 4 additions & 4 deletions Source/Private/ThirdParty/NetImgui/NetImgui_Api.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
//! @Name : NetImgui
//=================================================================================================
//! @author : Sammy Fatnassi
//! @date : 2021/05/30
//! @version : v1.5.0
//! @date : 2021/08/08
//! @version : v1.6.0
//! @Details : For integration info : https://github.com/sammyfreg/netImgui/wiki
//=================================================================================================
#define NETIMGUI_VERSION "1.5.0"
#define NETIMGUI_VERSION_NUM 10500
#define NETIMGUI_VERSION "1.6"
#define NETIMGUI_VERSION_NUM 10600

#include <stdint.h>
#include "Private/NetImgui_WarningDisable.h"
Expand Down
25 changes: 17 additions & 8 deletions Source/Private/ThirdParty/NetImgui/NetImgui_Config.h
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
#pragma once

//=================================================================================================
// Enable code compilation for this library
// Note: Useful to disable 'netImgui' on unsupported builds while keeping functions declared
//=================================================================================================
#ifndef NETIMGUI_ENABLED
#define NETIMGUI_ENABLED 1
#endif

#if NETIMGUI_ENABLED
//=================================================================================================
// Set the path to 'imgui.h' used by your codebase here.
// Also suppress a few warnings imgui.h generates in 'warning All' (-Wall)
//=================================================================================================
#include "Private/NetImgui_WarningDisableImgui.h"
#include "../DearImgui/imgui.h"

// This test is only to reduce compile time when this header isn't needed
#ifdef NETIMGUI_INTERNAL_INCLUDE
#include "../DearImgui/imgui_internal.h"
#endif

#include "Private/NetImgui_WarningReenable.h"

#endif // NETIMGUI_ENABLED

//=================================================================================================
// Port used by connect the Server and Client together
//=================================================================================================
Expand All @@ -20,14 +34,6 @@ constexpr uint32_t kDefaultServerPort = 8888; //!< Default port Server waits for
constexpr uint32_t kDefaultClientPort = 8889; //!< Default port Client waits for a connection
}

//=================================================================================================
// Enable code compilation for this library
// Note: Useful to disable 'netImgui' on unsupported builds while keeping functions declared
//=================================================================================================
#ifndef NETIMGUI_ENABLED
#define NETIMGUI_ENABLED 1
#endif

//=================================================================================================
// Enable default Win32/Posix networking code
// Note: By default, netImgui uses Winsock on Windows and Posix sockets on other platforms
Expand All @@ -47,3 +53,6 @@ constexpr uint32_t kDefaultClientPort = 8889; //!< Default port Client waits for
#if !defined(NETIMGUI_POSIX_SOCKETS_ENABLED) && !defined(__UNREAL__)
#define NETIMGUI_POSIX_SOCKETS_ENABLED !(NETIMGUI_WINSOCKET_ENABLED)
#endif

// Disable auto interception of ImGui::NewFrame() / ImGui::Render()
//#define NETIMGUI_IMGUI_CALLBACK_ENABLED 0
6 changes: 3 additions & 3 deletions Source/Private/ThirdParty/NetImgui/Private/NetImgui_Api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ void EndFrame(void)
// We were drawing frame for our remote connection, send the data
if( client.mbValidDrawFrame )
{
CmdDrawFrame* pNewDrawFrame = CreateCmdDrawDrame(ImGui::GetDrawData(), Cursor);
CmdDrawFrame* pNewDrawFrame = CreateCmdDrawFrame(ImGui::GetDrawData(), Cursor);
client.mPendingFrameOut.Assign(pNewDrawFrame);
}

Expand Down Expand Up @@ -323,14 +323,14 @@ void SendDataTexture(ImTextureID textureId, void* pData, uint16_t width, uint16_

// In unlikely event of too many textures, wait for them to be processed
// (if connected) or Process them now (if not)
while( client.mTexturesPendingCount >= static_cast<int32_t>(ArrayCount(client.mTexturesPending)) )
while( (client.mTexturesPendingCreated - client.mTexturesPendingSent) >= static_cast<uint32_t>(ArrayCount(client.mTexturesPending)) )
{
if( IsConnected() )
std::this_thread::yield();
else
client.TextureProcessPending();
}
int32_t idx = client.mTexturesPendingCount.fetch_add(1);
uint32_t idx = client.mTexturesPendingCreated.fetch_add(1) % static_cast<uint32_t>(ArrayCount(client.mTexturesPending));
client.mTexturesPending[idx] = pCmdTexture;

// If not connected to server yet, update all pending textures
Expand Down
13 changes: 7 additions & 6 deletions Source/Private/ThirdParty/NetImgui/Private/NetImgui_Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,8 @@ ClientInfo::ClientInfo()
: mpSocketPending(nullptr)
, mpSocketComs(nullptr)
, mpSocketListen(nullptr)
, mTexturesPendingCount(0)
, mTexturesPendingSent(0)
, mTexturesPendingCreated(0)
{
memset(mTexturesPending, 0, sizeof(mTexturesPending));
}
Expand Down Expand Up @@ -337,12 +338,12 @@ ClientInfo::~ClientInfo()
//=================================================================================================
void ClientInfo::TextureProcessPending()
{
mbHasTextureUpdate |= mTexturesPendingCount > 0;
while( mTexturesPendingCount > 0 )
while( mTexturesPendingCreated != mTexturesPendingSent )
{
int32_t count = mTexturesPendingCount.fetch_sub(1);
CmdTexture* pCmdTexture = mTexturesPending[count-1];
mTexturesPending[count-1] = nullptr;
mbHasTextureUpdate |= true;
uint32_t idx = mTexturesPendingSent.fetch_add(1) % static_cast<uint32_t>(ArrayCount(mTexturesPending));
CmdTexture* pCmdTexture = mTexturesPending[idx];
mTexturesPending[idx] = nullptr;
if( pCmdTexture )
{
// Find the TextureId from our list (or free slot)
Expand Down
7 changes: 4 additions & 3 deletions Source/Private/ThirdParty/NetImgui/Private/NetImgui_Client.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ struct ClientInfo
std::atomic<Network::SocketInfo*> mpSocketPending; // Hold socket info until communication is established
std::atomic<Network::SocketInfo*> mpSocketComs; // Socket used for communications with server
std::atomic<Network::SocketInfo*> mpSocketListen; // Socket used to wait for communication request from server
char mName[16] ={0};
char mName[64] ={};
VecTexture mTextures;
CmdTexture* mTexturesPending[16];
ExchangePtr<CmdDrawFrame> mPendingFrameOut;
Expand All @@ -79,7 +79,8 @@ struct ClientInfo
ImTextureID mFontTextureID = reinterpret_cast<ImTextureID>(0);
SavedImguiContext mSavedContextValues;
Time mTimeTracking; // Used to update Dear ImGui time delta on remote context //SF remove?
std::atomic_int32_t mTexturesPendingCount;
std::atomic_uint32_t mTexturesPendingSent;
std::atomic_uint32_t mTexturesPendingCreated;
float mMouseWheelVertPrev = 0.f;
float mMouseWheelHorizPrev = 0.f;
bool mbDisconnectRequest = false; // Waiting to Disconnect
Expand All @@ -91,7 +92,7 @@ struct ClientInfo
bool mbInsideHook = false; // Currently inside ImGui hook callback
bool mbInsideNewEnd = false; // Currently inside NetImgui::NewFrame() or NetImgui::EndFrame() (prevents recusrive hook call)
bool mbValidDrawFrame = false; // If we should forward the drawdata to the server at the end of ImGui::Render()
char PADDING[3];
char PADDING[7];

ImGuiID mhImguiHookNewframe = 0;
ImGuiID mhImguiHookEndframe = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ struct alignas(8) CmdVersion
ImguiVersionInfo = 3, // Added Dear Imgui/ NetImgui version info to 'CmdVersion'
ServerRefactor = 4, // Change to 'CmdInput' and 'CmdVersion' store size of 'ImWchar' to make sure they are compatible
BackgroundCmd = 5, // Added new command to control background appearance
ClientName = 6, // Increase maximum allowed client name that a program can set
// Insert new version here

//--------------------------------
Expand All @@ -46,7 +47,7 @@ struct alignas(8) CmdVersion

CmdHeader mHeader = CmdHeader(CmdHeader::eCommands::Version, sizeof(CmdVersion));
eVersion mVersion = eVersion::_Current;
char mClientName[16] = {0};
char mClientName[64] = {};
char mImguiVerName[16] = {IMGUI_VERSION};
char mNetImguiVerName[16] = {NETIMGUI_VERSION};
uint32_t mImguiVerID = IMGUI_VERSION_NUM;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ inline void ImGui_ExtractVertices(ImguiVert* pOutVertices, const ImDrawList* pCm

inline void ImGui_ExtractIndices(uint8_t* pOutIndices, const ImDrawList* pCmdList)
{
bool is16Bit = pCmdList->VtxBuffer.size() <= 0xFFFF;
bool is16Bit = sizeof(ImDrawIdx) == 2 || pCmdList->VtxBuffer.size() <= 0xFFFF; // When Dear Imgui is compiled with ImDrawIdx = uint16, we know fore certain that there won't be any drawcall with index > 65k, even if Vertex buffer is bigger than 65k.
size_t IndexSize = is16Bit ? 2 : 4;
int IndexCount = pCmdList->IdxBuffer.size();
// No conversion needed
Expand All @@ -52,14 +52,18 @@ inline void ImGui_ExtractIndices(uint8_t* pOutIndices, const ImDrawList* pCmdLis

inline void ImGui_ExtractDraws(uint32_t& indiceByteOffset, uint32_t& vertexIndex, uint32_t& drawIndex, ImguiDraw* pOutDraws, const ImDrawList* pCmdList)
{
const bool is16Bit = pCmdList->VtxBuffer.size() <= 0xFFFF;
const bool is16Bit = sizeof(ImDrawIdx) == 2 || pCmdList->VtxBuffer.size() <= 0xFFFF; // When Dear Imgui is compiled with ImDrawIdx = uint16, we know fore certain that there won't be any drawcall with index > 65k, even if Vertex buffer is bigger than 65k.
for(int cmd_i = 0; cmd_i < pCmdList->CmdBuffer.size(); cmd_i++)
{
const ImDrawCmd* pCmd = &pCmdList->CmdBuffer[cmd_i];
const ImDrawCmd* pCmd = &pCmdList->CmdBuffer[cmd_i];
if( pCmd->UserCallback == nullptr )
{
pOutDraws[drawIndex].mIdxOffset = indiceByteOffset;
#if IMGUI_VERSION_NUM >= 17100
pOutDraws[drawIndex].mVtxOffset = pCmd->VtxOffset + vertexIndex;
#else
pOutDraws[drawIndex].mVtxOffset = vertexIndex;
#endif
pOutDraws[drawIndex].mTextureId = reinterpret_cast<uint64_t>(pCmd->TextureId);
pOutDraws[drawIndex].mIdxCount = pCmd->ElemCount;
pOutDraws[drawIndex].mIndexSize = is16Bit ? 2 : 4;
Expand All @@ -74,7 +78,7 @@ inline void ImGui_ExtractDraws(uint32_t& indiceByteOffset, uint32_t& vertexIndex
indiceByteOffset = RoundUp(indiceByteOffset, 4u);
}

CmdDrawFrame* CreateCmdDrawDrame(const ImDrawData* pDearImguiData, ImGuiMouseCursor mouseCursor)
CmdDrawFrame* CreateCmdDrawFrame(const ImDrawData* pDearImguiData, ImGuiMouseCursor mouseCursor)
{
//-----------------------------------------------------------------------------------------
// Find memory needed for all the data
Expand Down
Loading

0 comments on commit fc122db

Please sign in to comment.