Merge remote-tracking branch 'arnaud-jamin/main'

# Conflicts:
#	Plugins/Cog/Source/CogDebug/Private/CogDebugDrawHelper.cpp
#	Plugins/Cog/Source/CogDebug/Private/CogDebugLog.cpp
#	Plugins/Cog/Source/CogWindow/Private/CogWindowManager.cpp
#	Plugins/Cog/Source/CogWindow/Private/CogWindow_Settings.cpp
#	Plugins/Cog/Source/ThirdParty/ImGui/imconfig.h
#	Plugins/Cog/Source/ThirdParty/ImGui/imgui.cpp
#	Plugins/Cog/Source/ThirdParty/ImGui/imgui.h
#	Plugins/Cog/Source/ThirdParty/ImGui/imgui_demo.cpp
#	Plugins/Cog/Source/ThirdParty/ImGui/imgui_draw.cpp
#	Plugins/Cog/Source/ThirdParty/ImGui/imgui_internal.h
#	Plugins/Cog/Source/ThirdParty/ImGui/imgui_tables.cpp
#	Plugins/Cog/Source/ThirdParty/ImGui/imgui_widgets.cpp
#	Plugins/Cog/Source/ThirdParty/ImGui/imstb_textedit.h
#	Plugins/Cog/Source/ThirdParty/ImGui/imstb_truetype.h
#	Plugins/Cog/Source/ThirdParty/ImPlot/README.md
#	Plugins/Cog/Source/ThirdParty/ImPlot/TODO.md
#	Plugins/Cog/Source/ThirdParty/ImPlot/implot.cpp
#	Plugins/Cog/Source/ThirdParty/ImPlot/implot.h
#	Plugins/Cog/Source/ThirdParty/ImPlot/implot_demo.cpp
#	Plugins/Cog/Source/ThirdParty/ImPlot/implot_internal.h
#	Plugins/Cog/Source/ThirdParty/ImPlot/implot_items.cpp
#	Plugins/CogInput/Source/CogInput/Public/CogInputDataAsset.h
This commit is contained in:
ed
2025-01-17 16:11:13 -05:00
231 changed files with 8935 additions and 1336 deletions
@@ -3,13 +3,14 @@
#include "CogCommonDebugFilteredActorInterface.h"
#include "CogDebugDrawHelper.h"
#include "CogDebugReplicator.h"
#include "imgui.h"
#include "Engine/World.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
#include "imgui.h"
#include "Kismet/KismetMathLibrary.h"
#include "Misc/EngineVersionComparison.h"
//--------------------------------------------------------------------------------------------------------------------------
TWeakObjectPtr<AActor> FCogDebug::Selection;
TWeakObjectPtr<AActor> FCogDebug::Selection[] = {};
FCogDebugSettings FCogDebug::Settings = FCogDebugSettings();
//--------------------------------------------------------------------------------------------------------------------------
@@ -32,7 +33,7 @@ bool FCogDebug::IsDebugActiveForObject(const UObject* WorldContextObject)
return true;
}
const bool Result = IsDebugActiveForObject_Internal(WorldContextObject, Selection.Get(), Settings.bIsFilteringBySelection);
const bool Result = IsDebugActiveForObject_Internal(WorldContextObject, Selection[GetPieSessionId()].Get(), Settings.bIsFilteringBySelection);
return Result;
}
@@ -88,21 +89,52 @@ bool FCogDebug::IsDebugActiveForObject_Internal(const UObject* WorldContextObjec
//--------------------------------------------------------------------------------------------------------------------------
AActor* FCogDebug::GetSelection()
{
return Selection.Get();
return Selection[GetPieSessionId()].Get();
}
//--------------------------------------------------------------------------------------------------------------------------
int32 FCogDebug::GetPieSessionId()
{
#if UE_EDITOR
#if UE_VERSION_OLDER_THAN(5, 5, 0)
return GPlayInEditorID;
#else
return UE::GetPlayInEditorID();
#endif
#else
return 0;
#endif
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogDebug::SetSelection(const UWorld* World, AActor* Value)
{
Selection = Value;
Selection[GetPieSessionId()] = Value;
if (World != nullptr && World->GetNetMode() == NM_Client)
ReplicateSelection(World, Value);
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogDebug::ReplicateSelection(const UWorld* World, AActor* Value)
{
if (World == nullptr)
{
if (ACogDebugReplicator* Replicator = ACogDebugReplicator::GetLocalReplicator(*World))
{
Replicator->Server_SetSelection(Value);
}
return;
}
ACogDebugReplicator* Replicator = ACogDebugReplicator::GetLocalReplicator(*World);
if (Replicator == nullptr)
{
return;
}
if (Replicator->HasAuthority())
{
return;
}
Replicator->Server_SetSelection(Value, Settings.ReplicateSelection);
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -306,7 +338,7 @@ void FCogDebug::GetDebugDrawOverlapSettings(FCogDebugDrawOverlapParams& Params)
Params.NoHitColor = Settings.CollisionQueryNoHitColor;
Params.DrawHitPrimitives = Settings.CollisionQueryDrawHitPrimitives;
Params.DrawHitPrimitiveActorsName = Settings.CollisionQueryDrawHitPrimitiveActorsName;
Params.HitPrimitiveActorsNameShadow = Settings.CollisionQueryHitPrimitiveActorsNameShadow;
Params.DrawHitPrimitiveActorsNameShadow = Settings.CollisionQueryHitPrimitiveActorsNameShadow;
Params.HitPrimitiveActorsNameSize = Settings.CollisionQueryHitPrimitiveActorsNameSize;
Params.Persistent = false;
Params.LifeTime = 0.0f;
@@ -1,19 +1,20 @@
#include "CogDebugDraw.h"
#include "CogDebug.h"
#include "CogDebugDrawHelper.h"
#include "CogDebugDrawImGui.h"
#include "CogDebugLog.h"
#include "CogDebugReplicator.h"
#include "CogDebug.h"
#include "CogDebugShape.h"
#include "CogImguiHelper.h"
#include "Components/BoxComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "DrawDebugHelpers.h"
#include "Engine/Engine.h"
#include "Engine/SkeletalMesh.h"
#include "VisualLogger/VisualLogger.h"
#include "Engine/World.h"
#include "DrawDebugHelpers.h"
#include "ReferenceSkeleton.h"
#include "Components/SkeletalMeshComponent.h"
#include "VisualLogger/VisualLogger.h"
#if ENABLE_COG
@@ -638,6 +639,96 @@ void FCogDebugDraw::Skeleton(const FLogCategoryBase& LogCategory, const USkeleta
}
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogDebugDraw::LineTrace(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FVector& Start, const FVector& End, const bool HasHits, TArray<FHitResult>& HitResults, const FCogDebugDrawLineTraceParams& Settings)
{
if (FCogDebugLog::IsLogCategoryActive(LogCategory) == false)
{ return; }
const UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (World == nullptr)
{ return; }
Settings.Persistent = FCogDebug::GetDebugPersistent(Settings.Persistent);
FCogDebugDrawHelper::DrawLineTrace(World, Start, End, HasHits, HitResults, Settings);
ReplicateShape(WorldContextObject,
FCogDebugShape::MakeArrow(Start,
End,
FCogDebug::Settings.ArrowSize,
HasHits
? Settings.HitColor
: Settings.NoHitColor,
FCogDebug::Settings.Thickness,
Settings.Persistent,
Settings.DepthPriority));
ReplicateHitResults(WorldContextObject, HitResults, Settings);
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogDebugDraw::Sweep(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FCollisionShape& Shape, const FVector& Start, const FVector& End, const FQuat& Rotation, const bool HasHits, TArray<FHitResult>& HitResults, const FCogDebugDrawSweepParams& Settings)
{
if (FCogDebugLog::IsLogCategoryActive(LogCategory) == false)
{ return; }
const UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (World == nullptr)
{ return; }
FCogDebugDrawHelper::DrawSweep(World, Shape, Start, End, Rotation, HasHits, HitResults, Settings);
const FColor Color = HasHits
? Settings.HitColor
: Settings.NoHitColor;
ReplicateShape(WorldContextObject,
FCogDebugShape::MakeCollisionShape(Shape,
Start,
Rotation,
Shape.GetExtent(),
Color,
FCogDebug::Settings.Thickness,
Settings.Persistent,
Settings.DepthPriority));
ReplicateShape(WorldContextObject,
FCogDebugShape::MakeArrow(Start,
End,
FCogDebug::Settings.ArrowSize,
Color,
FCogDebug::Settings.Thickness,
Settings.Persistent,
Settings.DepthPriority));
ReplicateHitResults(WorldContextObject, HitResults, Settings);
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogDebugDraw::Overlap(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FCollisionShape& Shape, const FVector& Location, const FQuat& Rotation, TArray<FOverlapResult>& OverlapResults, const FCogDebugDrawOverlapParams& Settings)
{
if (FCogDebugLog::IsLogCategoryActive(LogCategory) == false)
{ return; }
const UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (World == nullptr)
{ return; }
FCogDebugDrawHelper::DrawOverlap(World, Shape, Location, Rotation, OverlapResults, Settings);
const FColor Color = OverlapResults.Num() > 0
? Settings.HitColor
: Settings.NoHitColor;
ReplicateShape(WorldContextObject,
FCogDebugShape::MakeCollisionShape(Shape,
Location,
Rotation,
Shape.GetExtent(),
Color,
FCogDebug::Settings.Thickness,
Settings.Persistent,
Settings.DepthPriority));
// TODO: replicate overlap results
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogDebugDraw::ReplicateShape(const UObject* WorldContextObject, const FCogDebugShape& Shape)
@@ -673,4 +764,112 @@ void FCogDebugDraw::ReplicateShape(const UObject* WorldContextObject, const FCog
}
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogDebugDraw::ReplicateHitResults(const UObject* WorldContextObject, const TArray<FHitResult>& HitResults, const FCogDebugDrawLineTraceParams& Settings)
{
TSet<const UPrimitiveComponent*> AlreadyDrawnPrimitives;
TSet<const AActor*> AlreadyDrawnActors;
for (const FHitResult& HitResult : HitResults)
{
const FColor& HitColor = Settings.HitColor;
if (Settings.DrawHitLocation)
{
ReplicateShape(WorldContextObject,
FCogDebugShape::MakePoint(HitResult.Location,
Settings.HitPointSize,
HitColor,
Settings.Persistent,
Settings.DepthPriority));
}
if (Settings.DrawHitImpactPoints)
{
ReplicateShape(WorldContextObject,
FCogDebugShape::MakePoint(HitResult.ImpactPoint,
Settings.HitPointSize,
HitColor,
Settings.Persistent,
Settings.DepthPriority));
}
if (Settings.DrawHitNormals)
{
ReplicateShape(WorldContextObject,
FCogDebugShape::MakeArrow(HitResult.Location,
HitResult.Location + HitResult.Normal * 20.0f,
FCogDebug::Settings.ArrowSize,
HitColor,
Settings.Thickness,
Settings.Persistent,
Settings.DepthPriority));
}
if (Settings.DrawHitImpactNormals)
{
ReplicateShape(WorldContextObject,
FCogDebugShape::MakeArrow(HitResult.ImpactPoint,
HitResult.Location + HitResult.ImpactNormal * 20.0f,
FCogDebug::Settings.ArrowSize,
HitColor,
Settings.Thickness,
Settings.Persistent,
Settings.DepthPriority));
}
if (Settings.DrawHitPrimitives)
{
const UPrimitiveComponent* PrimitiveComponent = HitResult.GetComponent();
if (PrimitiveComponent == nullptr)
{
continue;
}
if (AlreadyDrawnPrimitives.Contains(PrimitiveComponent))
{
continue;
}
const UBoxComponent* BoxComponent = Cast<UBoxComponent>(PrimitiveComponent);
const FCollisionShape Shape = PrimitiveComponent->GetCollisionShape();
AlreadyDrawnPrimitives.Add(PrimitiveComponent);
const ECollisionChannel CollisionObjectType = PrimitiveComponent->GetCollisionObjectType();
const FColor PrimitiveColor = Settings.ChannelColors[CollisionObjectType];
if (Shape.ShapeType == ECollisionShape::Box && BoxComponent == nullptr)
{
FVector Location;
FVector Extent;
PrimitiveComponent->Bounds.GetBox().GetCenterAndExtents(Location, Extent);
// TODO: this adds padding to prevent Z fight. Maybe add this as a parameter.
Extent += FVector::OneVector;
ReplicateShape(WorldContextObject,
FCogDebugShape::MakeBox(Location,
FRotator::ZeroRotator,
Extent,
PrimitiveColor,
Settings.Thickness,
Settings.Persistent,
Settings.DepthPriority));
}
else
{
ReplicateShape(WorldContextObject,
FCogDebugShape::MakeCollisionShape(Shape,
PrimitiveComponent->GetComponentLocation(),
PrimitiveComponent->GetComponentQuat(),
Shape.GetExtent(),
PrimitiveColor,
Settings.Thickness,
Settings.Persistent,
Settings.DepthPriority));
}
}
}
}
#endif //ENABLE_COG
@@ -2,10 +2,14 @@
#include "CogDebug.h"
#include "Components/LineBatchComponent.h"
#include "Engine/Engine.h"
#include "DrawDebugHelpers.h"
#include "Components/BoxComponent.h"
#include "DrawDebugHelpers.h"
#include "Engine/Engine.h"
#include "Engine/OverlapResult.h"
#include "PhysicsEngine/PhysicsAsset.h"
#include "PhysicsEngine/SkeletalBodySetup.h"
#include "PhysicsEngine/SphylElem.h"
#include "Runtime/Experimental/Chaos/Private/Chaos/PhysicsObjectInternal.h"
namespace
{
@@ -307,9 +311,6 @@ void FCogDebugDrawHelper::DrawHitResults(
const TArray<FHitResult>& HitResults,
const FCogDebugDrawLineTraceParams& Settings)
{
TSet<const UPrimitiveComponent*> AlreadyDrawnPrimitives;
TSet<const AActor*> AlreadyDrawnActors;
for (const FHitResult& HitResult : HitResults)
{
const FColor& HitColor = Settings.HitColor;
@@ -374,33 +375,19 @@ void FCogDebugDrawHelper::DrawHitResults(
continue;
}
if (AlreadyDrawnPrimitives.Contains(PrimitiveComponent))
{
continue;
}
AlreadyDrawnPrimitives.Add(PrimitiveComponent);
const ECollisionChannel CollisionObjectType = PrimitiveComponent->GetCollisionObjectType();
const FColor PrimitiveColor = Settings.ChannelColors[CollisionObjectType];
DrawPrimitiveComponent(*PrimitiveComponent, PrimitiveColor, Settings.Persistent, Settings.LifeTime, Settings.DepthPriority, Settings.Thickness);
if (Settings.DrawHitPrimitiveActorsName)
{
const AActor* Actor = PrimitiveComponent->GetOwner();
if (Actor == nullptr)
{
continue;
}
if (AlreadyDrawnActors.Contains(Actor))
{
continue;
}
AlreadyDrawnActors.Add(Actor);
const FColor TextColor = PrimitiveColor.WithAlpha(255);
DrawDebugString(World, Actor->GetActorLocation(), GetNameSafe(Actor->GetClass()), nullptr, TextColor, 0.0f, Settings.HitPrimitiveActorsNameShadow, Settings.HitPrimitiveActorsNameSize);
}
DrawPrimitiveComponent(
*PrimitiveComponent,
HitResult.PhysicsObject->GetBodyIndex(),
PrimitiveColor,
Settings.Persistent,
Settings.LifeTime,
Settings.DepthPriority,
Settings.Thickness,
Settings.DrawHitPrimitiveActorsName,
Settings.DrawHitPrimitiveActorsNameShadow,
Settings.HitPrimitiveActorsNameSize);
}
}
}
@@ -488,7 +475,17 @@ void FCogDebugDrawHelper::DrawOverlap(
{
const ECollisionChannel CollisionObjectType = PrimitiveComponent->GetCollisionObjectType();
const FColor PrimitiveColor = Settings.ChannelColors[CollisionObjectType];
DrawPrimitiveComponent(*PrimitiveComponent, PrimitiveColor, Settings.Persistent, Settings.LifeTime, Settings.DepthPriority, Settings.Thickness);
DrawPrimitiveComponent(
*PrimitiveComponent,
OverlapResult.PhysicsObject->GetBodyIndex(),
PrimitiveColor,
Settings.Persistent,
Settings.LifeTime,
Settings.DepthPriority,
Settings.Thickness,
Settings.DrawHitPrimitiveActorsName,
Settings.DrawHitPrimitiveActorsNameShadow,
Settings.HitPrimitiveActorsNameSize);
}
}
}
@@ -496,12 +493,16 @@ void FCogDebugDrawHelper::DrawOverlap(
//--------------------------------------------------------------------------------------------------------------------------
void FCogDebugDrawHelper::DrawPrimitiveComponent(
const UPrimitiveComponent& PrimitiveComponent,
const FColor& Color,
const bool Persistent,
const float LifeTime,
const uint8 DepthPriority,
const float Thickness)
const UPrimitiveComponent& PrimitiveComponent,
const int32 BodyIndex,
const FColor& Color,
const bool Persistent,
const float LifeTime,
const uint8 DepthPriority,
const float Thickness,
const bool DrawName,
const bool DrawNameShadow,
const float DrawNameSize)
{
const UWorld* World = PrimitiveComponent.GetWorld();
if (World == nullptr)
@@ -509,46 +510,131 @@ void FCogDebugDrawHelper::DrawPrimitiveComponent(
return;
}
const UBoxComponent* BoxComponent = Cast<UBoxComponent>(&PrimitiveComponent);;
const FCollisionShape Shape = PrimitiveComponent.GetCollisionShape();
if (Shape.ShapeType == ECollisionShape::Box && BoxComponent == nullptr)
if (const USkeletalMeshComponent* SkeletalMeshComponent = Cast<USkeletalMeshComponent>(&PrimitiveComponent))
{
FVector Location;
FVector Extent;
PrimitiveComponent.Bounds.GetBox().GetCenterAndExtents(Location, Extent);
const UPhysicsAsset* PhysicsAsset = SkeletalMeshComponent->GetPhysicsAsset();
if (PhysicsAsset->SkeletalBodySetups.IsValidIndex(BodyIndex) == false)
{
return;
}
// TODO: this adds padding to prevent Z fight. Maybe add this as a parameter.
Extent += FVector::OneVector;
const USkeletalBodySetup* BodySetup = PhysicsAsset->SkeletalBodySetups[BodyIndex];
const int32 BoneIndex = SkeletalMeshComponent->GetBoneIndex(BodySetup->BoneName);
const FTransform BoneTransform = SkeletalMeshComponent->GetBoneTransform(BoneIndex);
DrawDebugSolidBox(
World,
Location,
Extent,
FQuat::Identity,
Color,
Persistent,
LifeTime,
DepthPriority);
if (DrawName)
{
DrawDebugString(World, BoneTransform.GetLocation(), BodySetup->BoneName.ToString(), nullptr, Color.WithAlpha(255), 0.0f, DrawNameShadow, DrawNameSize);
}
DrawDebugBox(
World,
Location,
Extent,
FQuat::Identity,
Color,
Persistent,
LifeTime,
DepthPriority,
Thickness);
for (const FKSphereElem& Sphere : BodySetup->AggGeom.SphereElems)
{
const FTransform transform = Sphere.GetTransform() * BoneTransform;
DrawSphere(
World,
transform.GetLocation(),
Sphere.Radius,
FCogDebug::GetDebugSegments(),
Color,
Persistent,
LifeTime,
DepthPriority,
Thickness);
}
for (const FKBoxElem& Box : BodySetup->AggGeom.BoxElems)
{
const FTransform transform = Box.GetTransform() * BoneTransform;
DrawDebugBox(
World,
transform.GetLocation(),
FVector(Box.X, Box.Y, Box.Z) * 0.5f,
transform.GetRotation(),
Color,
Persistent,
LifeTime,
DepthPriority,
Thickness);
}
for (const FKSphylElem& Sphy : BodySetup->AggGeom.SphylElems)
{
const FTransform transform = Sphy.GetTransform() * BoneTransform;
DrawDebugCapsule(
World,
transform.GetLocation(),
Sphy.Length * 0.5f,
Sphy.Radius,
transform.GetRotation(),
Color,
Persistent,
LifeTime,
DepthPriority,
Thickness);
}
}
else
{
const FVector Location = PrimitiveComponent.GetComponentLocation();
const FQuat Rotation = PrimitiveComponent.GetComponentQuat();
const FVector Scale = PrimitiveComponent.GetComponentScale();
DrawShape(World, Shape, Location, Rotation, Scale, Color, Persistent, LifeTime, DepthPriority, Thickness);
const AActor* Actor = PrimitiveComponent.GetOwner();
const UBoxComponent* BoxComponent = Cast<UBoxComponent>(&PrimitiveComponent);
const FCollisionShape Shape = PrimitiveComponent.GetCollisionShape();
if (Shape.ShapeType == ECollisionShape::Box && BoxComponent == nullptr)
{
FVector Location;
FVector Extent;
PrimitiveComponent.Bounds.GetBox().GetCenterAndExtents(Location, Extent);
if (DrawName)
{
DrawDebugString(World, Location, GetNameSafe(Actor), nullptr, Color.WithAlpha(255), 0.0f, DrawNameShadow, DrawNameSize);
}
// TODO: this adds padding to prevent Z fight. Maybe add this as a parameter.
Extent += FVector::OneVector;
DrawDebugSolidBox(
World,
Location,
Extent,
FQuat::Identity,
Color,
Persistent,
LifeTime,
DepthPriority);
DrawDebugBox(
World,
Location,
Extent,
FQuat::Identity,
Color,
Persistent,
LifeTime,
DepthPriority,
Thickness);
}
else
{
const FVector Location = PrimitiveComponent.GetComponentLocation();
const FQuat Rotation = PrimitiveComponent.GetComponentQuat();
const FVector Scale = PrimitiveComponent.GetComponentScale();
if (DrawName)
{
DrawDebugString(World, Location, GetNameSafe(Actor), nullptr, Color.WithAlpha(255), 0.0f, DrawNameShadow, DrawNameSize);
}
DrawShape(World, Shape, Location, Rotation, Scale, Color, Persistent, LifeTime, DepthPriority, Thickness);
}
}
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -130,6 +130,7 @@ FLogCategoryBase* FCogDebugLog::FindLogCategory(const FName CategoryName)
{
return LogCategoryInfo->LogCategory;
}
return nullptr;
}
@@ -1,7 +1,6 @@
#include "CogDebugPlot.h"
#include "CogDebug.h"
#include "CogDebugDraw.h"
#include "CogDebugHelper.h"
#include "CogImguiHelper.h"
#include "Engine/Engine.h"
@@ -9,24 +8,28 @@
FCogDebugPlotEvent FCogDebugPlot::DefaultEvent;
TArray<FCogDebugPlotEntry> FCogDebugPlot::Plots;
TArray<FCogDebugPlotEntry> FCogDebugPlot::Events;
bool FCogDebugPlot::IsVisible = false;
bool FCogDebugPlot::Pause = false;
FName FCogDebugPlot::LastAddedEventPlotName = NAME_None;
int32 FCogDebugPlot::LastAddedEventIndex = INDEX_NONE;
TMap<int32, TMap<int32, int32>> FCogDebugPlot::OccupationMap;
//--------------------------------------------------------------------------------------------------------------------------
// FCogPlotEvent
//--------------------------------------------------------------------------------------------------------------------------
float FCogDebugPlotEvent::GetActualEndTime(const FCogDebugPlotEntry& Plot) const
{
const float ActualEndTime = EndTime == 0.0f ? Plot.Time : EndTime;
const UWorld* World = Plot.World.Get();
const float WorldTime = World != nullptr ? World->GetTimeSeconds() : 0.0f;
const float ActualEndTime = EndTime != 0.0f ? EndTime : WorldTime;
return ActualEndTime;
}
//--------------------------------------------------------------------------------------------------------------------------
uint64 FCogDebugPlotEvent::GetActualEndFrame(const FCogDebugPlotEntry& Plot) const
{
const float ActualEndFame = EndFrame == 0.0f ? Plot.Frame : EndFrame;
const float ActualEndFame = EndFrame != 0.0f ? EndFrame : GFrameCounter;
return ActualEndFame;
}
@@ -119,9 +122,8 @@ void FCogDebugPlotEntry::AddPoint(float X, float Y)
//--------------------------------------------------------------------------------------------------------------------------
FCogDebugPlotEvent& FCogDebugPlotEntry::AddEvent(
const FCogDebugPlotEntry& OwnwePlot,
FString OwnerName,
bool IsInstant,
const FString& OwnerName,
const bool IsInstant,
const FName EventId,
const int32 Row,
const FColor& Color)
@@ -131,10 +133,9 @@ FCogDebugPlotEvent& FCogDebugPlotEntry::AddEvent(
Events.Reserve(200);
}
//-----------------------------------------------------------------------
// We currently having two events with the same name at the same time.
// So we stop the current one if any exist.
//-----------------------------------------------------------------------
//----------------------------
// Stop if it already exist.
//----------------------------
StopEvent(EventId);
FCogDebugPlotEvent* Event = nullptr;
@@ -155,11 +156,16 @@ FCogDebugPlotEvent& FCogDebugPlotEntry::AddEvent(
Event->Id = EventId;
Event->OwnerName = OwnerName;
Event->DisplayName = EventId.ToString();
Event->StartTime = OwnwePlot.Time;
Event->EndTime = IsInstant ? OwnwePlot.Time : 0.0f;
Event->StartFrame = OwnwePlot.Frame;
Event->EndFrame = IsInstant ? OwnwePlot.Frame : 0.0f;
Event->Row = (Row == FCogDebugPlot::AutoRow) ? OwnwePlot.FindFreeRow() : Row;
Event->StartTime = Time;
Event->EndTime = IsInstant ? Time : 0.0f;
Event->StartFrame = Frame;
Event->EndFrame = IsInstant ? Frame : 0.0f;
Event->Row = (Row == FCogDebugPlot::AutoRow) ? FCogDebugPlot::FindFreeGraphRow(GraphIndex) : Row;
if (IsInstant == false)
{
FCogDebugPlot::OccupyGraphRow(GraphIndex, Event->Row);
}
MaxRow = FMath::Max(Event->Row, MaxRow);
@@ -168,7 +174,7 @@ FCogDebugPlotEvent& FCogDebugPlotEntry::AddEvent(
Event->BorderColor = FCogImguiHelper::ToImColor(BorderColor);
Event->FillColor = FCogImguiHelper::ToImColor(FillColor);
FCogDebugPlot::LastAddedEventPlotName = OwnwePlot.Name;
FCogDebugPlot::LastAddedEventPlotName = Name;
FCogDebugPlot::LastAddedEventIndex = AddedIndex;
return *Event;
@@ -187,18 +193,13 @@ FCogDebugPlotEvent& FCogDebugPlotEntry::StopEvent(const FName EventId)
{
Event->EndTime = Time;
Event->EndFrame = Frame;
FCogDebugPlot::FreeGraphRow(GraphIndex, Event->Row);
}
return *Event;
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogDebugPlotEntry::UpdateTime(const UWorld* World)
{
Time = World ? World->GetTimeSeconds() : 0.0;
Frame = GFrameCounter;
}
//--------------------------------------------------------------------------------------------------------------------------
FCogDebugPlotEvent* FCogDebugPlotEntry::GetLastEvent()
{
@@ -240,65 +241,17 @@ FCogDebugPlotEvent* FCogDebugPlotEntry::FindLastEventByName(FName EventId)
}
//--------------------------------------------------------------------------------------------------------------------------
int32 FCogDebugPlotEntry::FindFreeRow() const
void FCogDebugPlotEntry::AssignGraphAndAxis(int32 InGraph, ImAxis InYAxis)
{
static float InstantTimeThreshold = 1.0f;
static float TotalTimeThreshold = 10.0f;
TSet<int32> OccupiedRows;
for (int32 i = Events.Num() - 1; i >= 0; --i)
{
int32 Index = i;
if (EventOffset != 0)
{
Index = (i + EventOffset) % Events.Num();
}
const FCogDebugPlotEvent& Event = Events[Index];
if (Event.EndTime != 0.0f && Time > Event.EndTime + TotalTimeThreshold)
{
break;
}
if (Event.StartTime == Event.EndTime && Time > Event.EndTime + InstantTimeThreshold)
{
continue;
}
if (Event.EndTime != 0.0f)
{
continue;
}
OccupiedRows.Add(Event.Row);
}
int32 FreeRow = 0;
while (true)
{
if (OccupiedRows.Contains(FreeRow) == false)
{
break;
}
FreeRow++;
}
return FreeRow;
GraphIndex = InGraph;
YAxis = InYAxis;
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogDebugPlotEntry::AssignAxis(int32 Row, ImAxis YAxis)
void FCogDebugPlotEntry::ResetGraphAndAxis()
{
CurrentRow = Row;
CurrentYAxis = YAxis;
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogDebugPlotEntry::ResetAxis()
{
CurrentRow = INDEX_NONE;
CurrentYAxis = ImAxis_COUNT;
GraphIndex = INDEX_NONE;
YAxis = ImAxis_COUNT;
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -341,7 +294,7 @@ bool FCogDebugPlotEntry::FindValue(float x, float& y) const
Index = (i + ValueOffset) % Values.size();
}
ImVec2 Point = Values[Index];
const ImVec2 Point = Values[Index];
if (Point.x > x)
{
FoundAfter = true;
@@ -368,6 +321,8 @@ bool FCogDebugPlotEntry::FindValue(float x, float& y) const
void FCogDebugPlot::Reset()
{
Plots.Empty();
Events.Empty();
OccupationMap.Empty();
Pause = false;
ResetLastAddedEvent();
}
@@ -375,11 +330,17 @@ void FCogDebugPlot::Reset()
//--------------------------------------------------------------------------------------------------------------------------
void FCogDebugPlot::Clear()
{
for (FCogDebugPlotEntry& Entry : FCogDebugPlot::Plots)
for (FCogDebugPlotEntry& Entry : Plots)
{
Entry.Clear();
}
for (FCogDebugPlotEntry& Entry : Events)
{
Entry.Clear();
}
OccupationMap.Empty();
ResetLastAddedEvent();
}
@@ -393,7 +354,7 @@ void FCogDebugPlot::ResetLastAddedEvent()
//--------------------------------------------------------------------------------------------------------------------------
FCogDebugPlotEvent* FCogDebugPlot::GetLastAddedEvent()
{
FCogDebugPlotEntry* Plot = FindPlot(LastAddedEventPlotName);
FCogDebugPlotEntry* Plot = FindEntry(true, LastAddedEventPlotName);
if (Plot == nullptr)
{
return nullptr;
@@ -403,10 +364,27 @@ FCogDebugPlotEvent* FCogDebugPlot::GetLastAddedEvent()
}
//--------------------------------------------------------------------------------------------------------------------------
FCogDebugPlotEntry* FCogDebugPlot::FindPlot(const FName Name)
FCogDebugPlotEntry* FCogDebugPlot::FindEntry(const FName Name)
{
FCogDebugPlotEntry* Plot = Plots.FindByPredicate([Name](const FCogDebugPlotEntry& P) { return P.Name == Name; });
return Plot;
if (FCogDebugPlotEntry* Event = Events.FindByPredicate([Name](const FCogDebugPlotEntry& P) { return P.Name == Name; }))
{
return Event;
}
if (FCogDebugPlotEntry* Plot = Plots.FindByPredicate([Name](const FCogDebugPlotEntry& P) { return P.Name == Name; }))
{
return Plot;
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------------------------------
FCogDebugPlotEntry* FCogDebugPlot::FindEntry(bool IsEvent, const FName Name)
{
TArray<FCogDebugPlotEntry>* Entries = IsEvent ? &Events : &Plots;
FCogDebugPlotEntry* Entry = Entries->FindByPredicate([Name](const FCogDebugPlotEntry& P) { return P.Name == Name; });
return Entry;
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -431,19 +409,20 @@ FCogDebugPlotEntry* FCogDebugPlot::RegisterPlot(const UObject* WorldContextObjec
return nullptr;
}
FCogDebugPlotEntry* EntryPtr = FindPlot(PlotName);
FCogDebugPlotEntry* EntryPtr = FindEntry(IsEventPlot, PlotName);
if (EntryPtr == nullptr)
{
EntryPtr = &Plots.AddDefaulted_GetRef();
TArray<FCogDebugPlotEntry>* Entries = IsEventPlot ? &Events : &Plots;
EntryPtr = &Entries->AddDefaulted_GetRef();
EntryPtr->Name = PlotName;
EntryPtr->IsEventPlot = IsEventPlot;
Plots.Sort([](const FCogDebugPlotEntry& A, const FCogDebugPlotEntry& B) { return A.Name.ToString().Compare(B.Name.ToString()) < 0; });
Entries->Sort([](const FCogDebugPlotEntry& A, const FCogDebugPlotEntry& B) { return A.Name.ToString().Compare(B.Name.ToString()) < 0; });
}
if (EntryPtr->CurrentYAxis == ImAxis_COUNT)
{
return nullptr;
}
//if (EntryPtr->YAxis == ImAxis_COUNT)
//{
// return nullptr;
//}
const float Time = World->GetTimeSeconds();
if (Time < EntryPtr->Time)
@@ -451,6 +430,7 @@ FCogDebugPlotEntry* FCogDebugPlot::RegisterPlot(const UObject* WorldContextObjec
EntryPtr->Clear();
}
EntryPtr->World = World;
EntryPtr->Time = World->GetTimeSeconds();
EntryPtr->Frame = GFrameCounter;
@@ -479,7 +459,7 @@ FCogDebugPlotEvent& FCogDebugPlot::PlotEvent(const UObject* WorldContextObject,
return DefaultEvent;
}
FCogDebugPlotEvent& Event = Plot->AddEvent(*Plot, GetNameSafe(WorldContextObject), IsInstant, EventId, Row, Color);
FCogDebugPlotEvent& Event = Plot->AddEvent(GetNameSafe(WorldContextObject), IsInstant, EventId, Row, Color);
return Event;
}
@@ -522,3 +502,59 @@ FCogDebugPlotEvent& FCogDebugPlot::PlotEventToggle(const UObject* WorldContextOb
return PlotEventStop(WorldContextObject, PlotName, EventId);
}
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogDebugPlot::OccupyGraphRow(const int32 InGraphIndex, const int32 InRow)
{
TMap<int32, int32>& GraphOccupation = OccupationMap.FindOrAdd(InGraphIndex);
if (int32* RowOccupation = GraphOccupation.Find(InRow))
{
(*RowOccupation)++;
}
else
{
GraphOccupation.Add(InRow, 1);
}
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogDebugPlot::FreeGraphRow(const int32 InGraphIndex, const int32 Row)
{
TMap<int32, int32>* GraphOccupation = OccupationMap.Find(InGraphIndex);
if (GraphOccupation == nullptr)
{ return; }
int32* RowOccupation = GraphOccupation->Find(Row);
if (RowOccupation == nullptr)
{ return; }
(*RowOccupation)--;
}
//--------------------------------------------------------------------------------------------------------------------------
int32 FCogDebugPlot::FindFreeGraphRow(const int32 InGraphIndex)
{
constexpr int32 MaxRows = 100;
int32 FreeRow = 0;
TMap<int32, int32>* GraphOccupation = OccupationMap.Find(InGraphIndex);
if (GraphOccupation == nullptr)
{
return FreeRow;
}
for (; FreeRow < MaxRows; ++FreeRow)
{
const int32* Occupation = GraphOccupation->Find(FreeRow);
if (Occupation == nullptr || *Occupation == 0)
{
break;
}
}
return FreeRow;
}
@@ -82,7 +82,7 @@ void ACogDebugReplicator::BeginPlay()
if (OwnerPlayerController != nullptr && OwnerPlayerController->IsLocalController())
{
Server_RequestAllCategoriesVerbosity();
Server_SetSelection(FCogDebug::GetSelection());
Server_SetSelection(FCogDebug::GetSelection(), FCogDebug::Settings.ReplicateSelection);
Server_SetIsFilteringBySelection(FCogDebug::GetIsFilteringBySelection());
}
}
@@ -210,12 +210,22 @@ void ACogDebugReplicator::Server_SetIsFilteringBySelection_Implementation(bool V
}
//--------------------------------------------------------------------------------------------------------------------------
void ACogDebugReplicator::Server_SetSelection_Implementation(AActor* Value)
void ACogDebugReplicator::Server_SetSelection_Implementation(AActor* Value, bool ForceSelection)
{
#if !UE_BUILD_SHIPPING
ServerSelection = Value;
if (ForceSelection)
{
FCogDebug::SetSelection(GetWorld(), Value);
}
else
{
FCogDebug::SetSelection(GetWorld(), nullptr);
}
#endif // !UE_BUILD_SHIPPING
}
@@ -596,6 +596,22 @@ FCogDebugShape FCogDebugShape::MakePolygon(const TArray<FVector>& Verts, const F
return NewElement;
}
//--------------------------------------------------------------------------------------------------------------------------
FCogDebugShape FCogDebugShape::MakeCollisionShape(const FCollisionShape& Shape, const FVector& Location, const FQuat& Rotation, const FVector& Extent, const FColor& Color, const float Thickness, const bool bPersistent, const uint8 DepthPriority)
{
switch (Shape.ShapeType)
{
case ECollisionShape::Box:
return MakeBox(Location, Rotation.Rotator(), Extent, Color, Thickness, bPersistent, DepthPriority);
case ECollisionShape::Capsule:
return MakeCapsule(Location, Rotation, Extent.X, Extent.Z, Color, Thickness, bPersistent, DepthPriority);
default:
case ECollisionShape::Sphere:
return MakeCapsule(Location, Rotation, Extent.X, 0.0f, Color, Thickness, bPersistent, DepthPriority);
}
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogDebugShape::DrawPolygon(const UWorld* World) const
{
+12 -3
View File
@@ -13,7 +13,7 @@ struct FCogDebugDrawOverlapParams;
struct FCogDebugDrawSweepParams;
UENUM()
enum ECogDebugRecolorMode : uint8
enum class ECogDebugRecolorMode : uint8
{
None,
Color,
@@ -29,6 +29,9 @@ struct FCogDebugSettings
UPROPERTY(Config)
bool bIsFilteringBySelection = true;
UPROPERTY(Config)
bool ReplicateSelection = true;
UPROPERTY(Config)
bool Persistent = false;
@@ -63,7 +66,7 @@ struct FCogDebugSettings
float AxesScale = 1.0f;
UPROPERTY(Config)
TEnumAsByte<ECogDebugRecolorMode> RecolorMode = None;
ECogDebugRecolorMode RecolorMode = ECogDebugRecolorMode::None;
UPROPERTY(Config)
float RecolorIntensity = 0.0f;
@@ -427,7 +430,13 @@ public:
private:
static int32 GetPieSessionId();
static void ReplicateSelection(const UWorld* World, AActor* Value);
static bool IsDebugActiveForObject_Internal(const UObject* WorldContextObject, const AActor* InSelection, bool InIsFilteringBySelection);
static TWeakObjectPtr<AActor> Selection;
static constexpr uint32 MaxPie = 16;
static TWeakObjectPtr<AActor> Selection[MaxPie];
};
@@ -5,6 +5,9 @@
class USkeletalMeshComponent;
struct FCogDebugShape;
struct FCollisionShape;
struct FHitResult;
struct FOverlapResult;
#if ENABLE_COG
@@ -52,7 +55,15 @@ struct COGDEBUG_API FCogDebugDraw
static void Skeleton(const FLogCategoryBase& LogCategory, const USkeletalMeshComponent* Skeleton, const FColor& Color, bool DrawSecondaryBones = false, const uint8 DepthPriority = 1);
static void LineTrace(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FVector& Start, const FVector& End, const bool HasHits, TArray<FHitResult>& HitResults, const FCogDebugDrawLineTraceParams& Settings);
static void Sweep(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FCollisionShape& Shape, const FVector& Start, const FVector& End, const FQuat& Rotation, const bool HasHits, TArray<FHitResult>& HitResults, const FCogDebugDrawSweepParams& Settings);
static void Overlap(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FCollisionShape& Shape, const FVector& Location, const FQuat& Rotation, TArray<FOverlapResult>& OverlapResults, const FCogDebugDrawOverlapParams& Settings);
static void ReplicateShape(const UObject* WorldContextObject, const FCogDebugShape& Shape);
static void ReplicateHitResults(const UObject* WorldContextObject, const TArray<FHitResult>& HitResults, const FCogDebugDrawLineTraceParams& Settings);
};
#endif //ENABLE_COG
@@ -17,9 +17,9 @@ struct FCogDebugDrawOverlapParams
FColor NoHitColor = FColor::Red;
bool DrawHitPrimitives = true;
bool DrawHitPrimitiveActorsName = false;
bool HitPrimitiveActorsNameShadow = true;
bool DrawHitPrimitiveActorsNameShadow = true;
float HitPrimitiveActorsNameSize = 1.0f;
bool Persistent = false;
mutable bool Persistent = false;
float LifeTime = 0.0f;
uint8 DepthPriority = 0;
float Thickness = 0.0f;
@@ -62,7 +62,7 @@ public:
static void DrawShape(const UWorld* World, const FCollisionShape& InShape, const FVector& Location, const FQuat& Rotation, const FVector& Scale, const FColor& Color, const bool Persistent, const float LifeTime, const uint8 DepthPriority, const float Thickness);
static void DrawPrimitiveComponent(const UPrimitiveComponent& PrimitiveComponent, const FColor& Color, const bool Persistent, const float LifeTime, const uint8 DepthPriority, const float Thickness);
static void DrawPrimitiveComponent(const UPrimitiveComponent& PrimitiveComponent, const int32 BodyIndex, const FColor& Color, const bool Persistent, const float LifeTime, const uint8 DepthPriority, const float Thickness, const bool DrawName = true, const bool DrawNameShadow = true, const float DrawNameSize = 1.0f);
static void DrawOverlap(const UWorld* World, const FCollisionShape& Shape, const FVector& Location, const FQuat& Rotation, TArray<FOverlapResult>& OverlapResults, const FCogDebugDrawOverlapParams& Settings);
@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
#include "imgui.h"
#include "UObject/Class.h"
class COGDEBUG_API FCogDebugHelper
{
@@ -49,38 +49,56 @@ struct COGDEBUG_API FCogDebugPlotEvent
//--------------------------------------------------------------------------------------------------------------------------
struct COGDEBUG_API FCogDebugPlotEntry
{
void AssignAxis(int32 AssignedRow, ImAxis CurrentYAxis);
void AddPoint(float X, float Y);
void AssignGraphAndAxis(int32 AssignedRow, ImAxis CurrentYAxis);
void AddPoint(float X, float Y);
bool FindValue(float Time, float& Value) const;
void ResetAxis();
void Clear();
FCogDebugPlotEvent& AddEvent(const FCogDebugPlotEntry& OwnwePlot, FString OwnerName, bool IsInstant, const FName EventId, const int32 Row, const FColor& Color);
FCogDebugPlotEvent& StopEvent(const FName EventId);
void UpdateTime(const UWorld* World);
int32 FindFreeRow() const;
FCogDebugPlotEvent* GetLastEvent();
FCogDebugPlotEvent* FindLastEventByName(FName EventId);
void ResetGraphAndAxis();
void Clear();
FCogDebugPlotEvent& AddEvent(const FString& OwnerName, bool IsInstant, const FName EventId, const int32 Row, const FColor& Color);
FCogDebugPlotEvent& StopEvent(const FName EventId);
FCogDebugPlotEvent* GetLastEvent();
FCogDebugPlotEvent* FindLastEventByName(FName EventId);
FName Name;
bool IsEventPlot = false;
int32 CurrentRow = INDEX_NONE;
ImAxis CurrentYAxis = ImAxis_COUNT;
float Time = 0;
uint64 Frame = 0;
bool IsEventPlot = false;
int32 GraphIndex = INDEX_NONE;
ImAxis YAxis = ImAxis_COUNT;
float Time = 0;
uint64 Frame = 0;
TWeakObjectPtr<const UWorld> World;
//--------------------------
// Values
//--------------------------
int32 ValueOffset = 0;
ImVector<ImVec2> Values;
bool ShowValuesMarkers = false;
ImVector<ImVec2> Values;
bool ShowValuesMarkers = false;
//--------------------------
// Events
//--------------------------
int32 EventOffset = 0;
TArray<FCogDebugPlotEvent> Events;
int32 MaxRow = 1;
TArray<FCogDebugPlotEvent> Events;
int32 MaxRow = 1;
};
//--------------------------------------------------------------------------------------------------------------------------
@@ -88,35 +106,59 @@ struct COGDEBUG_API FCogDebugPlotEntry
class COGDEBUG_API FCogDebugPlot
{
public:
static const int32 AutoRow = -1;
static constexpr int32 AutoRow = -1;
static void PlotValue(const UObject* WorldContextObject, const FName PlotName, const float Value);
static FCogDebugPlotEvent& PlotEvent(const UObject* WorldContextObject, const FName PlotName, const FName EventId, bool IsInstant, const int32 Row = AutoRow, const FColor& Color = FColor::Transparent);
static FCogDebugPlotEvent& PlotEventInstant(const UObject* WorldContextObject, const FName PlotName, const FName EventId, const int32 Row = AutoRow, const FColor& Color = FColor::Transparent);
static FCogDebugPlotEvent& PlotEventStart(const UObject* WorldContextObject, const FName PlotName, const FName EventId, const int32 Row = AutoRow, const FColor& Color = FColor::Transparent);
static FCogDebugPlotEvent& PlotEventStop(const UObject* WorldContextObject, const FName PlotName, const FName EventId);
static FCogDebugPlotEvent& PlotEventToggle(const UObject* WorldContextObject, const FName PlotName, const FName EventId, const bool ToggleValue, const int32 Row = AutoRow, const FColor& Color = FColor::Transparent);
static void Reset();
static void Clear();
static FCogDebugPlotEntry* FindPlot(const FName Name);
static FCogDebugPlotEntry* FindEntry(const FName Name);
static FCogDebugPlotEntry* FindEntry(bool IsEvent, const FName Name);
static TArray<FCogDebugPlotEntry> Plots;
static TArray<FCogDebugPlotEntry> Events;
static bool IsVisible;
static bool Pause;
private:
friend struct FCogDebugPlotEntry;
static void ResetLastAddedEvent();
static FCogDebugPlotEntry* RegisterPlot(const UObject* Owner, const FName PlotName, bool IsEventPlot);
FCogDebugPlotEventParams* PlotEventAddParam(const FName Name);
static FCogDebugPlotEntry* RegisterPlot(const UObject* Owner, const FName PlotName, bool IsEventPlot);
static FCogDebugPlotEvent* GetLastAddedEvent();
static void OccupyGraphRow(const int32 InGraphIndex, const int32 InRow);
static void FreeGraphRow(const int32 InGraphIndex, const int32 InRow);
static int32 FindFreeGraphRow(const int32 InGraphIndex);
static FName LastAddedEventPlotName;
static int32 LastAddedEventIndex;
static int32 LastAddedEventIndex;
static FCogDebugPlotEvent DefaultEvent;
// graph index to row index to number of objects occupying the row
static TMap<int32, TMap<int32, int32>> OccupationMap;
};
#endif //ENABLE_COG
@@ -88,7 +88,7 @@ public:
AActor* GetServerSelection() const { return ServerSelection.Get(); }
UFUNCTION(Server, Reliable)
void Server_SetSelection(AActor* Value);
void Server_SetSelection(AActor* Value, bool ForceSelection);
bool IsServerFilteringBySelection() const { return bIsServerFilteringBySelection; }
@@ -4,6 +4,8 @@
DECLARE_LOG_CATEGORY_EXTERN(LogCogServerVLOG, Verbose, All);
struct FCollisionShape;
enum class ECogDebugShape : uint8
{
Invalid,
@@ -59,6 +61,7 @@ struct COGDEBUG_API FCogDebugShape
static FCogDebugShape MakeCapsule(const FVector& Center, const FQuat& Rotation, const float Radius, const float HalfHeight, const FColor& Color, const float Thickness, const bool bPersistent, const uint8 DepthPriority);
static FCogDebugShape MakeFlatCapsule(const FVector2D& Start, const FVector2D& End, const float Radius, const float Z, const FColor& Color, const float Thickness, const bool bPersistent, const uint8 DepthPriority);
static FCogDebugShape MakePolygon(const TArray<FVector>& Verts, const FColor& Color, const bool bPersistent, const uint8 DepthPriority);
static FCogDebugShape MakeCollisionShape(const FCollisionShape& Shape, const FVector& Location, const FQuat& Rotation, const FVector& Extent, const FColor& Color, const float Thickness, const bool bPersistent, const uint8 DepthPriority);
void DrawPoint(const UWorld* World) const;
void DrawSegment(const UWorld* World) const;
@@ -21,7 +21,7 @@ private:
EAssetTypeCategories::Type AssetCategory;
};
IMPLEMENT_MODULE(FCogDebugEditorModule, CogEditor);
IMPLEMENT_MODULE(FCogDebugEditorModule, CogDebugEditor);
//----------------------------------------------------------------------------------------------------------------------
void FCogDebugEditorModule::StartupModule()
@@ -0,0 +1,18 @@
#include "CogEngineDataAsset.h"
//--------------------------------------------------------------------------------------------------------------------------
void UCogEngineCheat_Execution::Execute_Implementation(const AActor* Instigator, const TArray<AActor*>& Targets) const
{
}
//--------------------------------------------------------------------------------------------------------------------------
ECogEngineCheat_ActiveState UCogEngineCheat_Execution::IsActiveOnTargets_Implementation(const TArray<AActor*>& Targets) const
{
return ECogEngineCheat_ActiveState::Inactive;
}
//--------------------------------------------------------------------------------------------------------------------------
bool UCogEngineCheat_Execution::GetColor(const FCogWindow& InCallingWindow, FLinearColor& OutColor) const
{
return false;
}
@@ -18,19 +18,7 @@ void FCogEngineHelper::ActorContextMenu(AActor& Actor)
FCogWindowWidgets::ThinSeparatorText("Object");
#if WITH_EDITOR
const UObject* DefaultObject = Actor.GetClass()->GetDefaultObject();
if (DefaultObject == nullptr)
{
ImGui::BeginDisabled();
}
if (ImGui::Button("Browse To Asset", ImVec2(-1, 0)))
{
IAssetTools::Get().SyncBrowserToAssets({ DefaultObject });
}
if (DefaultObject == nullptr)
{
ImGui::EndDisabled();
}
FCogWindowWidgets::OpenObjectAssetButton(&Actor, ImVec2(-1, 0));
#endif
if (ImGui::Button("Delete", ImVec2(-1, 0)))
@@ -220,3 +220,26 @@ void ACogEngineReplicator::Server_DeleteActor_Implementation(AActor* Actor)
#endif // !UE_BUILD_SHIPPING
}
//--------------------------------------------------------------------------------------------------------------------------
void ACogEngineReplicator::Server_ApplyCheat_Implementation(const AActor* CheatInstigator, const TArray<AActor*>& Targets, const FCogEngineCheat& Cheat) const
{
if (Cheat.Execution == nullptr)
{
return;
}
Cheat.Execution->Execute(CheatInstigator, Targets);
}
//--------------------------------------------------------------------------------------------------------------------------
ECogEngineCheat_ActiveState ACogEngineReplicator::IsCheatActiveOnTargets(const TArray<AActor*>& Targets, const FCogEngineCheat& Cheat)
{
if (Cheat.Execution == nullptr)
{
return ECogEngineCheat_ActiveState::Inactive;
}
return Cheat.Execution->IsActiveOnTargets(Targets);
}
@@ -0,0 +1,467 @@
#include "CogEngineWindow_Cheats.h"
#include "CogEngineDataAsset.h"
#include "CogEngineReplicator.h"
#include "CogCommonAllegianceActorInterface.h"
#include "CogImguiHelper.h"
#include "CogWindowConsoleCommandManager.h"
#include "CogWindowWidgets.h"
#include "EngineUtils.h"
#include "GameFramework/Character.h"
#include "imgui.h"
#include "imgui_internal.h"
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Cheats::RenderHelp()
{
ImGui::Text(
"This window can be used to apply cheats to the selected actor (by default). "
"The cheats can be configured in the '%s' data asset. "
"When clicking a cheat button, press:\n"
" [CTRL] to apply the cheat to controlled actor\n"
" [ALT] to apply the cheat to the allies of the selected actor\n"
" [SHIFT] to apply the cheat to the enemies of the selected actor\n"
, TCHAR_TO_ANSI(*GetNameSafe(Asset.Get()))
);
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Cheats::Initialize()
{
Super::Initialize();
bHasMenu = true;
Asset = GetAsset<UCogEngineDataAsset>();
Config = GetConfig<UCogEngineConfig_Cheats>();
FCogWindowConsoleCommandManager::RegisterWorldConsoleCommand(
TEXT("Cog.Cheat"),
TEXT("Apply a cheat to the selection. Cog.Cheat <CheatName> -Allies -Enemies -Controlled"),
GetWorld(),
FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray<FString>& InArgs, UWorld* InWorld)
{
if (InArgs.Num() > 0)
{
if (const FCogEngineCheat* cheat = FindCheatByName(InArgs[0], false))
{
const bool ApplyToEnemies = InArgs.Contains("-Enemies");
const bool ApplyToAllies = InArgs.Contains("-Allies");
const bool ApplyToControlled = InArgs.Contains("-Controlled");
RequestCheat(GetLocalPlayerPawn(), GetSelection(), *cheat, ApplyToEnemies, ApplyToAllies, ApplyToControlled);
}
else
{
UE_LOG(LogCogImGui, Warning, TEXT("Cog.Cheat %s | Cheat not found"), *InArgs[0]);
}
}
}));
if (Asset == nullptr)
{ return; }
for (const FCogEngineCheatCategory& CheatCategory : Asset->CheatCategories)
{
for (const FCogEngineCheat& Cheat : CheatCategory.PersistentEffects)
{
UpdateCheatColor(Cheat);
}
for (const FCogEngineCheat& Cheat : CheatCategory.InstantEffects)
{
UpdateCheatColor(Cheat);
}
}
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Cheats::UpdateCheatColor(const FCogEngineCheat& Cheat) const
{
FLinearColor CustomColor;
if (Cheat.Execution != nullptr && Cheat.Execution->GetColor(*this, CustomColor))
{
Cheat.CustomColor = CustomColor;
}
else
{
Cheat.CustomColor = Cheat.Color;
}
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Cheats::ResetConfig()
{
Super::ResetConfig();
Config->Reset();
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Cheats::GameTick(float DeltaTime)
{
Super::GameTick(DeltaTime);
TryReapplyCheats();
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Cheats::TryReapplyCheats()
{
if (Config == nullptr)
{ return; }
if (bHasReappliedCheats)
{ return; }
if (Config->bReapplyCheatsBetweenPlays == false)
{ return; }
static int32 IsFirstLaunch = true;
if (IsFirstLaunch && Config->bReapplyCheatsBetweenLaunches == false)
{ return; }
IsFirstLaunch = false;
if (Asset == nullptr)
{ return; }
APawn* ControlledActor = GetLocalPlayerPawn();
if (ControlledActor == nullptr)
{ return; }
const ACogEngineReplicator* Replicator = ACogEngineReplicator::GetLocalReplicator(*GetWorld());
if (Replicator == nullptr)
{ return; }
TArray<AActor*> Targets { ControlledActor };
for (int32 i = Config->AppliedCheats.Num() - 1; i >= 0; i--)
{
if (const FCogEngineCheat* Cheat = FindCheatByName(Config->AppliedCheats[i], true))
{
Replicator->Server_ApplyCheat(ControlledActor, Targets, *Cheat);
}
else
{
//-----------------------------------------------------
// This cheat doesn't exist anymore. We can remove it.
//-----------------------------------------------------
Config->AppliedCheats.RemoveAt(i);
}
}
bHasReappliedCheats = true;
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogEngineWindow_Cheats::DrawTable()
{
const bool Open = ImGui::BeginTable("Cheats", 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_NoBordersInBodyUntilResize);
ImGui::TableSetupColumn("Toggle", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Instant", ImGuiTableColumnFlags_WidthStretch);
return Open;
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Cheats::RenderContent()
{
Super::RenderContent();
if (Config == nullptr)
{
ImGui::TextDisabled("Invalid Config");
return;
}
AActor* SelectedActor = GetSelection();
if (SelectedActor == nullptr)
{
ImGui::TextDisabled("Invalid Selection");
return;
}
if (Asset == nullptr)
{
ImGui::TextDisabled("Invalid Asset");
return;
}
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("Options"))
{
ImGui::Checkbox("Group By Category", &Config->bGroupByCategories);
ImGui::Checkbox("Use Two Columns", &Config->bUseTwoColumns);
ImGui::Separator();
ImGui::Checkbox("Reapply Cheats Between Plays", &Config->bReapplyCheatsBetweenPlays);
if (Config->bReapplyCheatsBetweenPlays == false)
{
ImGui::BeginDisabled();
}
ImGui::Checkbox("Reapply Cheats Between Launches", &Config->bReapplyCheatsBetweenLaunches);
if (Config->bReapplyCheatsBetweenPlays == false)
{
ImGui::EndDisabled();
}
ImGui::Separator();
if (ImGui::MenuItem("Reset Settings"))
{
ResetConfig();
}
ImGui::EndMenu();
}
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 7);
if (ImGui::BeginMenu("Filters"))
{
for (const FCogEngineCheatCategory& CheatCategory : Asset->CheatCategories)
{
const auto CategoryStr = StringCast<ANSICHAR>(*CheatCategory.Name);
bool IsSelected = Config->SelectedCategories.Contains(CheatCategory.Name);
if (ImGui::Checkbox(CategoryStr.Get(), &IsSelected))
{
if (IsSelected)
{
Config->SelectedCategories.Add(CheatCategory.Name);
}
else
{
Config->SelectedCategories.Remove(CheatCategory.Name);
}
}
}
ImGui::EndMenu();
}
FCogWindowWidgets::SearchBar(Filter);
ImGui::EndMenuBar();
}
APawn* ControlledActor = GetLocalPlayerPawn();
bool OpenTable = false;
if (Config->bGroupByCategories == false && Config->bUseTwoColumns)
{
OpenTable = DrawTable();
}
for (const FCogEngineCheatCategory& CheatCategory : Asset->CheatCategories)
{
const auto CategoryStr = StringCast<ANSICHAR>(*CheatCategory.Name);
if (Config->SelectedCategories.Num() != 0 && Config->SelectedCategories.Contains(CheatCategory.Name) == false)
{ continue; }
bool Open = true;
if (Config->bGroupByCategories)
{
Open = FCogWindowWidgets::DarkCollapsingHeader(CategoryStr.Get(), ImGuiTreeNodeFlags_DefaultOpen);
if (Open && Config->bUseTwoColumns)
{
DrawTable();
}
}
if (Open)
{
if (Config->bUseTwoColumns)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
}
int Index = 0;
for (const FCogEngineCheat& Cheat : CheatCategory.PersistentEffects)
{
AddCheat(Index, ControlledActor, SelectedActor, Cheat, true);
Index++;
}
//----------------------------------------------------------------------------
// Update the config of applied cheat to reapply them on the next launch.
// We do not update them only when the user input is pressed because
// the state of the cheat is lagging when connected to a server.
// So we check if the array should be updated all the time.
//----------------------------------------------------------------------------
if (SelectedActor == ControlledActor)
{
for (const FCogEngineCheat& Cheat : CheatCategory.PersistentEffects)
{
TArray<AActor*> Targets = { SelectedActor };
if (ACogEngineReplicator::IsCheatActiveOnTargets(Targets, Cheat) == ECogEngineCheat_ActiveState::Active)
{
Config->AppliedCheats.AddUnique(Cheat.Name);
}
else
{
Config->AppliedCheats.Remove(Cheat.Name);
}
}
}
ImGui::TableNextColumn();
Index = 0;
for (const FCogEngineCheat& Cheat : CheatCategory.InstantEffects)
{
AddCheat(Index, ControlledActor, SelectedActor, Cheat, false);
Index++;
}
if (Config->bGroupByCategories && Config->bUseTwoColumns)
{
ImGui::EndTable();
}
}
}
if (OpenTable)
{
ImGui::EndTable();
}
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogEngineWindow_Cheats::AddCheat(const int32 Index, AActor* ControlledActor, AActor* SelectedActor, const FCogEngineCheat& Cheat, bool IsPersistent)
{
const auto CheatName = StringCast<ANSICHAR>(*Cheat.Name);
if (Filter.PassFilter(CheatName.Get()) == false)
{ return false; }
ImGui::PushID(Index);
FCogWindowWidgets::PushBackColor(FCogImguiHelper::ToImVec4(Cheat.CustomColor));
const bool IsShiftDown = (ImGui::GetCurrentContext()->IO.KeyMods & ImGuiMod_Shift) != 0;
const bool IsAltDown = (ImGui::GetCurrentContext()->IO.KeyMods & ImGuiMod_Alt) != 0;
const bool IsControlDown = (ImGui::GetCurrentContext()->IO.KeyMods & ImGuiMod_Ctrl) != 0;
bool bIsPressed = false;
if (IsPersistent)
{
TArray<AActor*> Targets = { SelectedActor };
bool isEnabled = ACogEngineReplicator::IsCheatActiveOnTargets(Targets, Cheat) == ECogEngineCheat_ActiveState::Active;
if (ImGui::Checkbox(CheatName.Get(), &isEnabled))
{
RequestCheat(ControlledActor, SelectedActor, Cheat, IsShiftDown, IsAltDown, IsControlDown);
bIsPressed = true;
}
}
else
{
if (ImGui::Button(CheatName.Get(), ImVec2(-1, 0)))
{
RequestCheat(ControlledActor, SelectedActor, Cheat, IsShiftDown, IsAltDown, IsControlDown);
bIsPressed = true;
}
}
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, IsShiftDown || IsAltDown || IsControlDown ? 0.5f : 1.0f), "On Selection");
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, IsShiftDown ? 1.0f : 0.5f), "On Enemies [SHIFT]");
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, IsAltDown ? 1.0f : 0.5f), "On Allies [ALT]");
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, IsControlDown ? 1.0f : 0.5f), "On Controlled [CTRL]");
ImGui::EndTooltip();
}
FCogWindowWidgets::PopBackColor();
ImGui::PopID();
return bIsPressed;
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Cheats::RequestCheat(AActor* ControlledActor, AActor* SelectedActor, const FCogEngineCheat& Cheat, bool ApplyToEnemies, bool ApplyToAllies, bool ApplyToControlled)
{
TArray<AActor*> Actors;
if (ApplyToControlled)
{
Actors.Add(ControlledActor);
}
if (ApplyToEnemies || ApplyToAllies)
{
for (TActorIterator<ACharacter> It(GetWorld(), ACharacter::StaticClass()); It; ++It)
{
if (AActor* OtherActor = *It)
{
ECogCommonAllegiance Allegiance = ECogCommonAllegiance::Enemy;
if (ICogCommonAllegianceActorInterface* AllegianceInterface = Cast<ICogCommonAllegianceActorInterface>(OtherActor))
{
Allegiance = AllegianceInterface->GetAllegianceWithOtherActor(ControlledActor);
}
if ((ApplyToEnemies && (Allegiance == ECogCommonAllegiance::Enemy))
|| (ApplyToAllies && (Allegiance == ECogCommonAllegiance::Friendly)))
{
Actors.Add(OtherActor);
}
}
}
}
if ((ApplyToControlled || ApplyToEnemies || ApplyToAllies) == false)
{
Actors.Add(SelectedActor);
}
if (ACogEngineReplicator* Replicator = ACogEngineReplicator::GetLocalReplicator(*GetWorld()))
{
Replicator->Server_ApplyCheat(ControlledActor, Actors, Cheat);
}
else
{
UE_LOG(LogCogImGui, Warning, TEXT("FCogAbilityWindow_Cheats::RequestCheat | Replicator not found"));
}
}
//--------------------------------------------------------------------------------------------------------------------------
const FCogEngineCheat* FCogEngineWindow_Cheats::FindCheatByName(const FString& CheatName, const bool OnlyPersistentCheats)
{
for (const FCogEngineCheatCategory& CheatCategory : Asset->CheatCategories)
{
for (const FCogEngineCheat& Cheat : CheatCategory.PersistentEffects)
{
if (Cheat.Name == CheatName)
{
return &Cheat;
}
}
if (OnlyPersistentCheats)
{
continue;
}
for (const FCogEngineCheat& Cheat : CheatCategory.InstantEffects)
{
if (Cheat.Name == CheatName)
{
return &Cheat;
}
}
}
return nullptr;
}
@@ -84,7 +84,11 @@ void FCogEngineWindow_CollisionTester::RenderContent()
}
ImGui::SetNextItemWidth(-1);
FCogWindowWidgets::MenuActorsCombo("CollisionTesters", *GetWorld(), ACogEngineCollisionTester::StaticClass());
AActor* NewSelection = nullptr;
if (FCogWindowWidgets::MenuActorsCombo("CollisionTesters", NewSelection, *GetWorld(), ACogEngineCollisionTester::StaticClass()))
{
FCogDebug::SetSelection(GetWorld(), NewSelection);
}
ImGui::EndMenuBar();
}
@@ -80,98 +80,56 @@ void FCogEngineWindow_DebugSettings::RenderContent()
{
FCogDebug::SetIsFilteringBySelection(GetWorld(), Config->Data.bIsFilteringBySelection);
}
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("If checked, only show the debug of the currently selected actor. Otherwise show the debug of all actors.");
}
ImGui::SetItemTooltip("If checked, only show the debug of the currently selected actor. Otherwise show the debug of all actors.");
ImGui::Checkbox("Persistent", &Settings.Persistent);
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("Make debug draw always persist");
}
ImGui::Checkbox("Persistent", &Settings.Persistent);
ImGui::SetItemTooltip("Make debug draw always persist");
ImGui::Checkbox("Actor Name Use Label", &Settings.ActorNameUseLabel);
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("Use the actor label when displaying the actor name.");
}
ImGui::Checkbox("Actor Name Use Label", &Settings.ActorNameUseLabel);
ImGui::SetItemTooltip("Use the actor label when displaying the actor name.");
ImGui::Checkbox("Text Shadow", &Settings.TextShadow);
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("Show a shadow below debug text.");
}
ImGui::Checkbox("Text Shadow", &Settings.TextShadow);
ImGui::SetItemTooltip("Show a shadow below debug text.");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::Checkbox("Fade 2D", &Settings.Fade2D);
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("Does the 2D debug is fading out.");
}
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::Checkbox("Fade 2D", &Settings.Fade2D);
ImGui::SetItemTooltip("Does the 2D debug is fading out.");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Duration", &Settings.Duration, 0.01f, 0.0f, 100.0f, "%.1f");
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("The duration of debug elements.");
}
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Duration", &Settings.Duration, 0.01f, 0.0f, 100.0f, "%.1f");
ImGui::SetItemTooltip("The duration of debug elements.");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Thickness", &Settings.Thickness, 0.05f, 0.0f, 5.0f, "%.1f");
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("The thickness of debug lines.");
}
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Thickness", &Settings.Thickness, 0.05f, 0.0f, 5.0f, "%.1f");
ImGui::SetItemTooltip("The thickness of debug lines.");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Server Thickness", &Settings.ServerThickness, 0.05f, 0.0f, 5.0f, "%.1f");
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("The thickness the server debug lines.");
}
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Server Thickness", &Settings.ServerThickness, 0.05f, 0.0f, 5.0f, "%.1f");
ImGui::SetItemTooltip("The thickness the server debug lines.");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Server Color Mult", &Settings.ServerColorMultiplier, 0.01f, 0.0f, 1.0f, "%.1f");
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("The color multiplier applied to the server debug lines.");
}
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Server Color Mult", &Settings.ServerColorMultiplier, 0.01f, 0.0f, 1.0f, "%.1f");
ImGui::SetItemTooltip("The color multiplier applied to the server debug lines.");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragInt("Depth Priority", &Settings.DepthPriority, 0.1f, 0, 100);
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("The depth priority of debug elements.");
}
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragInt("Depth Priority", &Settings.DepthPriority, 0.1f, 0, 100);
ImGui::SetItemTooltip("The depth priority of debug elements.");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragInt("Segments", &Settings.Segments, 0.1f, 4, 20.0f);
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("The number of segments used for circular shapes.");
}
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragInt("Segments", &Settings.Segments, 0.1f, 4, 20.0f);
ImGui::SetItemTooltip("The number of segments used for circular shapes.");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Axes Scale", &Settings.AxesScale, 0.1f, 0, 10.0f, "%.1f");
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("The scaling debug axis.");
}
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Axes Scale", &Settings.AxesScale, 0.1f, 0, 10.0f, "%.1f");
ImGui::SetItemTooltip("The scaling debug axis.");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Arrow Size", &Settings.ArrowSize, 1.0f, 0.0f, 200.0f, "%.0f");
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("The size of debug arrows.");
}
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Arrow Size", &Settings.ArrowSize, 1.0f, 0.0f, 200.0f, "%.0f");
ImGui::SetItemTooltip("The size of debug arrows.");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Text Size", &Settings.TextSize, 0.1f, 0.1f, 5.0f, "%.1f");
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("The size of the debug texts.");
}
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Text Size", &Settings.TextSize, 0.1f, 0.1f, 5.0f, "%.1f");
ImGui::SetItemTooltip("The size of the debug texts.");
}
if (ImGui::CollapsingHeader("Recolor", ImGuiTreeNodeFlags_DefaultOpen))
@@ -183,42 +141,30 @@ void FCogEngineWindow_DebugSettings::RenderContent()
{
Settings.RecolorMode = Mode;
}
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("How the debug element should be recolored.");
}
ImGui::SetItemTooltip("How the debug element should be recolored.");
if (Settings.RecolorMode != ECogDebugRecolorMode::None)
{
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Recolor Intensity", &Settings.RecolorIntensity, 0.01f, 0.0f, 1.0f, "%.2f");
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("How much the debug elements color should be changed.");
}
ImGui::SetItemTooltip("How much the debug elements color should be changed.");
}
if (Settings.RecolorMode == ECogDebugRecolorMode::Color)
{
FCogImguiHelper::ColorEdit4("Recolor Color", Settings.RecolorColor, ColorEditFlags);
}
else if (Settings.RecolorMode == HueOverTime)
else if (Settings.RecolorMode == ECogDebugRecolorMode::HueOverTime)
{
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Recolor Speed", &Settings.RecolorTimeSpeed, 0.1f, 0.0f, 10.0f, "%.1f");
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("The speed of the recolor.");
}
ImGui::SetItemTooltip("The speed of the recolor.");
}
else if (Settings.RecolorMode == ECogDebugRecolorMode::HueOverFrames)
{
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragInt("Recolor Cycle", &Settings.RecolorFrameCycle, 1, 2, 100);
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("How many frames are used to perform a full hue cycle.");
}
ImGui::SetItemTooltip("How many frames are used to perform a full hue cycle.");
}
}
@@ -230,10 +176,7 @@ void FCogEngineWindow_DebugSettings::RenderContent()
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Gizmo Scale", &Settings.GizmoScale, 0.1f, 0.1f, 10.0f, "%.1f");
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
{
ImGui::SetTooltip("The scale of the gizmo.");
}
ImGui::SetItemTooltip("The scale of the gizmo.");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragInt("Z Low", &Settings.GizmoZLow, 0.5f, 0, 1000);
@@ -384,23 +327,47 @@ void FCogEngineWindow_DebugSettings::RenderContent()
if (ImGui::CollapsingHeader("Collision Query"))
{
ImGui::Checkbox("Draw Hit Primitives", &Settings.CollisionQueryDrawHitPrimitives);
ImGui::Checkbox("Draw Hit Primitive Actors Name", &Settings.CollisionQueryDrawHitPrimitiveActorsName);
ImGui::Checkbox("Prim Hit Primitive Actors Name Shadow", &Settings.CollisionQueryHitPrimitiveActorsNameShadow);
ImGui::Checkbox("Draw Hit Shapes", &Settings.CollisionQueryDrawHitShapes);
ImGui::SetItemTooltip("Draw the shape of the primitives that have been hit.");
ImGui::Checkbox("Draw Hit Primitive Actors Name", &Settings.CollisionQueryDrawHitPrimitiveActorsName);
ImGui::SetItemTooltip("Draw the actor name of the primitives that have been hit.");
ImGui::Checkbox("Prim Hit Primitive Actors Name Shadow", &Settings.CollisionQueryHitPrimitiveActorsNameShadow);
ImGui::SetItemTooltip("Draw the actor name shadow of the primitives that have been hit.");
ImGui::Checkbox("Draw Hit Shapes", &Settings.CollisionQueryDrawHitShapes);
ImGui::SetItemTooltip("Draw the sweep shape at every impact location.");
ImGui::Checkbox("Draw Hit Location", &Settings.CollisionQueryDrawHitLocation);
ImGui::SetItemTooltip("Draw the location of hit results.");
ImGui::Checkbox("Draw Hit Impact Points", &Settings.CollisionQueryDrawHitImpactPoints);
ImGui::SetItemTooltip("Draw the impact point of hit results.");
ImGui::Checkbox("Draw Hit Normals", &Settings.CollisionQueryDrawHitNormals);
ImGui::SetItemTooltip("Draw the hit normal of hit results.");
ImGui::Checkbox("Draw Hit Impact Normals", &Settings.CollisionQueryDrawHitImpactNormals);
ImGui::SetItemTooltip("Draw the hit impact normal of hit results.");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Primitive Actors Name Size", &Settings.CollisionQueryHitPrimitiveActorsNameSize);
ImGui::DragFloat("Primitive Actors Name Size", &Settings.CollisionQueryHitPrimitiveActorsNameSize, 0.1f, 0.5f, 10.0f, "%0.1f");
ImGui::SetItemTooltip("Size of the actor name of the primitives that have been hit.");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::DragFloat("Hit Point Size", &Settings.CollisionQueryHitPointSize);
ImGui::DragFloat("Hit Point Size", &Settings.CollisionQueryHitPointSize, 0.5f, 0.0f, 100.0f, "%0.1f");
ImGui::SetItemTooltip("Size of the hit result location and impact point.");
FCogImguiHelper::ColorEdit4("Hit Color", Settings.CollisionQueryHitColor, ColorEditFlags);
ImGui::SetItemTooltip("Color of the collision query when the query has hit.");
FCogImguiHelper::ColorEdit4("No Hit Color", Settings.CollisionQueryNoHitColor, ColorEditFlags);
ImGui::SetItemTooltip("Color of the collision query when the query has not hit.");
FCogImguiHelper::ColorEdit4("Normal Color", Settings.CollisionQueryNormalColor, ColorEditFlags);
ImGui::SetItemTooltip("Color of the hit result normal color.");
FCogImguiHelper::ColorEdit4("Impact Normal Color", Settings.CollisionQueryImpactNormalColor, ColorEditFlags);
ImGui::SetItemTooltip("Color of the hit result impact normal color.");
}
}
@@ -205,6 +205,7 @@ void FCogEngineWindow_Inspector::RenderMenu()
ImGui::CloseCurrentPopup();
}
ImGui::PushID("Favorites");
for (Favorite& Favorite : Favorites)
{
const TWeakObjectPtr<UObject>& Object = Favorite.Object;
@@ -214,21 +215,26 @@ void FCogEngineWindow_Inspector::RenderMenu()
ImGui::CloseCurrentPopup();
}
}
ImGui::PopID();
//-----------------------------------
// HISTORY
//-----------------------------------
ImGui::Spacing();
ImGui::SeparatorText("HISTORY");
ImGui::PushID("History");
for (int32 i = History.Num() - 1; i >= 0; i--)
{
ImGui::PushID(i);
const TWeakObjectPtr<const UObject>& Object = History[i];
if (ImGui::MenuItem(TCHAR_TO_ANSI(*GetNameSafe(Object.Get())), nullptr, i == HistoryIndex))
{
NewHistoryIndex = i;
ImGui::CloseCurrentPopup();
}
ImGui::PopID();
}
ImGui::PopID();
ImGui::EndChild();
ImGui::EndPopup();
@@ -954,12 +960,8 @@ bool FCogEngineWindow_Inspector::HasPropertyAnyChildren(const FProperty* Propert
{
if (const FStructProperty* StructProperty = CastField<FStructProperty>(Property))
{
for (TFieldIterator<FProperty> It(StructProperty->Struct); It; ++It)
{
return true;
}
return false;
const TFieldIterator<FProperty> It(StructProperty->Struct);
return It ? true : false;
}
else if (const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property))
{
@@ -0,0 +1,402 @@
#include "CogEngineWindow_NetImGui.h"
#include "CogImguiContext.h"
#include "CogImguiHelper.h"
#include "CogWindowConsoleCommandManager.h"
#include "CogWindowManager.h"
#include "CogWindowWidgets.h"
#include "Engine/EngineBaseTypes.h"
#include "Engine/World.h"
#include "imgui.h"
#include "Misc/CoreMisc.h"
#include "Misc/Paths.h"
#include "NetImgui_Api.h"
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_NetImgui::Initialize()
{
Super::Initialize();
Config = GetConfig<UCogEngineWindowConfig_NetImgui>();
FCogWindowConsoleCommandManager::RegisterWorldConsoleCommand(
TEXT("Cog.NetImgui.Connect"),
TEXT("Connect to NetImgui server"),
GetWorld(),
FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray<FString>& InArgs, UWorld* InWorld)
{
ConnectTo();
}));
FCogWindowConsoleCommandManager::RegisterWorldConsoleCommand(
TEXT("Cog.NetImgui.Listen"),
TEXT("Listen for NetImgui server connection"),
GetWorld(),
FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray<FString>& InArgs, UWorld* InWorld)
{
ConnectFrom();
}));
FCogWindowConsoleCommandManager::RegisterWorldConsoleCommand(
TEXT("Cog.NetImgui.Disconnect"),
TEXT("Disconnect from NetImgui server"),
GetWorld(),
FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray<FString>& InArgs, UWorld* InWorld)
{
Disconnect();
}));
FCogWindowConsoleCommandManager::RegisterWorldConsoleCommand(
TEXT("Cog.NetImgui.RunServer"),
TEXT("Run NetImgui server application"),
GetWorld(),
FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray<FString>& InArgs, UWorld* InWorld)
{
RunServer();
}));
FCogWindowConsoleCommandManager::RegisterWorldConsoleCommand(
TEXT("Cog.NetImgui.CloseServer"),
TEXT("Close NetImgui server application"),
GetWorld(),
FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray<FString>& InArgs, UWorld* InWorld)
{
CloseServer();
}));
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_NetImgui::Shutdown()
{
if (NetImgui::IsConnected() || NetImgui::IsConnectionPending())
{
NetImgui::Disconnect();
}
CloseServer();
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_NetImgui::ResetConfig()
{
Super::ResetConfig();
Config->Reset();
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_NetImgui::RenderHelp()
{
ImGui::Text("This window manage the connection to the NetImgui server."
"See https://github.com/sammyfreg/netImgui for more info.");
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_NetImgui::RenderTick(float DeltaTime)
{
//------------------------------------------------------
// Before auto connecting, wait for NetImgui startup,
// which require imgui context to be initialized
//------------------------------------------------------
if (HasStartedAutoConnection == false && FCogImguiContext::GetIsNetImguiInitialized())
{
HasStartedAutoConnection = true;
if (Config->AutoRunServer && GetWorld()->IsPlayInEditor())
{
RunServer();
}
ECogNetImguiAutoConnectionMode AutoConnectMode = ECogNetImguiAutoConnectionMode::NoAutoConnect;
switch (GetWorld()->GetNetMode())
{
case NM_Client: AutoConnectMode = Config->AutoConnectOnClient; break;
case NM_ListenServer: AutoConnectMode = Config->AutoConnectOnListenServer; break;
case NM_Standalone: AutoConnectMode = Config->AutoConnectOnStandalone; break;
case NM_DedicatedServer: AutoConnectMode = Config->AutoConnectOnDedicatedServer; break;
default: AutoConnectMode = ECogNetImguiAutoConnectionMode::NoAutoConnect;
}
if (AutoConnectMode == ECogNetImguiAutoConnectionMode::AutoListen)
{
ConnectFrom();
}
else if (AutoConnectMode == ECogNetImguiAutoConnectionMode::AutoConnect)
{
ConnectTo();
}
}
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_NetImgui::RenderContent()
{
Super::RenderContent();
#if NETIMGUI_ENABLED
//----------------------------------------
// Status
//----------------------------------------
if (NetImgui::IsConnected())
{
ImGui::TextColored(ImVec4(0.5f, 1.0f, 0.6f, 1.0f), "Connected");
}
else if (NetImgui::IsConnectionPending())
{
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Waiting Server");
}
else
{
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.3f, 1.0f), "Not Connected");
}
//----------------------------------------
// Connection buttons
//----------------------------------------
if (NetImgui::IsConnected())
{
if (ImGui::Button("Disconnect", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
{
NetImgui::Disconnect();
}
}
else if (NetImgui::IsConnectionPending())
{
if (ImGui::Button("Cancel Connection", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
{
NetImgui::Disconnect();
}
}
else
{
if (ImGui::Button("Connect", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
{
ConnectTo();
}
ImGui::SetItemTooltip("Attempt a connection to a remote netImgui server at the provided address.");
if (ImGui::Button("Listen", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
{
ConnectFrom();
}
ImGui::SetItemTooltip("Start listening for a connection request by a remote netImgui server, on the provided Port.");
}
//----------------------------------------
// Run/Close server button
//----------------------------------------
if (FPlatformProcess::IsProcRunning(ServerProcess))
{
if (ImGui::Button("Close Server", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
{
CloseServer();
}
if (ImGui::IsItemHovered())
{
ImGui::SetTooltip("Close the NetImgui server executable.");
}
}
else
{
if (NetImgui::IsConnected() == false)
{
if (ImGui::Button("Run Server", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
{
RunServer();
}
if (ImGui::IsItemHovered())
{
ImGui::SetTooltip("Run the NetImgui server executable.");
}
}
}
ImGui::Spacing();
//----------------------------------------
// Settings
//----------------------------------------
if (ImGui::CollapsingHeader("Settings"))
{
ImGui::SeparatorText("Connection");
FCogWindowWidgets::SetNextItemToShortWidth();
FCogWindowWidgets::InputText("Server Address", Config->ServerAddress);
ImGui::SetItemTooltip("NetImgui server application address.");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::InputInt("Server Port", &Config->ServerPort);
ImGui::SetItemTooltip("Port of the NetImgui Server application to connect to.");
FCogWindowWidgets::SetNextItemToShortWidth();
FCogWindowWidgets::InputText("Client Name", Config->ClientName);
ImGui::SetItemTooltip("Client name displayed in the server's clients list.");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::InputInt("Client Port", &Config->ClientPort);
ImGui::SetItemTooltip("Port this client should wait for connection from server application.");
ImGui::SeparatorText("Auto-Connect");
FCogWindowWidgets::SetNextItemToShortWidth();
FCogWindowWidgets::ComboboxEnum("Dedicated Server", Config->AutoConnectOnDedicatedServer);
ImGui::SetItemTooltip("Auto-connect mode to the NetImgui server when launching on dedicated server mode.");
FCogWindowWidgets::SetNextItemToShortWidth();
FCogWindowWidgets::ComboboxEnum("Listen Server", Config->AutoConnectOnListenServer);
ImGui::SetItemTooltip("Auto-connect mode to the NetImgui server when launching on listen server mode.");
FCogWindowWidgets::SetNextItemToShortWidth();
FCogWindowWidgets::ComboboxEnum("Client", Config->AutoConnectOnClient);
ImGui::SetItemTooltip("Auto-connect mode to the NetImgui server when launching on client mode.");
FCogWindowWidgets::SetNextItemToShortWidth();
FCogWindowWidgets::ComboboxEnum("Standalone", Config->AutoConnectOnStandalone);
ImGui::SetItemTooltip("Auto-connect mode to the NetImgui server when launching on standalone mode.");
ImGui::SeparatorText("Server App");
ImGui::Checkbox("Auto Run Server", &Config->AutoRunServer);
ImGui::SetItemTooltip("Automatically run the NetImgui server executable at startup.");
FCogWindowWidgets::SetNextItemToShortWidth();
FCogWindowWidgets::InputText("Server Executable", Config->ServerExecutable);
ImGui::SetItemTooltip("Filename of the NetImgui server executable.");
FCogWindowWidgets::SetNextItemToShortWidth();
FCogWindowWidgets::InputText("Server Directory", Config->ServerDirectory);
ImGui::SetItemTooltip("Directory of the NetImgui server executable.");
FCogWindowWidgets::SetNextItemToShortWidth();
FCogWindowWidgets::InputText("Server Arguments", Config->ServerArguments);
ImGui::SetItemTooltip("Argument used when launching the NetImgui server executable.");
}
#endif // #if NETIMGUI_ENABLED
}
//--------------------------------------------------------------------------------------------------------------------------
FString FCogEngineWindow_NetImgui::GetClientName() const
{
switch (GetWorld()->GetNetMode())
{
case NM_Standalone: return FString::Printf(TEXT("%s_%s"), *Config->ClientName, TEXT("Standalone"));
case NM_DedicatedServer: return FString::Printf(TEXT("%s_%s"), *Config->ClientName, TEXT("DedicatedServer"));
case NM_ListenServer: return FString::Printf(TEXT("%s_%s"), *Config->ClientName, TEXT("ListenServer"));
case NM_Client: return FString::Printf(TEXT("%s_%s"), *Config->ClientName, TEXT("Client"));
default:;
}
return Config->ClientName;
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_NetImgui::ConnectTo()
{
FCogImGuiContextScope ImGuiContextScope(GetOwner()->GetContext());
const FString ClientName = GetClientName();
UE_LOG(LogCogImGui, Verbose, TEXT("FCogEngineWindow_NetImgui::ConnectTo | ClientName:%s | ServerAddress:%s | ServerPort:%d"),
*ClientName,
*Config->ServerAddress,
Config->ServerPort);
const auto clientName = StringCast<ANSICHAR>(*ClientName);
const auto serverAddress = StringCast<ANSICHAR>(*Config->ServerAddress);
NetImgui::ConnectToApp(clientName.Get(), serverAddress.Get(), Config->ServerPort, nullptr, nullptr);
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_NetImgui::ConnectFrom()
{
FCogImGuiContextScope ImGuiContextScope(GetOwner()->GetContext());
const FString ClientName = GetClientName();
UE_LOG(LogCogImGui, Verbose, TEXT("FCogEngineWindow_NetImgui::ConnectFrom | ClientName:%s | ClientPort:%d"),
*ClientName,
Config->ClientPort);
const auto clientName = StringCast<ANSICHAR>(*ClientName);
NetImgui::ConnectFromApp(clientName.Get(), Config->ClientPort, nullptr, nullptr);
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_NetImgui::Disconnect()
{
FCogImGuiContextScope ImGuiContextScope(GetOwner()->GetContext());
UE_LOG(LogCogImGui, Verbose, TEXT("FCogEngineWindow_NetImgui::Disconnect"));
NetImgui::Disconnect();
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_NetImgui::RunServer()
{
if (FPlatformProcess::IsProcRunning(ServerProcess))
{
UE_LOG(LogCogImGui, Verbose, TEXT("FCogEngineWindow_NetImgui::RunServer | Already running"));
return;
}
const FString ServerPath = FPaths::Combine(Config->ServerDirectory, Config->ServerExecutable);
if (IPlatformFile::GetPlatformPhysical().FileExists(*ServerPath) == false)
{
UE_LOG(LogCogImGui, Verbose, TEXT("FCogEngineWindow_NetImgui::RunServer | Invalid Server Path:%s"), *ServerPath);
return;
}
UE_LOG(LogCogImGui, Verbose, TEXT("FCogEngineWindow_NetImgui::RunServer | Server Path:%s"), *ServerPath);
ServerProcess = FPlatformProcess::CreateProc(
*ServerPath,
*Config->ServerArguments,
false,
false,
false,
nullptr,
0,
*Config->ServerDirectory,
nullptr
);
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_NetImgui::CloseServer()
{
if (FPlatformProcess::IsProcRunning(ServerProcess) == false)
{
return;
}
FPlatformProcess::TerminateProc(ServerProcess);
}
//--------------------------------------------------------------------------------------------------------------------------
// Config
//--------------------------------------------------------------------------------------------------------------------------
void UCogEngineWindowConfig_NetImgui::Reset()
{
Super::Reset();
#if NETIMGUI_ENABLED
ClientName = FString("Cog");
ClientPort = NetImgui::kDefaultClientPort;
AutoConnectOnDedicatedServer = ECogNetImguiAutoConnectionMode::AutoConnect;
AutoConnectOnListenServer = ECogNetImguiAutoConnectionMode::NoAutoConnect;
AutoConnectOnClient = ECogNetImguiAutoConnectionMode::NoAutoConnect;
AutoConnectOnStandalone = ECogNetImguiAutoConnectionMode::NoAutoConnect;
ServerAddress = FString("127.0.0.1");
ServerPort = NetImgui::kDefaultServerPort;
ServerDirectory = FString("C:\\NetImgui\\Server_Exe");
ServerExecutable = FString("NetImguiServer.exe");
ServerArguments = FString("");
AutoRunServer = false;
#endif
}
@@ -1,6 +1,7 @@
#include "CogEngineWindow_Plots.h"
#include "CogDebugPlot.h"
#include "CogImguiHelper.h"
#include "CogWindowWidgets.h"
#include "Engine/World.h"
#include "imgui.h"
@@ -15,6 +16,8 @@ void FCogEngineWindow_Plots::Initialize()
bNoPadding = true;
Config = GetConfig<UCogEngineConfig_Plots>();
FCogDebugPlot::Clear();
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -46,28 +49,52 @@ void FCogEngineWindow_Plots::RenderContent()
{
Super::RenderContent();
TArray<FCogDebugPlotEntry*> VisiblePlots;
for (FCogDebugPlotEntry& Plot : FCogDebugPlot::Plots)
{
if (Plot.YAxis != ImAxis_COUNT && Plot.GraphIndex != INDEX_NONE)
{
VisiblePlots.Add(&Plot);
}
}
for (FCogDebugPlotEntry& Event : FCogDebugPlot::Events)
{
if (Event.YAxis != ImAxis_COUNT && Event.GraphIndex != INDEX_NONE)
{
VisiblePlots.Add(&Event);
}
}
RenderMenu();
if (ImGui::BeginTable("PlotTable", 2, ImGuiTableFlags_RowBg
| ImGuiTableFlags_Resizable
| ImGuiTableFlags_BordersV
| ImGuiTableFlags_NoPadInnerX
| ImGuiTableFlags_NoPadOuterX))
if (Config->DockEntries)
{
if (ImGui::BeginTable("PlotTable", 2, ImGuiTableFlags_RowBg
| ImGuiTableFlags_Resizable
| ImGuiTableFlags_BordersV
| ImGuiTableFlags_NoPadInnerX
| ImGuiTableFlags_NoPadOuterX))
{
ImGui::TableSetupColumn("PlotsList", ImGuiTableColumnFlags_WidthFixed, FCogWindowWidgets::GetFontWidth() * 20.0f);
ImGui::TableSetupColumn("Plots", ImGuiTableColumnFlags_WidthStretch, 0.0f);
ImGui::TableNextRow();
ImGui::TableSetupColumn("PlotsList", ImGuiTableColumnFlags_WidthFixed, FCogWindowWidgets::GetFontWidth() * 20.0f);
ImGui::TableSetupColumn("Plots", ImGuiTableColumnFlags_WidthStretch, 0.0f);
ImGui::TableNextRow();
TArray<FCogDebugPlotEntry*> VisiblePlots;
ImGui::TableNextColumn();
RenderPlotsList(VisiblePlots);
ImGui::TableNextColumn();
RenderAllEntriesNames(ImVec2(0, -1));
ImGui::TableNextColumn();
ImGui::TableNextColumn();
RenderPlots(VisiblePlots);
ImGui::EndTable();
}
}
else
{
RenderPlots(VisiblePlots);
ImGui::EndTable();
}
bApplyTimeScale = false;
@@ -78,13 +105,17 @@ void FCogEngineWindow_Plots::RenderMenu()
{
if (ImGui::BeginMenuBar())
{
if (Config->DockEntries == false)
{
if (ImGui::BeginMenu("Entries"))
{
RenderAllEntriesNames(ImVec2(ImGui::GetFontSize() * 15, ImGui::GetFontSize() * 20));
ImGui::EndMenu();
}
}
if (ImGui::BeginMenu("Options"))
{
if (ImGui::MenuItem("Clear Data"))
{
FCogDebugPlot::Clear();
}
if (ImGui::MenuItem("Reset"))
{
FCogDebugPlot::Pause = false;
@@ -95,20 +126,47 @@ void FCogEngineWindow_Plots::RenderMenu()
ImGui::Separator();
FCogWindowWidgets::SetNextItemToShortWidth();
if (ImGui::SliderInt("Rows", &Config->Rows, 1, 5))
if (ImGui::SliderInt("Num Graphs", &Config->NumGraphs, 1, 5))
{
bApplyTimeScale = true;
}
FCogWindowWidgets::SetNextItemToShortWidth();
if (ImGui::SliderFloat("Time Range", &Config->TimeRange, 1.0f, 30.0f, "%0.1f"))
ImGui::SliderInt("Num YAxis", &Config->NumYAxis, 0, 3);
FCogWindowWidgets::SetNextItemToShortWidth();
if (ImGui::SliderFloat("Time range", &Config->TimeRange, 1.0f, 100.0f, "%0.1f"))
{
bApplyTimeScale = true;
}
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::SliderFloat("Drag pause sensitivity", &Config->DragPauseSensitivity, 1.0f, 50.0f, "%0.0f");
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::Checkbox("Show time bar at game time", &Config->ShowTimeBarAtGameTime);
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::Checkbox("Show time bar at cursor", &Config->ShowTimeBarAtCursor);
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::Checkbox("Show value at cursor", &Config->ShowValueAtCursor);
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::Checkbox("Dock entries", &Config->DockEntries);
constexpr ImGuiColorEditFlags ColorEditFlags = ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_AlphaPreviewHalf;
FCogImguiHelper::ColorEdit4("Pause background color", Config->PauseBackgroundColor, ColorEditFlags);
ImGui::SetItemTooltip("Background color of the plot when paused.");
ImGui::EndMenu();
}
if (ImGui::MenuItem("Clear"))
{
FCogDebugPlot::Clear();
}
FCogWindowWidgets::ToggleMenuButton(&FCogDebugPlot::Pause, "Pause", ImVec4(1.0f, 0.0f, 0.0f, 1.0f));
ImGui::EndMenuBar();
@@ -116,40 +174,82 @@ void FCogEngineWindow_Plots::RenderMenu()
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Plots::RenderPlotsList(TArray<FCogDebugPlotEntry*>& VisiblePlots)
void FCogEngineWindow_Plots::RenderEntryName(const int Index, FCogDebugPlotEntry& Entry)
{
if (ImGui::BeginChild("Plots", ImVec2(0, -1)))
ImGui::PushID(Index);
const bool IsAssignedToRow = Entry.GraphIndex != INDEX_NONE;
if (ImGui::Selectable(TCHAR_TO_ANSI(*Entry.Name.ToString()), IsAssignedToRow, ImGuiSelectableFlags_AllowDoubleClick))
{
int Index = 0;
ImGui::Indent(6);
for (FCogDebugPlotEntry& Entry : FCogDebugPlot::Plots)
if (IsAssignedToRow)
{
if (Entry.CurrentYAxis != ImAxis_COUNT && Entry.CurrentRow != INDEX_NONE)
{
VisiblePlots.Add(&Entry);
}
Entry.ResetGraphAndAxis();
}
else
{
Entry.AssignGraphAndAxis(0, ImAxis_Y1);
}
}
ImGui::PushID(Index);
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
{
const auto EntryName = StringCast<ANSICHAR>(*Entry.Name.ToString());
ImGui::SetDragDropPayload("DragAndDrop", EntryName.Get(), EntryName.Length() + 1);
ImGui::Text("%s", EntryName.Get());
ImGui::EndDragDropSource();
}
ImGui::PushStyleColor(ImGuiCol_Text, Entry.IsEventPlot ? IM_COL32(128, 128, 255, 255) : IM_COL32(255, 255, 255, 255));
ImGui::Selectable(TCHAR_TO_ANSI(*Entry.Name.ToString()), false, 0);
ImGui::PopStyleColor();
ImGui::PopID();
}
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
{
const auto EntryName = StringCast<ANSICHAR>(*Entry.Name.ToString());
ImGui::SetDragDropPayload("DragAndDrop", EntryName.Get(), EntryName.Length() + 1);
ImGui::Text("%s", EntryName.Get());
ImGui::EndDragDropSource();
}
ImGui::PopID();
Index++;
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Plots::RenderAllEntriesNames(const ImVec2& InSize)
{
if (ImGui::BeginChild("Entries", InSize))
{
if (Config->DockEntries)
{
ImGui::Indent(6);
}
ImGui::Unindent();
int Index = 0;
if (FCogWindowWidgets::DarkCollapsingHeader("Events", ImGuiTreeNodeFlags_DefaultOpen))
{
if (FCogDebugPlot::Events.IsEmpty())
{
ImGui::TextDisabled("No event added yet");
}
else
{
for (FCogDebugPlotEntry& Event : FCogDebugPlot::Events)
{
RenderEntryName(Index, Event);
Index++;
}
}
}
if (FCogWindowWidgets::DarkCollapsingHeader("Plots", ImGuiTreeNodeFlags_DefaultOpen))
{
if (FCogDebugPlot::Plots.IsEmpty())
{
ImGui::TextDisabled("No plot added yet");
}
else
{
for (FCogDebugPlotEntry& Plot : FCogDebugPlot::Plots)
{
RenderEntryName(Index, Plot);
Index++;
}
}
}
if (Config->DockEntries)
{
ImGui::Unindent();
}
}
ImGui::EndChild();
@@ -157,9 +257,9 @@ void FCogEngineWindow_Plots::RenderPlotsList(TArray<FCogDebugPlotEntry*>& Visibl
{
if (const ImGuiPayload* Payload = ImGui::AcceptDragDropPayload("DragAndDrop"))
{
if (FCogDebugPlotEntry* Plot = FCogDebugPlot::FindPlot(FName((const char*)Payload->Data)))
if (FCogDebugPlotEntry* Plot = FCogDebugPlot::FindEntry(FName((const char*)Payload->Data)))
{
Plot->ResetAxis();
Plot->ResetGraphAndAxis();
}
}
ImGui::EndDragDropTarget();
@@ -174,9 +274,16 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
static float RowRatios[] = { 1, 1, 1, 1, 1, 1 };
static float ColRatios[] = { 1 };
static ImPlotSubplotFlags SubplotsFlags = ImPlotSubplotFlags_LinkCols;
if (ImPlot::BeginSubplots("", Config->Rows, 1, ImVec2(-1, -1), SubplotsFlags, RowRatios, ColRatios))
const bool PushPlotBgStyle = FCogDebugPlot::Pause;
if (PushPlotBgStyle)
{
for (int PlotIndex = 0; PlotIndex < Config->Rows; ++PlotIndex)
ImPlot::PushStyleColor(ImPlotCol_PlotBg, FCogImguiHelper::ToImVec4(Config->PauseBackgroundColor));
}
if (ImPlot::BeginSubplots("", Config->NumGraphs, 1, ImVec2(-1, -1), SubplotsFlags, RowRatios, ColRatios))
{
for (int PlotIndex = 0; PlotIndex < Config->NumGraphs; ++PlotIndex)
{
if (ImPlot::BeginPlot("##Plot", ImVec2(-1, 250)))
{
@@ -186,15 +293,27 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
for (const FCogDebugPlotEntry* PlotPtr : VisiblePlots)
{
HasPlotOnAxisY1 |= PlotPtr->CurrentYAxis == ImAxis_Y1 && PlotPtr->CurrentRow == PlotIndex;
HasPlotOnAxisY2 |= PlotPtr->CurrentYAxis == ImAxis_Y2 && PlotPtr->CurrentRow == PlotIndex;
HasPlotOnAxisY3 |= PlotPtr->CurrentYAxis == ImAxis_Y3 && PlotPtr->CurrentRow == PlotIndex;
HasPlotOnAxisY1 |= PlotPtr->YAxis == ImAxis_Y1 && PlotPtr->GraphIndex == PlotIndex;
HasPlotOnAxisY2 |= PlotPtr->YAxis == ImAxis_Y2 && PlotPtr->GraphIndex == PlotIndex;
HasPlotOnAxisY3 |= PlotPtr->YAxis == ImAxis_Y3 && PlotPtr->GraphIndex == PlotIndex;
}
ImPlot::SetupAxis(ImAxis_X1, nullptr, ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines);
ImPlot::SetupAxis(ImAxis_Y1, HasPlotOnAxisY1 ? "" : "[drop here]", (HasPlotOnAxisY1 ? ImPlotAxisFlags_None : (ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines)) | ImPlotAxisFlags_AutoFit);
ImPlot::SetupAxis(ImAxis_Y2, HasPlotOnAxisY2 ? "" : "[drop here]", (HasPlotOnAxisY2 ? ImPlotAxisFlags_None : (ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines)) | ImPlotAxisFlags_AutoFit | ImPlotAxisFlags_Opposite);
ImPlot::SetupAxis(ImAxis_Y3, HasPlotOnAxisY3 ? "" : "[drop here]", (HasPlotOnAxisY3 ? ImPlotAxisFlags_None : (ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines)) | ImPlotAxisFlags_AutoFit | ImPlotAxisFlags_Opposite);
if (Config->NumYAxis > 0)
{
ImPlot::SetupAxis(ImAxis_Y1, HasPlotOnAxisY1 ? "" : "[drop here]", (HasPlotOnAxisY1 ? ImPlotAxisFlags_None : (ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines)) | ImPlotAxisFlags_AutoFit);
}
if (Config->NumYAxis > 1)
{
ImPlot::SetupAxis(ImAxis_Y2, HasPlotOnAxisY2 ? "" : "[drop here]", (HasPlotOnAxisY2 ? ImPlotAxisFlags_None : (ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines)) | ImPlotAxisFlags_AutoFit | ImPlotAxisFlags_Opposite);
}
if (Config->NumYAxis > 2)
{
ImPlot::SetupAxis(ImAxis_Y3, HasPlotOnAxisY3 ? "" : "[drop here]", (HasPlotOnAxisY3 ? ImPlotAxisFlags_None : (ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines)) | ImPlotAxisFlags_AutoFit | ImPlotAxisFlags_Opposite);
}
//--------------------------------------------------------------------------------------------------
// Set the initial X axis range. After, it is automatically updated to move with the current time.
@@ -209,94 +328,136 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
Config->TimeRange = TimeRange;
}
const float Time = GetWorld() ? GetWorld()->GetTimeSeconds() : 0.0;
//------------------------------------------------------------------
// Setup all the Z and Y axis limits. Must be done before calling
// ImPlot::GetPlotPos or ImPlot::GetPlotSize as it calls SetupLock()
//------------------------------------------------------------------
{
//--------------------------------------------------------------------------------
// Make the time axis move forward automatically, unless the user pauses or zoom.
//--------------------------------------------------------------------------------
if (FCogDebugPlot::Pause == false && ImGui::GetIO().MouseWheel == 0)
{
ImPlot::SetupAxisLimits(ImAxis_X1, Time - TimeRange, Time, ImGuiCond_Always);
}
if (bApplyTimeScale)
{
ImPlot::SetupAxisLimits(ImAxis_X1, Time - Config->TimeRange, Time, ImGuiCond_Always);
}
//--------------------------------------------------------------------------------
// Set the Y axis limit for Events.
//--------------------------------------------------------------------------------
for (const FCogDebugPlotEntry* PlotPtr : VisiblePlots)
{
if (PlotPtr == nullptr)
{ continue; }
if (PlotPtr->GraphIndex != PlotIndex)
{ continue; }
if (PlotPtr->IsEventPlot)
{
ImPlot::SetupAxisLimits(PlotPtr->YAxis, 0, PlotPtr->MaxRow + 2, ImGuiCond_Always);
}
}
}
const ImVec2 PlotMin = ImPlot::GetPlotPos();
const ImVec2 PlotSize = ImPlot::GetPlotSize();
const ImVec2 PlotMax = PlotMin + PlotSize;
//----------------------------------------------------------------
// Draw a vertical lines representing the current time and the mouse time
// Pause the scrolling if the user drag inside
//----------------------------------------------------------------
RenderTimeMarker();
const ImVec2 Mouse = ImGui::GetMousePos();
if (Mouse.x > PlotMin.x
&& Mouse.y > PlotMin.y
&& Mouse.x < PlotMax.x
&& Mouse.y < PlotMax.y
&& ImGui::GetDragDropPayload() == nullptr)
{
const ImVec2 Drag = ImGui::GetMouseDragDelta(0);
if (FMath::Abs(Drag.x) > Config->DragPauseSensitivity)
{
FCogDebugPlot::Pause = true;
}
}
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
FCogDebugPlot::Pause = false;
}
//---------------------------------------------------------------------------
// Draw a vertical lines representing the current time and the mouse time
//---------------------------------------------------------------------------
if (Config->ShowTimeBarAtGameTime || Config->ShowTimeBarAtGameTime)
{
ImDrawList* PlotDrawList = ImPlot::GetPlotDrawList();
const float PlotTop = PlotMin.y;
const float TimeBarBottom = PlotTop + PlotSize.y;
ImPlot::PushPlotClipRect();
if (Config->ShowTimeBarAtCursor)
{
PlotDrawList->AddLine(ImVec2(ImGui::GetMousePos().x, PlotTop), ImVec2(ImGui::GetMousePos().x, TimeBarBottom), IM_COL32(128, 128, 128, 64));
}
if (Config->ShowTimeBarAtGameTime && FCogDebugPlot::Pause)
{
const float TimeBarX = ImPlot::PlotToPixels(Time, 0.0f).x;
PlotDrawList->AddLine(ImVec2(TimeBarX, PlotTop), ImVec2(TimeBarX, TimeBarBottom), IM_COL32(255, 255, 255, 64));
}
ImPlot::PopPlotClipRect();
}
//-----------------------------------------------------------
// Draw all the plots assigned to this row
//-----------------------------------------------------------
for (FCogDebugPlotEntry* PlotPtr : VisiblePlots)
{
if (PlotPtr == nullptr)
{ continue; }
FCogDebugPlotEntry& Plot = *PlotPtr;
if (Plot.GraphIndex != PlotIndex)
{ continue; }
ImPlot::SetAxis(Plot.YAxis);
ImPlot::SetNextLineStyle(IMPLOT_AUTO_COL);
const auto Label = StringCast<ANSICHAR>(*Plot.Name.ToString());
//-------------------------------------------------------
// Plot Events
//-------------------------------------------------------
if (Plot.IsEventPlot)
{
continue;
RenderEvents(Plot, Label.Get(), PlotMin, PlotMax);
}
//-------------------------------------------------------
// Plot Values
//-------------------------------------------------------
else if (Plot.Values.empty() == false)
{
RenderValues(Plot, Label.Get());
}
FCogDebugPlotEntry& Entry = *PlotPtr;
if (Entry.Values.empty())
//-------------------------------------------------------
// Allow legend item labels to be drag and drop sources
//-------------------------------------------------------
if (ImPlot::BeginDragDropSourceItem(Label.Get()))
{
continue;
const auto EntryName = StringCast<ANSICHAR>(*Plot.Name.ToString());
ImGui::SetDragDropPayload("DragAndDrop", EntryName.Get(), EntryName.Length() + 1);
ImGui::TextUnformatted(EntryName.Get());
ImPlot::EndDragDropSource();
}
if (Entry.CurrentRow == PlotIndex)
{
//--------------------------------------------------------------------------------
// Make the time axis move forward automatically, unless the user pauses or zoom.
//--------------------------------------------------------------------------------
if (FCogDebugPlot::Pause == false && ImGui::GetIO().MouseWheel == 0)
{
ImPlot::SetupAxisLimits(ImAxis_X1, Entry.Time - TimeRange, Entry.Time, ImGuiCond_Always);
}
if (bApplyTimeScale)
{
ImPlot::SetupAxisLimits(ImAxis_X1, Entry.Time - Config->TimeRange, Entry.Time, ImGuiCond_Always);
}
ImPlot::SetAxis(Entry.CurrentYAxis);
ImPlot::SetNextLineStyle(IMPLOT_AUTO_COL);
const auto Label = StringCast<ANSICHAR>(*Entry.Name.ToString());
//----------------------------------------------------------------
// Pause the scrolling if the user drag inside
//----------------------------------------------------------------
const ImVec2 Mouse = ImGui::GetMousePos();
if (Mouse.x > PlotMin.x
&& Mouse.y > PlotMin.y
&& Mouse.x < PlotMax.x
&& Mouse.y < PlotMax.y
&& ImGui::GetDragDropPayload() == nullptr)
{
const ImVec2 Drag = ImGui::GetMouseDragDelta(0);
if (FMath::Abs(Drag.x) > 10)
{
FCogDebugPlot::Pause = true;
}
}
//-------------------------------------------------------
// Plot Events
//-------------------------------------------------------
const bool IsEventPlot = Entry.Events.Num() > 0;
if (IsEventPlot)
{
RenderEvents(Entry, Label.Get(), PlotMin, PlotMax);
}
//-------------------------------------------------------
// Plot Values
//-------------------------------------------------------
else
{
RenderValues(Entry, Label.Get());
}
//-------------------------------------------------------
// Allow legend item labels to be drag and drop sources
//-------------------------------------------------------
if (ImPlot::BeginDragDropSourceItem(Label.Get()))
{
const auto EntryName = StringCast<ANSICHAR>(*Entry.Name.ToString());
ImGui::SetDragDropPayload("DragAndDrop", EntryName.Get(), EntryName.Length() + 1);
ImGui::TextUnformatted(EntryName.Get());
ImPlot::EndDragDropSource();
}
}
}
//-------------------------------------------------------
@@ -306,9 +467,9 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
{
if (const ImGuiPayload* Payload = ImGui::AcceptDragDropPayload("DragAndDrop"))
{
if (FCogDebugPlotEntry* Plot = FCogDebugPlot::FindPlot(FName((const char*)Payload->Data)))
if (FCogDebugPlotEntry* Plot = FCogDebugPlot::FindEntry(FName((const char*)Payload->Data)))
{
Plot->AssignAxis(PlotIndex, ImAxis_Y1);
Plot->AssignGraphAndAxis(PlotIndex, ImAxis_Y1);
}
}
ImPlot::EndDragDropTarget();
@@ -323,9 +484,9 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
{
if (const ImGuiPayload* Payload = ImGui::AcceptDragDropPayload("DragAndDrop"))
{
if (FCogDebugPlotEntry* Plot = FCogDebugPlot::FindPlot(FName((const char*)Payload->Data)))
if (FCogDebugPlotEntry* Plot = FCogDebugPlot::FindEntry(FName((const char*)Payload->Data)))
{
Plot->AssignAxis(PlotIndex, y);
Plot->AssignGraphAndAxis(PlotIndex, y);
}
}
ImPlot::EndDragDropTarget();
@@ -339,9 +500,9 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
{
if (const ImGuiPayload* Payload = ImGui::AcceptDragDropPayload("DragAndDrop"))
{
if (FCogDebugPlotEntry* Plot = FCogDebugPlot::FindPlot(FName((const char*)Payload->Data)))
if (FCogDebugPlotEntry* Plot = FCogDebugPlot::FindEntry(FName((const char*)Payload->Data)))
{
Plot->AssignAxis(PlotIndex, ImAxis_Y1);
Plot->AssignGraphAndAxis(PlotIndex, ImAxis_Y1);
}
}
ImPlot::EndDragDropTarget();
@@ -352,45 +513,39 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray<FCogDebugPlotEntry*>& Visi
}
ImPlot::EndSubplots();
}
if (PushPlotBgStyle)
{
ImPlot::PopStyleColor();
}
}
ImGui::EndChild();
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Plots::RenderTimeMarker() const
{
const ImVec2 PlotMin = ImPlot::GetPlotPos();
const ImVec2 PlotSize = ImPlot::GetPlotSize();
ImDrawList* PlotDrawList = ImPlot::GetPlotDrawList();
const float PlotTop = PlotMin.y;
const float TimeBarBottom = PlotTop + PlotSize.y;
ImPlot::PushPlotClipRect();
PlotDrawList->AddLine(ImVec2(ImGui::GetMousePos().x, PlotTop), ImVec2(ImGui::GetMousePos().x, TimeBarBottom), IM_COL32(128, 128, 128, 64));
if (FCogDebugPlot::Pause)
{
const float Time = GetWorld() ? GetWorld()->GetTimeSeconds() : 0.0;
const float TimeBarX = ImPlot::PlotToPixels(Time, 0.0f).x;
PlotDrawList->AddLine(ImVec2(TimeBarX, PlotTop), ImVec2(TimeBarX, TimeBarBottom), IM_COL32(255, 255, 255, 64));
}
ImPlot::PopPlotClipRect();
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Plots::RenderValues(FCogDebugPlotEntry& Entry, const char* Label)
void FCogEngineWindow_Plots::RenderValues(FCogDebugPlotEntry& Entry, const char* Label) const
{
//----------------------------------------------------------------
// Custom tooltip
// Value at cursor tooltip
//----------------------------------------------------------------
if (ImPlot::IsPlotHovered())
if (Config->ShowValueAtCursor && ImPlot::IsPlotHovered())
{
float Value;
if (Entry.FindValue(ImPlot::GetPlotMousePos().x, Value))
{
ImGui::BeginTooltip();
ImGui::Text("%s: %0.2f", Label, Value);
ImGui::EndTooltip();
if (FCogWindowWidgets::BeginTableTooltip())
{
if (ImGui::BeginTable("Params", 2, ImGuiTableFlags_Borders))
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%s", Label);
ImGui::TableNextColumn();
ImGui::Text("%0.2f", Value);
ImGui::EndTable();
}
FCogWindowWidgets::EndTableTooltip();
}
}
}
@@ -418,16 +573,8 @@ void FCogEngineWindow_Plots::RenderEvents(FCogDebugPlotEntry& Entry, const char*
const ImVec2 Mouse = ImGui::GetMousePos();
ImDrawList* PlotDrawList = ImPlot::GetPlotDrawList();
//--------------------------------------------------------------------
// Update plot time for events as events are not pushed every frames
//--------------------------------------------------------------------
Entry.UpdateTime(GetWorld());
ImPlot::SetupAxisLimits(Entry.CurrentYAxis, 0, Entry.MaxRow + 2, ImGuiCond_Always);
ImPlot::PushPlotClipRect();
//----------------------------------------------------------------
// Plot line only to make the plotter move in time and auto scale
//----------------------------------------------------------------
@@ -447,7 +594,7 @@ void FCogEngineWindow_Plots::RenderEvents(FCogDebugPlotEntry& Entry, const char*
const bool IsInstant = Event.StartTime == Event.EndTime;
if (IsInstant)
{
constexpr float Radius = 10.0f;
constexpr float Radius = 10.0f;
PlotDrawList->AddNgon(PosMid, 10, Event.BorderColor, 4);
PlotDrawList->AddNgonFilled(PosMid, 10, Event.FillColor, 4);
PlotDrawList->AddText(ImVec2(PosMid.x + 15, PosMid.y - 6), IM_COL32(255, 255, 255, 255), TCHAR_TO_ANSI(*Event.DisplayName));
@@ -494,76 +641,78 @@ void FCogEngineWindow_Plots::RenderEvents(FCogDebugPlotEntry& Entry, const char*
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Plots::RenderEventTooltip(const FCogDebugPlotEvent* HoveredEvent, FCogDebugPlotEntry& Entry)
void FCogEngineWindow_Plots::RenderEventTooltip(const FCogDebugPlotEvent* HoveredEvent, const FCogDebugPlotEntry& Entry)
{
if (ImPlot::IsPlotHovered() && HoveredEvent != nullptr)
{
FCogWindowWidgets::BeginTableTooltip();
if (ImGui::BeginTable("Params", 2, ImGuiTableFlags_Borders))
if (FCogWindowWidgets::BeginTableTooltip())
{
//------------------------
// Event Name
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Name");
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*HoveredEvent->DisplayName));
//------------------------
// Owner Name
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Owner");
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*HoveredEvent->OwnerName));
//------------------------
// Times
//------------------------
if (HoveredEvent->EndTime != HoveredEvent->StartTime)
if (ImGui::BeginTable("Params", 2, ImGuiTableFlags_Borders))
{
const float ActualEndTime = HoveredEvent->GetActualEndTime(Entry);
const uint64 ActualEndFrame = HoveredEvent->GetActualEndFrame(Entry);
//------------------------
// Event Name
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Name");
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*HoveredEvent->DisplayName));
//------------------------
// Owner Name
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Duration");
ImGui::Text("Owner");
ImGui::TableNextColumn();
ImGui::Text("%0.2fs", ActualEndTime - HoveredEvent->StartTime);
ImGui::Text("%s", TCHAR_TO_ANSI(*HoveredEvent->OwnerName));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Frames");
ImGui::TableNextColumn();
ImGui::Text("%d [%d-%d]",
(int32)(ActualEndFrame - HoveredEvent->StartFrame),
(int32)(HoveredEvent->StartFrame % 1000),
(int32)(ActualEndFrame % 1000));
}
else
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Frame");
ImGui::TableNextColumn();
ImGui::Text("%d", (int32)(HoveredEvent->StartFrame % 1000));
}
//------------------------
// Times
//------------------------
if (HoveredEvent->EndTime != HoveredEvent->StartTime)
{
const float ActualEndTime = HoveredEvent->GetActualEndTime(Entry);
const uint64 ActualEndFrame = HoveredEvent->GetActualEndFrame(Entry);
//------------------------
// Params
//------------------------
for (FCogDebugPlotEventParams Param : HoveredEvent->Params)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*Param.Name.ToString()));
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*Param.Value));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Duration");
ImGui::TableNextColumn();
ImGui::Text("%0.2fs", ActualEndTime - HoveredEvent->StartTime);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Frames");
ImGui::TableNextColumn();
ImGui::Text("%d [%d-%d]",
(int32)(ActualEndFrame - HoveredEvent->StartFrame),
(int32)(HoveredEvent->StartFrame % 1000),
(int32)(ActualEndFrame % 1000));
}
else
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Frame");
ImGui::TableNextColumn();
ImGui::Text("%d", (int32)(HoveredEvent->StartFrame % 1000));
}
//------------------------
// Params
//------------------------
for (FCogDebugPlotEventParams Param : HoveredEvent->Params)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*Param.Name.ToString()));
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*Param.Value));
}
ImGui::EndTable();
}
ImGui::EndTable();
FCogWindowWidgets::EndTableTooltip();
}
FCogWindowWidgets::EndTableTooltip();
}
}
@@ -2,9 +2,11 @@
#include "CogDebug.h"
#include "CogEngineHelper.h"
#include "CogEngineReplicator.h"
#include "CogEngineWindow_ImGui.h"
#include "CogImguiHelper.h"
#include "CogImguiInputHelper.h"
#include "CogWindowConsoleCommandManager.h"
#include "CogWindowManager.h"
#include "CogWindowWidgets.h"
#include "Components/PrimitiveComponent.h"
@@ -27,11 +29,14 @@ void FCogEngineWindow_Selection::Initialize()
Config = GetConfig<UCogEngineConfig_Selection>();
ConsoleCommands.Add(IConsoleManager::Get().RegisterConsoleCommand(
FCogWindowConsoleCommandManager::RegisterWorldConsoleCommand(
*ToggleSelectionModeCommand,
TEXT("Toggle the actor selection mode"),
FConsoleCommandWithArgsDelegate::CreateLambda([this](const TArray<FString>& Args) { ToggleSelectionMode(); }),
ECVF_Cheat));
GetWorld(),
FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray<FString>& InArgs, UWorld* InWorld)
{
ToggleSelectionMode();
}));
TryReapplySelection();
}
@@ -50,10 +55,6 @@ void FCogEngineWindow_Selection::RenderHelp()
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Selection::Shutdown()
{
for (IConsoleObject* ConsoleCommand : ConsoleCommands)
{
IConsoleManager::Get().UnregisterConsoleObject(ConsoleCommand);
}
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -75,7 +76,7 @@ void FCogEngineWindow_Selection::PreSaveConfig()
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Selection::TryReapplySelection() const
{
const UWorld* World = GetWorld();
const UWorld* World = GetWorld();
if (World == nullptr)
{
return;
@@ -139,7 +140,7 @@ void FCogEngineWindow_Selection::ActivateSelectionMode()
bSelectionModeActive = true;
bIsInputEnabledBeforeEnteringSelectionMode = GetOwner()->GetContext().GetEnableInput();
GetOwner()->GetContext().SetEnableInput(true);
GetOwner()->SetHideAllWindows(true);
GetOwner()->SetActivateSelectionMode(true);
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -161,7 +162,7 @@ void FCogEngineWindow_Selection::DeactivateSelectionMode()
//--------------------------------------------------------------------------------------------
GetOwner()->GetContext().SetEnableInput(bIsInputEnabledBeforeEnteringSelectionMode);
GetOwner()->SetHideAllWindows(false);
GetOwner()->SetActivateSelectionMode(false);
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -204,7 +205,14 @@ void FCogEngineWindow_Selection::RenderContent()
if (ImGui::BeginMenu("Options"))
{
ImGui::Checkbox("Save selection", &Config->bReapplySelection);
ImGui::SetItemTooltip("Should the last selection be saved and reapplied on startup.");
ImGui::Checkbox("Actor Name Use Label", &FCogDebug::Settings.ActorNameUseLabel);
ImGui::SetItemTooltip("Should actor names be displayed using their label. Labels are more readable.");
ImGui::Checkbox("Replicate Selection", &FCogDebug::Settings.ReplicateSelection);
ImGui::SetItemTooltip("Should the client replicate its actor selection to the server.");
ImGui::EndMenu();
}
@@ -219,7 +227,14 @@ void FCogEngineWindow_Selection::RenderContent()
//--------------------------------------------------------------------------------------------------------------------------
bool FCogEngineWindow_Selection::DrawSelectionCombo()
{
return FCogWindowWidgets::ActorsListWithFilters(*GetWorld(), ActorClasses, Config->SelectedClassIndex, &Filter, GetLocalPlayerPawn());
AActor* NewSelection = nullptr;
const bool result = FCogWindowWidgets::ActorsListWithFilters(NewSelection, *GetWorld(), ActorClasses, Config->SelectedClassIndex, &Filter, GetLocalPlayerPawn());
if (result)
{
SetGlobalSelection(NewSelection);
}
return result;
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -254,7 +269,15 @@ void FCogEngineWindow_Selection::TickSelectionMode()
AActor* HoveredActor = nullptr;
FVector WorldOrigin, WorldDirection;
if (UGameplayStatics::DeprojectScreenToWorld(PlayerController, FCogImguiHelper::ToFVector2D(ImGui::GetMousePos() - ViewportPos), WorldOrigin, WorldDirection))
//-----------------------------------------------------------------------------------------------
// Do not use imgui mouse pos because when connected to NetImgui, the mouse position is invalid.
// See https://github.com/sammyfreg/netImgui/issues/61
//-----------------------------------------------------------------------------------------------
//ImVec2 mousePos = ImGui::GetMousePos();
ImVec2 mousePos = GetOwner()->GetContext().GetImguiMousePos();
if (UGameplayStatics::DeprojectScreenToWorld(PlayerController, FCogImguiHelper::ToFVector2D(mousePos - ViewportPos), WorldOrigin, WorldDirection))
{
//--------------------------------------------------------------------------------------------------------
// Prioritize another actor than the selected actor unless we only touch the selected actor.
@@ -345,7 +368,19 @@ void FCogEngineWindow_Selection::RenderMainMenuWidget(int32 SubWidgetIndex, floa
else if (SubWidgetIndex == 1)
{
ImGui::SetNextItemWidth(Width);
FCogWindowWidgets::MenuActorsCombo("MenuActorSelection", *GetWorld(), ActorClasses, Config->SelectedClassIndex, &Filter, GetLocalPlayerPawn(), [this](AActor& Actor) { RenderActorContextMenu(Actor); });
AActor* NewSelection = nullptr;
if (FCogWindowWidgets::MenuActorsCombo(
"MenuActorSelection",
NewSelection,
*GetWorld(),
ActorClasses,
Config->SelectedClassIndex,
&Filter,
GetLocalPlayerPawn(),
[this](AActor& Actor) { RenderActorContextMenu(Actor); }))
{
SetGlobalSelection(NewSelection);
}
}
else if (SubWidgetIndex == 2)
{
@@ -161,11 +161,11 @@ void FCogEngineWindow_Skeleton::RenderBoneEntry(int32 BoneIndex, bool OpenAllChi
//------------------------
if (BoneInfo.Children.Num() > 0 && Filter.IsActive() == false)
{
OpenChildren = ImGui::TreeNodeEx("##Bone", ImGuiSelectableFlags_AllowOverlap | ImGuiTreeNodeFlags_SpanFullWidth);
OpenChildren = ImGui::TreeNodeEx("##Bone", ImGuiTreeNodeFlags_AllowOverlap | ImGuiTreeNodeFlags_SpanFullWidth);
}
else
{
ImGui::TreeNodeEx("##Bone", ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiSelectableFlags_AllowOverlap | ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TreeNodeEx("##Bone", ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_AllowOverlap | ImGuiTreeNodeFlags_SpanFullWidth);
}
const bool IsControlDown = ImGui::GetCurrentContext()->IO.KeyCtrl;
@@ -56,14 +56,10 @@ void FCogEngineWindow_Spawns::RenderContent()
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Spawns::RenderSpawnGroup(ACogEngineReplicator& Replicator, const FCogEngineSpawnGroup& SpawnGroup)
{
ImGui::PushStyleColor(ImGuiCol_Header, IM_COL32(66, 66, 66, 79));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, IM_COL32(62, 62, 62, 204));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, IM_COL32(86, 86, 86, 255));
if (ImGui::CollapsingHeader(TCHAR_TO_ANSI(*SpawnGroup.Name), ImGuiTreeNodeFlags_DefaultOpen))
if (FCogWindowWidgets::DarkCollapsingHeader(TCHAR_TO_ANSI(*SpawnGroup.Name), ImGuiTreeNodeFlags_DefaultOpen))
{
int32 GroupIndex = 0;
ImGui::PushID(GroupIndex);
int32 GroupIndex = 0;
ImGui::PushID(GroupIndex);
const bool PushColor = (SpawnGroup.Color != FColor::Transparent);
if (PushColor)
@@ -92,8 +88,6 @@ void FCogEngineWindow_Spawns::RenderSpawnGroup(ACogEngineReplicator& Replicator,
ImGui::PopID();
GroupIndex++;
}
ImGui::PopStyleColor(3);
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -117,7 +111,7 @@ bool FCogEngineWindow_Spawns::RenderSpawnAsset(ACogEngineReplicator& Replicator,
if (ImGui::Button(TCHAR_TO_ANSI(*EntryName), ImVec2(-1, 0)))
{
IsPressed = true;
Replicator.Server_Spawn(SpawnEntry);
Replicator.Server_Spawn(SpawnEntry);
}
ImGui::PopStyleVar(1);
@@ -5,6 +5,68 @@
#include "Engine/EngineTypes.h"
#include "CogEngineDataAsset.generated.h"
class FCogWindow;
//--------------------------------------------------------------------------------------------------------------------------
UENUM(BlueprintType)
enum class ECogEngineCheat_ActiveState : uint8
{
Inactive,
Partial,
Active,
};
//--------------------------------------------------------------------------------------------------------------------------
UCLASS(BlueprintType, Abstract, Const, DefaultToInstanced, EditInlineNew, CollapseCategories)
class COGENGINE_API UCogEngineCheat_Execution
: public UObject
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintNativeEvent)
void Execute(const AActor* Instigator, const TArray<AActor*>& Targets) const;
UFUNCTION(BlueprintNativeEvent)
ECogEngineCheat_ActiveState IsActiveOnTargets(const TArray<AActor*>& Targets) const;
virtual bool GetColor(const FCogWindow& InCallingWindow, FLinearColor& OutColor) const;
};
//--------------------------------------------------------------------------------------------------------------------------
USTRUCT()
struct COGENGINE_API FCogEngineCheat
{
GENERATED_BODY()
UPROPERTY(EditAnywhere)
FString Name;
UPROPERTY(EditAnywhere, Instanced)
TObjectPtr<UCogEngineCheat_Execution> Execution;
UPROPERTY(EditAnywhere)
FLinearColor Color = FLinearColor::White;
mutable FLinearColor CustomColor = FLinearColor::White;
};
//--------------------------------------------------------------------------------------------------------------------------
USTRUCT()
struct COGENGINE_API FCogEngineCheatCategory
{
GENERATED_BODY()
UPROPERTY(Category = "Cheats", EditAnywhere)
FString Name;
UPROPERTY(Category = "Cheats", EditAnywhere, meta = (TitleProperty = "Name"))
TArray<FCogEngineCheat> PersistentEffects;
UPROPERTY(Category = "Cheats", EditAnywhere, meta = (TitleProperty = "Name"))
TArray<FCogEngineCheat> InstantEffects;
};
//--------------------------------------------------------------------------------------------------------------------------
USTRUCT()
struct COGENGINE_API FCogEngineSpawnEntry
@@ -44,6 +106,9 @@ public:
UCogEngineDataAsset() {}
UPROPERTY(Category = "Cheats", EditAnywhere, meta = (TitleProperty = "Name"))
TArray<FCogEngineCheatCategory> CheatCategories;
UPROPERTY(Category = "Spawns", EditAnywhere, meta = (TitleProperty = "Name"))
TArray<FCogEngineSpawnGroup> SpawnGroups;
};
@@ -1,6 +1,7 @@
#pragma once
#include "CoreMinimal.h"
#include "CogEngineDataAsset.h"
#include "GameFramework/Actor.h"
#include "UObject/Class.h"
#include "UObject/ObjectMacros.h"
@@ -50,6 +51,11 @@ public:
UFUNCTION(Server, Reliable)
void Server_DeleteActor(AActor* Actor);
UFUNCTION(Reliable, Server)
void Server_ApplyCheat(const AActor* CheatInstigator, const TArray<AActor*>& TargetActors, const FCogEngineCheat& Cheat) const;
static ECogEngineCheat_ActiveState IsCheatActiveOnTargets(const TArray<AActor*>& Targets, const FCogEngineCheat& Cheat);
protected:
UFUNCTION(Server, Reliable)
@@ -0,0 +1,90 @@
#pragma once
#include "CoreMinimal.h"
#include "CogCommonConfig.h"
#include "CogWindow.h"
#include "CogEngineWindow_Cheats.generated.h"
class AActor;
class UCogEngineConfig_Cheats;
class UCogEngineDataAsset;
struct FCogEngineCheat;
//--------------------------------------------------------------------------------------------------------------------------
class COGENGINE_API FCogEngineWindow_Cheats : public FCogWindow
{
typedef FCogWindow Super;
public:
virtual void Initialize() override;
protected:
virtual void GameTick(float DeltaTime) override;
virtual void ResetConfig() override;
virtual void RenderHelp() override;
virtual void RenderContent() override;
virtual void TryReapplyCheats();
virtual bool AddCheat(const int32 Index, AActor* ControlledActor, AActor* TargetActor, const FCogEngineCheat& CheatEffect, bool IsPersistent);
virtual void RequestCheat(AActor* ControlledActor, AActor* SelectedActor, const FCogEngineCheat& Cheat, bool ApplyToEnemies, bool ApplyToAllies, bool ApplyToControlled);
virtual const FCogEngineCheat* FindCheatByName(const FString& CheatName, const bool OnlyPersistentCheats);
static bool DrawTable();
void UpdateCheatColor(const FCogEngineCheat& Cheat) const;
TObjectPtr<const UCogEngineDataAsset> Asset = nullptr;
TObjectPtr<UCogEngineConfig_Cheats> Config = nullptr;
bool bHasReappliedCheats = false;
ImGuiTextFilter Filter;
TArray<FCogEngineCheat> AllCheats;
};
//--------------------------------------------------------------------------------------------------------------------------
UCLASS(Config = Cog)
class UCogEngineConfig_Cheats : public UCogCommonConfig
{
GENERATED_BODY()
public:
UPROPERTY(Config)
bool bGroupByCategories = true;
UPROPERTY(Config)
bool bReapplyCheatsBetweenPlays = true;
UPROPERTY(Config)
bool bReapplyCheatsBetweenLaunches = true;
UPROPERTY(Config)
bool bUseTwoColumns = true;
UPROPERTY(Config)
TArray<FString> AppliedCheats;
UPROPERTY(Config)
TSet<FString> SelectedCategories;
virtual void Reset() override
{
Super::Reset();
bReapplyCheatsBetweenPlays = true;
bReapplyCheatsBetweenLaunches = true;
AppliedCheats.Empty();
SelectedCategories.Empty();
}
};
@@ -0,0 +1,106 @@
#pragma once
#include "CoreMinimal.h"
#include "CogCommonConfig.h"
#include "CogWindow.h"
#include "CogEngineWindow_NetImgui.generated.h"
class UCogEngineWindowConfig_NetImgui;
UENUM()
enum class ECogNetImguiAutoConnectionMode : uint8
{
NoAutoConnect,
AutoConnect,
AutoListen,
};
class COGENGINE_API FCogEngineWindow_NetImgui : public FCogWindow
{
typedef FCogWindow Super;
public:
virtual void Initialize() override;
virtual void Shutdown() override;
protected:
virtual void ResetConfig() override;
virtual void RenderHelp() override;
virtual void RenderContent() override;
virtual void RenderTick(float DeltaTime);
void ConnectTo();
void ConnectFrom();
void Disconnect();
void TryStartup();
void RunServer();
void CloseServer();
private:
FString GetClientName() const;
TObjectPtr<UCogEngineWindowConfig_NetImgui> Config = nullptr;
bool HasStartedAutoConnection = false;
FProcHandle ServerProcess;
};
//--------------------------------------------------------------------------------------------------------------------------
UCLASS(Config = Cog)
class UCogEngineWindowConfig_NetImgui : public UCogCommonConfig
{
GENERATED_BODY()
public:
virtual void Reset() override;
UPROPERTY(Config)
FString ClientName = FString("Cog");
UPROPERTY(Config)
int32 ClientPort = 8889;
UPROPERTY(Config)
ECogNetImguiAutoConnectionMode AutoConnectOnDedicatedServer = ECogNetImguiAutoConnectionMode::AutoConnect;
UPROPERTY(Config)
ECogNetImguiAutoConnectionMode AutoConnectOnListenServer = ECogNetImguiAutoConnectionMode::NoAutoConnect;
UPROPERTY(Config)
ECogNetImguiAutoConnectionMode AutoConnectOnClient = ECogNetImguiAutoConnectionMode::NoAutoConnect;
UPROPERTY(Config)
ECogNetImguiAutoConnectionMode AutoConnectOnStandalone = ECogNetImguiAutoConnectionMode::NoAutoConnect;
UPROPERTY(Config)
FString ServerAddress = FString("127.0.0.1");
UPROPERTY(Config)
int32 ServerPort = 8888;
UPROPERTY(Config)
FString ServerDirectory = FString("C:\\NetImgui\\Server_Exe");
UPROPERTY(Config)
FString ServerExecutable = FString("NetImguiServer.exe");
UPROPERTY(Config)
FString ServerArguments = FString("");
UPROPERTY(Config)
bool AutoRunServer = false;
};
@@ -26,19 +26,19 @@ protected:
virtual void RenderContent() override;
static void RenderPlotsList(TArray<FCogDebugPlotEntry*>& VisiblePlots);
virtual void RenderAllEntriesNames(const ImVec2& InSize);
void RenderPlots(const TArray<FCogDebugPlotEntry*>& VisiblePlots) const;
virtual void RenderEntryName(const int Index, FCogDebugPlotEntry& Entry);
void RenderMenu();
virtual void RenderPlots(const TArray<FCogDebugPlotEntry*>& VisiblePlots) const;
void RenderTimeMarker() const;
virtual void RenderMenu();
static void RenderValues(FCogDebugPlotEntry& Entry, const char* Label);
virtual void RenderValues(FCogDebugPlotEntry& Entry, const char* Label) const;
void RenderEvents(FCogDebugPlotEntry& Entry, const char* Label, const ImVec2& PlotMin, const ImVec2& PlotMax) const;
virtual void RenderEvents(FCogDebugPlotEntry& Entry, const char* Label, const ImVec2& PlotMin, const ImVec2& PlotMax) const;
static void RenderEventTooltip(const FCogDebugPlotEvent* HoveredEvent, FCogDebugPlotEntry& Entry);
static void RenderEventTooltip(const FCogDebugPlotEvent* HoveredEvent, const FCogDebugPlotEntry& Entry);
TObjectPtr<UCogEngineConfig_Plots> Config = nullptr;
@@ -57,14 +57,41 @@ class UCogEngineConfig_Plots : public UCogCommonConfig
public:
UPROPERTY(Config)
int Rows = 1;
int NumGraphs = 1;
UPROPERTY(Config)
float TimeRange = 10.0f;
int NumYAxis = 1;
UPROPERTY(Config)
float TimeRange = 20.0f;
UPROPERTY(Config)
bool ShowTimeBarAtGameTime = true;
UPROPERTY(Config)
bool ShowTimeBarAtCursor = true;
UPROPERTY(Config)
bool ShowValueAtCursor = true;
UPROPERTY(Config)
float DragPauseSensitivity = 10.0f;
UPROPERTY(Config)
FColor PauseBackgroundColor = FColor(10, 0, 0, 255);
UPROPERTY(Config)
bool DockEntries = false;
virtual void Reset() override
{
Rows = 1;
TimeRange = 10.0f;
NumGraphs = 1;
TimeRange = 20.0f;
ShowTimeBarAtGameTime = true;
ShowTimeBarAtCursor = true;
ShowValueAtCursor = true;
DragPauseSensitivity = 10.0f;
PauseBackgroundColor = FColor(10, 0, 0, 255);
DockEntries = false;
}
};
@@ -75,7 +75,7 @@ protected:
bool bSelectionModeActive = false;
bool bIsInputEnabledBeforeEnteringSelectionMode;
bool bIsInputEnabledBeforeEnteringSelectionMode = false;
int32 WaitInputReleased = 0;
@@ -83,8 +83,6 @@ protected:
ETraceTypeQuery TraceType = TraceTypeQuery1;
TArray<IConsoleObject*> ConsoleCommands;
TObjectPtr<UCogEngineConfig_Selection> Config;
ImGuiTextFilter Filter;
@@ -12,6 +12,7 @@ public class CogImgui : ModuleRules
"Core",
"ImGui",
"ImPlot",
"NetImgui",
});
PrivateDependencyModuleNames.AddRange(new[]
@@ -21,7 +22,8 @@ public class CogImgui : ModuleRules
"Engine",
"InputCore",
"Slate",
"SlateCore"
"SlateCore",
"Sockets"
});
if (Target.bBuildEditor)
@@ -1,12 +1,22 @@
#include "CogImguiConfig.h"
THIRD_PARTY_INCLUDES_START
#include <imgui.cpp>
#include <imgui_demo.cpp>
#include <imgui_draw.cpp>
#include <imgui_tables.cpp>
#include <imgui_widgets.cpp>
#include <implot.cpp>
#include <implot_demo.cpp>
#include <implot_items.cpp>
#include "Private/NetImgui_Api.cpp"
#include "Private/NetImgui_Client.cpp"
#include "Private/NetImgui_CmdPackets_DrawFrame.cpp"
#include "Private/NetImgui_NetworkPosix.cpp"
#include "Private/NetImgui_NetworkUE4.cpp"
#include "Private/NetImgui_NetworkWin32.cpp"
THIRD_PARTY_INCLUDES_END
@@ -17,43 +17,72 @@
#include "imgui.h"
#include "imgui_internal.h"
#include "implot.h"
#include "Misc/EngineVersionComparison.h"
#include "NetImgui_Api.h"
#include "TextureResource.h"
#include "Widgets/SViewport.h"
#include "Widgets/SWindow.h"
static UPlayerInput* GetPlayerInput(const UWorld* World);
FCogImGuiContextScope::
FCogImGuiContextScope(FCogImguiContext& CogImguiContext)
{
PrevContext = ImGui::GetCurrentContext();
PrevPlotContext = ImPlot::GetCurrentContext();
ImGui::SetCurrentContext(CogImguiContext.ImGuiContext);
ImPlot::SetCurrentContext(CogImguiContext.PlotContext);
}
FCogImGuiContextScope::
FCogImGuiContextScope(ImGuiContext* GuiCtx, ImPlotContext* PlotCtx)
{
PrevContext = ImGui::GetCurrentContext();
PrevPlotContext = ImPlot::GetCurrentContext();
ImGui::SetCurrentContext(GuiCtx);
ImPlot::SetCurrentContext(PlotCtx);
}
FCogImGuiContextScope::
~FCogImGuiContextScope()
{
ImGui::SetCurrentContext(PrevContext);
ImPlot::SetCurrentContext(PrevPlotContext);
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogImguiContext::bIsNetImguiInitialized = false;
//--------------------------------------------------------------------------------------------------------------------------
void FCogImguiContext::Initialize()
{
IMGUI_CHECKVERSION();
if (FSlateApplication::IsInitialized() == false)
{
return;
}
FSlateApplication& SlateApp = FSlateApplication::Get();
GameViewport = GEngine->GameViewport;
SAssignNew(MainWidget, SCogImguiWidget).Context(this);
GameViewport->AddViewportWidgetContent(MainWidget.ToSharedRef(), TNumericLimits<int32>::Max());
if (GameViewport != nullptr)
{
SAssignNew(MainWidget, SCogImguiWidget).Context(this);
GameViewport->AddViewportWidgetContent(MainWidget.ToSharedRef(), TNumericLimits<int32>::Max());
SAssignNew(InputCatcherWidget, SCogImguiInputCatcherWidget).Context(this);
GameViewport->AddViewportWidgetContent(InputCatcherWidget.ToSharedRef(), -TNumericLimits<int32>::Max());
SAssignNew(InputCatcherWidget, SCogImguiInputCatcherWidget).Context(this);
GameViewport->AddViewportWidgetContent(InputCatcherWidget.ToSharedRef(), -TNumericLimits<int32>::Max());
}
ImGuiContext = ImGui::CreateContext();
PlotContext = ImPlot::CreateContext();
ImGui::SetCurrentContext(ImGuiContext);
ImPlot::SetImGuiContext(ImGuiContext);
ImPlot::SetCurrentContext(PlotContext);
ImGuiIO& IO = ImGui::GetIO();
IO.UserData = this;
IO.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
IO.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
IO.ConfigFlags |= ImGuiConfigFlags_NavNoCaptureKeyboard;
IO.ConfigFlags |= ImGuiConfigFlags_NavEnableSetMousePos;
IO.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
IO.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
@@ -70,7 +99,7 @@ void FCogImguiContext::Initialize()
ImGuiViewport* MainViewport = ImGui::GetMainViewport();
FCogImGuiViewportData* ViewportData = new FCogImGuiViewportData();
MainViewport->PlatformUserData = ViewportData;
ViewportData->Window = SlateApp.GetActiveTopLevelWindow();
ViewportData->Window = FSlateApplication::IsInitialized() ? FSlateApplication::Get().GetActiveTopLevelWindow() : nullptr;
ViewportData->Context = this;
ViewportData->Widget = MainWidget;
@@ -93,35 +122,73 @@ void FCogImguiContext::Initialize()
PlatformIO.Platform_SetWindowAlpha = ImGui_SetWindowAlpha;
PlatformIO.Platform_RenderWindow = ImGui_RenderWindow;
if (const TSharedPtr<GenericApplication> PlatformApplication = SlateApp.GetPlatformApplication())
if (FSlateApplication::IsInitialized())
{
if (const TSharedPtr<GenericApplication> PlatformApplication = FSlateApplication::Get().GetPlatformApplication())
{
FDisplayMetrics DisplayMetrics;
PlatformApplication->GetInitialDisplayMetrics(DisplayMetrics);
PlatformApplication->OnDisplayMetricsChanged().AddRaw(this, &FCogImguiContext::OnDisplayMetricsChanged);
OnDisplayMetricsChanged(DisplayMetrics);
}
}
else
{
FMonitorInfo monitorInfo;
monitorInfo.bIsPrimary = true;
monitorInfo.DisplayRect = FPlatformRect(0, 0, 1080, 720);
FDisplayMetrics DisplayMetrics;
PlatformApplication->GetInitialDisplayMetrics(DisplayMetrics);
PlatformApplication->OnDisplayMetricsChanged().AddRaw(this, &FCogImguiContext::OnDisplayMetricsChanged);
DisplayMetrics.MonitorInfo.Add(monitorInfo);
OnDisplayMetricsChanged(DisplayMetrics);
}
#if NETIMGUI_ENABLED
if (bIsNetImguiInitialized == false)
{
NetImgui::Startup();
bIsNetImguiInitialized = true;
}
#endif
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogImguiContext::Shutdown()
{
ImGuiViewport* MainViewport = ImGui::GetMainViewport();
if (const FCogImGuiViewportData* ViewportData = static_cast<FCogImGuiViewportData*>(MainViewport->PlatformUserData))
FCogImGuiContextScope ImGuiContextScope(ImGuiContext, PlotContext);
//------------------------------------------------------------------
// NetImgui must be shutdown before imgui as it uses context hooks
//------------------------------------------------------------------
#if NETIMGUI_ENABLED
if (bIsNetImguiInitialized)
{
delete ViewportData;
MainViewport->PlatformUserData = nullptr;
NetImgui::Shutdown();
bIsNetImguiInitialized = false;
}
#endif
if (ImGuiViewport* MainViewport = ImGui::GetMainViewport())
{
if (const FCogImGuiViewportData* ViewportData = static_cast<FCogImGuiViewportData*>(MainViewport->PlatformUserData))
{
delete ViewportData;
MainViewport->PlatformUserData = nullptr;
}
}
if (FSlateApplication::IsInitialized())
{
FSlateApplication& SlateApp = FSlateApplication::Get();
if (const TSharedPtr<GenericApplication> PlatformApplication = SlateApp.GetPlatformApplication())
if (const TSharedPtr<GenericApplication> PlatformApplication = FSlateApplication::Get().GetPlatformApplication())
{
PlatformApplication->OnDisplayMetricsChanged().RemoveAll(this);
}
}
GameViewport->RemoveViewportWidgetContent(MainWidget.ToSharedRef());
if (GameViewport != nullptr)
{
GameViewport->RemoveViewportWidgetContent(MainWidget.ToSharedRef());
GameViewport->RemoveViewportWidgetContent(InputCatcherWidget.ToSharedRef());
}
if (PlotContext)
{
@@ -139,6 +206,8 @@ void FCogImguiContext::Shutdown()
//--------------------------------------------------------------------------------------------------------------------------
void FCogImguiContext::OnDisplayMetricsChanged(const FDisplayMetrics& DisplayMetrics) const
{
FCogImGuiContextScope ImGuiContextScope(ImGuiContext, PlotContext);
ImGuiPlatformIO& PlatformIO = ImGui::GetPlatformIO();
PlatformIO.Monitors.resize(0);
@@ -149,7 +218,7 @@ void FCogImguiContext::OnDisplayMetricsChanged(const FDisplayMetrics& DisplayMet
ImGuiMonitor.MainSize = ImVec2(Monitor.DisplayRect.Right - Monitor.DisplayRect.Left, Monitor.DisplayRect.Bottom - Monitor.DisplayRect.Top);
ImGuiMonitor.WorkPos = ImVec2(Monitor.WorkArea.Left, Monitor.WorkArea.Top);
ImGuiMonitor.WorkSize = ImVec2(Monitor.WorkArea.Right - Monitor.WorkArea.Left, Monitor.WorkArea.Bottom - Monitor.WorkArea.Top);
ImGuiMonitor.DpiScale = Monitor.DPI;
ImGuiMonitor.DpiScale = 1.0f;
if (Monitor.bIsPrimary)
{
@@ -165,6 +234,8 @@ void FCogImguiContext::OnDisplayMetricsChanged(const FDisplayMetrics& DisplayMet
//--------------------------------------------------------------------------------------------------------------------------
bool FCogImguiContext::BeginFrame(float InDeltaTime)
{
FCogImGuiContextScope ImGuiContextScope(ImGuiContext, PlotContext);
//-------------------------------------------------------------------------------------------------------
// Skip the first frame, to let the main widget update its TickSpaceGeometry which is returned by the
// plateform callback ImGui_GetWindowPos. When using viewports Imgui needs to know the main viewport
@@ -176,13 +247,17 @@ bool FCogImguiContext::BeginFrame(float InDeltaTime)
return false;
}
ImGui::SetCurrentContext(ImGuiContext);
ImPlot::SetImGuiContext(ImGuiContext);
ImPlot::SetCurrentContext(PlotContext);
ImGuiIO& IO = ImGui::GetIO();
IO.DeltaTime = InDeltaTime;
IO.DisplaySize = FCogImguiHelper::ToImVec2(MainWidget->GetTickSpaceGeometry().GetAbsoluteSize());
if (MainWidget != nullptr)
{
IO.DisplaySize = FCogImguiHelper::ToImVec2(MainWidget->GetTickSpaceGeometry().GetAbsoluteSize());
}
else
{
IO.DisplaySize = ImVec2(1080, 720);
}
//-------------------------------------------------------------------------------------------------------
// Build font
@@ -195,40 +270,45 @@ bool FCogImguiContext::BeginFrame(float InDeltaTime)
//-------------------------------------------------------------------------------------------------------
// Update which viewport is under the mouse
//-------------------------------------------------------------------------------------------------------
ImGuiID MouseViewportId = 0;
FSlateApplication& SlateApp = FSlateApplication::Get();
FWidgetPath WidgetsUnderCursor = SlateApp.LocateWindowUnderMouse(SlateApp.GetCursorPos(), SlateApp.GetInteractiveTopLevelWindows());
if (WidgetsUnderCursor.IsValid())
if (FSlateApplication::IsInitialized())
{
TSharedRef<SWindow> Window = WidgetsUnderCursor.GetWindow();
ImGuiID* ViewportId = WindowToViewportMap.Find(Window);
ImGuiID MouseViewportId = 0;
if (ViewportId != nullptr)
FWidgetPath WidgetsUnderCursor = FSlateApplication::Get().LocateWindowUnderMouse(
FSlateApplication::Get().GetCursorPos(),
FSlateApplication::Get().GetInteractiveTopLevelWindows());
if (WidgetsUnderCursor.IsValid())
{
MouseViewportId = *ViewportId;
}
else
{
MouseViewportId = ImGui::GetMainViewport()->ID;
TSharedRef<SWindow> Window = WidgetsUnderCursor.GetWindow();
ImGuiID* ViewportId = WindowToViewportMap.Find(Window);
if (ViewportId != nullptr)
{
MouseViewportId = *ViewportId;
}
else
{
MouseViewportId = ImGui::GetMainViewport()->ID;
}
}
IO.AddMouseViewportEvent(MouseViewportId);
//-------------------------------------------------------------------------------------------------------
// Refresh modifiers otherwise, when pressing ALT-TAB, the Alt modifier is always true
//-------------------------------------------------------------------------------------------------------
FModifierKeysState ModifierKeys = FSlateApplication::Get().GetModifierKeys();
if (ModifierKeys.IsControlDown() != IO.KeyCtrl) { IO.AddKeyEvent(ImGuiMod_Ctrl, ModifierKeys.IsControlDown()); }
if (ModifierKeys.IsShiftDown() != IO.KeyShift) { IO.AddKeyEvent(ImGuiMod_Shift, ModifierKeys.IsShiftDown()); }
if (ModifierKeys.IsAltDown() != IO.KeyAlt) { IO.AddKeyEvent(ImGuiMod_Alt, ModifierKeys.IsAltDown()); }
if (ModifierKeys.IsCommandDown() != IO.KeySuper) { IO.AddKeyEvent(ImGuiMod_Super, ModifierKeys.IsCommandDown()); }
}
IO.AddMouseViewportEvent(MouseViewportId);
//-------------------------------------------------------------------------------------------------------
// Refresh modifiers otherwise, when pressing ALT-TAB, the Alt modifier is always true
//-------------------------------------------------------------------------------------------------------
FModifierKeysState ModifierKeys = FSlateApplication::Get().GetModifierKeys();
if (ModifierKeys.IsControlDown() != IO.KeyCtrl) { IO.AddKeyEvent(ImGuiMod_Ctrl, ModifierKeys.IsControlDown()); }
if (ModifierKeys.IsShiftDown() != IO.KeyShift) { IO.AddKeyEvent(ImGuiMod_Shift, ModifierKeys.IsShiftDown()); }
if (ModifierKeys.IsAltDown() != IO.KeyAlt) { IO.AddKeyEvent(ImGuiMod_Alt, ModifierKeys.IsAltDown()); }
if (ModifierKeys.IsCommandDown() != IO.KeySuper) { IO.AddKeyEvent(ImGuiMod_Super, ModifierKeys.IsCommandDown()); }
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
if (bEnableInput)
if (bEnableInput || NetImgui::IsConnected())
{
IO.ConfigFlags &= ~ImGuiConfigFlags_NoMouse;
}
@@ -237,35 +317,24 @@ bool FCogImguiContext::BeginFrame(float InDeltaTime)
IO.ConfigFlags |= ImGuiConfigFlags_NoMouse;
}
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
const bool bHasMouse = (IO.ConfigFlags & ImGuiConfigFlags_NoMouse) == 0;
const bool bUpdateMouseMouseCursor = (IO.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) == 0;
if (bHasMouse && bUpdateMouseMouseCursor)
if (MainWidget != nullptr && FSlateApplication::IsInitialized())
{
MainWidget->SetCursor(FCogImguiInputHelper::ToSlateMouseCursor(ImGui::GetMouseCursor()));
}
const bool bHasMouse = (IO.ConfigFlags & ImGuiConfigFlags_NoMouse) == 0;
const bool bUpdateMouseMouseCursor = (IO.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) == 0;
if (bHasMouse && bUpdateMouseMouseCursor)
{
MainWidget->SetCursor(FCogImguiInputHelper::ToSlateMouseCursor(ImGui::GetMouseCursor()));
}
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
const FVector2D& MousePosition = SlateApp.GetCursorPos();
if (IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
IO.AddMousePosEvent(MousePosition.X, MousePosition.Y);
}
else
{
const FVector2D TransformedMousePosition = MousePosition - MainWidget->GetTickSpaceGeometry().GetAbsolutePosition();
IO.AddMousePosEvent(TransformedMousePosition.X, TransformedMousePosition.Y);
if (bEnableInput)
{
const ImVec2 mousePos = GetImguiMousePos();
IO.AddMousePosEvent(mousePos.x, mousePos.y);
}
}
bWantCaptureMouse = ImGui::GetIO().WantCaptureMouse;
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
if (bRefreshDPIScale)
{
bRefreshDPIScale = false;
@@ -278,16 +347,37 @@ bool FCogImguiContext::BeginFrame(float InDeltaTime)
}
ImGui::NewFrame();
//if (NetImgui::NewFrame(true) == false)
//{
// return false;
//}
//DrawDebug();
return true;
}
//--------------------------------------------------------------------------------------------------------------------------
ImVec2 FCogImguiContext::GetImguiMousePos()
{
const FVector2D& MousePosition = FSlateApplication::Get().GetCursorPos();
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
return ImVec2(MousePosition.X, MousePosition.Y);
}
const FVector2D TransformedMousePosition = MousePosition - MainWidget->GetTickSpaceGeometry().GetAbsolutePosition();
return ImVec2(TransformedMousePosition.X, TransformedMousePosition.Y);
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogImguiContext::EndFrame()
{
FCogImGuiContextScope ImGuiContextScope(ImGuiContext, PlotContext);
ImGui::Render();
//NetImgui::EndFrame();
ImGui_RenderWindow(ImGui::GetMainViewport(), nullptr);
ImGui::UpdatePlatformWindows();
@@ -297,6 +387,11 @@ void FCogImguiContext::EndFrame()
//--------------------------------------------------------------------------------------------------------------------------
void FCogImguiContext::ImGui_CreateWindow(ImGuiViewport* Viewport)
{
if (FSlateApplication::IsInitialized() == false)
{
return;
}
if (Viewport->ParentViewportId == 0)
{
return;
@@ -592,13 +687,16 @@ void FCogImguiContext::SetEnableInput(bool Value)
{
bEnableInput = Value;
if (FSlateApplication::IsInitialized() == false)
{
return;
}
if (bEnableInput)
{
FSlateThrottleManager::Get().DisableThrottle(true);
bIsThrottleDisabled = true;
FSlateApplication& SlateApp = FSlateApplication::Get();
if (ULocalPlayer* LocalPlayer = GetLocalPlayer())
{
LocalPlayer->GetSlateOperations()
@@ -612,7 +710,7 @@ void FCogImguiContext::SetEnableInput(bool Value)
{
FSlateThrottleManager::Get().DisableThrottle(false);
}
if (ULocalPlayer* LocalPlayer = GetLocalPlayer())
{
LocalPlayer->GetSlateOperations().CaptureMouse(GameViewport->GetGameViewportWidget().ToSharedRef());
@@ -639,6 +737,11 @@ void FCogImguiContext::SetShareMouseWithGameplay(bool Value)
//--------------------------------------------------------------------------------------------------------------------------
void FCogImguiContext::RefreshMouseCursor()
{
if (FSlateApplication::IsInitialized() == false)
{
return;
}
//-------------------------------------------------------------------------------------------
// Focus the main widget when enabling input otherwise the mouse can still be hidden because
// the gameplay might have the focus and might hide the cursor.
@@ -652,21 +755,24 @@ void FCogImguiContext::RefreshMouseCursor()
//-------------------------------------------------------------------------------------------
// Force to show the cursor when sharing mouse with gameplay for games that hide the cursor
//-------------------------------------------------------------------------------------------
if (APlayerController* PlayerController = GetLocalPlayerController(GameViewport->GetWorld()))
if (GameViewport != nullptr)
{
if (bHasSavedInitialCursorVisibility == false)
if (APlayerController* PlayerController = GetLocalPlayerController(GameViewport->GetWorld()))
{
bIsCursorInitiallyVisible = PlayerController->ShouldShowMouseCursor();
bHasSavedInitialCursorVisibility = true;
}
if (bHasSavedInitialCursorVisibility == false)
{
bIsCursorInitiallyVisible = PlayerController->ShouldShowMouseCursor();
bHasSavedInitialCursorVisibility = true;
}
if (bEnableInput && bShareMouse && bShareMouseWithGameplay)
{
PlayerController->SetShowMouseCursor(true);
}
else
{
PlayerController->SetShowMouseCursor(bIsCursorInitiallyVisible);
if (bEnableInput && bShareMouse && bShareMouseWithGameplay)
{
PlayerController->SetShowMouseCursor(true);
}
else
{
PlayerController->SetShowMouseCursor(bIsCursorInitiallyVisible);
}
}
}
}
@@ -686,6 +792,8 @@ void FCogImguiContext::SetDPIScale(float Value)
//--------------------------------------------------------------------------------------------------------------------------
void FCogImguiContext::BuildFont()
{
FCogImGuiContextScope ImGuiContextScope(ImGuiContext, PlotContext);
if (FontAtlasTexture != nullptr)
{
FontAtlasTexture->RemoveFromRoot();
@@ -703,7 +811,15 @@ void FCogImguiContext::BuildFont()
int32 TextureWidth, TextureHeight, BytesPerPixel;
IO.Fonts->GetTexDataAsRGBA32(&TextureDataRaw, &TextureWidth, &TextureHeight, &BytesPerPixel);
FontAtlasTexture = UTexture2D::CreateTransient(TextureWidth, TextureHeight, PF_R8G8B8A8, TEXT("ImGuiFontAtlas"));
#if UE_VERSION_OLDER_THAN(5, 5, 0)
const int32 PieSessionId = GPlayInEditorID;
#else
const int32 PieSessionId = UE::GetPlayInEditorID();
#endif
FString TextureName = FString::Format(TEXT("ImGuiFontAtlas{0}"), { PieSessionId });
FontAtlasTexture = UTexture2D::CreateTransient(TextureWidth, TextureHeight, PF_R8G8B8A8, *TextureName);
FontAtlasTexture->Filter = TF_Bilinear;
FontAtlasTexture->AddressX = TA_Wrap;
FontAtlasTexture->AddressY = TA_Wrap;
@@ -720,7 +836,9 @@ void FCogImguiContext::BuildFont()
//--------------------------------------------------------------------------------------------------------------------------
bool FCogImguiContext::IsConsoleOpened() const
{
return GameViewport->ViewportConsole && GameViewport->ViewportConsole->ConsoleState != NAME_None;
return GameViewport != nullptr
&& GameViewport->ViewportConsole
&& GameViewport->ViewportConsole->ConsoleState != NAME_None;
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -739,15 +857,14 @@ void FCogImguiContext::DrawDebug()
ImVec2 LocalSize = FCogImguiHelper::ToImVec2(MainWidget->GetTickSpaceGeometry().GetLocalSize());
ImGui::InputFloat2("Widget Local Size", &LocalSize.x, "%0.1f");
FSlateApplication& SlateApp = FSlateApplication::Get();
ImVec2 MousePosition = FCogImguiHelper::ToImVec2(SlateApp.GetCursorPos());
ImVec2 MousePosition = FCogImguiHelper::ToImVec2(FSlateApplication::Get().GetCursorPos());
ImGui::InputFloat2("Mouse", &MousePosition.x, "%0.1f");
ImGuiIO& IO = ImGui::GetIO();
ImGui::InputFloat2("ImGui Mouse", &IO.MousePos.x, "%0.1f");
FString Focus = "None";
if (TSharedPtr<SWidget> KeyboardFocusedWidget = SlateApp.GetKeyboardFocusedWidget())
if (TSharedPtr<SWidget> KeyboardFocusedWidget = FSlateApplication::Get().GetKeyboardFocusedWidget())
{
Focus = KeyboardFocusedWidget->ToString();
}
@@ -777,3 +894,16 @@ ULocalPlayer* FCogImguiContext::GetLocalPlayer() const
ULocalPlayer* LocalPlayer = World->GetFirstLocalPlayerFromController();
return LocalPlayer;
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogImguiContext::GetSkipRendering() const
{
return bSkipRendering;
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogImguiContext::SetSkipRendering(bool Value)
{
bSkipRendering = Value;
}
@@ -38,7 +38,7 @@ float FCogImguiHelper::GetNextItemWidth()
{
float Width;
const ImGuiContext& g = *GImGui;
if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth)
{
Width = g.NextItemData.Width;
}
@@ -93,6 +93,8 @@ FReply SCogImguiInputCatcherWidget::OnMouseButtonUp(const FGeometry& MyGeometry,
//--------------------------------------------------------------------------------------------------------------------------
FReply SCogImguiInputCatcherWidget::HandleMouseButtonEvent(const FPointerEvent& MouseEvent, bool Down)
{
FCogImGuiContextScope ImGuiContextScope(*Context);
if (Context->GetEnableInput() == false)
{
UE_LOG(LogCogImGui, VeryVerbose, TEXT("SCogImguiInputCatcherWidget::HandleMouseButtonEvent | Window:%s | Unhandled | EnableInput == false | Down:%d"), Window.IsValid() ? *Window->GetTitle().ToString() : *FString("None"), Down);
@@ -110,6 +112,8 @@ FReply SCogImguiInputCatcherWidget::HandleMouseButtonEvent(const FPointerEvent&
//--------------------------------------------------------------------------------------------------------------------------
FReply SCogImguiInputCatcherWidget::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
FCogImGuiContextScope ImGuiContextScope(*Context);
if (Context->GetEnableInput() == false)
{
return FReply::Unhandled();
@@ -33,4 +33,4 @@ void FCogImguiModule::ShutdownModule()
//--------------------------------------------------------------------------------------------------------------------------
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FCogImguiModule, CogImGui)
IMPLEMENT_MODULE(FCogImguiModule, CogImgui)
@@ -45,6 +45,10 @@ int32 SCogImguiWidget::OnPaint(
const FWidgetStyle& WidgetStyle,
bool bParentEnabled) const
{
if (Context == nullptr || Context->GetSkipRendering())
{
return LayerId;
}
const FSlateRenderTransform Transform(FCogImguiHelper::RoundTranslation(AllottedGeometry.GetAccumulatedRenderTransform().GetTranslation() - FVector2d(DrawData.DisplayPos)));
@@ -71,7 +75,7 @@ int32 SCogImguiWidget::OnPaint(
TArray VerticesSlice(Vertices.GetData() + DrawCmd.VtxOffset, Vertices.Num() - DrawCmd.VtxOffset);
TArray IndicesSlice(Indices.GetData() + DrawCmd.IdxOffset, DrawCmd.ElemCount);
UTexture2D* Texture = DrawCmd.GetTexID();
UTexture2D* Texture = Cast<UTexture2D>(DrawCmd.GetTexID());
if (TextureBrush.GetResourceObject() != Texture)
{
TextureBrush.SetResourceObject(Texture);
@@ -112,6 +116,8 @@ FVector2D SCogImguiWidget::ComputeDesiredSize(float Scale) const
//--------------------------------------------------------------------------------------------------------------------------
FReply SCogImguiWidget::OnKeyChar(const FGeometry& MyGeometry, const FCharacterEvent& CharacterEvent)
{
FCogImGuiContextScope ImGuiContextScope(*Context);
ImGuiIO& IO = ImGui::GetIO();
IO.AddInputCharacter(FCogImguiInputHelper::CastInputChar(CharacterEvent.GetCharacter()));
@@ -134,6 +140,8 @@ FReply SCogImguiWidget::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& Ke
//--------------------------------------------------------------------------------------------------------------------------
FReply SCogImguiWidget::HandleKeyEvent(const FKeyEvent& KeyEvent, bool Down)
{
FCogImGuiContextScope ImGuiContextScope(*Context);
if (Context->GetEnableInput() == false)
{
return FReply::Unhandled();
@@ -196,6 +204,8 @@ FReply SCogImguiWidget::OnMouseButtonUp(const FGeometry& MyGeometry, const FPoin
//--------------------------------------------------------------------------------------------------------------------------
FReply SCogImguiWidget::HandleMouseButtonEvent(const FPointerEvent& MouseEvent, bool Down)
{
FCogImGuiContextScope ImGuiContextScope(*Context);
if (Context->GetEnableInput() == false)
{
UE_LOG(LogCogImGui, VeryVerbose, TEXT("SCogImguiWidget::HandleMouseButtonEvent | %s | Unhandled | EnableInput == false | Down:%d"), Window.IsValid() ? *Window->GetTitle().ToString() : *FString("None"), Down);
@@ -212,6 +222,8 @@ FReply SCogImguiWidget::HandleMouseButtonEvent(const FPointerEvent& MouseEvent,
//--------------------------------------------------------------------------------------------------------------------------
FReply SCogImguiWidget::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
FCogImGuiContextScope ImGuiContextScope(*Context);
if (Context->GetEnableInput() == false)
{
return FReply::Unhandled();
@@ -224,6 +236,8 @@ FReply SCogImguiWidget::OnMouseWheel(const FGeometry& MyGeometry, const FPointer
//--------------------------------------------------------------------------------------------------------------------------
FReply SCogImguiWidget::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
FCogImGuiContextScope ImGuiContextScope(*Context);
if (Context->GetEnableInput() == false)
{
//UE_LOG(LogCogImGui, VeryVerbose, TEXT("SCogImguiWidget::OnMouseMove | Window:%s | Unhandled | EnableInput == false"), Window.IsValid() ? *Window->GetTitle().ToString() : *FString("None"));
@@ -25,6 +25,18 @@ struct COGIMGUI_API FCogImGuiViewportData
TWeakPtr<SCogImguiWidget> Widget = nullptr;
};
struct COGIMGUI_API FCogImGuiContextScope
{
UE_NODISCARD_CTOR explicit FCogImGuiContextScope(FCogImguiContext& CogImguiContext);
UE_NODISCARD_CTOR explicit FCogImGuiContextScope(ImGuiContext* GuiCtx, ImPlotContext* PlotCtx);
~FCogImGuiContextScope();
private:
ImGuiContext* PrevContext = nullptr;
ImPlotContext* PrevPlotContext = nullptr;
};
class COGIMGUI_API FCogImguiContext : public TSharedFromThis<FCogImguiContext>
{
public:
@@ -53,18 +65,30 @@ public:
bool BeginFrame(float InDeltaTime);
void GetCursorPos(ImGuiIO& IO);
void EndFrame();
float GetDpiScale() const { return DpiScale; }
void SetDPIScale(float Value);
bool GetSkipRendering() const;
void SetSkipRendering(bool Value);
ImVec2 GetImguiMousePos();
TObjectPtr<const UGameViewportClient> GetGameViewport() const { return GameViewport; }
TSharedPtr<const SCogImguiWidget> GetMainWidget() const { return MainWidget; }
static bool GetIsNetImguiInitialized() { return bIsNetImguiInitialized; }
private:
friend struct FCogImGuiContextScope;
void OnDisplayMetricsChanged(const FDisplayMetrics& DisplayMetrics) const;
bool IsConsoleOpened() const;
@@ -147,4 +171,9 @@ private:
bool bWantCaptureMouse = false;
float DpiScale = 1.f;
bool bSkipRendering = false;
static bool bIsNetImguiInitialized;
};
@@ -5,7 +5,7 @@
#include "Layout/SlateRect.h"
#include "Rendering/SlateRenderTransform.h"
COGIMGUI_API DECLARE_LOG_CATEGORY_EXTERN(LogCogImGui, Warning, All);
COGIMGUI_API DECLARE_LOG_CATEGORY_EXTERN(LogCogImGui, Verbose, All);
struct ImGuiWindow;
@@ -40,9 +40,18 @@ public class CogWindow : ModuleRules
"NetCore",
}
);
DynamicallyLoadedModuleNames.AddRange(
if (Target.bBuildEditor)
{
PrivateDependencyModuleNames.AddRange(new string[]
{
"UnrealEd",
"AssetTools"
});
}
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
}
@@ -60,7 +60,7 @@ void FCogWindow::Render(float DeltaTime)
const FString WindowTitle = GetTitle() + "##" + Name;
if (bHasMenu && bHideMenu == false)
if (bHasMenu && bShowMenu)
{
WindowFlags |= ImGuiWindowFlags_MenuBar;
}
@@ -81,10 +81,10 @@ void FCogWindow::Render(float DeltaTime)
{
if (bHasMenu)
{
ImGui::Checkbox("Hide Menu", &bHideMenu);
ImGui::Checkbox("Show Menu", &bShowMenu);
}
if (ImGui::Button("Reset"))
if (ImGui::Button("Reset Settings"))
{
ResetConfig();
}
@@ -138,6 +138,17 @@ void FCogWindow::SetSelection(AActor* NewSelection)
OnSelectionChanged(OldActor, NewSelection);
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogWindow::SetIsVisible(const bool Value)
{
if (bIsVisible == Value)
{ return; }
bIsVisible = Value;
OnWindowVisibilityChanged(Value);
}
//--------------------------------------------------------------------------------------------------------------------------
APawn* FCogWindow::GetLocalPlayerPawn() const
{
@@ -0,0 +1,66 @@
#include "CogWindowConsoleCommandManager.h"
#include "Engine/World.h"
TMap<FString, FCogCommandInfo> FCogWindowConsoleCommandManager::CommandMap;
//--------------------------------------------------------------------------------------------------------------------------
void FCogWindowConsoleCommandManager::RegisterWorldConsoleCommand(const TCHAR* InName, const TCHAR* InHelp, UWorld* InWorld, const FCogWindowConsoleCommandDelegate& InDelegate)
{
FCogCommandInfo& commandInfo = CommandMap.FindOrAdd(InName);
if (commandInfo.Receivers.Num() == 0)
{
commandInfo.ConsoleObject = IConsoleManager::Get().RegisterConsoleCommand
(
InName,
InHelp,
FConsoleCommandWithWorldAndArgsDelegate::CreateLambda(
[InName](const TArray<FString>& Args, UWorld* InCommandWorld)
{
FCogCommandInfo* commandInfo = CommandMap.Find(InName);
if (commandInfo == nullptr)
{
return;
}
for (auto& receiver : commandInfo->Receivers)
{
if (receiver.World == InCommandWorld)
{
receiver.Delegate.ExecuteIfBound(Args, InCommandWorld);
break;
}
}
}),
ECVF_Cheat
);
}
FCogCommandReceiver& receiver = commandInfo.Receivers.AddDefaulted_GetRef();
receiver.World = InWorld;
receiver.Delegate = InDelegate;
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogWindowConsoleCommandManager::UnregisterAllWorldConsoleCommands(const UWorld* InWorld)
{
for (auto& kv : CommandMap)
{
FCogCommandInfo& commandInfo = kv.Value;
for (int32 i = commandInfo.Receivers.Num() - 1; i >= 0; --i)
{
if (commandInfo.Receivers[i].World == InWorld)
{
commandInfo.Receivers.RemoveAt(i);
}
}
if (commandInfo.Receivers.Num() == 0 && commandInfo.ConsoleObject != nullptr)
{
IConsoleManager::Get().UnregisterConsoleObject(commandInfo.ConsoleObject);
commandInfo.ConsoleObject = nullptr;
}
}
}
@@ -6,14 +6,18 @@
#include "CogWindow_Layouts.h"
#include "CogWindow_Settings.h"
#include "CogWindow_Spacing.h"
#include "CogWindowConsoleCommandManager.h"
#include "CogWindowHelper.h"
#include "CogWindowWidgets.h"
#include "Engine/Engine.h"
#include "GameFramework/PlayerInput.h"
#include "HAL/IConsoleManager.h"
#include "imgui_internal.h"
#include "Misc/CoreMisc.h"
#include "NetImgui_Api.h"
FString UCogWindowManager::ToggleInputCommand = TEXT("Cog.ToggleInput");
FString UCogWindowManager::DisableInputCommand = TEXT("Cog.DisableInput");
FString UCogWindowManager::LoadLayoutCommand = TEXT("Cog.LoadLayout");
FString UCogWindowManager::SaveLayoutCommand = TEXT("Cog.SaveLayout");
FString UCogWindowManager::ResetLayoutCommand = TEXT("Cog.ResetLayout");
@@ -28,13 +32,21 @@ void UCogWindowManager::PostInitProperties()
{
Super::PostInitProperties();
if (bRegisterDefaultCommands)
{
if (RegisterDefaultCommandBindings())
{
bRegisterDefaultCommands = false;
}
}
//if (bRegisterDefaultCommands)
//{
// if (RegisterDefaultCommandBindings())
// {
// bRegisterDefaultCommands = false;
// }
//}
//-------------------------------------------------------------------------------
// Currently always register default commands.
// Since UE5.4, the ini files must have this to be saved:
// [SectionsToSave]
// bCanSaveAllSections = True
//-------------------------------------------------------------------------------
RegisterDefaultCommandBindings();
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -42,6 +54,8 @@ void UCogWindowManager::InitializeInternal()
{
Context.Initialize();
FCogImGuiContextScope ImGuiContextScope(Context);
ImGuiSettingsHandler IniHandler;
IniHandler.TypeName = "Cog";
IniHandler.TypeHash = ImHashStr("Cog");
@@ -61,50 +75,86 @@ void UCogWindowManager::InitializeInternal()
LayoutsWindow = AddWindow<FCogWindow_Layouts>("Window.Layouts", false);
SettingsWindow = AddWindow<FCogWindow_Settings>("Window.Settings", false);
ConsoleCommands.Add(IConsoleManager::Get().RegisterConsoleCommand(
*ToggleInputCommand,
FCogWindowConsoleCommandManager::RegisterWorldConsoleCommand(
*ToggleInputCommand,
TEXT("Toggle the input focus between the Game and ImGui"),
FConsoleCommandWithArgsDelegate::CreateLambda([this](const TArray<FString>& Args) { ToggleInputMode(); }),
ECVF_Cheat));
GetWorld(),
FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray<FString>& InArgs, UWorld* InWorld)
{
ToggleInputMode();
}));
ConsoleCommands.Add(IConsoleManager::Get().RegisterConsoleCommand(
FCogWindowConsoleCommandManager::RegisterWorldConsoleCommand(
*DisableInputCommand,
TEXT("Disable ImGui input"),
GetWorld(),
FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray<FString>& InArgs, UWorld* InWorld)
{
DisableInputMode();
}));
FCogWindowConsoleCommandManager::RegisterWorldConsoleCommand(
*ResetLayoutCommand,
TEXT("Reset the layout."),
FConsoleCommandWithArgsDelegate::CreateLambda([this](const TArray<FString>& Args) { if (Args.Num() > 0) { ResetLayout(); }}),
ECVF_Cheat));
GetWorld(),
FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray<FString>& InArgs, UWorld* InWorld)
{
if (InArgs.Num() > 0)
{
ResetLayout();
}
}));
ConsoleCommands.Add(IConsoleManager::Get().RegisterConsoleCommand(
FCogWindowConsoleCommandManager::RegisterWorldConsoleCommand(
*LoadLayoutCommand,
TEXT("Load the layout. Cog.LoadLayout <Index>"),
FConsoleCommandWithArgsDelegate::CreateLambda([this](const TArray<FString>& Args) { if (Args.Num() > 0) { LoadLayout(FCString::Atoi(*Args[0])); }}),
ECVF_Cheat));
GetWorld(),
FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray<FString>& InArgs, UWorld* InWorld)
{
if (InArgs.Num() > 0)
{
LoadLayout(FCString::Atoi(*InArgs[0]));
}
}));
ConsoleCommands.Add(IConsoleManager::Get().RegisterConsoleCommand(
*SaveLayoutCommand,
FCogWindowConsoleCommandManager::RegisterWorldConsoleCommand(
*SaveLayoutCommand,
TEXT("Save the layout. Cog.SaveLayout <Index>"),
FConsoleCommandWithArgsDelegate::CreateLambda([this](const TArray<FString>& Args) { if (Args.Num() > 0) { SaveLayout(FCString::Atoi(*Args[0])); }}),
ECVF_Cheat));
GetWorld(),
FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray<FString>& InArgs, UWorld* InWorld)
{
if (InArgs.Num() > 0)
{
SaveLayout(FCString::Atoi(*InArgs[0]));
}
}));
IsInitialized = true;
}
//--------------------------------------------------------------------------------------------------------------------------
void UCogWindowManager::Shutdown()
{
//------------------------------------------------------------
FCogImGuiContextScope ImGuiContextScope(Context);
//------------------------------------------------------------------
// Call PreSaveConfig before destroying imgui context
// if PreSaveConfig needs to read ImGui IO for example
//------------------------------------------------------------
//------------------------------------------------------------------
for (FCogWindow* Window : Windows)
{
Window->PreSaveConfig();
}
//------------------------------------------------------------
//------------------------------------------------------------------
// Destroy ImGui before destroying the windows to make sure
// imgui serialize their visibility state in imgui.ini
//------------------------------------------------------------
Context.Shutdown();
//------------------------------------------------------------------
if (IsInitialized == true)
{
Context.Shutdown();
}
SaveConfig();
@@ -120,16 +170,15 @@ void UCogWindowManager::Shutdown()
Config->SaveConfig();
}
for (IConsoleObject* ConsoleCommand : ConsoleCommands)
{
IConsoleManager::Get().UnregisterConsoleObject(ConsoleCommand);
}
FCogWindowConsoleCommandManager::UnregisterAllWorldConsoleCommands(GetWorld());
}
//--------------------------------------------------------------------------------------------------------------------------
void UCogWindowManager::Tick(float DeltaTime)
{
if (GEngine->GameViewport == nullptr)
FCogImGuiContextScope ImGuiContextScope(Context);
if (GEngine->GameViewport == nullptr && IsRunningDedicatedServer() == false)
{
return;
}
@@ -151,6 +200,9 @@ void UCogWindowManager::Tick(float DeltaTime)
Window->GameTick(DeltaTime);
}
const bool shouldSkipRendering = NetImgui::IsConnected() && bIsSelectionModeActive == false;
Context.SetSkipRendering(shouldSkipRendering);
if (Context.BeginFrame(DeltaTime))
{
Render(DeltaTime);
@@ -161,8 +213,10 @@ void UCogWindowManager::Tick(float DeltaTime)
//--------------------------------------------------------------------------------------------------------------------------
void UCogWindowManager::Render(float DeltaTime)
{
FCogImGuiContextScope ImGuiContextScope(Context);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0));
ImGui::DockSpaceOverViewport(0, ImGui::GetWindowViewport(), ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_NoDockingOverCentralNode | ImGuiDockNodeFlags_AutoHideTabBar);
ImGui::DockSpaceOverViewport(0, nullptr, ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_NoDockingOverCentralNode | ImGuiDockNodeFlags_AutoHideTabBar);
ImGui::PopStyleColor(1);
@@ -172,19 +226,25 @@ void UCogWindowManager::Render(float DeltaTime)
FCogWindowWidgets::PushStyleCompact();
}
if (bHideAllWindows == false)
//----------------------------------------------------------------------
// There is no need to have Imgui input enabled if the imgui rendering
// is only done on the NetImgui server. So we disable imgui input.
//----------------------------------------------------------------------
if (Context.GetEnableInput() && NetImgui::IsConnected() && bIsSelectionModeActive == false)
{
if (Context.GetEnableInput())
{
RenderMainMenu();
}
Context.SetEnableInput(false);
}
if ((Context.GetEnableInput() || NetImgui::IsConnected()) && bIsSelectionModeActive == false)
{
RenderMainMenu();
}
for (FCogWindow* Window : Windows)
{
Window->RenderTick(DeltaTime);
if (Window->GetIsVisible() && bHideAllWindows == false)
if (Window->GetIsVisible() && bIsSelectionModeActive == false)
{
if (SettingsWindow->GetSettingsConfig()->bTransparentMode)
{
@@ -240,15 +300,22 @@ FCogWindow* UCogWindowManager::FindWindowByID(const ImGuiID ID)
}
//--------------------------------------------------------------------------------------------------------------------------
void UCogWindowManager::SetHideAllWindows(const bool Value)
void UCogWindowManager::SetActivateSelectionMode(const bool Value)
{
HideAllWindowsCounter = FMath::Max(HideAllWindowsCounter + (Value ? +1 : -1), 0);
bHideAllWindows = HideAllWindowsCounter > 0;
SelectionModeActiveCounter = FMath::Max(SelectionModeActiveCounter + (Value ? 1 : -1), 0);
bIsSelectionModeActive = SelectionModeActiveCounter > 0;
if (bIsSelectionModeActive)
{
Context.SetEnableInput(true);
}
}
//--------------------------------------------------------------------------------------------------------------------------
void UCogWindowManager::ResetLayout()
{
FCogImGuiContextScope ImGuiContextScope(Context);
for (const FCogWindow* Window : Windows)
{
ImGui::SetWindowPos(TCHAR_TO_ANSI(*Window->GetName()), ImVec2(10, 10), ImGuiCond_Always);
@@ -280,6 +347,8 @@ void UCogWindowManager::LoadLayout(const int32 LayoutIndex)
//--------------------------------------------------------------------------------------------------------------------------
void UCogWindowManager::SaveLayout(const int32 LayoutIndex)
{
FCogImGuiContextScope ImGuiContextScope(Context);
const FString Filename = *FCogImguiHelper::GetIniFilePath(FString::Printf(TEXT("imgui_layout_%d"), LayoutIndex));
ImGui::SaveIniSettingsToDisk(TCHAR_TO_ANSI(*Filename));
}
@@ -351,7 +420,6 @@ void UCogWindowManager::RenderMainMenu()
ImGui::Separator();
RenderMenuItem(*LayoutsWindow, "Layouts");
RenderMenuItem(*SettingsWindow, "Settings");
if (ImGui::BeginMenu("Spacing"))
@@ -602,11 +670,15 @@ void* UCogWindowManager::SettingsHandler_ReadOpen(ImGuiContext* Context, ImGuiSe
//--------------------------------------------------------------------------------------------------------------------------
void UCogWindowManager::SettingsHandler_ReadLine(ImGuiContext* Context, ImGuiSettingsHandler* Handler, void* Entry, const char* Line)
{
//-----------------------------------------------------------------------------------
// Load the visibility of windows.
//-----------------------------------------------------------------------------------
if (Entry == (void*)1)
{
ImGuiID Id;
int32 ShowMenu;
#if PLATFORM_WINDOWS || PLATFORM_MICROSOFT
if (sscanf_s(Line, "0x%08X", &Id) == 1)
if (sscanf_s(Line, "0x%08X %d", &Id, &ShowMenu) == 2)
#else
if (sscanf(Line, "0x%08X", &Id) == 1)
#endif
@@ -615,9 +687,13 @@ void UCogWindowManager::SettingsHandler_ReadLine(ImGuiContext* Context, ImGuiSet
if (FCogWindow* Window = Manager->FindWindowByID(Id))
{
Window->SetIsVisible(true);
Window->bShowMenu = (ShowMenu > 0);
}
}
}
//-----------------------------------------------------------------------------------
// Load which widgets are present in the main menu bar and with what order.
//-----------------------------------------------------------------------------------
else if (Entry == (void*)2)
{
ImGuiID Id;
@@ -645,16 +721,28 @@ void UCogWindowManager::SettingsHandler_WriteAll(ImGuiContext* Context, ImGuiSet
{
const UCogWindowManager* Manager = (UCogWindowManager*)Handler->UserData;
//-----------------------------------------------------------------------------------
// Save the visibility of windows. Example:
// [Cog][Windows]
// 0xB5D96693
// 0xBF3390B5
//-----------------------------------------------------------------------------------
Buffer->appendf("[%s][Windows]\n", Handler->TypeName);
for (const FCogWindow* Window : Manager->Windows)
{
if (Window->GetIsVisible())
{
Buffer->appendf("0x%08X\n", Window->GetID());
Buffer->appendf("0x%08X %d\n", Window->GetID(), (int32)Window->bShowMenu);
}
}
Buffer->append("\n");
//-----------------------------------------------------------------------------------
// Save which widgets are present in the main menu bar and with what order. Example:
// [Cog][Widgets]
// 0x639F1181 1
// 0x52BDE3E0 1
//-----------------------------------------------------------------------------------
Buffer->appendf("[%s][Widgets]\n", Handler->TypeName);
for (const FCogWindow* Window : Manager->Widgets)
{
@@ -681,6 +769,10 @@ bool UCogWindowManager::RegisterDefaultCommandBindings()
}
UPlayerInput* PlayerInput = FCogImguiInputHelper::GetPlayerInput(*GetWorld());
if (PlayerInput == nullptr)
{
return false;
}
AddCommand(PlayerInput, "Cog.ToggleInput", EKeys::F1);
AddCommand(PlayerInput, "Cog.LoadLayout 1", EKeys::F2);
@@ -690,13 +782,17 @@ bool UCogWindowManager::RegisterDefaultCommandBindings()
SortCommands(PlayerInput);
PlayerInput->SaveConfig();
return true;
}
//--------------------------------------------------------------------------------------------------------------------------
void UCogWindowManager::AddCommand(UPlayerInput* PlayerInput, const FString& Command, const FKey& Key)
{
if (PlayerInput == nullptr)
{
return;
}
//---------------------------------------------------
// Reassign conflicting commands
//---------------------------------------------------
@@ -788,3 +884,9 @@ void UCogWindowManager::ToggleInputMode()
Context.SetEnableInput(!Context.GetEnableInput());
}
//--------------------------------------------------------------------------------------------------------------------------
void UCogWindowManager::DisableInputMode()
{
UE_LOG(LogCogImGui, Verbose, TEXT("UCogWindowManager::DisableInputMode"));
Context.SetEnableInput(false);
}
@@ -13,13 +13,23 @@
#include "imgui_internal.h"
#include "InputCoreTypes.h"
#if WITH_EDITOR
#include "IAssetTools.h"
#include "Subsystems/AssetEditorSubsystem.h"
#endif
//--------------------------------------------------------------------------------------------------------------------------
void FCogWindowWidgets::BeginTableTooltip()
bool FCogWindowWidgets::BeginTableTooltip()
{
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(4, 4));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::PushStyleColor(ImGuiCol_PopupBg, IM_COL32(29, 42, 62, 240));
ImGui::BeginTooltip();
if (ImGui::BeginTooltip() == false)
{
EndTableTooltip();
return false;
}
return true;
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -30,16 +40,42 @@ void FCogWindowWidgets::EndTableTooltip()
ImGui::PopStyleVar(2);
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogWindowWidgets::BeginItemTableTooltip()
{
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort) == false)
{ return false; }
return BeginTableTooltip();
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogWindowWidgets::EndItemTableTooltip()
{
return EndTableTooltip();
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogWindowWidgets::ThinSeparatorText(const char* Label)
{
ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextBorderSize, 2);
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(100, 100, 100, 255));
ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextBorderSize, 2);
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(100, 100, 100, 255));
ImGui::SeparatorText(Label);
ImGui::SeparatorText(Label);
ImGui::PopStyleColor();
ImGui::PopStyleVar();
ImGui::PopStyleVar();
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogWindowWidgets::DarkCollapsingHeader(const char* InLabel, ImGuiTreeNodeFlags InFlags)
{
ImGui::PushStyleColor(ImGuiCol_Header, IM_COL32(66, 66, 66, 79));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, IM_COL32(62, 62, 62, 204));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, IM_COL32(86, 86, 86, 255));
const bool open = ImGui::CollapsingHeader(InLabel, InFlags);
ImGui::PopStyleColor(3);
return open;
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -226,10 +262,10 @@ void FCogWindowWidgets::AddTextWithShadow(ImDrawList* DrawList, const ImVec2& Po
//--------------------------------------------------------------------------------------------------------------------------
void FCogWindowWidgets::SearchBar(ImGuiTextFilter& Filter, float Width /*= -1*/)
{
const ImGuiWindow* Window = FCogImguiHelper::GetCurrentWindow();
const ImVec2 Pos1 = Window->DC.CursorPos;
const ImGuiWindow* Window = FCogImguiHelper::GetCurrentWindow();
const ImVec2 Pos1 = Window->DC.CursorPos;
Filter.Draw("##Filter", Width);
const ImVec2 Pos2 = Window->DC.CursorPosPrevLine;
const ImVec2 Pos2 = Window->DC.CursorPosPrevLine;
if (ImGui::IsItemActive() == false && Filter.Filters.empty())
{
@@ -355,7 +391,7 @@ bool FCogWindowWidgets::ComboboxEnum(const char* Label, UEnum* Enum, int64 Curre
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogWindowWidgets::CheckBoxState(const char* Label, ECheckBoxState& State)
bool FCogWindowWidgets::CheckBoxState(const char* Label, ECheckBoxState& State, bool ShowTooltip)
{
const char* TooltipText = "Invalid";
@@ -399,14 +435,14 @@ bool FCogWindowWidgets::CheckBoxState(const char* Label, ECheckBoxState& State)
ImGui::PopStyleColor(5);
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
if (ShowTooltip)
{
ImGui::SetTooltip("%s", TooltipText);
ImGui::SetItemTooltip("%s", TooltipText);
}
if (IsPressed)
if (IsPressed)
{
switch (State)
switch (State)
{
case ECheckBoxState::Checked: State = ECheckBoxState::Unchecked; break;
case ECheckBoxState::Unchecked: State = ECheckBoxState::Undetermined; break;
@@ -682,7 +718,7 @@ bool FCogWindowWidgets::CollisionProfileChannel(const UCollisionProfile& Collisi
bool Result = false;
FCogImguiHelper::ColorEdit4("Color", ChannelColor, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel);
ImGui::SameLine();
ImGui::SameLine();
bool IsCollisionActive = (Channels & ECC_TO_BITFIELD(ChannelIndex)) > 0;
const FName ChannelName = CollisionProfile.ReturnChannelNameFromContainerIndex(ChannelIndex);
@@ -700,7 +736,7 @@ bool FCogWindowWidgets::CollisionProfileChannel(const UCollisionProfile& Collisi
}
}
return Result;
return Result;
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -734,7 +770,7 @@ bool FCogWindowWidgets::CollisionProfileChannels(int32& Channels)
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogWindowWidgets::ActorsListWithFilters(const UWorld& World, const TArray<TSubclassOf<AActor>>& ActorClasses, int32& SelectedActorClassIndex, ImGuiTextFilter* Filter, const APawn* LocalPlayerPawn, const FCogWindowActorContextMenuFunction& ContextMenuFunction)
bool FCogWindowWidgets::ActorsListWithFilters(AActor*& NewSelection, const UWorld& World, const TArray<TSubclassOf<AActor>>& ActorClasses, int32& SelectedActorClassIndex, ImGuiTextFilter* Filter, const APawn* LocalPlayerPawn, const FCogWindowActorContextMenuFunction& ContextMenuFunction)
{
TSubclassOf<AActor> SelectedClass = AActor::StaticClass();
if (ActorClasses.IsValidIndex(SelectedActorClassIndex))
@@ -783,14 +819,14 @@ bool FCogWindowWidgets::ActorsListWithFilters(const UWorld& World, const TArray<
// Actor List
//------------------------
ImGui::BeginChild("ActorsList", ImVec2(-1, -1), false);
const bool SelectionChanged = ActorsList(World, SelectedClass, Filter, LocalPlayerPawn, ContextMenuFunction);
const bool SelectionChanged = ActorsList(NewSelection, World, SelectedClass, Filter, LocalPlayerPawn, ContextMenuFunction);
ImGui::EndChild();
return SelectionChanged;
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogWindowWidgets::ActorsList(const UWorld& World, const TSubclassOf<AActor> ActorClass, const ImGuiTextFilter* Filter, const APawn* LocalPlayerPawn, const FCogWindowActorContextMenuFunction& ContextMenuFunction)
bool FCogWindowWidgets::ActorsList(AActor*& NewSelection, const UWorld& World, const TSubclassOf<AActor> ActorClass, const ImGuiTextFilter* Filter, const APawn* LocalPlayerPawn, const FCogWindowActorContextMenuFunction& ContextMenuFunction)
{
TArray<AActor*> Actors;
for (TActorIterator It(&World, ActorClass); It; ++It)
@@ -813,8 +849,8 @@ bool FCogWindowWidgets::ActorsList(const UWorld& World, const TSubclassOf<AActor
}
}
const AActor* OldSelection = FCogDebug::GetSelection();
const AActor* NewSelection = OldSelection;
AActor* OldSelection = FCogDebug::GetSelection();
NewSelection = OldSelection;
ImGuiListClipper Clipper;
Clipper.Begin(Actors.Num());
@@ -822,7 +858,7 @@ bool FCogWindowWidgets::ActorsList(const UWorld& World, const TSubclassOf<AActor
{
for (int32 i = Clipper.DisplayStart; i < Clipper.DisplayEnd; i++)
{
AActor* Actor = Actors[i];
AActor* Actor = Actors[i];
if (Actor == nullptr)
{
continue;
@@ -833,7 +869,7 @@ bool FCogWindowWidgets::ActorsList(const UWorld& World, const TSubclassOf<AActor
const bool bIsSelected = Actor == FCogDebug::GetSelection();
if (ImGui::Selectable(TCHAR_TO_ANSI(*FCogWindowHelper::GetActorName(*Actor)), bIsSelected))
{
FCogDebug::SetSelection(&World, Actor);
//FCogDebug::SetSelection(&World, Actor);
NewSelection = Actor;
}
@@ -862,17 +898,17 @@ bool FCogWindowWidgets::ActorsList(const UWorld& World, const TSubclassOf<AActor
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogWindowWidgets::MenuActorsCombo(const char* StrID, const UWorld& World, TSubclassOf<AActor> ActorClass, const FCogWindowActorContextMenuFunction& ContextMenuFunction)
bool FCogWindowWidgets::MenuActorsCombo(const char* StrID, AActor*& NewSelection, const UWorld& World, TSubclassOf<AActor> ActorClass, const FCogWindowActorContextMenuFunction& ContextMenuFunction)
{
int32 SelectedActorClassIndex = 0;
const TArray ActorClasses = { ActorClass };
AActor* Actor = nullptr;
return MenuActorsCombo(StrID, World, ActorClasses, SelectedActorClassIndex, nullptr, nullptr, ContextMenuFunction);
return MenuActorsCombo(StrID, NewSelection, World, ActorClasses, SelectedActorClassIndex, nullptr, nullptr, ContextMenuFunction);
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogWindowWidgets::MenuActorsCombo(const char* StrID, const UWorld& World, const TArray<TSubclassOf<AActor>>& ActorClasses, int32& SelectedActorClassIndex, ImGuiTextFilter* Filter, const APawn* LocalPlayerPawn, const FCogWindowActorContextMenuFunction& ContextMenuFunction)
bool FCogWindowWidgets::MenuActorsCombo(const char* StrID, AActor*& NewSelection, const UWorld& World, const TArray<TSubclassOf<AActor>>& ActorClasses, int32& SelectedActorClassIndex, ImGuiTextFilter* Filter, const APawn* LocalPlayerPawn, const FCogWindowActorContextMenuFunction& ContextMenuFunction)
{
bool Result = false;
ImGui::PushID(StrID);
@@ -880,9 +916,9 @@ bool FCogWindowWidgets::MenuActorsCombo(const char* StrID, const UWorld& World,
const ImVec2 Pos1 = ImGui::GetCursorScreenPos();
const float Width = FCogImguiHelper::GetNextItemWidth();
//-----------------------------------
// Combo button
//-----------------------------------
//-----------------------------------
// Combo button
//-----------------------------------
{
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.5f));
@@ -920,15 +956,15 @@ bool FCogWindowWidgets::MenuActorsCombo(const char* StrID, const UWorld& World,
}
//-----------------------------------
// Popup
//-----------------------------------
// Popup
//-----------------------------------
const ImVec2 Pos2 = ImGui::GetCursorScreenPos();
ImGui::SetNextWindowPos(ImVec2(Pos1.x, Pos1.y + ImGui::GetFrameHeight()));
if (ImGui::BeginPopup("ActorListPopup"))
{
ImGui::BeginChild("Child", ImVec2(Pos2.x - Pos1.x, GetFontWidth() * 40), false);
Result = ActorsListWithFilters(World, ActorClasses, SelectedActorClassIndex, Filter, LocalPlayerPawn, ContextMenuFunction);
Result = ActorsListWithFilters(NewSelection, World, ActorClasses, SelectedActorClassIndex, Filter, LocalPlayerPawn, ContextMenuFunction);
if (Result)
{
ImGui::CloseCurrentPopup();
@@ -946,10 +982,10 @@ bool FCogWindowWidgets::MenuActorsCombo(const char* StrID, const UWorld& World,
//--------------------------------------------------------------------------------------------------------------------------
void FCogWindowWidgets::ActorContextMenu(AActor& Selection, const FCogWindowActorContextMenuFunction& ContextMenuFunction)
{
if (ContextMenuFunction == nullptr)
{
if (ContextMenuFunction == nullptr)
{
return;
}
}
ImGui::SetNextWindowSize(ImVec2(GetFontWidth() * 30, 0));
if (ImGui::BeginPopupContextItem())
@@ -962,7 +998,7 @@ void FCogWindowWidgets::ActorContextMenu(AActor& Selection, const FCogWindowActo
//--------------------------------------------------------------------------------------------------------------------------
void FCogWindowWidgets::ActorFrame(const AActor& Actor)
{
const APlayerController* PlayerController = Actor.GetWorld()->GetFirstPlayerController();
const APlayerController* PlayerController = Actor.GetWorld()->GetFirstPlayerController();
if (PlayerController == nullptr)
{
return;
@@ -1029,4 +1065,155 @@ void FCogWindowWidgets::SmallButton(const char* Text, const ImVec4& Color)
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(Color.x, Color.y, Color.z, Color.w * 1.0f));
ImGui::SmallButton(Text);
ImGui::PopStyleColor(3);
}
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogWindowWidgets::InputText(const char* Text, FString& Value)
{
static char Buffer[256] = "";
ImStrncpy(Buffer, TCHAR_TO_ANSI(*Value), IM_ARRAYSIZE(Buffer));
bool result = ImGui::InputText(Text, Buffer, IM_ARRAYSIZE(Buffer));
if (result)
{
Value = FString(Buffer);
}
return result;
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogWindowWidgets::BeginRightAlign(const char* Id)
{
if (ImGui::BeginTable(Id, 2, ImGuiTableFlags_SizingFixedFit, ImVec2(-1, 0)))
{
ImGui::TableSetupColumn("a", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextColumn();
ImGui::TableNextColumn();
return true;
}
return false;
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogWindowWidgets::EndRightAlign()
{
ImGui::EndTable();
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogWindowWidgets::MenuItemShortcut(const char* Id, const FString& Text)
{
ImGui::SameLine();
if (BeginRightAlign(Id))
{
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);
const auto TextStr = StringCast<ANSICHAR>(*Text);
ImGui::Text("%s", TextStr.Get());
ImGui::PopStyleColor();
EndRightAlign();
}
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogWindowWidgets::BrowseToAssetButton(const UObject* InAsset, const ImVec2& InSize)
{
#if WITH_EDITOR
if (InAsset == nullptr)
{
ImGui::BeginDisabled();
}
const bool result = ImGui::Button("Browse To Asset", InSize);
if (result)
{
IAssetTools::Get().SyncBrowserToAssets({ InAsset });
}
if (InAsset == nullptr)
{
ImGui::EndDisabled();
}
return result;
#else
return false;
#endif
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogWindowWidgets::BrowseToObjectAssetButton(const UObject* InObject, const ImVec2& InSize)
{
#if WITH_EDITOR
const UObject* ObjectAsset = nullptr;
if (InObject != nullptr && InObject->GetClass() != nullptr)
{
ObjectAsset = InObject->GetClass()->ClassGeneratedBy;
}
return BrowseToAssetButton(ObjectAsset, InSize);
#else
return false;
#endif
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogWindowWidgets::OpenAssetButton(const UObject* InAsset, const ImVec2& InSize)
{
#if WITH_EDITOR
UAssetEditorSubsystem* editorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
if (InAsset == nullptr || editorSubsystem == nullptr)
{
ImGui::BeginDisabled();
}
const bool result = ImGui::Button("Open Asset", InSize);
if (result)
{
if (editorSubsystem != nullptr)
{
editorSubsystem->OpenEditorForAsset(InAsset);
}
}
if (InAsset == nullptr)
{
ImGui::EndDisabled();
}
return result;
#else
return false;
#endif
}
//--------------------------------------------------------------------------------------------------------------------------
bool FCogWindowWidgets::OpenObjectAssetButton(const UObject* InObject, const ImVec2& InSize)
{
#if WITH_EDITOR
const UObject* ObjectAsset = nullptr;
if (InObject != nullptr && InObject->GetClass() != nullptr)
{
ObjectAsset = InObject->GetClass()->ClassGeneratedBy;
}
return OpenAssetButton(ObjectAsset, InSize);
#else
return false;
#endif
}
@@ -5,6 +5,7 @@
#include "CogWindowManager.h"
#include "CogWindowWidgets.h"
#include "imgui.h"
#include "imgui.h"
#include "InputCoreTypes.h"
//--------------------------------------------------------------------------------------------------------------------------
@@ -77,17 +78,7 @@ void FCogWindow_Settings::RenderContent()
Context.SetEnableInput(bEnableInput);
}
ImGui::SetItemTooltip("Enable ImGui inputs. When enabled the ImGui menu is shown and inputs are forwarded to ImGui.");
const auto ShortcutText = StringCast<ANSICHAR>(*FCogImguiInputHelper::CommandToString(PlayerInput, UCogWindowManager::ToggleInputCommand));
const float ShortcutWidth = (ShortcutText.Get() != nullptr && ShortcutText.Get()[0]) ? ImGui::CalcTextSize(ShortcutText.Get(), NULL).x : 0.0f;
if (ShortcutWidth > 0.0f)
{
ImGui::SameLine();
ImGui::SetCursorPosX(ImGui::GetContentRegionAvail().x); // https://github.com/ocornut/imgui/issues/7838
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);
ImGui::Text("%s", ShortcutText.Get());
ImGui::PopStyleColor();
}
FCogWindowWidgets::MenuItemShortcut("EnableInputShortcut", FCogImguiInputHelper::CommandToString(PlayerInput, UCogWindowManager::ToggleInputCommand));
//-------------------------------------------------------------------------------------------
bool bShareKeyboard = Context.GetShareKeyboard();
@@ -53,13 +53,13 @@ public:
/** The short name of the window. "Effect" if the window full name is "Gameplay.Character.Effect" */
const FString& GetName() const { return Name; }
AActor* GetSelection() { return CurrentSelection.Get(); }
AActor* GetSelection() const { return CurrentSelection.Get(); }
void SetSelection(AActor* Actor);
bool GetIsVisible() const { return bIsVisible; }
void SetIsVisible(bool Value) { bIsVisible = Value; }
void SetIsVisible(bool Value);
bool HasWidget() const { return bHasWidget; }
@@ -76,12 +76,12 @@ public:
UCogWindowManager* GetOwner() const { return Owner; }
template<class T>
T* GetConfig() { return Cast<T>(GetConfig(T::StaticClass())); }
T* GetConfig() const { return Cast<T>(GetConfig(T::StaticClass())); }
UCogCommonConfig* GetConfig(const TSubclassOf<UCogCommonConfig> ConfigClass) const;
template<class T>
const T* GetAsset() { return Cast<T>(GetAsset(T::StaticClass())); }
const T* GetAsset() const { return Cast<T>(GetAsset(T::StaticClass())); }
const UObject* GetAsset(const TSubclassOf<UObject> AssetClass) const;
@@ -103,6 +103,8 @@ protected:
virtual bool CheckEditorVisibility();
virtual void OnWindowVisibilityChanged(bool NewVisibility) { }
virtual void OnSelectionChanged(AActor* OldSelection, AActor* NewSelection) {}
APawn* GetLocalPlayerPawn() const;
@@ -113,7 +115,7 @@ protected:
protected:
bool bHideMenu = false;
bool bShowMenu = true;
bool bNoPadding = false;
@@ -0,0 +1,39 @@
#pragma once
#include "CoreMinimal.h"
#include "HAL/IConsoleManager.h"
#include "Templates/Function.h"
class UWorld;
DECLARE_DELEGATE_TwoParams(FCogWindowConsoleCommandDelegate, const TArray<FString>&, UWorld*);
//--------------------------------------------------------------------------------------------------------------------------
struct FCogCommandReceiver
{
UWorld* World = nullptr;
FCogWindowConsoleCommandDelegate Delegate;
};
//--------------------------------------------------------------------------------------------------------------------------
struct FCogCommandInfo
{
IConsoleObject* ConsoleObject = nullptr;
TArray<FCogCommandReceiver> Receivers;
};
//--------------------------------------------------------------------------------------------------------------------------
struct COGWINDOW_API FCogWindowConsoleCommandManager
{
public:
static void RegisterWorldConsoleCommand(const TCHAR* InName, const TCHAR* InHelp, UWorld* InWorld, const FCogWindowConsoleCommandDelegate& InDelegate);
static void UnregisterAllWorldConsoleCommands(const UWorld* InWorld);
protected:
static TMap<FString, FCogCommandInfo> CommandMap;
};
@@ -36,6 +36,7 @@ public:
virtual void Tick(float DeltaTime);
virtual void AddWindow(FCogWindow* Window, const FString& Name, bool AddToMainMenu = true);
template<class T>
@@ -51,9 +52,9 @@ public:
virtual void SaveLayout(int32 LayoutIndex);
virtual bool GetHideAllWindows() const { return bHideAllWindows; }
virtual bool GetHideAllWindows() const { return bIsSelectionModeActive; }
virtual void SetHideAllWindows(bool Value);
virtual void SetActivateSelectionMode(bool Value);
virtual void ResetAllWindowsConfig();
@@ -101,10 +102,12 @@ protected:
virtual void RenderMenuItem(FCogWindow& Window, const char* MenuItemName);
void RenderMenuItemHelp(FCogWindow& Window);
virtual void RenderMenuItemHelp(FCogWindow& Window);
virtual void ToggleInputMode();
virtual void DisableInputMode();
static void SettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*);
static void SettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*);
@@ -116,6 +119,8 @@ protected:
static void SettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
static FString ToggleInputCommand;
static FString DisableInputCommand;
static FString LoadLayoutCommand;
@@ -150,13 +155,11 @@ protected:
int32 LayoutToLoad = -1;
int32 HideAllWindowsCounter = 0;
int32 SelectionModeActiveCounter = 0;
bool bHideAllWindows = false;
bool bIsSelectionModeActive = false;
bool IsInitialized = false;
TArray<IConsoleObject*> ConsoleCommands;
};
//--------------------------------------------------------------------------------------------------------------------------
@@ -4,6 +4,10 @@
#include "imgui.h"
#include "UObject/ReflectedTypeAccessors.h"
#include <Templates/SubclassOf.h>
class AActor;
class APawn;
class FEnumProperty;
class UCollisionProfile;
class UEnum;
@@ -19,12 +23,18 @@ class COGWINDOW_API FCogWindowWidgets
{
public:
static void BeginTableTooltip();
static bool BeginTableTooltip();
static void EndTableTooltip();
static bool BeginItemTableTooltip();
static void EndItemTableTooltip();
static void ThinSeparatorText(const char* Label);
static bool DarkCollapsingHeader(const char* InLabel, ImGuiTreeNodeFlags InFlags);
static void ProgressBarCentered(float Fraction, const ImVec2& Size, const char* Overlay);
static bool ToggleMenuButton(bool* Value, const char* Text, const ImVec4& TrueColor);
@@ -73,7 +83,7 @@ public:
static bool ComboboxEnum(const char* Label, const FEnumProperty* EnumProperty, uint8* PointerToEnumValue);
static bool CheckBoxState(const char* Label, ECheckBoxState& State);
static bool CheckBoxState(const char* Label, ECheckBoxState& State, bool ShowTooltip = true);
static bool InputKey(const char* Label, FCogImGuiKeyInfo& KeyInfo);
@@ -91,19 +101,36 @@ public:
static bool CollisionProfileChannels(int32& Channels);
static bool MenuActorsCombo(const char* StrID, const UWorld& World, TSubclassOf<AActor> ActorClass, const FCogWindowActorContextMenuFunction& ContextMenuFunction = nullptr);
static bool MenuActorsCombo(const char* StrID, AActor*& NewSelection, const UWorld& World, TSubclassOf<AActor> ActorClass, const FCogWindowActorContextMenuFunction& ContextMenuFunction = nullptr);
static bool MenuActorsCombo(const char* StrID, const UWorld& World, const TArray<TSubclassOf<AActor>>& ActorClasses, int32& SelectedActorClassIndex, ImGuiTextFilter* Filter, const APawn* LocalPlayerPawn, const FCogWindowActorContextMenuFunction& ContextMenuFunction = nullptr);
static bool MenuActorsCombo(const char* StrID, AActor*& NewSelection, const UWorld& World, const TArray<TSubclassOf<AActor>>& ActorClasses, int32& SelectedActorClassIndex, ImGuiTextFilter* Filter, const APawn* LocalPlayerPawn, const FCogWindowActorContextMenuFunction& ContextMenuFunction = nullptr);
static bool ActorsListWithFilters(const UWorld& World, const TArray<TSubclassOf<AActor>>& ActorClasses, int32& SelectedActorClassIndex, ImGuiTextFilter* Filter, const APawn* LocalPlayerPawn, const FCogWindowActorContextMenuFunction& ContextMenuFunction = nullptr);
static bool ActorsListWithFilters(AActor*& NewSelection, const UWorld& World, const TArray<TSubclassOf<AActor>>& ActorClasses, int32& SelectedActorClassIndex, ImGuiTextFilter* Filter, const APawn* LocalPlayerPawn, const FCogWindowActorContextMenuFunction& ContextMenuFunction = nullptr);
static bool ActorsList(const UWorld& World, const TSubclassOf<AActor> ActorClass, const ImGuiTextFilter* Filter = nullptr, const APawn* LocalPlayerPawn = nullptr, const FCogWindowActorContextMenuFunction& ContextMenuFunction = nullptr);
static bool ActorsList(AActor*& NewSelection, const UWorld& World, const TSubclassOf<AActor> ActorClass, const ImGuiTextFilter* Filter = nullptr, const APawn* LocalPlayerPawn = nullptr, const FCogWindowActorContextMenuFunction& ContextMenuFunction = nullptr);
static void ActorContextMenu(AActor& Selection, const FCogWindowActorContextMenuFunction& ContextMenuFunction);
static void ActorFrame(const AActor& Actor);
static void SmallButton(const char* Text, const ImVec4& Color);
static bool InputText(const char* Text, FString& Value);
static bool BeginRightAlign(const char* Id);
static void EndRightAlign();
static void MenuItemShortcut(const char* Id, const FString& Text);
static bool BrowseToAssetButton(const UObject* InAsset, const ImVec2& InSize = ImVec2(0, 0));
static bool BrowseToObjectAssetButton(const UObject* InObject, const ImVec2& InSize = ImVec2(0, 0));
static bool OpenAssetButton(const UObject* InAsset, const ImVec2& InSize = ImVec2(0, 0));
static bool OpenObjectAssetButton(const UObject* InObject, const ImVec2& InSize = ImVec2(0, 0));
};
template<typename EnumType>
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2025 Omar Cornut
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,11 @@
using UnrealBuildTool;
using System.IO;
public class NetImgui : ModuleRules
{
public NetImgui(ReadOnlyTargetRules Target) : base(Target)
{
Type = ModuleType.External;
PublicSystemIncludePaths.Add(ModuleDirectory);
}
}
+285
View File
@@ -0,0 +1,285 @@
#pragma once
//=================================================================================================
//! @Name : NetImgui
//=================================================================================================
//! @author : Sammy Fatnassi
//! @date : 2024/12/10
//! @version : v1.12.1
//! @Details : For integration info : https://github.com/sammyfreg/netImgui/wiki
//=================================================================================================
#define NETIMGUI_VERSION "1.12.1" // Fixed disconnect thread contention and clipboard command
#define NETIMGUI_VERSION_NUM 11201
//-------------------------------------------------------------------------------------------------
// Deactivate a few warnings to allow Imgui header include
// without generating warnings in maximum level '-Wall'
//-------------------------------------------------------------------------------------------------
#if defined (__clang__)
#pragma clang diagnostic push
// ImGui.h warnings(s)
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
#pragma clang diagnostic ignored "-Wreserved-identifier" // Enum values using '__' or member starting with '_' in imgui.h
// NetImgui_Api.h Warning(s)
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // Not using nullptr in case this file is used in pre C++11
#elif defined(_MSC_VER)
#pragma warning (push)
// ImGui.h warnings(s)
#pragma warning (disable: 4514) // 'xxx': unreferenced inline function has been removed
#pragma warning (disable: 4710) // 'xxx': function not inlined
#pragma warning (disable: 4820) // 'xxx': 'yyy' bytes padding added after data member 'zzz'
#pragma warning (disable: 5045) // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
#endif
//=================================================================================================
// Include the user config file. It should contain the include for :
// 'imgui.h' : always
// 'imgui_internal.h' when 'NETIMGUI_INTERNAL_INCLUDE' is defined
//=================================================================================================
#ifdef NETIMGUI_IMPLEMENTATION
#define NETIMGUI_INTERNAL_INCLUDE
#include "NetImgui_Config.h"
#undef NETIMGUI_INTERNAL_INCLUDE
#else
#include "NetImgui_Config.h"
#endif
//-------------------------------------------------------------------------------------------------
// If 'NETIMGUI_ENABLED' hasn't been defined yet (in project settings or NetImgui_Config.h')
// we define this library as 'Disabled'
//-------------------------------------------------------------------------------------------------
#ifndef NETIMGUI_ENABLED
#define NETIMGUI_ENABLED 0
#endif
//-------------------------------------------------------------------------------------------------
// NetImgui needs to detect Dear ImGui to be active, otherwise we disable it
// When including this header, make sure imgui.h is included first
// (either always included in NetImgui_config.h or have it included after Imgui.h in your cpp)
//-------------------------------------------------------------------------------------------------
#if !defined(IMGUI_VERSION)
#undef NETIMGUI_ENABLED
#define NETIMGUI_ENABLED 0
#endif
#if NETIMGUI_ENABLED
#include <stdint.h>
//=================================================================================================
// Default Build settings defines values
// Assign default values when not set in user NetImgui_Config.h
//=================================================================================================
//-------------------------------------------------------------------------------------------------
// Prepended to functions signature, for dll export/import
//-------------------------------------------------------------------------------------------------
#ifndef NETIMGUI_API
#define NETIMGUI_API IMGUI_API // Use same value as defined by Dear ImGui by default
#endif
//-------------------------------------------------------------------------------------------------
// Enable TCP socket 'reuse port' option when opening it as a 'listener'.
// Note: Can help when unable to open a socket because it wasn't properly released after a crash.
//-------------------------------------------------------------------------------------------------
#ifndef NETIMGUI_FORCE_TCP_LISTEN_BINDING
#define NETIMGUI_FORCE_TCP_LISTEN_BINDING 0 // Doesn't seem to be needed on Window/Linux
#endif
//-------------------------------------------------------------------------------------------------
// Enable Dear ImGui Callbacks support for BeginFrame/Render automatic interception.
// Note: Avoid having to replace ImGui::BeginFrame/ImGui::Render with in library user code, by
// 'NetImgui::NewFrame/NetImgui::EndFrame'. But prevent benefit of skipping frame draw
// when unneeded, that 'NetImgui::NewFrame' can provide.
// For more info, consult 'SampleNewFrame.cpp'.
// Needs Dear ImGui 1.81+
//-------------------------------------------------------------------------------------------------
#ifndef NETIMGUI_IMGUI_CALLBACK_ENABLED
#define NETIMGUI_IMGUI_CALLBACK_ENABLED (IMGUI_VERSION_NUM >= 18100) // Not supported pre Dear ImGui 1.81
#endif
namespace NetImgui
{
//=================================================================================================
// List of texture format supported
//=================================================================================================
enum eTexFormat {
kTexFmtA8,
kTexFmtRGBA8,
// Support of 'user defined' texture format.
// Implementation must be added on both client and Server code.
// Search for TEXTURE_CUSTOM_SAMPLE for example implementation.
kTexFmtCustom,
//
kTexFmt_Count,
kTexFmt_Invalid=kTexFmt_Count
};
//=================================================================================================
// Data Compression wanted status
//=================================================================================================
enum eCompressionMode {
kForceDisable, // Disable data compression for communications
kForceEnable, // Enable data compression for communications
kUseServerSetting // Use Server setting for compression (default)
};
//-------------------------------------------------------------------------------------------------
// Function typedefs
//-------------------------------------------------------------------------------------------------
typedef void (*ThreadFunctPtr)(void threadedFunction(void* pClientInfo), void* pClientInfo);
typedef void (*FontCreateFuncPtr)(float PreviousDPIScale, float NewDPIScale);
//=================================================================================================
// Initialize the Network Library
//=================================================================================================
NETIMGUI_API bool Startup(void);
//=================================================================================================
// Free Resources
// Wait until all communication threads have terminated before returning
//=================================================================================================
NETIMGUI_API void Shutdown();
//=================================================================================================
// Establish a connection between the NetImgui server application and this client.
//
// Can connect with NetImgui Server application by either reaching it directly
// using 'ConnectToApp' or waiting for Server to reach us after Client called 'ConnectFromApp'.
//
// Note: Start a new communication thread using std::Thread by default, but can receive custom
// thread start function instead (Look at ClientExample 'CustomCommunicationThread').
//-------------------------------------------------------------------------------------------------
// clientName : Client name displayed in the Server's clients list
// serverHost : NetImgui Server Application address (Ex1: 127.0.0.2, Ex2: localhost)
// serverPort : PortID of the NetImgui Server application to connect to
// clientPort : PortID this Client should wait for connection from Server application
// threadFunction : User provided function to launch new networking thread.
// Use 'DefaultStartCommunicationThread' by default (uses 'std::thread').
// fontCreateFunction : User provided function to call when the Server expect an update of
// the font atlas, because of a monitor DPI change. When left to nullptr,
// uses 'ImGuiIO.FontGlobalScale' instead to increase text size,
// with blurier results.
//=================================================================================================
NETIMGUI_API bool ConnectToApp(const char* clientName, const char* serverHost, uint32_t serverPort=kDefaultServerPort, ThreadFunctPtr threadFunction=0, FontCreateFuncPtr FontCreateFunction=0);
NETIMGUI_API bool ConnectFromApp(const char* clientName, uint32_t clientPort=kDefaultClientPort, ThreadFunctPtr threadFunction=0, FontCreateFuncPtr fontCreateFunction=0);
//=================================================================================================
// Request a disconnect from the NetImguiServer application
//=================================================================================================
NETIMGUI_API void Disconnect(void);
//=================================================================================================
// True if connected to the NetImguiServer application
//=================================================================================================
NETIMGUI_API bool IsConnected(void);
//=================================================================================================
// True if connection request is waiting to be completed. For example, while waiting for
// Server to reach ud after having called 'ConnectFromApp()'
//=================================================================================================
NETIMGUI_API bool IsConnectionPending(void);
//=================================================================================================
// True when Dear ImGui is currently expecting draw commands
// This means that we are between NewFrame() and EndFrame()
//=================================================================================================
NETIMGUI_API bool IsDrawing(void);
//=================================================================================================
// True when we are currently drawing on the NetImguiServer application
// Means that we are between NewFrame() and EndFrame() of drawing for remote application
//=================================================================================================
NETIMGUI_API bool IsDrawingRemote(void);
//=================================================================================================
// Send an updated texture used by imgui, to the NetImguiServer application
// Note: To remove a texture, set pData to nullptr
// Note: User needs to provide a valid 'dataSize' when using format 'kTexFmtCustom',
// can be ignored otherwise
//=================================================================================================
NETIMGUI_API void SendDataTexture(ImTextureID textureId, void* pData, uint16_t width, uint16_t height, eTexFormat format, uint32_t dataSize=0);
//=================================================================================================
// Start a new Imgui Frame and wait for Draws commands, using ImContext that was active on connect.
// Returns true if we are awaiting a new ImGui frame.
//
// All ImGui drawing should be skipped when return is false.
//
// Note: This code can be used instead, to know if you should be drawing or not :
// 'if( !NetImgui::IsDrawing() )'
//
// Note: If your code cannot handle skipping a ImGui frame, leave 'bSupportFrameSkip=false',
// and this function will always call 'ImGui::NewFrame()' internally and return true
//
// Note: With Dear ImGui 1.81+, you can keep using the ImGui::BeginFrame()/Imgui::Render()
// without having to use NetImgui::NewFrame()/NetImgui::EndFrame()
// (unless wanting to support frame skip)
//=================================================================================================
NETIMGUI_API bool NewFrame(bool bSupportFrameSkip=false);
//=================================================================================================
// Process all receives draws, send them to remote connection and restore the ImGui Context
//=================================================================================================
NETIMGUI_API void EndFrame(void);
//=================================================================================================
// Return the context associated to this remote connection. Null when not connected.
//=================================================================================================
NETIMGUI_API ImGuiContext* GetContext();
//=================================================================================================
// Set the remote client background color and texture
// Note: If no TextureID is specified, will use the default server texture
//=================================================================================================
NETIMGUI_API void SetBackground(const ImVec4& bgColor);
NETIMGUI_API void SetBackground(const ImVec4& bgColor, const ImVec4& textureTint );
NETIMGUI_API void SetBackground(const ImVec4& bgColor, const ImVec4& textureTint, ImTextureID bgTextureID);
//=================================================================================================
// Control the data compression for communications between Client/Server
//=================================================================================================
NETIMGUI_API void SetCompressionMode(eCompressionMode eMode);
NETIMGUI_API eCompressionMode GetCompressionMode();
//=================================================================================================
// Helper functions
//=================================================================================================
NETIMGUI_API uint8_t GetTexture_BitsPerPixel (eTexFormat eFormat);
NETIMGUI_API uint32_t GetTexture_BytePerLine (eTexFormat eFormat, uint32_t pixelWidth);
NETIMGUI_API uint32_t GetTexture_BytePerImage (eTexFormat eFormat, uint32_t pixelWidth, uint32_t pixelHeight);
}
//=================================================================================================
// Optional single include compiling option
// Note: User wanting to avoid adding the few NetImgui sources files to their project,
// can instead define 'NETIMGUI_IMPLEMENTATION' *once* before including 'NetImgui_Api.h'
// to pull all the needed cpp files alongside for compilation
//=================================================================================================
#if defined(NETIMGUI_IMPLEMENTATION)
#include "Private/NetImgui_Api.cpp"
#include "Private/NetImgui_Client.cpp"
#include "Private/NetImgui_CmdPackets_DrawFrame.cpp"
#include "Private/NetImgui_NetworkPosix.cpp"
#include "Private/NetImgui_NetworkUE4.cpp"
#include "Private/NetImgui_NetworkWin32.cpp"
#endif
#endif // NETIMGUI_ENABLED
//-------------------------------------------------------------------------------------------------
// Re-Enable the Deactivated warnings
//-------------------------------------------------------------------------------------------------
#if defined (__clang__)
#pragma clang diagnostic pop
#elif defined(_MSC_VER)
#pragma warning (pop)
#endif
@@ -0,0 +1,62 @@
#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
#include <imgui.h>
#ifdef NETIMGUI_INTERNAL_INCLUDE
#include "Private/NetImgui_WarningDisableImgui.h" // Disable some extra warning generated by imgui_internal in '-Wall'
#include <imgui_internal.h> // Only needed when compiling NetImgui, not when using the NetImgui Api
#include "Private/NetImgui_WarningReenable.h"
#endif
#endif // NETIMGUI_ENABLED
//=================================================================================================
// Default Ports used to reach the Server or the Client (listen port for incoming connection)
//=================================================================================================
namespace NetImgui
{
enum Constants{
kDefaultServerPort = 8888, //!< Default port Server waits for a connection
kDefaultClientPort = 8889 //!< Default port Client waits for a connection
};
}
//=================================================================================================
// Enable default Win32/Posix networking code
// Note: By default, netImgui uses Winsock on Windows and Posix sockets on other platforms
//
// The use your own code, turn off both NETIMGUI_WINSOCKET_ENABLED,
// NETIMGUI_POSIX_SOCKETS_ENABLED and provide your own implementation of the functions
// declared in 'NetImgui_Network.h'.
//
// As an example, 'SampleCompression' disable default com implementation and use its own
//=================================================================================================
#if !defined(NETIMGUI_WINSOCKET_ENABLED) && !defined(__UNREAL__)
#ifdef _WIN32
#define NETIMGUI_WINSOCKET_ENABLED 1 // Project needs 'ws2_32.lib' added to input libraries
#else
#define NETIMGUI_WINSOCKET_ENABLED 0
#endif
#endif
#if !defined(NETIMGUI_POSIX_SOCKETS_ENABLED) && !defined(__UNREAL__)
#define NETIMGUI_POSIX_SOCKETS_ENABLED !(NETIMGUI_WINSOCKET_ENABLED)
#endif
//=================================================================================================
// Various build settings define
// Note: for more information, please look in 'NetImgui_Api.h' for description and default values
//=================================================================================================
//#define NETIMGUI_IMGUI_CALLBACK_ENABLED (IMGUI_VERSION_NUM >= 18100) // Not supported pre Dear ImGui 1.81
//#define NETIMGUI_FORCE_TCP_LISTEN_BINDING 0 // Doesn't seem to be needed on Window/Linux
//#define NETIMGUI_API IMGUI_API // Use same value as defined by Dear ImGui by default
@@ -0,0 +1,795 @@
#include "NetImgui_Shared.h"
#include "NetImgui_WarningDisable.h"
#if NETIMGUI_ENABLED
#include <algorithm>
#include <thread>
#include "NetImgui_Client.h"
#include "NetImgui_Network.h"
#include "NetImgui_CmdPackets.h"
using namespace NetImgui::Internal;
namespace NetImgui {
static Client::ClientInfo* gpClientInfo = nullptr;
bool ProcessInputData(Client::ClientInfo& client);
//=================================================================================================
void DefaultStartCommunicationThread(void ComFunctPtr(void*), void* pClient)
//=================================================================================================
{
// Visual Studio 2017 generate this warning on std::thread, avoid the warning preventing build
#if defined(_MSC_VER) && (_MSC_VER < 1920)
#pragma warning (push)
#pragma warning (disable: 4625) // 'std::_LaunchPad<_Target>' : copy constructor was implicitly defined as deleted
#pragma warning (disable: 4626) // 'std::_LaunchPad<_Target>' : assignment operator was implicitly defined as deleted
#endif
std::thread(ComFunctPtr, pClient).detach();
#if defined(_MSC_VER) && (_MSC_VER < 1920)
#pragma warning (pop)
#endif
}
//=================================================================================================
bool ConnectToApp(const char* clientName, const char* ServerHost, uint32_t serverPort, ThreadFunctPtr threadFunction, FontCreateFuncPtr FontCreateFunction)
//=================================================================================================
{
if (!gpClientInfo) return false;
Client::ClientInfo& client = *gpClientInfo;
Disconnect();
while (client.IsActive())
std::this_thread::yield();
client.ContextRestore(); // Restore context setting override, after a disconnect
client.ContextRemoveHooks(); // Remove hooks callback only when completely disconnected
StringCopy(client.mName, (clientName == nullptr || clientName[0] == 0 ? "Unnamed" : clientName));
client.mpSocketPending = Network::Connect(ServerHost, serverPort);
client.mFontCreationFunction = FontCreateFunction;
if (client.mpSocketPending.load() != nullptr)
{
client.ContextInitialize();
threadFunction = threadFunction == nullptr ? DefaultStartCommunicationThread : threadFunction;
threadFunction(Client::CommunicationsConnect, &client);
}
return client.IsActive();
}
//=================================================================================================
bool ConnectFromApp(const char* clientName, uint32_t serverPort, ThreadFunctPtr threadFunction, FontCreateFuncPtr FontCreateFunction)
//=================================================================================================
{
if (!gpClientInfo) return false;
Client::ClientInfo& client = *gpClientInfo;
Disconnect();
while (client.IsActive())
std::this_thread::yield();
client.ContextRestore(); // Restore context setting override, after a disconnect
client.ContextRemoveHooks(); // Remove hooks callback only when completly disconnected
StringCopy(client.mName, (clientName == nullptr || clientName[0] == 0 ? "Unnamed" : clientName));
client.mpSocketPending = Network::ListenStart(serverPort);
client.mFontCreationFunction = FontCreateFunction;
client.mThreadFunction = (threadFunction == nullptr) ? DefaultStartCommunicationThread : threadFunction;
if (client.mpSocketPending.load() != nullptr)
{
client.ContextInitialize();
client.mSocketListenPort = serverPort;
client.mThreadFunction(Client::CommunicationsHost, &client);
}
return client.IsActive();
}
//=================================================================================================
void Disconnect(void)
//=================================================================================================
{
if (!gpClientInfo) return;
// Attempt fake connection on local socket waiting for a Server connection,
// so the blocking operation can terminate and release the communication thread
Client::ClientInfo& client = *gpClientInfo;
client.mbDisconnectPending = true;
client.mbDisconnectListen = true;
if( client.mpSocketListen.load() != nullptr && client.mSocketListenPort != 0 )
{
Network::SocketInfo* pFakeSocket = Network::Connect("127.0.0.1", client.mSocketListenPort);
client.mSocketListenPort = 0;
client.mbDisconnectPending = true;
if(pFakeSocket){
Network::Disconnect(pFakeSocket);
}
}
// Wait for connection attempt to complete and fail
while( client.mbComInitActive || client.mbClientThreadActive );
// If fake connection to exit Listening failed, force disconnect socket directly
// even though it might potentially cause a race condition
Network::SocketInfo* pListenSocket = client.mpSocketListen.exchange(nullptr);
if( pListenSocket ){
Network::Disconnect(pListenSocket);
}
Network::SocketInfo* pPendingSocket = client.mpSocketPending.exchange(nullptr);
if( pPendingSocket ){
Network::Disconnect(pPendingSocket);
}
}
//=================================================================================================
bool IsConnected(void)
//=================================================================================================
{
if (!gpClientInfo) return false;
Client::ClientInfo& client = *gpClientInfo;
// If disconnected in middle of a remote frame drawing,
// want to behave like it is still connected to finish frame properly
return client.IsConnected() || IsDrawingRemote();
}
//=================================================================================================
bool IsConnectionPending(void)
//=================================================================================================
{
if (!gpClientInfo) return false;
Client::ClientInfo& client = *gpClientInfo;
return client.IsConnectPending();
}
//=================================================================================================
bool IsDrawing(void)
//=================================================================================================
{
if (!gpClientInfo) return false;
Client::ClientInfo& client = *gpClientInfo;
return client.mbIsDrawing;
}
//=================================================================================================
bool IsDrawingRemote(void)
//=================================================================================================
{
if (!gpClientInfo) return false;
Client::ClientInfo& client = *gpClientInfo;
return IsDrawing() && client.mbIsRemoteDrawing;
}
//=================================================================================================
bool NewFrame(bool bSupportFrameSkip)
//=================================================================================================
{
if (!gpClientInfo || gpClientInfo->mbIsDrawing) return false;
Client::ClientInfo& client = *gpClientInfo;
ScopedBool scopedInside(client.mbInsideNewEnd, true);
// ImGui Newframe handled by remote connection settings
if( NetImgui::IsConnected() )
{
ImGui::SetCurrentContext(client.mpContext);
// Save current context settings and override settings to fit our netImgui usage
if (!client.IsContextOverriden() )
{
client.ContextOverride();
}
auto elapsedCheck = std::chrono::steady_clock::now() - client.mLastOutgoingDrawCheckTime;
auto elapsedDraw = std::chrono::steady_clock::now() - client.mLastOutgoingDrawTime;
auto elapsedCheckMs = static_cast<float>(std::chrono::duration_cast<std::chrono::microseconds>(elapsedCheck).count()) / 1000.f;
auto elapsedDrawMs = static_cast<float>(std::chrono::duration_cast<std::chrono::microseconds>(elapsedDraw).count()) / 1000.f;
client.mLastOutgoingDrawCheckTime = std::chrono::steady_clock::now();
// Update input and see if remote netImgui expect a new frame
client.mSavedDisplaySize = ImGui::GetIO().DisplaySize;
client.mbValidDrawFrame = false;
// Take into account delay until next method call, for more precise fps
if( client.mDesiredFps > 0.f && (elapsedDrawMs + elapsedCheckMs/2.f) > (1000.f/client.mDesiredFps) )
{
client.mLastOutgoingDrawTime = std::chrono::steady_clock::now();
client.mbValidDrawFrame = true;
}
ProcessInputData(client);
// We are about to start drawing for remote context, check for font data update
const ImFontAtlas* pFonts = ImGui::GetIO().Fonts;
if( pFonts->TexPixelsAlpha8 &&
(pFonts->TexPixelsAlpha8 != client.mpFontTextureData || client.mFontTextureID != pFonts->TexID ))
{
uint8_t* pPixelData(nullptr); int width(0), height(0);
ImGui::GetIO().Fonts->GetTexDataAsAlpha8(&pPixelData, &width, &height);
SendDataTexture(pFonts->TexID, pPixelData, static_cast<uint16_t>(width), static_cast<uint16_t>(height), eTexFormat::kTexFmtA8);
}
// No font texture has been sent to the netImgui server, you can either
// 1. Leave font data available in ImGui (not call ImGui::ClearTexData) for netImgui to auto send it
// 2. Manually call 'NetImgui::SendDataTexture' with font texture data
assert(client.mbFontUploaded);
// Update current active content with our time
ImGui::GetIO().DeltaTime = std::max<float>(1.f / 1000.f, elapsedCheckMs/1000.f);
// NetImgui isn't waiting for a new frame, try to skip drawing when caller supports it
if( !client.mbValidDrawFrame && bSupportFrameSkip )
{
return false;
}
}
// Regular Imgui NewFrame
else
{
// Restore context setting override, after a disconnect
client.ContextRestore();
// Remove hooks callback only when completly disconnected
if (!client.IsConnectPending())
{
client.ContextRemoveHooks();
}
}
// A new frame is expected, update the current time of the drawing context, and let Imgui know to prepare a new drawing frame
client.mbIsRemoteDrawing = NetImgui::IsConnected();
client.mbIsDrawing = true;
// This function can be called from a 'NewFrame' ImGui hook, we should not start a new frame again
if (!client.mbInsideHook)
{
ImGui::NewFrame();
}
return true;
}
//=================================================================================================
void EndFrame(void)
//=================================================================================================
{
if (!gpClientInfo) return;
Client::ClientInfo& client = *gpClientInfo;
ScopedBool scopedInside(client.mbInsideNewEnd, true);
if ( client.mbIsDrawing )
{
// Must be fetched before 'Render'
ImGuiMouseCursor Cursor = ImGui::GetMouseCursor();
// This function can be called from a 'NewFrame' ImGui hook, in which case no need to call this again
if( !client.mbInsideHook ){
ImGui::Render();
}
// Prepare the Dear Imgui DrawData for later tranmission to Server
client.ProcessDrawData(ImGui::GetDrawData(), Cursor);
// Detect change to background settings by user, and forward them to server
if( client.mBGSetting != client.mBGSettingSent )
{
CmdBackground* pCmdBackground = netImguiNew<CmdBackground>();
*pCmdBackground = client.mBGSetting;
client.mBGSettingSent = client.mBGSetting;
client.mPendingBackgroundOut.Assign(pCmdBackground);
}
// Restore display size, so we never lose original setting that may get updated after initial connection
if( client.mbIsRemoteDrawing ) {
ImGui::GetIO().DisplaySize = client.mSavedDisplaySize;
}
}
client.mbIsRemoteDrawing = false;
client.mbIsDrawing = false;
client.mbValidDrawFrame = false;
}
//=================================================================================================
ImGuiContext* GetContext()
//=================================================================================================
{
if (!gpClientInfo) return nullptr;
Client::ClientInfo& client = *gpClientInfo;
return client.mpContext;
}
//=================================================================================================
void SendDataTexture(ImTextureID textureId, void* pData, uint16_t width, uint16_t height, eTexFormat format, uint32_t dataSize)
//=================================================================================================
{
if (!gpClientInfo) return;
Client::ClientInfo& client = *gpClientInfo;
CmdTexture* pCmdTexture = nullptr;
// Makes sure even 32bits ImTextureID value are received properly as 64bits
uint64_t texId64(0);
static_assert(sizeof(uint64_t) >= sizeof(textureId), "ImTextureID is bigger than 64bits, CmdTexture::mTextureId needs to be updated to support it");
reinterpret_cast<ImTextureID*>(&texId64)[0] = textureId;
// Add/Update a texture
if( pData != nullptr )
{
if( format != eTexFormat::kTexFmtCustom ){
dataSize = GetTexture_BytePerImage(format, width, height);
}
uint32_t SizeNeeded = dataSize + sizeof(CmdTexture);
pCmdTexture = netImguiSizedNew<CmdTexture>(SizeNeeded);
pCmdTexture->mpTextureData.SetPtr(reinterpret_cast<uint8_t*>(&pCmdTexture[1]));
memcpy(pCmdTexture->mpTextureData.Get(), pData, dataSize);
pCmdTexture->mSize = SizeNeeded;
pCmdTexture->mWidth = width;
pCmdTexture->mHeight = height;
pCmdTexture->mTextureId = texId64;
pCmdTexture->mFormat = static_cast<uint8_t>(format);
pCmdTexture->mpTextureData.ToOffset();
// Detects when user is sending the font texture
ScopedImguiContext scopedCtx(client.mpContext ? client.mpContext : ImGui::GetCurrentContext());
if( ImGui::GetIO().Fonts && ImGui::GetIO().Fonts->TexID == textureId )
{
client.mbFontUploaded |= true;
client.mpFontTextureData = ImGui::GetIO().Fonts->TexPixelsAlpha8;
client.mFontTextureID = textureId;
}
}
// Texture to remove
else
{
pCmdTexture = netImguiNew<CmdTexture>();
pCmdTexture->mTextureId = texId64;
pCmdTexture->mWidth = 0;
pCmdTexture->mHeight = 0;
pCmdTexture->mFormat = eTexFormat::kTexFmt_Invalid;
pCmdTexture->mpTextureData.SetOff(0);
}
// In unlikely event of too many textures, wait for them to be processed
// (if connected) or Process them now (if not)
while( (client.mTexturesPendingCreated - client.mTexturesPendingSent) >= static_cast<uint32_t>(ArrayCount(client.mTexturesPending)) )
{
if( IsConnected() )
std::this_thread::yield();
else
client.ProcessTexturePending();
}
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
if( !IsConnected() )
client.ProcessTexturePending();
}
//=================================================================================================
void SetBackground(const ImVec4& bgColor)
//=================================================================================================
{
if (!gpClientInfo) return;
Client::ClientInfo& client = *gpClientInfo;
client.mBGSetting = NetImgui::Internal::CmdBackground();
client.mBGSetting.mClearColor[0] = bgColor.x;
client.mBGSetting.mClearColor[1] = bgColor.y;
client.mBGSetting.mClearColor[2] = bgColor.z;
client.mBGSetting.mClearColor[3] = bgColor.w;
}
//=================================================================================================
void SetBackground(const ImVec4& bgColor, const ImVec4& textureTint )
//=================================================================================================
{
if (!gpClientInfo) return;
Client::ClientInfo& client = *gpClientInfo;
client.mBGSetting.mClearColor[0] = bgColor.x;
client.mBGSetting.mClearColor[1] = bgColor.y;
client.mBGSetting.mClearColor[2] = bgColor.z;
client.mBGSetting.mClearColor[3] = bgColor.w;
client.mBGSetting.mTextureTint[0] = textureTint.x;
client.mBGSetting.mTextureTint[1] = textureTint.y;
client.mBGSetting.mTextureTint[2] = textureTint.z;
client.mBGSetting.mTextureTint[3] = textureTint.w;
client.mBGSetting.mTextureId = NetImgui::Internal::CmdBackground::kDefaultTexture;
}
//=================================================================================================
void SetBackground(const ImVec4& bgColor, const ImVec4& textureTint, ImTextureID bgTextureID)
//=================================================================================================
{
if (!gpClientInfo) return;
Client::ClientInfo& client = *gpClientInfo;
client.mBGSetting.mClearColor[0] = bgColor.x;
client.mBGSetting.mClearColor[1] = bgColor.y;
client.mBGSetting.mClearColor[2] = bgColor.z;
client.mBGSetting.mClearColor[3] = bgColor.w;
client.mBGSetting.mTextureTint[0] = textureTint.x;
client.mBGSetting.mTextureTint[1] = textureTint.y;
client.mBGSetting.mTextureTint[2] = textureTint.z;
client.mBGSetting.mTextureTint[3] = textureTint.w;
uint64_t texId64(0);
reinterpret_cast<ImTextureID*>(&texId64)[0] = bgTextureID;
client.mBGSetting.mTextureId = texId64;
}
//=================================================================================================
void SetCompressionMode(eCompressionMode eMode)
//=================================================================================================
{
if (!gpClientInfo) return;
Client::ClientInfo& client = *gpClientInfo;
client.mClientCompressionMode = static_cast<uint8_t>(eMode);
}
//=================================================================================================
eCompressionMode GetCompressionMode()
//=================================================================================================
{
if (!gpClientInfo) return eCompressionMode::kUseServerSetting;
Client::ClientInfo& client = *gpClientInfo;
return static_cast<eCompressionMode>(client.mClientCompressionMode);
}
//=================================================================================================
bool Startup(void)
//=================================================================================================
{
if (!gpClientInfo)
{
gpClientInfo = netImguiNew<Client::ClientInfo>();
}
return Network::Startup();
}
//=================================================================================================
void Shutdown()
//=================================================================================================
{
if (!gpClientInfo) return;
Disconnect();
while( gpClientInfo->IsActive() )
std::this_thread::yield();
Network::Shutdown();
netImguiDeleteSafe(gpClientInfo);
}
//=================================================================================================
ImGuiContext* CloneContext(ImGuiContext* pSourceContext)
//=================================================================================================
{
// Create a context duplicate
ScopedImguiContext scopedSourceCtx(pSourceContext);
ImGuiContext* pContextClone = ImGui::CreateContext(ImGui::GetIO().Fonts);
ImGuiIO& sourceIO = ImGui::GetIO();
ImGuiStyle& sourceStyle = ImGui::GetStyle();
{
ScopedImguiContext scopedCloneCtx(pContextClone);
ImGuiIO& newIO = ImGui::GetIO();
ImGuiStyle& newStyle = ImGui::GetStyle();
// Import the style/options settings of current context, into this one
memcpy(&newStyle, &sourceStyle, sizeof(newStyle));
memcpy(&newIO, &sourceIO, sizeof(newIO));
//memcpy(newIO.KeyMap, sourceIO.KeyMap, sizeof(newIO.KeyMap));
newIO.InputQueueCharacters.Data = nullptr;
newIO.InputQueueCharacters.Size = 0;
newIO.InputQueueCharacters.Capacity = 0;
}
return pContextClone;
}
//=================================================================================================
uint8_t GetTexture_BitsPerPixel(eTexFormat eFormat)
//=================================================================================================
{
switch(eFormat)
{
case eTexFormat::kTexFmtA8: return 8*1;
case eTexFormat::kTexFmtRGBA8: return 8*4;
case eTexFormat::kTexFmtCustom: return 0;
case eTexFormat::kTexFmt_Invalid: return 0;
}
return 0;
}
//=================================================================================================
uint32_t GetTexture_BytePerLine(eTexFormat eFormat, uint32_t pixelWidth)
//=================================================================================================
{
uint32_t bitsPerPixel = static_cast<uint32_t>(GetTexture_BitsPerPixel(eFormat));
return pixelWidth * bitsPerPixel / 8;
//Note: If adding support to BC compression format, have to take into account 4x4 size alignment
}
//=================================================================================================
uint32_t GetTexture_BytePerImage(eTexFormat eFormat, uint32_t pixelWidth, uint32_t pixelHeight)
//=================================================================================================
{
return GetTexture_BytePerLine(eFormat, pixelWidth) * pixelHeight;
//Note: If adding support to BC compression format, have to take into account 4x4 size alignement
}
static inline void AddKeyEvent(const Client::ClientInfo& client, const CmdInput* pCmdInput, CmdInput::NetImguiKeys netimguiKey, ImGuiKey imguiKey)
{
uint32_t valIndex = netimguiKey/64;
uint64_t valMask = 0x0000000000000001ull << (netimguiKey%64);
#if IMGUI_VERSION_NUM < 18700
IM_UNUSED(client);
ImGui::GetIO().KeysDown[imguiKey] = (pCmdInput->mInputDownMask[valIndex] & valMask) != 0;
#else
bool bChanged = (pCmdInput->mInputDownMask[valIndex] ^ client.mPreviousInputState.mInputDownMask[valIndex]) & valMask;
if( bChanged ){
ImGui::GetIO().AddKeyEvent(imguiKey, pCmdInput->mInputDownMask[valIndex] & valMask );
}
#endif
}
static inline void AddKeyAnalogEvent(const Client::ClientInfo& client, const CmdInput* pCmdInput, CmdInput::NetImguiKeys netimguiKey, ImGuiKey imguiKey)
{
uint32_t valIndex = netimguiKey/64;
uint64_t valMask = 0x0000000000000001ull << (netimguiKey%64);
assert(CmdInput::kAnalog_First <= static_cast<uint32_t>(netimguiKey) && static_cast<uint32_t>(netimguiKey) <= CmdInput::kAnalog_Last);
#if IMGUI_VERSION_NUM < 18700
IM_UNUSED(client); IM_UNUSED(pCmdInput); IM_UNUSED(netimguiKey); IM_UNUSED(imguiKey);
#else
int indexAnalog = netimguiKey - CmdInput::kAnalog_First;
indexAnalog = indexAnalog >= static_cast<int>(CmdInput::kAnalog_Count) ? CmdInput::kAnalog_Count - 1 : indexAnalog;
float analogValue = pCmdInput->mInputAnalog[indexAnalog];
bool bChanged = (pCmdInput->mInputDownMask[valIndex] ^ client.mPreviousInputState.mInputDownMask[valIndex]) & valMask;
bChanged |= abs(client.mPreviousInputState.mInputAnalog[indexAnalog] - analogValue) > 0.001f;
if(bChanged){
ImGui::GetIO().AddKeyAnalogEvent(imguiKey, pCmdInput->mInputDownMask[valIndex] & valMask, analogValue);
}
#endif
}
//=================================================================================================
bool ProcessInputData(Client::ClientInfo& client)
//=================================================================================================
{
// Update the current clipboard data received from Server
CmdClipboard* pCmdClipboardNew = client.mPendingClipboardIn.Release();
if( pCmdClipboardNew ){
netImguiDeleteSafe(client.mpCmdClipboard);
client.mpCmdClipboard = pCmdClipboardNew;
}
// Update the keyboard/mouse/gamepad inputs
CmdInput* pCmdInputNew = client.mPendingInputIn.Release();
bool hasNewInput = pCmdInputNew != nullptr;
CmdInput* pCmdInput = hasNewInput ? pCmdInputNew : client.mpCmdInputPending;
ImGuiIO& io = ImGui::GetIO();
if (pCmdInput)
{
const float wheelY = pCmdInput->mMouseWheelVert - client.mPreviousInputState.mMouseWheelVertPrev;
const float wheelX = pCmdInput->mMouseWheelHoriz - client.mPreviousInputState.mMouseWheelHorizPrev;
io.DisplaySize = ImVec2(pCmdInput->mScreenSize[0], pCmdInput->mScreenSize[1]);
// User assigned a function callback handling FontScaling,
// use it to request a Font update on DPI scaling change on the server
if (gpClientInfo->mFontCreationFunction != nullptr)
{
if(abs(gpClientInfo->mFontCreationScaling - pCmdInput->mFontDPIScaling) > 0.01f)
{
gpClientInfo->mFontCreationFunction(gpClientInfo->mFontCreationScaling, pCmdInput->mFontDPIScaling);
gpClientInfo->mFontCreationScaling = pCmdInput->mFontDPIScaling;
}
}
// Client doesn't support regenerating the font at new DPI
// Use FontGlobalScale to affect rendering size, resulting in blurrier result
else
{
io.FontGlobalScale = pCmdInput->mFontDPIScaling;
}
#if IMGUI_VERSION_NUM < 18700
io.MousePos = ImVec2(pCmdInput->mMousePos[0], pCmdInput->mMousePos[1]);
io.MouseWheel = wheelY;
io.MouseWheelH = wheelX;
for (uint32_t i(0); i < CmdInput::NetImguiMouseButton::ImGuiMouseButton_COUNT; ++i) {
io.MouseDown[i] = (pCmdInput->mMouseDownMask & (0x0000000000000001ull << i)) != 0;
}
#define AddInputDown(KEYNAME) AddKeyEvent(client, pCmdInput, CmdInput::KEYNAME, ImGuiKey_::KEYNAME);
AddInputDown(ImGuiKey_Tab)
AddInputDown(ImGuiKey_LeftArrow)
AddInputDown(ImGuiKey_RightArrow)
AddInputDown(ImGuiKey_UpArrow)
AddInputDown(ImGuiKey_DownArrow)
AddInputDown(ImGuiKey_PageUp)
AddInputDown(ImGuiKey_PageDown)
AddInputDown(ImGuiKey_Home)
AddInputDown(ImGuiKey_End)
AddInputDown(ImGuiKey_Insert)
AddInputDown(ImGuiKey_Delete)
AddInputDown(ImGuiKey_Backspace)
AddInputDown(ImGuiKey_Space)
AddInputDown(ImGuiKey_Enter)
AddInputDown(ImGuiKey_Escape)
AddInputDown(ImGuiKey_A) // for text edit CTRL+A: select all
AddInputDown(ImGuiKey_C) // for text edit CTRL+C: copy
AddInputDown(ImGuiKey_V) // for text edit CTRL+V: paste
AddInputDown(ImGuiKey_X) // for text edit CTRL+X: cut
AddInputDown(ImGuiKey_Y) // for text edit CTRL+Y: redo
AddInputDown(ImGuiKey_Z) // for text edit CTRL+Z: undo
io.KeyShift = pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModShift);
io.KeyCtrl = pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModCtrl);
io.KeyAlt = pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModAlt);
io.KeySuper = pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModSuper);
#else
#if IMGUI_VERSION_NUM < 18837
#define ImGuiKey ImGuiKey_
#endif
// At the moment All Dear Imgui version share the same ImGuiKey_ enum (with a 512 value offset),
// but could change in the future, so convert from our own enum version, to Dear ImGui.
#define AddInputDown(KEYNAME) AddKeyEvent(client, pCmdInput, CmdInput::KEYNAME, ImGuiKey::KEYNAME);
#define AddAnalogInputDown(KEYNAME) AddKeyAnalogEvent(client, pCmdInput, CmdInput::KEYNAME, ImGuiKey::KEYNAME);
AddInputDown(ImGuiKey_Tab)
AddInputDown(ImGuiKey_LeftArrow)
AddInputDown(ImGuiKey_RightArrow)
AddInputDown(ImGuiKey_UpArrow)
AddInputDown(ImGuiKey_DownArrow)
AddInputDown(ImGuiKey_PageUp)
AddInputDown(ImGuiKey_PageDown)
AddInputDown(ImGuiKey_Home)
AddInputDown(ImGuiKey_End)
AddInputDown(ImGuiKey_Insert)
AddInputDown(ImGuiKey_Delete)
AddInputDown(ImGuiKey_Backspace)
AddInputDown(ImGuiKey_Space)
AddInputDown(ImGuiKey_Enter)
AddInputDown(ImGuiKey_Escape)
AddInputDown(ImGuiKey_LeftCtrl) AddInputDown(ImGuiKey_LeftShift) AddInputDown(ImGuiKey_LeftAlt) AddInputDown(ImGuiKey_LeftSuper)
AddInputDown(ImGuiKey_RightCtrl) AddInputDown(ImGuiKey_RightShift) AddInputDown(ImGuiKey_RightAlt) AddInputDown(ImGuiKey_RightSuper)
AddInputDown(ImGuiKey_Menu)
AddInputDown(ImGuiKey_0) AddInputDown(ImGuiKey_1) AddInputDown(ImGuiKey_2) AddInputDown(ImGuiKey_3) AddInputDown(ImGuiKey_4) AddInputDown(ImGuiKey_5) AddInputDown(ImGuiKey_6) AddInputDown(ImGuiKey_7) AddInputDown(ImGuiKey_8) AddInputDown(ImGuiKey_9)
AddInputDown(ImGuiKey_A) AddInputDown(ImGuiKey_B) AddInputDown(ImGuiKey_C) AddInputDown(ImGuiKey_D) AddInputDown(ImGuiKey_E) AddInputDown(ImGuiKey_F) AddInputDown(ImGuiKey_G) AddInputDown(ImGuiKey_H) AddInputDown(ImGuiKey_I) AddInputDown(ImGuiKey_J)
AddInputDown(ImGuiKey_K) AddInputDown(ImGuiKey_L) AddInputDown(ImGuiKey_M) AddInputDown(ImGuiKey_N) AddInputDown(ImGuiKey_O) AddInputDown(ImGuiKey_P) AddInputDown(ImGuiKey_Q) AddInputDown(ImGuiKey_R) AddInputDown(ImGuiKey_S) AddInputDown(ImGuiKey_T)
AddInputDown(ImGuiKey_U) AddInputDown(ImGuiKey_V) AddInputDown(ImGuiKey_W) AddInputDown(ImGuiKey_X) AddInputDown(ImGuiKey_Y) AddInputDown(ImGuiKey_Z)
AddInputDown(ImGuiKey_F1) AddInputDown(ImGuiKey_F2) AddInputDown(ImGuiKey_F3) AddInputDown(ImGuiKey_F4) AddInputDown(ImGuiKey_F5) AddInputDown(ImGuiKey_F6)
AddInputDown(ImGuiKey_F7) AddInputDown(ImGuiKey_F8) AddInputDown(ImGuiKey_F9) AddInputDown(ImGuiKey_F10) AddInputDown(ImGuiKey_F11) AddInputDown(ImGuiKey_F12)
AddInputDown(ImGuiKey_Apostrophe)
AddInputDown(ImGuiKey_Comma)
AddInputDown(ImGuiKey_Minus)
AddInputDown(ImGuiKey_Period)
AddInputDown(ImGuiKey_Slash)
AddInputDown(ImGuiKey_Semicolon)
AddInputDown(ImGuiKey_Equal)
AddInputDown(ImGuiKey_LeftBracket)
AddInputDown(ImGuiKey_Backslash)
AddInputDown(ImGuiKey_RightBracket)
AddInputDown(ImGuiKey_GraveAccent)
AddInputDown(ImGuiKey_CapsLock)
AddInputDown(ImGuiKey_ScrollLock)
AddInputDown(ImGuiKey_NumLock)
AddInputDown(ImGuiKey_PrintScreen)
AddInputDown(ImGuiKey_Pause)
AddInputDown(ImGuiKey_Keypad0) AddInputDown(ImGuiKey_Keypad1) AddInputDown(ImGuiKey_Keypad2) AddInputDown(ImGuiKey_Keypad3) AddInputDown(ImGuiKey_Keypad4)
AddInputDown(ImGuiKey_Keypad5) AddInputDown(ImGuiKey_Keypad6) AddInputDown(ImGuiKey_Keypad7) AddInputDown(ImGuiKey_Keypad8) AddInputDown(ImGuiKey_Keypad9)
AddInputDown(ImGuiKey_KeypadDecimal) AddInputDown(ImGuiKey_KeypadDivide) AddInputDown(ImGuiKey_KeypadMultiply)
AddInputDown(ImGuiKey_KeypadSubtract) AddInputDown(ImGuiKey_KeypadAdd) AddInputDown(ImGuiKey_KeypadEnter)
AddInputDown(ImGuiKey_KeypadEqual)
#if IMGUI_VERSION_NUM >= 19000
AddInputDown(ImGuiKey_F13) AddInputDown(ImGuiKey_F14) AddInputDown(ImGuiKey_F15) AddInputDown(ImGuiKey_F16) AddInputDown(ImGuiKey_F17) AddInputDown(ImGuiKey_F18)
AddInputDown(ImGuiKey_F19) AddInputDown(ImGuiKey_F20) AddInputDown(ImGuiKey_F21) AddInputDown(ImGuiKey_F22) AddInputDown(ImGuiKey_F23) AddInputDown(ImGuiKey_F24)
AddInputDown(ImGuiKey_AppBack)
AddInputDown(ImGuiKey_AppForward)
#endif
// Gamepad
AddInputDown(ImGuiKey_GamepadStart)
AddInputDown(ImGuiKey_GamepadBack)
AddInputDown(ImGuiKey_GamepadFaceUp)
AddInputDown(ImGuiKey_GamepadFaceDown)
AddInputDown(ImGuiKey_GamepadFaceLeft)
AddInputDown(ImGuiKey_GamepadFaceRight)
AddInputDown(ImGuiKey_GamepadDpadUp)
AddInputDown(ImGuiKey_GamepadDpadDown)
AddInputDown(ImGuiKey_GamepadDpadLeft)
AddInputDown(ImGuiKey_GamepadDpadRight)
AddInputDown(ImGuiKey_GamepadL1)
AddInputDown(ImGuiKey_GamepadR1)
AddInputDown(ImGuiKey_GamepadL2)
AddInputDown(ImGuiKey_GamepadR2)
AddInputDown(ImGuiKey_GamepadL3)
AddInputDown(ImGuiKey_GamepadR3)
AddAnalogInputDown(ImGuiKey_GamepadLStickUp)
AddAnalogInputDown(ImGuiKey_GamepadLStickDown)
AddAnalogInputDown(ImGuiKey_GamepadLStickLeft)
AddAnalogInputDown(ImGuiKey_GamepadLStickRight)
AddAnalogInputDown(ImGuiKey_GamepadRStickUp)
AddAnalogInputDown(ImGuiKey_GamepadRStickDown)
AddAnalogInputDown(ImGuiKey_GamepadRStickLeft)
AddAnalogInputDown(ImGuiKey_GamepadRStickRight)
#undef AddInputDown
#undef AddAnalogInputDown
#if IMGUI_VERSION_NUM < 18837
#undef ImGuiKey
#endif
#if IMGUI_VERSION_NUM < 18837
#define ImGuiMod_Ctrl ImGuiKey_ModCtrl
#define ImGuiMod_Shift ImGuiKey_ModShift
#define ImGuiMod_Alt ImGuiKey_ModAlt
#define ImGuiMod_Super ImGuiKey_ModSuper
#endif
io.AddKeyEvent(ImGuiMod_Ctrl, pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModCtrl));
io.AddKeyEvent(ImGuiMod_Shift, pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModShift));
io.AddKeyEvent(ImGuiMod_Alt, pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModAlt));
io.AddKeyEvent(ImGuiMod_Super, pCmdInput->IsKeyDown(CmdInput::NetImguiKeys::ImGuiKey_ReservedForModSuper));
// Mouse
io.AddMouseWheelEvent(wheelX, wheelY);
io.AddMousePosEvent(pCmdInput->mMousePos[0], pCmdInput->mMousePos[1]);
for(int i(0); i<CmdInput::NetImguiMouseButton::ImGuiMouseButton_COUNT; ++i){
uint64_t valMask = 0x0000000000000001ull << i;
if((pCmdInput->mMouseDownMask ^ client.mPreviousInputState.mMouseDownMask) & valMask){
io.AddMouseButtonEvent(i, pCmdInput->mMouseDownMask & valMask);
}
}
#endif
uint16_t character;
io.InputQueueCharacters.resize(0);
while (client.mPendingKeyIn.ReadData(&character)){
ImWchar ConvertedKey = static_cast<ImWchar>(character);
io.AddInputCharacter(ConvertedKey);
}
static_assert(sizeof(client.mPreviousInputState.mInputDownMask) == sizeof(pCmdInput->mInputDownMask), "Array size should match");
static_assert(sizeof(client.mPreviousInputState.mInputAnalog) == sizeof(pCmdInput->mInputAnalog), "Array size should match");
memcpy(client.mPreviousInputState.mInputDownMask, pCmdInput->mInputDownMask, sizeof(client.mPreviousInputState.mInputDownMask));
memcpy(client.mPreviousInputState.mInputAnalog, pCmdInput->mInputAnalog, sizeof(client.mPreviousInputState.mInputAnalog));
client.mPreviousInputState.mMouseDownMask = pCmdInput->mMouseDownMask;
client.mPreviousInputState.mMouseWheelVertPrev = pCmdInput->mMouseWheelVert;
client.mPreviousInputState.mMouseWheelHorizPrev = pCmdInput->mMouseWheelHoriz;
client.mServerCompressionEnabled = pCmdInput->mCompressionUse;
client.mServerCompressionSkip |= pCmdInput->mCompressionSkip;
}
if( hasNewInput ){
netImguiDeleteSafe(client.mpCmdInputPending);
client.mpCmdInputPending = pCmdInputNew;
}
return hasNewInput;
}
} // namespace NetImgui
#endif //NETIMGUI_ENABLED
#include "NetImgui_WarningReenable.h"
@@ -0,0 +1,725 @@
#include "NetImgui_Shared.h"
#if NETIMGUI_ENABLED
#include "NetImgui_WarningDisable.h"
#include "NetImgui_Client.h"
#include "NetImgui_Network.h"
#include "NetImgui_CmdPackets.h"
namespace NetImgui { namespace Internal { namespace Client
{
//=================================================================================================
// SAVED IMGUI CONTEXT
// Because we overwrite some Imgui context IO values, we save them before makign any change
// and restore them after detecting a disconnection
//=================================================================================================
void SavedImguiContext::Save(ImGuiContext* copyFrom)
{
ScopedImguiContext scopedContext(copyFrom);
ImGuiIO& sourceIO = ImGui::GetIO();
mSavedContext = true;
mConfigFlags = sourceIO.ConfigFlags;
mBackendFlags = sourceIO.BackendFlags;
mBackendPlatformName = sourceIO.BackendPlatformName;
mBackendRendererName = sourceIO.BackendRendererName;
mDrawMouse = sourceIO.MouseDrawCursor;
mFontGlobalScale = sourceIO.FontGlobalScale;
mFontGeneratedSize = sourceIO.Fonts->Fonts.Size > 0 ? sourceIO.Fonts->Fonts[0]->FontSize : 13.f; // Save size to restore the font to original size
#if IMGUI_VERSION_NUM < 18700
memcpy(mKeyMap, sourceIO.KeyMap, sizeof(mKeyMap));
#endif
#if IMGUI_VERSION_NUM < 19110
mGetClipboardTextFn = sourceIO.GetClipboardTextFn;
mSetClipboardTextFn = sourceIO.SetClipboardTextFn;
mClipboardUserData = sourceIO.ClipboardUserData;
#else
ImGuiPlatformIO& plaformIO = ImGui::GetPlatformIO();
mGetClipboardTextFn = plaformIO.Platform_GetClipboardTextFn;
mSetClipboardTextFn = plaformIO.Platform_SetClipboardTextFn;
mClipboardUserData = plaformIO.Platform_ClipboardUserData;
#endif
}
void SavedImguiContext::Restore(ImGuiContext* copyTo)
{
ScopedImguiContext scopedContext(copyTo);
ImGuiIO& destIO = ImGui::GetIO();
mSavedContext = false;
destIO.ConfigFlags = mConfigFlags;
destIO.BackendFlags = mBackendFlags;
destIO.BackendPlatformName = mBackendPlatformName;
destIO.BackendRendererName = mBackendRendererName;
destIO.MouseDrawCursor = mDrawMouse;
destIO.FontGlobalScale = mFontGlobalScale;
#if IMGUI_VERSION_NUM < 18700
memcpy(destIO.KeyMap, mKeyMap, sizeof(destIO.KeyMap));
#endif
#if IMGUI_VERSION_NUM < 19110
destIO.GetClipboardTextFn = mGetClipboardTextFn;
destIO.SetClipboardTextFn = mSetClipboardTextFn;
destIO.ClipboardUserData = mClipboardUserData;
#else
ImGuiPlatformIO& plaformIO = ImGui::GetPlatformIO();
plaformIO.Platform_GetClipboardTextFn = mGetClipboardTextFn;
plaformIO.Platform_SetClipboardTextFn = mSetClipboardTextFn;
plaformIO.Platform_ClipboardUserData = mClipboardUserData;
#endif
}
//=================================================================================================
// GET CLIPBOARD
// Content received from the Server
//=================================================================================================
#if IMGUI_VERSION_NUM < 19110
static const char* GetClipboardTextFn_NetImguiImpl(void* user_data_ctx)
{
const ClientInfo* pClient = reinterpret_cast<const ClientInfo*>(user_data_ctx);
return pClient && pClient->mpCmdClipboard ? pClient->mpCmdClipboard->mContentUTF8.Get() : nullptr;
}
#else
static const char* GetClipboardTextFn_NetImguiImpl(ImGuiContext* ctx)
{
ScopedImguiContext scopedContext(ctx);
const ClientInfo* pClient = reinterpret_cast<const ClientInfo*>(ImGui::GetPlatformIO().Platform_ClipboardUserData);
return pClient && pClient->mpCmdClipboard ? pClient->mpCmdClipboard->mContentUTF8.Get() : nullptr;
}
#endif
//=================================================================================================
// SET CLIPBOARD
//=================================================================================================
#if IMGUI_VERSION_NUM < 19110
static void SetClipboardTextFn_NetImguiImpl(void* user_data_ctx, const char* text)
{
if(user_data_ctx){
ClientInfo* pClient = reinterpret_cast<ClientInfo*>(user_data_ctx);
CmdClipboard* pClipboardOut = CmdClipboard::Create(text);
pClient->mPendingClipboardOut.Assign(pClipboardOut);
}
}
#else
static void SetClipboardTextFn_NetImguiImpl(ImGuiContext* ctx, const char* text)
{
ScopedImguiContext scopedContext(ctx);
ClientInfo* pClient = reinterpret_cast<ClientInfo*>(ImGui::GetPlatformIO().Platform_ClipboardUserData);
CmdClipboard* pClipboardOut = CmdClipboard::Create(text);
pClient->mPendingClipboardOut.Assign(pClipboardOut);
}
#endif
//=================================================================================================
// INCOM: INPUT
// Receive new keyboard/mouse/screen resolution input to pass on to dearImgui
//=================================================================================================
void Communications_Incoming_Input(ClientInfo& client)
{
auto pCmdInput = static_cast<CmdInput*>(client.mPendingRcv.pCommand);
client.mPendingRcv.bAutoFree = false; // Taking ownership of the data
client.mDesiredFps = pCmdInput->mDesiredFps > 0.f ? pCmdInput->mDesiredFps : 0.f;
size_t keyCount(pCmdInput->mKeyCharCount);
client.mPendingKeyIn.AddData(pCmdInput->mKeyChars, keyCount);
client.mPendingInputIn.Assign(pCmdInput);
}
//=================================================================================================
// INCOM: CLIPBOARD
// Receive server new clipboard content, updating internal cache
//=================================================================================================
void Communications_Incoming_Clipboard(ClientInfo& client)
{
auto pCmdClipboard = static_cast<CmdClipboard*>(client.mPendingRcv.pCommand);
client.mPendingRcv.bAutoFree = false; // Taking ownership of the data
pCmdClipboard->ToPointers();
client.mPendingClipboardIn.Assign(pCmdClipboard);
}
//=================================================================================================
// OUTCOM: TEXTURE
// Transmit all pending new/updated texture
//=================================================================================================
void Communications_Outgoing_Textures(ClientInfo& client)
{
client.ProcessTexturePending();
if( client.mbHasTextureUpdate )
{
for(auto& cmdTexture : client.mTextures)
{
if( !cmdTexture.mbSent && cmdTexture.mpCmdTexture )
{
client.mPendingSend.pCommand = cmdTexture.mpCmdTexture;
client.mPendingSend.bAutoFree = cmdTexture.mpCmdTexture->mFormat == eTexFormat::kTexFmt_Invalid; // Texture as been marked for deletion, can now free memory allocated for this
cmdTexture.mbSent = true;
return; // Exit as soon as we find a texture to send, so thread can proceed with transmitting it
}
}
client.mbHasTextureUpdate = false; // No pending texture detected, mark as 'all sent'
}
}
//=================================================================================================
// OUTCOM: BACKGROUND
// Transmit the current client background settings
//=================================================================================================
void Communications_Outgoing_Background(ClientInfo& client)
{
CmdBackground* pPendingBackground = client.mPendingBackgroundOut.Release();
if( pPendingBackground )
{
client.mPendingSend.pCommand = pPendingBackground;
client.mPendingSend.bAutoFree = false;
}
}
//=================================================================================================
// OUTCOM: FRAME
// Transmit a new dearImgui frame to render
//=================================================================================================
void Communications_Outgoing_Frame(ClientInfo& client)
{
CmdDrawFrame* pPendingDraw = client.mPendingFrameOut.Release();
if( pPendingDraw )
{
pPendingDraw->mFrameIndex = client.mFrameIndex++;
//---------------------------------------------------------------------
// Apply delta compression to DrawCommand, when requested
if( pPendingDraw->mCompressed )
{
// Create a new Compressed DrawFrame Command
if( client.mpCmdDrawLast && !client.mServerCompressionSkip ){
client.mpCmdDrawLast->ToPointers();
CmdDrawFrame* pDrawCompressed = CompressCmdDrawFrame(client.mpCmdDrawLast, pPendingDraw);
netImguiDeleteSafe(client.mpCmdDrawLast);
client.mpCmdDrawLast = pPendingDraw; // Keep original new command for next frame delta compression
pPendingDraw = pDrawCompressed; // Request compressed copy to be sent to server
}
// Save DrawCmd for next frame delta compression
else {
pPendingDraw->mCompressed = false;
client.mpCmdDrawLast = pPendingDraw;
}
}
client.mServerCompressionSkip = false;
//---------------------------------------------------------------------
// Ready to send command to server
pPendingDraw->ToOffsets();
client.mPendingSend.pCommand = pPendingDraw;
client.mPendingSend.bAutoFree = client.mpCmdDrawLast != pPendingDraw;
}
}
//=================================================================================================
// OUTCOM: Clipboard
// Send client 'Copy' clipboard content to Server
//=================================================================================================
void Communications_Outgoing_Clipboard(ClientInfo& client)
{
CmdClipboard* pPendingClipboard = client.mPendingClipboardOut.Release();
if( pPendingClipboard ){
pPendingClipboard->ToOffsets();
client.mPendingSend.pCommand = pPendingClipboard;
client.mPendingSend.bAutoFree = true;
}
}
//=================================================================================================
// INCOMING COMMUNICATIONS
//=================================================================================================
void Communications_Incoming(ClientInfo& client)
{
if( Network::DataReceivePending(client.mpSocketComs) )
{
//-----------------------------------------------------------------------------------------
// 1. Ready to receive new command, starts the process by reading Header
//-----------------------------------------------------------------------------------------
if( client.mPendingRcv.IsReady() )
{
client.mCmdPendingRead = CmdPendingRead();
client.mPendingRcv.pCommand = &client.mCmdPendingRead;
client.mPendingRcv.bAutoFree= false;
}
//-----------------------------------------------------------------------------------------
// 2. Read incoming command from server
//-----------------------------------------------------------------------------------------
if( client.mPendingRcv.IsPending() )
{
Network::DataReceive(client.mpSocketComs, client.mPendingRcv);
// Detected a new command bigger than header, allocate memory for it
if( client.mPendingRcv.pCommand->mSize > sizeof(CmdPendingRead) &&
client.mPendingRcv.pCommand == &client.mCmdPendingRead )
{
CmdPendingRead* pCmdHeader = reinterpret_cast<CmdPendingRead*>(netImguiSizedNew<uint8_t>(client.mPendingRcv.pCommand->mSize));
*pCmdHeader = client.mCmdPendingRead;
client.mPendingRcv.pCommand = pCmdHeader;
client.mPendingRcv.bAutoFree= true;
}
}
//-----------------------------------------------------------------------------------------
// 3. Command fully received from Server, process it
//-----------------------------------------------------------------------------------------
if( client.mPendingRcv.IsDone() )
{
if( !client.mPendingRcv.IsError() )
{
switch( client.mPendingRcv.pCommand->mType )
{
case CmdHeader::eCommands::Input: Communications_Incoming_Input(client); break;
case CmdHeader::eCommands::Clipboard: Communications_Incoming_Clipboard(client); break;
// Commands not received in main loop, by Client
case CmdHeader::eCommands::Version:
case CmdHeader::eCommands::Texture:
case CmdHeader::eCommands::DrawFrame:
case CmdHeader::eCommands::Background:
case CmdHeader::eCommands::Count: break;
}
}
// Cleanup after read completion
if( client.mPendingRcv.IsError() ){
client.mbDisconnectPending = true;
}
if( client.mPendingRcv.bAutoFree ){
netImguiDeleteSafe(client.mPendingRcv.pCommand);
}
client.mPendingRcv = PendingCom();
}
}
// Prevent high CPU usage when waiting for new data
else
{
//std::this_thread::yield();
std::this_thread::sleep_for(std::chrono::microseconds(250));
}
}
//=================================================================================================
// OUTGOING COMMUNICATIONS
//=================================================================================================
void Communications_Outgoing(ClientInfo& client)
{
//---------------------------------------------------------------------------------------------
// Try finishing sending a pending command to Server
//---------------------------------------------------------------------------------------------
if( client.mPendingSend.IsPending() )
{
Network::DataSend(client.mpSocketComs, client.mPendingSend);
// Free allocated memory for command
if( client.mPendingSend.IsDone() )
{
if( client.mPendingSend.IsError() ){
client.mbDisconnectPending = true;
}
if( client.mPendingSend.bAutoFree ){
netImguiDeleteSafe(client.mPendingSend.pCommand);
}
client.mPendingSend = PendingCom();
}
}
//---------------------------------------------------------------------------------------------
// Initiate sending next command to Server, when none are in flight
// Keep track of what command was last send to prevent 1 type to monopolize coms
//---------------------------------------------------------------------------------------------
constexpr CmdHeader::eCommands kCommandsOrder[] = {
CmdHeader::eCommands::Texture, CmdHeader::eCommands::Background,
CmdHeader::eCommands::Clipboard, CmdHeader::eCommands::DrawFrame};
constexpr uint32_t kCommandCounts = static_cast<uint32_t>(sizeof(kCommandsOrder)/sizeof(CmdHeader::eCommands));
uint32_t Index(client.mPendingSendNext);
while( client.mPendingSend.IsReady() && (Index-client.mPendingSendNext)<kCommandCounts )
{
CmdHeader::eCommands NextCmd = kCommandsOrder[Index++ % kCommandCounts];
switch( NextCmd )
{
case CmdHeader::eCommands::Texture: Communications_Outgoing_Textures(client); break;
case CmdHeader::eCommands::Background: Communications_Outgoing_Background(client); break;
case CmdHeader::eCommands::Clipboard: Communications_Outgoing_Clipboard(client); break;
case CmdHeader::eCommands::DrawFrame: Communications_Outgoing_Frame(client); break;
// Commands not sent in main loop, by Client
case CmdHeader::eCommands::Input:
case CmdHeader::eCommands::Version:
case CmdHeader::eCommands::Count: break;
}
if( client.mPendingSend.IsPending() ){
client.mPendingSendNext = Index;
}
}
}
//=================================================================================================
// COMMUNICATIONS INITIALIZE
// Initialize a new connection to a RemoteImgui server
//=================================================================================================
bool Communications_Initialize(ClientInfo& client)
{
CmdVersion cmdVersionSend, cmdVersionRcv;
PendingCom PendingRcv, PendingSend;
client.mbComInitActive = true;
//---------------------------------------------------------------------
// Handshake confirming connection validity
//---------------------------------------------------------------------
PendingRcv.pCommand = reinterpret_cast<CmdPendingRead*>(&cmdVersionRcv);
while( !PendingRcv.IsDone() && cmdVersionRcv.mType == CmdHeader::eCommands::Version )
{
while( !client.mbDisconnectPending && !Network::DataReceivePending(client.mpSocketPending) ){
std::this_thread::yield(); // Idle until we receive the remote data
}
Network::DataReceive(client.mpSocketPending, PendingRcv);
}
bool bForceConnect = client.mServerForceConnectEnabled && (cmdVersionRcv.mFlags & static_cast<uint8_t>(CmdVersion::eFlags::ConnectForce)) != 0;
bool bCanConnect = !PendingRcv.IsError() &&
cmdVersionRcv.mType == cmdVersionSend.mType &&
cmdVersionRcv.mVersion == cmdVersionSend.mVersion &&
cmdVersionRcv.mWCharSize == cmdVersionSend.mWCharSize &&
(!client.IsConnected() || bForceConnect);
StringCopy(cmdVersionSend.mClientName, client.mName);
cmdVersionSend.mFlags = client.IsConnected() && !bCanConnect ? static_cast<uint8_t>(CmdVersion::eFlags::IsConnected): 0;
cmdVersionSend.mFlags |= client.IsConnected() && !client.mServerForceConnectEnabled ? static_cast<uint8_t>(CmdVersion::eFlags::IsUnavailable) : 0;
PendingSend.pCommand = reinterpret_cast<CmdPendingRead*>(&cmdVersionSend);
while( !PendingSend.IsDone() ){
Network::DataSend(client.mpSocketPending, PendingSend);
}
//---------------------------------------------------------------------
// Connection established, init client
//---------------------------------------------------------------------
if( bCanConnect && !PendingSend.IsError() && (!client.IsConnected() || bForceConnect) )
{
Network::SocketInfo* pNewComSocket = client.mpSocketPending.exchange(nullptr);
// If we detect an active connection with Server and 'ForceConnect was requested, close it first
if( client.IsConnected() )
{
client.mbDisconnectPending = true;
while( client.IsConnected() );
}
for(auto& texture : client.mTextures)
{
texture.mbSent = false;
}
client.mpSocketComs = pNewComSocket; // Take ownerhip of socket
client.mbHasTextureUpdate = true; // Force sending the client textures
client.mBGSettingSent.mTextureId = client.mBGSetting.mTextureId-1u; // Force sending the Background settings (by making different than current settings)
client.mFrameIndex = 0;
client.mPendingSendNext = 0;
client.mServerForceConnectEnabled = (cmdVersionRcv.mFlags & static_cast<uint8_t>(CmdVersion::eFlags::ConnectExclusive)) == 0;
client.mPendingRcv = PendingCom();
client.mPendingSend = PendingCom();
}
// Disconnect pending socket if init failed
Network::SocketInfo* SocketPending = client.mpSocketPending.exchange(nullptr);
bool bValidConnection = SocketPending == nullptr;
if( SocketPending ){
NetImgui::Internal::Network::Disconnect(SocketPending);
}
client.mbComInitActive = false;
return bValidConnection;
}
//=================================================================================================
// COMMUNICATIONS MAIN LOOP
//=================================================================================================
void Communications_Loop(void* pClientVoid)
{
IM_ASSERT(pClientVoid != nullptr);
ClientInfo* pClient = reinterpret_cast<ClientInfo*>(pClientVoid);
pClient->mbDisconnectPending = false;
pClient->mbClientThreadActive = true;
while( !pClient->mbDisconnectPending )
{
Communications_Outgoing(*pClient);
Communications_Incoming(*pClient);
}
Network::SocketInfo* pSocket = pClient->mpSocketComs.exchange(nullptr);
if (pSocket){
NetImgui::Internal::Network::Disconnect(pSocket);
}
pClient->mbClientThreadActive = false;
}
//=================================================================================================
// COMMUNICATIONS CONNECT THREAD : Reach out and connect to a NetImGuiServer
//=================================================================================================
void CommunicationsConnect(void* pClientVoid)
{
IM_ASSERT(pClientVoid != nullptr);
ClientInfo* pClient = reinterpret_cast<ClientInfo*>(pClientVoid);
if( Communications_Initialize(*pClient) )
{
Communications_Loop(pClientVoid);
}
}
//=================================================================================================
// COMMUNICATIONS HOST THREAD : Waiting NetImGuiServer reaching out to us.
// Launch a new com loop when connection is established
//=================================================================================================
void CommunicationsHost(void* pClientVoid)
{
ClientInfo* pClient = reinterpret_cast<ClientInfo*>(pClientVoid);
pClient->mbListenThreadActive = true;
pClient->mbDisconnectListen = false;
pClient->mpSocketListen = pClient->mpSocketPending.exchange(nullptr);
while( pClient->mpSocketListen.load() != nullptr && !pClient->mbDisconnectListen )
{
pClient->mpSocketPending = Network::ListenConnect(pClient->mpSocketListen);
if( pClient->mpSocketPending.load() != nullptr && Communications_Initialize(*pClient) )
{
pClient->mThreadFunction(Client::Communications_Loop, pClient);
}
std::this_thread::sleep_for(std::chrono::milliseconds(16)); // Prevents this thread from taking entire core, waiting on server connection requests
}
Network::SocketInfo* pSocket = pClient->mpSocketListen.exchange(nullptr);
NetImgui::Internal::Network::Disconnect(pSocket);
pClient->mbListenThreadActive = false;
}
//=================================================================================================
// Support of the Dear ImGui hooks
// (automatic call to NetImgui::BeginFrame()/EndFrame() on ImGui::BeginFrame()/Imgui::Render()
//=================================================================================================
#if NETIMGUI_IMGUI_CALLBACK_ENABLED
void HookBeginFrame(ImGuiContext*, ImGuiContextHook* hook)
{
Client::ClientInfo& client = *reinterpret_cast<Client::ClientInfo*>(hook->UserData);
if (!client.mbInsideNewEnd)
{
ScopedBool scopedInside(client.mbInsideHook, true);
NetImgui::NewFrame(false);
}
}
void HookEndFrame(ImGuiContext*, ImGuiContextHook* hook)
{
Client::ClientInfo& client = *reinterpret_cast<Client::ClientInfo*>(hook->UserData);
if (!client.mbInsideNewEnd)
{
ScopedBool scopedInside(client.mbInsideHook, true);
NetImgui::EndFrame();
}
}
#endif // NETIMGUI_IMGUI_CALLBACK_ENABLED
//=================================================================================================
// CLIENT INFO Constructor
//=================================================================================================
ClientInfo::ClientInfo()
: mpSocketPending(nullptr)
, mpSocketComs(nullptr)
, mpSocketListen(nullptr)
, mFontTextureID(TextureCastFromUInt(uint64_t(0u)))
, mTexturesPendingSent(0)
, mTexturesPendingCreated(0)
, mbClientThreadActive(false)
, mbListenThreadActive(false)
, mbComInitActive(false)
{
memset(mTexturesPending, 0, sizeof(mTexturesPending));
}
//=================================================================================================
// CLIENT INFO Constructor
//=================================================================================================
ClientInfo::~ClientInfo()
{
ContextRemoveHooks();
for( auto& texture : mTextures ){
texture.Set(nullptr);
}
for(size_t i(0); i<ArrayCount(mTexturesPending); ++i){
netImguiDeleteSafe(mTexturesPending[i]);
}
netImguiDeleteSafe(mpCmdInputPending);
netImguiDeleteSafe(mpCmdDrawLast);
netImguiDeleteSafe(mpCmdClipboard);
}
//=================================================================================================
// Initialize the associated ImguiContext
//=================================================================================================
void ClientInfo::ContextInitialize()
{
mpContext = ImGui::GetCurrentContext();
#if NETIMGUI_IMGUI_CALLBACK_ENABLED
ImGuiContextHook hookNewframe, hookEndframe;
ContextRemoveHooks();
hookNewframe.HookId = 0;
hookNewframe.Type = ImGuiContextHookType_NewFramePre;
hookNewframe.Callback = HookBeginFrame;
hookNewframe.UserData = this;
mhImguiHookNewframe = ImGui::AddContextHook(mpContext, &hookNewframe);
hookEndframe.HookId = 0;
hookEndframe.Type = ImGuiContextHookType_RenderPost;
hookEndframe.Callback = HookEndFrame;
hookEndframe.UserData = this;
mhImguiHookEndframe = ImGui::AddContextHook(mpContext, &hookEndframe);
#endif
}
//=================================================================================================
// Take over a Dear ImGui context for use with NetImgui
//=================================================================================================
void ClientInfo::ContextOverride()
{
ScopedImguiContext scopedSourceCtx(mpContext);
// Keep a copy of original settings of this context
mSavedContextValues.Save(mpContext);
mLastOutgoingDrawCheckTime = std::chrono::steady_clock::now();
// Override some settings
// Note: Make sure every setting overwritten here, are handled in 'SavedImguiContext::Save(...)'
{
ImGuiIO& newIO = ImGui::GetIO();
newIO.MouseDrawCursor = false;
newIO.BackendPlatformName = "NetImgui";
newIO.BackendRendererName = "DirectX11";
if( mFontCreationFunction != nullptr )
{
newIO.FontGlobalScale = 1;
mFontCreationScaling = -1;
}
#if IMGUI_VERSION_NUM < 18700
for (uint32_t i(0); i < ImGuiKey_COUNT; ++i) {
newIO.KeyMap[i] = i;
}
#endif
#if IMGUI_VERSION_NUM < 19110
newIO.GetClipboardTextFn = GetClipboardTextFn_NetImguiImpl;
newIO.SetClipboardTextFn = SetClipboardTextFn_NetImguiImpl;
newIO.ClipboardUserData = this;
#else
ImGuiPlatformIO& platformIO = ImGui::GetPlatformIO();
platformIO.Platform_GetClipboardTextFn = GetClipboardTextFn_NetImguiImpl;
platformIO.Platform_SetClipboardTextFn = SetClipboardTextFn_NetImguiImpl;
platformIO.Platform_ClipboardUserData = this;
#endif
#if defined(IMGUI_HAS_VIEWPORT)
newIO.ConfigFlags &= ~(ImGuiConfigFlags_ViewportsEnable); // Viewport unsupported at the moment
#endif
}
}
//=================================================================================================
// Restore a Dear ImGui context to initial state before we modified it
//=================================================================================================
void ClientInfo::ContextRestore()
{
// Note: only happens if context overriden is same as current one, to prevent trying to restore to a deleted context
if (IsContextOverriden() && ImGui::GetCurrentContext() == mpContext)
{
#ifdef IMGUI_HAS_VIEWPORT
//*************************************************************************************************
// COG: Disabling the call ImGui::UpdatePlatformWindows() otherwise it gets called twice when
// disconnecting causing this assert in ImGui::UpdatePlatformWindows():
//
// IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount);
//*************************************************************************************************
//ImGui::UpdatePlatformWindows(); // Prevents issue with mismatched frame tracking, when restoring enabled viewport feature
#endif
if( mFontCreationFunction && ImGui::GetIO().Fonts && ImGui::GetIO().Fonts->Fonts.size() > 0)
{
float noScaleSize = ImGui::GetIO().Fonts->Fonts[0]->FontSize / mFontCreationScaling;
float originalScale = mSavedContextValues.mFontGeneratedSize / noScaleSize;
mFontCreationFunction(mFontCreationScaling, originalScale);
}
mSavedContextValues.Restore(mpContext);
}
}
//=================================================================================================
// Remove callback hooks, once we detect a disconnection
//=================================================================================================
void ClientInfo::ContextRemoveHooks()
{
#if NETIMGUI_IMGUI_CALLBACK_ENABLED
if (mpContext && mhImguiHookNewframe != 0)
{
ImGui::RemoveContextHook(mpContext, mhImguiHookNewframe);
ImGui::RemoveContextHook(mpContext, mhImguiHookEndframe);
mhImguiHookNewframe = mhImguiHookNewframe = 0;
}
#endif
}
//=================================================================================================
// Process textures waiting to be sent to server
// 1. New textures are added tp pending queue (Main Thread)
// 2. Pending textures are sent to Server and added to our active texture list (Com Thread)
//=================================================================================================
void ClientInfo::ProcessTexturePending()
{
while( mTexturesPendingCreated != mTexturesPendingSent )
{
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)
int texIdx = 0;
int texFreeSlot = static_cast<int>(mTextures.size());
while( texIdx < mTextures.size() && ( !mTextures[texIdx].IsValid() || mTextures[texIdx].mpCmdTexture->mTextureId != pCmdTexture->mTextureId) )
{
texFreeSlot = !mTextures[texIdx].IsValid() ? texIdx : texFreeSlot;
++texIdx;
}
if( texIdx == mTextures.size() )
texIdx = texFreeSlot;
if( texIdx == mTextures.size() )
mTextures.push_back(ClientTexture());
mTextures[texIdx].Set( pCmdTexture );
mTextures[texIdx].mbSent = false;
}
}
}
//=================================================================================================
// Create a new Draw Command from Dear Imgui draw data.
// 1. New ImGui frame has been completed, create a new draw command from draw data (Main Thread)
// 2. We see a pending Draw Command, take ownership of it and send it to Server (Com thread)
//=================================================================================================
void ClientInfo::ProcessDrawData(const ImDrawData* pDearImguiData, ImGuiMouseCursor mouseCursor)
{
if( !mbValidDrawFrame )
return;
CmdDrawFrame* pDrawFrameNew = ConvertToCmdDrawFrame(pDearImguiData, mouseCursor);
pDrawFrameNew->mCompressed = mClientCompressionMode == eCompressionMode::kForceEnable || (mClientCompressionMode == eCompressionMode::kUseServerSetting && mServerCompressionEnabled);
mPendingFrameOut.Assign(pDrawFrameNew);
}
}}} // namespace NetImgui::Internal::Client
#include "NetImgui_WarningReenable.h"
#endif //#if NETIMGUI_ENABLED
@@ -0,0 +1,161 @@
#pragma once
#include "NetImgui_Shared.h"
#include "NetImgui_CmdPackets.h"
//=============================================================================
// Forward Declares
//=============================================================================
namespace NetImgui { namespace Internal { namespace Network { struct SocketInfo; } } }
namespace NetImgui { namespace Internal { namespace Client
{
//=============================================================================
// Keep a list of textures used by Imgui, needed by server
//=============================================================================
struct ClientTexture
{
inline void Set( CmdTexture* pCmdTexture );
inline bool IsValid()const;
CmdTexture* mpCmdTexture= nullptr;
bool mbSent = false;
uint8_t mPadding[7] = {};
};
//=============================================================================
// Keeps a list of ImGui context values NetImgui overrides (to restore)
//=============================================================================
struct SavedImguiContext
{
void Save(ImGuiContext* copyFrom);
void Restore(ImGuiContext* copyTo);
const char* mBackendPlatformName = nullptr;
const char* mBackendRendererName = nullptr;
void* mImeWindowHandle = nullptr;
float mFontGlobalScale = 1.f;
float mFontGeneratedSize = 0.f;
ImGuiBackendFlags mBackendFlags = 0;
ImGuiConfigFlags mConfigFlags = 0;
bool mDrawMouse = false;
bool mSavedContext = false;
char mPadding1[2] = {};
void* mClipboardUserData = nullptr;
#if IMGUI_VERSION_NUM < 19110
const char* (*mGetClipboardTextFn)(void*) = nullptr;
void (*mSetClipboardTextFn)(void*, const char*) = nullptr;
#else
const char* (*mGetClipboardTextFn)(ImGuiContext*) = nullptr;
void (*mSetClipboardTextFn)(ImGuiContext*, const char*) = nullptr;
#endif
#if IMGUI_VERSION_NUM < 18700
int mKeyMap[ImGuiKey_COUNT] = {};
char mPadding2[8 - (sizeof(mKeyMap) % 8)] = {};
#endif
};
//=============================================================================
// Keep all Client infos needed for communication with server
//=============================================================================
struct ClientInfo
{
using VecTexture = ImVector<ClientTexture>;
using BufferKeys = Ringbuffer<uint16_t, 1024>;
using TimePoint = std::chrono::time_point<std::chrono::steady_clock>;
struct InputState
{
uint64_t mInputDownMask[(CmdInput::ImGuiKey_COUNT+63)/64] = {};
float mInputAnalog[CmdInput::kAnalog_Count] = {};
uint64_t mMouseDownMask = 0;
float mMouseWheelVertPrev = 0.f;
float mMouseWheelHorizPrev = 0.f;
};
ClientInfo();
~ClientInfo();
void ContextInitialize();
void ContextOverride();
void ContextRestore();
void ContextRemoveHooks();
inline bool IsContextOverriden()const;
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
std::atomic_bool mbDisconnectPending; // Terminate Client/Server coms
std::atomic_bool mbDisconnectListen; // Terminate waiting connection from Server
uint32_t mSocketListenPort = 0; // Socket Port number used to wait for communication request from server
VecTexture mTextures; // List if textures created by this client (used un main thread)
char mName[64] = {};
uint64_t mFrameIndex = 0; // Incremented everytime we send a DrawFrame Command
CmdTexture* mTexturesPending[16] = {};
ExchangePtr<CmdDrawFrame> mPendingFrameOut;
ExchangePtr<CmdBackground> mPendingBackgroundOut;
ExchangePtr<CmdInput> mPendingInputIn;
ExchangePtr<CmdClipboard> mPendingClipboardIn; // Clipboard content received from Server and waiting to be taken by client
ExchangePtr<CmdClipboard> mPendingClipboardOut; // Clipboard content copied on Client and waiting to be sent to Server
ImGuiContext* mpContext = nullptr; // Context that the remote drawing should use (either the one active when connection request happened, or a clone)
PendingCom mPendingRcv; // Data being currently received from Server
PendingCom mPendingSend; // Data being currently sent to Server
uint32_t mPendingSendNext = 0; // Type of Cmd to next attempt sending, when channel is available
CmdPendingRead mCmdPendingRead; // Used to get info on the next incoming command from Server
CmdInput* mpCmdInputPending = nullptr; // Last Input Command from server, waiting to be processed by client
CmdClipboard* mpCmdClipboard = nullptr; // Last received clipboad command
CmdDrawFrame* mpCmdDrawLast = nullptr; // Last sent Draw Command. Used by data compression, to generate delta between previous and current frame
CmdBackground mBGSetting; // Current value assigned to background appearance by user
CmdBackground mBGSettingSent; // Last sent value to remote server
BufferKeys mPendingKeyIn; // Keys pressed received. Results of 2 CmdInputs are concatenated if received before being processed
TimePoint mLastOutgoingDrawCheckTime; // When we last checked if we have a pending draw command to send
TimePoint mLastOutgoingDrawTime; // When we last sent an updated draw command to the server
ImVec2 mSavedDisplaySize = {0, 0}; // Save original display size on 'NewFrame' and restore it on 'EndFrame' (making sure size is still valid after a disconnect)
const void* mpFontTextureData = nullptr; // Last font texture data send to server (used to detect if font was changed)
ImTextureID mFontTextureID;
SavedImguiContext mSavedContextValues;
std::atomic_uint32_t mTexturesPendingSent;
std::atomic_uint32_t mTexturesPendingCreated;
std::atomic_bool mbClientThreadActive; // True when connected and communicating with Server
std::atomic_bool mbListenThreadActive; // True when listening from connection request from Server
std::atomic_bool mbComInitActive; // True when attempting to initialize a new connection
bool mbHasTextureUpdate = false;
bool mbIsDrawing = false; // We are inside a 'NetImgui::NewFrame' / 'NetImgui::EndFrame' (even if not for a remote draw)
bool mbIsRemoteDrawing = false; // True if the rendering it meant for the remote netImgui server
bool mbRestorePending = false; // Original context has had some settings overridden, original values stored in mRestoreXXX
bool mbFontUploaded = false; // Auto detect if font was sent to server
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()
uint8_t mClientCompressionMode = eCompressionMode::kUseServerSetting;
bool mServerCompressionEnabled = false; // If Server would like compression to be enabled (mClientCompressionMode value can override this value)
bool mServerCompressionSkip = false; // Force ignore compression setting for 1 frame
bool mServerForceConnectEnabled = true; // If another NetImguiServer can take connection away from the one currently active
ThreadFunctPtr mThreadFunction = nullptr; // Function to use when laucnhing new threads
FontCreateFuncPtr mFontCreationFunction = nullptr; // Method to call to generate the remote ImGui font. By default, re-use the local font, but this doesn't handle native DPI scaling on remote server
float mFontCreationScaling = 1.f; // Last font scaling used when generating the NetImgui font
float mDesiredFps = 30.f; // How often we should update the remote drawing. Received from server
InputState mPreviousInputState; // Keeping track of last keyboard/mouse state
ImGuiID mhImguiHookNewframe = 0;
ImGuiID mhImguiHookEndframe = 0;
void ProcessDrawData(const ImDrawData* pDearImguiData, ImGuiMouseCursor mouseCursor);
void ProcessTexturePending();
inline bool IsConnected()const;
inline bool IsConnectPending()const;
inline bool IsActive()const;
// Prevent warnings about implicitly created copy
protected:
ClientInfo(const ClientInfo&)=delete;
ClientInfo(const ClientInfo&&)=delete;
void operator=(const ClientInfo&)=delete;
};
//=============================================================================
// Main communication loop threads that are run in separate threads
//=============================================================================
void CommunicationsConnect(void* pClientVoid);
void CommunicationsHost(void* pClientVoid);
}}} //namespace NetImgui::Internal::Client
#include "NetImgui_Client.inl"
@@ -0,0 +1,38 @@
#include "NetImgui_Network.h"
namespace NetImgui { namespace Internal { namespace Client {
void ClientTexture::Set( CmdTexture* pCmdTexture )
{
netImguiDeleteSafe(mpCmdTexture);
mpCmdTexture = pCmdTexture;
mbSent = pCmdTexture == nullptr;
}
bool ClientTexture::IsValid()const
{
return mpCmdTexture != nullptr;
}
bool ClientInfo::IsConnected()const
{
return mpSocketComs.load() != nullptr;
}
bool ClientInfo::IsConnectPending()const
{
return mbComInitActive || mpSocketPending.load() != nullptr || mpSocketListen.load() != nullptr;
}
bool ClientInfo::IsActive()const
{
return mbClientThreadActive || mbListenThreadActive;
}
bool ClientInfo::IsContextOverriden()const
{
return mSavedContextValues.mSavedContext;
}
}}} // namespace NetImgui::Internal::Client
@@ -0,0 +1,276 @@
#pragma once
#include "NetImgui_Shared.h"
#include "NetImgui_CmdPackets_DrawFrame.h"
namespace NetImgui { namespace Internal
{
//Note: If updating any of these commands data structure, increase 'CmdVersion::eVersion'
struct alignas(8) CmdHeader
{
enum class eCommands : uint8_t { Version, Texture, Input, DrawFrame, Background, Clipboard, Count };
CmdHeader(eCommands CmdType, uint16_t Size) : mSize(Size), mType(CmdType){}
uint32_t mSize = 0;
eCommands mType = eCommands::Count;
uint8_t mPadding[3] = {};
};
// Used as step 1 of 2 of reading incoming transmission between Client/Server, to get header whose size we know
struct alignas(8) CmdPendingRead : public CmdHeader
{
CmdPendingRead() : CmdHeader(eCommands::Count, sizeof(CmdPendingRead) ){}
};
struct alignas(8) CmdVersion : public CmdHeader
{
enum class eVersion : uint32_t
{
Initial = 1,
NewTextureFormat = 2,
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
DataCompression = 7, // Adding support for data compression between client/server. Simple low cost delta compressor (only send difference from previous frame)
DataCompression2 = 8, // Improvement to data compression (save corner position and use SoA for vertices data)
VertexUVRange = 9, // Changed vertices UV value range to [0,1] for increased precision on large font texture
Imgui_1_87 = 10, // Added Dear ImGui Input refactor
OffetPointer = 11, // Updated the handling of OffsetPoint. Moved flag bit from last bit to first bit. Addresses and data are always at least 4 bytes aligned, so should never conflict with potential address space
CustomTexture = 12, // Added a 'custom' texture format to let user potentially handle their how format
DPIScale = 13, // Server now handle monitor DPI
Clipboard = 14, // Added clipboard support between server/client
ForceReconnect = 15, // Server can now take over the connection from another server
UpdatedComs = 16, // Faster protocol by removing blocking coms
RemDisconnect = 17, // Removed Disconnect command
// Insert new version here
//--------------------------------
_count,
_current = _count -1
};
enum class eFlags : uint8_t
{
IsUnavailable = 0x01, // Client telling Server it cannot be used
IsConnected = 0x02, // Client telling Server there's already a valid connection (can potentially be taken over if !IsUnavailable)
ConnectForce = 0x04, // Server telling Client it want to take over connection if there's already one
ConnectExclusive = 0x08, // Server telling Client that once connected, others servers should be denied access
};
CmdVersion() : CmdHeader(CmdHeader::eCommands::Version, sizeof(CmdVersion)){}
char mClientName[64] = {};
char mImguiVerName[16] = {IMGUI_VERSION};
char mNetImguiVerName[16] = {NETIMGUI_VERSION};
eVersion mVersion = eVersion::_current;
uint32_t mImguiVerID = IMGUI_VERSION_NUM;
uint32_t mNetImguiVerID = NETIMGUI_VERSION_NUM;
uint8_t mWCharSize = static_cast<uint8_t>(sizeof(ImWchar));
uint8_t mFlags = 0;
char PADDING[2];
};
struct alignas(8) CmdInput : public CmdHeader
{
// Identify a mouse button.
// Those values are guaranteed to be stable and we frequently use 0/1 directly. Named enums provided for convenience.
enum NetImguiMouseButton
{
ImGuiMouseButton_Left = 0,
ImGuiMouseButton_Right = 1,
ImGuiMouseButton_Middle = 2,
ImGuiMouseButton_Extra1 = 3, // Additional entry
ImGuiMouseButton_Extra2 = 4, // Additional entry
ImGuiMouseButton_COUNT = 5
};
// Copy of Dear ImGui key enum
// We keep our own internal version, to make sure Client key is the same as Server Key (since they can have different Imgui version)
enum NetImguiKeys
{
// Keyboard
ImGuiKey_Tab,
ImGuiKey_LeftArrow,
ImGuiKey_RightArrow,
ImGuiKey_UpArrow,
ImGuiKey_DownArrow,
ImGuiKey_PageUp,
ImGuiKey_PageDown,
ImGuiKey_Home,
ImGuiKey_End,
ImGuiKey_Insert,
ImGuiKey_Delete,
ImGuiKey_Backspace,
ImGuiKey_Space,
ImGuiKey_Enter,
ImGuiKey_Escape,
ImGuiKey_LeftCtrl, ImGuiKey_LeftShift, ImGuiKey_LeftAlt, ImGuiKey_LeftSuper,
ImGuiKey_RightCtrl, ImGuiKey_RightShift, ImGuiKey_RightAlt, ImGuiKey_RightSuper,
ImGuiKey_Menu,
ImGuiKey_0, ImGuiKey_1, ImGuiKey_2, ImGuiKey_3, ImGuiKey_4, ImGuiKey_5, ImGuiKey_6, ImGuiKey_7, ImGuiKey_8, ImGuiKey_9,
ImGuiKey_A, ImGuiKey_B, ImGuiKey_C, ImGuiKey_D, ImGuiKey_E, ImGuiKey_F, ImGuiKey_G, ImGuiKey_H, ImGuiKey_I, ImGuiKey_J,
ImGuiKey_K, ImGuiKey_L, ImGuiKey_M, ImGuiKey_N, ImGuiKey_O, ImGuiKey_P, ImGuiKey_Q, ImGuiKey_R, ImGuiKey_S, ImGuiKey_T,
ImGuiKey_U, ImGuiKey_V, ImGuiKey_W, ImGuiKey_X, ImGuiKey_Y, ImGuiKey_Z,
ImGuiKey_F1, ImGuiKey_F2, ImGuiKey_F3, ImGuiKey_F4, ImGuiKey_F5, ImGuiKey_F6,
ImGuiKey_F7, ImGuiKey_F8, ImGuiKey_F9, ImGuiKey_F10, ImGuiKey_F11, ImGuiKey_F12,
ImGuiKey_F13, ImGuiKey_F14, ImGuiKey_F15, ImGuiKey_F16, ImGuiKey_F17, ImGuiKey_F18,
ImGuiKey_F19, ImGuiKey_F20, ImGuiKey_F21, ImGuiKey_F22, ImGuiKey_F23, ImGuiKey_F24,
ImGuiKey_Apostrophe, // '
ImGuiKey_Comma, // ,
ImGuiKey_Minus, // -
ImGuiKey_Period, // .
ImGuiKey_Slash, // /
ImGuiKey_Semicolon, // ;
ImGuiKey_Equal, // =
ImGuiKey_LeftBracket, // [
ImGuiKey_Backslash, // \ (this text inhibit multiline comment caused by backslash)
ImGuiKey_RightBracket, // ]
ImGuiKey_GraveAccent, // `
ImGuiKey_CapsLock,
ImGuiKey_ScrollLock,
ImGuiKey_NumLock,
ImGuiKey_PrintScreen,
ImGuiKey_Pause,
ImGuiKey_Keypad0, ImGuiKey_Keypad1, ImGuiKey_Keypad2, ImGuiKey_Keypad3, ImGuiKey_Keypad4,
ImGuiKey_Keypad5, ImGuiKey_Keypad6, ImGuiKey_Keypad7, ImGuiKey_Keypad8, ImGuiKey_Keypad9,
ImGuiKey_KeypadDecimal,
ImGuiKey_KeypadDivide, ImGuiKey_KeypadMultiply, ImGuiKey_KeypadSubtract,
ImGuiKey_KeypadAdd, ImGuiKey_KeypadEnter, ImGuiKey_KeypadEqual,
ImGuiKey_AppBack, // Available on some keyboard/mouses. Often referred as "Browser Back"
ImGuiKey_AppForward,
// Gamepad (some of those are analog values, 0.0f to 1.0f) // GAME NAVIGATION ACTION
// (download controller mapping PNG/PSD at http://dearimgui.org/controls_sheets)
ImGuiKey_GamepadStart, // Menu (Xbox) + (Switch) Start/Options (PS)
ImGuiKey_GamepadBack, // View (Xbox) - (Switch) Share (PS)
ImGuiKey_GamepadFaceLeft, // X (Xbox) Y (Switch) Square (PS) // Tap: Toggle Menu. Hold: Windowing mode (Focus/Move/Resize windows)
ImGuiKey_GamepadFaceRight, // B (Xbox) A (Switch) Circle (PS) // Cancel / Close / Exit
ImGuiKey_GamepadFaceUp, // Y (Xbox) X (Switch) Triangle (PS) // Text Input / On-screen Keyboard
ImGuiKey_GamepadFaceDown, // A (Xbox) B (Switch) Cross (PS) // Activate / Open / Toggle / Tweak
ImGuiKey_GamepadDpadLeft, // D-pad Left // Move / Tweak / Resize Window (in Windowing mode)
ImGuiKey_GamepadDpadRight, // D-pad Right // Move / Tweak / Resize Window (in Windowing mode)
ImGuiKey_GamepadDpadUp, // D-pad Up // Move / Tweak / Resize Window (in Windowing mode)
ImGuiKey_GamepadDpadDown, // D-pad Down // Move / Tweak / Resize Window (in Windowing mode)
ImGuiKey_GamepadL1, // L Bumper (Xbox) L (Switch) L1 (PS) // Tweak Slower / Focus Previous (in Windowing mode)
ImGuiKey_GamepadR1, // R Bumper (Xbox) R (Switch) R1 (PS) // Tweak Faster / Focus Next (in Windowing mode)
ImGuiKey_GamepadL2, // L Trig. (Xbox) ZL (Switch) L2 (PS) [Analog]
ImGuiKey_GamepadR2, // R Trig. (Xbox) ZR (Switch) R2 (PS) [Analog]
ImGuiKey_GamepadL3, // L Stick (Xbox) L3 (Switch) L3 (PS)
ImGuiKey_GamepadR3, // R Stick (Xbox) R3 (Switch) R3 (PS)
ImGuiKey_GamepadLStickLeft, // [Analog] // Move Window (in Windowing mode)
ImGuiKey_GamepadLStickRight, // [Analog] // Move Window (in Windowing mode)
ImGuiKey_GamepadLStickUp, // [Analog] // Move Window (in Windowing mode)
ImGuiKey_GamepadLStickDown, // [Analog] // Move Window (in Windowing mode)
ImGuiKey_GamepadRStickLeft, // [Analog]
ImGuiKey_GamepadRStickRight, // [Analog]
ImGuiKey_GamepadRStickUp, // [Analog]
ImGuiKey_GamepadRStickDown, // [Analog]
// Mouse Buttons (auto-submitted from AddMouseButtonEvent() calls)
// - This is mirroring the data also written to io.MouseDown[], io.MouseWheel, in a format allowing them to be accessed via standard key API.
ImGuiKey_MouseLeft, ImGuiKey_MouseRight, ImGuiKey_MouseMiddle, ImGuiKey_MouseX1, ImGuiKey_MouseX2, ImGuiKey_MouseWheelX, ImGuiKey_MouseWheelY,
// Keyboard Modifiers (explicitly submitted by backend via AddKeyEvent() calls)
// - This is mirroring the data also written to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper, in a format allowing
// them to be accessed via standard key API, allowing calls such as IsKeyPressed(), IsKeyReleased(), querying duration etc.
// - Code polling every keys (e.g. an interface to detect a key press for input mapping) might want to ignore those
// and prefer using the real keys (e.g. ImGuiKey_LeftCtrl, ImGuiKey_RightCtrl instead of ImGuiKey_ModCtrl).
// - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys.
// In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and
// backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user...
ImGuiKey_ReservedForModCtrl, ImGuiKey_ReservedForModShift, ImGuiKey_ReservedForModAlt, ImGuiKey_ReservedForModSuper,
// End of list
ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value
};
static constexpr uint32_t kAnalog_First = ImGuiKey_GamepadLStickLeft;
static constexpr uint32_t kAnalog_Last = ImGuiKey_GamepadRStickDown;
static constexpr uint32_t kAnalog_Count = kAnalog_Last - kAnalog_First + 1;
CmdInput() : CmdHeader(CmdHeader::eCommands::Input, sizeof(CmdInput)){}
uint16_t mScreenSize[2] = {};
int16_t mMousePos[2] = {};
float mMouseWheelVert = 0.f;
float mMouseWheelHoriz = 0.f;
uint16_t mKeyChars[256] = {}; // Input characters
uint16_t mKeyCharCount = 0; // Number of valid input characters
bool mCompressionUse = false; // Server would like client to compress the communication data
bool mCompressionSkip = false; // Server forcing next client's frame data to be uncompressed
float mFontDPIScaling = 1.f; // Font scaling request by Server accounting for monitor DPI
float mDesiredFps = 30.f; // Requested redraw speed
uint64_t mMouseDownMask = 0;
uint64_t mInputDownMask[(ImGuiKey_COUNT+63)/64]={};
float mInputAnalog[kAnalog_Count] = {};
inline bool IsKeyDown(NetImguiKeys netimguiKey) const;
};
struct alignas(8) CmdTexture : public CmdHeader
{
CmdTexture() : CmdHeader(CmdHeader::eCommands::Texture, sizeof(CmdTexture)){}
OffsetPointer<uint8_t> mpTextureData;
uint64_t mTextureId = 0;
uint16_t mWidth = 0;
uint16_t mHeight = 0;
uint8_t mFormat = eTexFormat::kTexFmt_Invalid; // eTexFormat
uint8_t PADDING[3] = {};
};
struct alignas(8) CmdDrawFrame : public CmdHeader
{
CmdDrawFrame() : CmdHeader(CmdHeader::eCommands::DrawFrame, sizeof(CmdDrawFrame)){}
uint64_t mFrameIndex = 0;
uint32_t mMouseCursor = 0; // ImGuiMouseCursor value
float mDisplayArea[4] = {};
uint32_t mIndiceByteSize = 0;
uint32_t mDrawGroupCount = 0;
uint32_t mTotalVerticeCount = 0;
uint32_t mTotalIndiceCount = 0;
uint32_t mTotalDrawCount = 0;
uint32_t mUncompressedSize = 0;
uint8_t mCompressed = false;
uint8_t PADDING[3] = {};
OffsetPointer<ImguiDrawGroup> mpDrawGroups;
inline void ToPointers();
inline void ToOffsets();
};
struct alignas(8) CmdBackground : public CmdHeader
{
CmdBackground() : CmdHeader(CmdHeader::eCommands::Background, sizeof(CmdBackground)){}
static constexpr uint64_t kDefaultTexture = ~0u;
float mClearColor[4] = {0.2f, 0.2f, 0.2f, 1.f}; // Background color
float mTextureTint[4] = {1.f, 1.f, 1.f, 0.5f}; // Tint/alpha applied to texture
uint64_t mTextureId = kDefaultTexture; // Texture rendered in background, use server texture by default
inline bool operator==(const CmdBackground& cmp)const;
inline bool operator!=(const CmdBackground& cmp)const;
};
struct alignas(8) CmdClipboard : public CmdHeader
{
CmdClipboard() : CmdHeader(CmdHeader::eCommands::Clipboard, sizeof(CmdClipboard)){}
size_t mByteSize = 0;
OffsetPointer<char> mContentUTF8;
inline void ToPointers();
inline void ToOffsets();
inline static CmdClipboard* Create(const char* clipboard);
};
//=============================================================================
// Keeping track of partial incoming/outgoing transmissions
//=============================================================================
struct PendingCom
{
size_t SizeCurrent = 0; // Amount of data sent or received so far
bool bAutoFree = false; // Need to free data buffer at the end of processing
bool bError = false; // If an error occurs during coms
CmdHeader* pCommand = nullptr; // Where to store incoming data or read to send data
inline bool IsError()const{ return bError; }
inline bool IsDone()const { return IsError() || (pCommand && pCommand->mSize == SizeCurrent); }
inline bool IsReady()const{ return !IsError() && pCommand == nullptr; }
inline bool IsPending()const{ return !IsError() && !IsDone() && !IsReady(); }
};
}} // namespace NetImgui::Internal
#include "NetImgui_CmdPackets.inl"
@@ -0,0 +1,101 @@
#include "NetImgui_CmdPackets.h"
namespace NetImgui { namespace Internal
{
void CmdDrawFrame::ToPointers()
{
if( !mpDrawGroups.IsPointer() )
{
mpDrawGroups.ToPointer();
for (uint32_t i(0); i < mDrawGroupCount; ++i) {
mpDrawGroups[i].ToPointers();
}
}
}
void CmdDrawFrame::ToOffsets()
{
if( !mpDrawGroups.IsOffset() )
{
for (uint32_t i(0); i < mDrawGroupCount; ++i) {
mpDrawGroups[i].ToOffsets();
}
mpDrawGroups.ToOffset();
}
}
void ImguiDrawGroup::ToPointers()
{
if( !mpIndices.IsPointer() ) //Safer to test the first element after CmdHeader
{
mpIndices.ToPointer();
mpVertices.ToPointer();
mpDraws.ToPointer();
}
}
void ImguiDrawGroup::ToOffsets()
{
if( !mpIndices.IsOffset() ) //Safer to test the first element after CmdHeader
{
mpIndices.ToOffset();
mpVertices.ToOffset();
mpDraws.ToOffset();
}
}
bool CmdInput::IsKeyDown( CmdInput::NetImguiKeys netimguiKey) const
{
uint32_t valIndex = netimguiKey/64;
uint64_t valMask = 0x0000000000000001ull << (netimguiKey%64);
return mInputDownMask[valIndex] & valMask;
}
bool CmdBackground::operator==(const CmdBackground& cmp)const
{
bool sameValue(true);
for(size_t i(0); i<sizeof(CmdBackground)/8; i++){
sameValue &= reinterpret_cast<const uint64_t*>(this)[i] == reinterpret_cast<const uint64_t*>(&cmp)[i];
}
return sameValue;
}
bool CmdBackground::operator!=(const CmdBackground& cmp)const
{
return (*this == cmp) == false;
}
void CmdClipboard::ToPointers()
{
if( !mContentUTF8.IsPointer() ){
mContentUTF8.ToPointer();
}
}
void CmdClipboard::ToOffsets()
{
if( !mContentUTF8.IsOffset() ){
mContentUTF8.ToOffset();
}
}
CmdClipboard* CmdClipboard::Create(const char* clipboard)
{
if( clipboard )
{
size_t clipboardByteSize(0);
while(clipboard[clipboardByteSize++] != 0);
size_t totalDataCount = sizeof(CmdClipboard) + DivUp<size_t>(clipboardByteSize, ComDataSize);
auto pNewClipboard = NetImgui::Internal::netImguiSizedNew<CmdClipboard>(totalDataCount*ComDataSize);
pNewClipboard->mSize = static_cast<uint32_t>(totalDataCount*ComDataSize);
pNewClipboard->mByteSize = clipboardByteSize;
pNewClipboard->mContentUTF8.SetPtr(reinterpret_cast<char*>(&pNewClipboard[1]));
memcpy(pNewClipboard->mContentUTF8.Get(), clipboard, clipboardByteSize);
return pNewClipboard;
}
return nullptr;
}
}} // namespace NetImgui::Internal
@@ -0,0 +1,381 @@
#include "NetImgui_Shared.h"
#if NETIMGUI_ENABLED
#include "NetImgui_WarningDisable.h"
#include "NetImgui_CmdPackets.h"
namespace NetImgui { namespace Internal
{
template <typename TType>
inline void SetAndIncreaseDataPointer(OffsetPointer<TType>& dataPointer, uint32_t dataSize, ComDataType*& pDataOutput)
{
dataPointer.SetComDataPtr(pDataOutput);
const size_t dataCount = DivUp<size_t>(dataSize, ComDataSize);
pDataOutput[dataCount-1] = 0;
pDataOutput += dataCount;
}
//=============================================================================
// Safely convert a pointer to a int value, even if int storage size > pointer
//=============================================================================
template<typename TInt, typename TPointer>
TInt PointerCast(TPointer* pointer)
{
union CastHelperUnion
{
TInt ValueInt;
TPointer* ValuePointer;
};
CastHelperUnion helperObject = {};
helperObject.ValuePointer = pointer;
return helperObject.ValueInt;
}
//=================================================================================================
//
//=================================================================================================
inline void ImGui_ExtractIndices(const ImDrawList& cmdList, ImguiDrawGroup& drawGroupOut, ComDataType*& pDataOutput)
{
bool is16Bit = sizeof(ImDrawIdx) == 2 || cmdList.VtxBuffer.size() <= 0xFFFF; // When Dear Imgui is compiled with ImDrawIdx = uint16, we know for certain that there won't be any drawcall with index > 65k, even if Vertex buffer is bigger than 65k.
drawGroupOut.mBytePerIndex = is16Bit ? 2 : 4;
drawGroupOut.mIndiceCount = static_cast<uint32_t>(cmdList.IdxBuffer.size());
uint32_t sizeNeeded = drawGroupOut.mIndiceCount*drawGroupOut.mBytePerIndex;
SetAndIncreaseDataPointer(drawGroupOut.mpIndices, sizeNeeded, pDataOutput);
// No conversion needed, straight copy
if( drawGroupOut.mBytePerIndex == sizeof(ImDrawIdx) )
{
memcpy(drawGroupOut.mpIndices.Get(), &cmdList.IdxBuffer.front(), sizeNeeded);
}
// From 32bits to 16bits
else if(is16Bit)
{
for(int i(0); i < static_cast<int>(drawGroupOut.mIndiceCount); ++i)
reinterpret_cast<uint16_t*>(drawGroupOut.mpIndices.Get())[i] = static_cast<uint16_t>(cmdList.IdxBuffer[i]);
}
// From 16bits to 32bits
else
{
for(int i(0); i < static_cast<int>(drawGroupOut.mIndiceCount); ++i)
reinterpret_cast<uint32_t*>(drawGroupOut.mpIndices.Get())[i] = static_cast<uint32_t>(cmdList.IdxBuffer[i]);
}
}
//=================================================================================================
//
//=================================================================================================
inline void ImGui_ExtractVertices(const ImDrawList& cmdList, ImguiDrawGroup& drawGroupOut, ComDataType*& pDataOutput)
{
drawGroupOut.mVerticeCount = static_cast<uint32_t>(cmdList.VtxBuffer.size());
drawGroupOut.mReferenceCoord[0] = drawGroupOut.mVerticeCount > 0 ? cmdList.VtxBuffer[0].pos.x : 0.f;
drawGroupOut.mReferenceCoord[1] = drawGroupOut.mVerticeCount > 0 ? cmdList.VtxBuffer[0].pos.y : 0.f;
SetAndIncreaseDataPointer(drawGroupOut.mpVertices, drawGroupOut.mVerticeCount*sizeof(ImguiVert), pDataOutput);
ImguiVert* pVertices = drawGroupOut.mpVertices.Get();
for(int i(0); i<static_cast<int>(drawGroupOut.mVerticeCount); ++i)
{
const auto& Vtx = cmdList.VtxBuffer[i];
pVertices[i].mColor = Vtx.col;
pVertices[i].mUV[0] = static_cast<uint16_t>((Vtx.uv.x - static_cast<float>(ImguiVert::kUvRange_Min) + 0.5f/65535.f) * 0xFFFF / (ImguiVert::kUvRange_Max - ImguiVert::kUvRange_Min));
pVertices[i].mUV[1] = static_cast<uint16_t>((Vtx.uv.y - static_cast<float>(ImguiVert::kUvRange_Min) + 0.5f/65535.f) * 0xFFFF / (ImguiVert::kUvRange_Max - ImguiVert::kUvRange_Min));
pVertices[i].mPos[0] = static_cast<uint16_t>((Vtx.pos.x - drawGroupOut.mReferenceCoord[0] - static_cast<float>(ImguiVert::kPosRange_Min)) * 0xFFFF / (ImguiVert::kPosRange_Max - ImguiVert::kPosRange_Min));
pVertices[i].mPos[1] = static_cast<uint16_t>((Vtx.pos.y - drawGroupOut.mReferenceCoord[1] - static_cast<float>(ImguiVert::kPosRange_Min)) * 0xFFFF / (ImguiVert::kPosRange_Max - ImguiVert::kPosRange_Min));
}
}
//=================================================================================================
//
//=================================================================================================
inline void ImGui_ExtractDraws(const ImDrawList& cmdList, ImguiDrawGroup& drawGroupOut, ComDataType*& pDataOutput)
{
int maxDrawCount = static_cast<int>(cmdList.CmdBuffer.size());
uint32_t drawCount = 0;
ImguiDraw* pOutDraws = reinterpret_cast<ImguiDraw*>(pDataOutput);
for(int cmd_i = 0; cmd_i < maxDrawCount; ++cmd_i)
{
const ImDrawCmd* pCmd = &cmdList.CmdBuffer[cmd_i];
if( pCmd->UserCallback == nullptr )
{
#if IMGUI_VERSION_NUM >= 17100
pOutDraws[drawCount].mVtxOffset = pCmd->VtxOffset;
pOutDraws[drawCount].mIdxOffset = pCmd->IdxOffset;
#else
pOutDraws[drawCount].mVtxOffset = 0;
pOutDraws[drawCount].mIdxOffset = 0;
#endif
pOutDraws[drawCount].mTextureId = TextureCastFromID(pCmd->TextureId);
pOutDraws[drawCount].mIdxCount = pCmd->ElemCount;
pOutDraws[drawCount].mClipRect[0] = pCmd->ClipRect.x;
pOutDraws[drawCount].mClipRect[1] = pCmd->ClipRect.y;
pOutDraws[drawCount].mClipRect[2] = pCmd->ClipRect.z;
pOutDraws[drawCount].mClipRect[3] = pCmd->ClipRect.w;
++drawCount;
}
}
drawGroupOut.mDrawCount = drawCount;
static_assert(sizeof(ImguiDraw) % ComDataSize == 0, "Need to support zero-ing the pending bytes, when not a size multiple of DataComType");
drawGroupOut.mpDraws.SetComDataPtr(pDataOutput);
pDataOutput += drawGroupOut.mDrawCount * sizeof(ImguiDraw) / ComDataSize;
}
//=================================================================================================
// Delta comress data.
// Take a data stream and output a version with only the difference from other stream is written
//=================================================================================================
void CompressData(const ComDataType* pDataPrev, size_t dataSizePrev, const ComDataType* pDataNew, size_t dataSizeNew, ComDataType*& pCommandMemoryInOut)
{
static_assert(sizeof(uint32_t)*2 <= ComDataSize, "Need to adjust compression algorithm pointer calculation");
const size_t elemCountPrev = static_cast<size_t>(DivUp(dataSizePrev, sizeof(uint64_t)));
const size_t elemCountNew = static_cast<size_t>(DivUp(dataSizeNew, sizeof(uint64_t)));
const size_t elemCount = elemCountPrev < elemCountNew ? elemCountPrev : elemCountNew;
size_t n = 0;
if( pDataPrev )
{
while(n < elemCount)
{
uint32_t* pBlockInfo = reinterpret_cast<uint32_t*>(pCommandMemoryInOut++); // Add a new block info to output
// Find number of elements with same value as last frame
size_t startN = n;
while( n < elemCount && pDataPrev[n] == pDataNew[n] )
++n;
pBlockInfo[0] = static_cast<uint32_t>(n - startN);
// Find number of elements with different value as last frame, and save new value
while (n < elemCount && pDataPrev[n] != pDataNew[n]) {
*pCommandMemoryInOut = pDataNew[n++];
++pCommandMemoryInOut;
}
pBlockInfo[1] = static_cast<uint32_t>(pCommandMemoryInOut - reinterpret_cast<ComDataType*>(pBlockInfo)) - 1;
}
}
// New frame has more element than previous frame, add the remaining entries
if(elemCount < elemCountNew)
{
uint32_t* pBlockInfo = reinterpret_cast<uint32_t*>(pCommandMemoryInOut++); // Add a new block info to output
while (n < elemCountNew) {
*pCommandMemoryInOut = pDataNew[n++];
++pCommandMemoryInOut;
}
pBlockInfo[0] = 0;
pBlockInfo[1] = static_cast<uint32_t>(pCommandMemoryInOut - reinterpret_cast<uint64_t*>(pBlockInfo)) - 1;
}
}
//=================================================================================================
// Unpack a delta data compressed stream
//=================================================================================================
void DecompressData(const ComDataType* pDataPrev, size_t dataSizePrev, const ComDataType* pDataPack, size_t dataUnpackSize, ComDataType*& pCommandMemoryInOut)
{
const size_t elemCountPrev = DivUp(dataSizePrev, ComDataSize);
const size_t elemCountUnpack = DivUp(dataUnpackSize, ComDataSize);
const size_t elemCountCopy = elemCountPrev < elemCountUnpack ? elemCountPrev : elemCountUnpack;
uint64_t* pCommandMemoryEnd = &pCommandMemoryInOut[elemCountUnpack];
if( pDataPrev ){
memcpy(pCommandMemoryInOut, pDataPrev, elemCountCopy * ComDataSize);
}
while(pCommandMemoryInOut < pCommandMemoryEnd)
{
const uint32_t* pBlockInfo = reinterpret_cast<const uint32_t*>(pDataPack++); // Add a new block info to output
pCommandMemoryInOut += pBlockInfo[0];
memcpy(pCommandMemoryInOut, pDataPack, pBlockInfo[1] * sizeof(uint64_t));
pCommandMemoryInOut += pBlockInfo[1];
pDataPack += pBlockInfo[1];
}
}
//=================================================================================================
// Take a regular NetImgui DrawFrame command and create a new compressed command
// It uses a basic delta compression method that works really well with Imgui data
// - Most of the drawing data do not change between 2 frames
// - Even if 1 window content changes, the others windows probably won't be changing at all
// - This means that for each window, we can only send the data that changed
// - This requires little cpu usage and generate good results
// - In 'SampleBasic' with 3 windows open (Main Window, ImGui Demo, ImGui Metric) at 30fps
// - Compression Off: 1650KB/sec of transfert
// - Compression On : 12KB/sec of transfert (130x less data)
//=================================================================================================
CmdDrawFrame* CompressCmdDrawFrame(const CmdDrawFrame* pDrawFramePrev, const CmdDrawFrame* pDrawFrameNew)
{
//-----------------------------------------------------------------------------------------
// Allocate memory for the new compressed command
//-----------------------------------------------------------------------------------------
// Allocate memory for worst case scenario (no compression possible)
// New DrawFrame size + 2 'compression block info' per data stream
size_t neededDataCount = DivUp<size_t>(pDrawFrameNew->mSize, ComDataSize) + 6*static_cast<size_t>(pDrawFrameNew->mDrawGroupCount);
CmdDrawFrame* pDrawFramePacked = netImguiSizedNew<CmdDrawFrame>(neededDataCount*ComDataSize);
*pDrawFramePacked = *pDrawFrameNew;
pDrawFramePacked->mCompressed = true;
ComDataType* pDataOutput = reinterpret_cast<ComDataType*>(&pDrawFramePacked[1]);
SetAndIncreaseDataPointer(pDrawFramePacked->mpDrawGroups, pDrawFramePacked->mDrawGroupCount * sizeof(ImguiDrawGroup), pDataOutput);
//-----------------------------------------------------------------------------------------
// Copy draw data (vertices, indices, drawcall info, ...)
//-----------------------------------------------------------------------------------------
const uint32_t groupCountPrev = pDrawFramePrev->mDrawGroupCount;
for(uint32_t n = 0; n < pDrawFramePacked->mDrawGroupCount; n++)
{
// Look for the same drawgroup in previous frame
// Can usually avoid a search by checking same index in previous frame (drawgroup ordering shouldn't change often)
const ImguiDrawGroup& drawGroupNew = pDrawFrameNew->mpDrawGroups[n];
ImguiDrawGroup& drawGroup = pDrawFramePacked->mpDrawGroups[n];
drawGroup = drawGroupNew;
drawGroup.mDrawGroupIdxPrev = (n < groupCountPrev && drawGroup.mGroupID == pDrawFramePrev->mpDrawGroups[n].mGroupID) ? n : ImguiDrawGroup::kInvalidDrawGroup;
for(uint32_t j(0); j<groupCountPrev && drawGroup.mDrawGroupIdxPrev == ImguiDrawGroup::kInvalidDrawGroup; ++j){
drawGroup.mDrawGroupIdxPrev = (drawGroup.mGroupID == pDrawFramePrev->mpDrawGroups[j].mGroupID) ? j : ImguiDrawGroup::kInvalidDrawGroup;
}
// Delta compress the 3 data streams
const uint64_t *pVerticePrev(nullptr), *pIndicePrev(nullptr), *pDrawsPrev(nullptr);
size_t verticeSizePrev(0), indiceSizePrev(0), drawSizePrev(0);
if (drawGroup.mDrawGroupIdxPrev < pDrawFramePrev->mDrawGroupCount) {
const ImguiDrawGroup& drawGroupPrev = pDrawFramePrev->mpDrawGroups[drawGroup.mDrawGroupIdxPrev];
pVerticePrev = reinterpret_cast<const uint64_t*>(drawGroupPrev.mpVertices.Get());
pIndicePrev = reinterpret_cast<const uint64_t*>(drawGroupPrev.mpIndices.Get());
pDrawsPrev = reinterpret_cast<const uint64_t*>(drawGroupPrev.mpDraws.Get());
verticeSizePrev = drawGroupPrev.mVerticeCount * sizeof(ImguiVert);
indiceSizePrev = drawGroupPrev.mIndiceCount*static_cast<size_t>(drawGroupPrev.mBytePerIndex);
drawSizePrev = drawGroupPrev.mDrawCount*sizeof(ImguiDraw);
}
drawGroup.mpIndices.SetComDataPtr(pDataOutput);
CompressData( pIndicePrev, indiceSizePrev,
drawGroupNew.mpIndices.GetComData(), drawGroupNew.mIndiceCount*static_cast<size_t>(drawGroupNew.mBytePerIndex),
pDataOutput);
drawGroup.mpVertices.SetComDataPtr(pDataOutput);
CompressData( pVerticePrev, verticeSizePrev,
drawGroupNew.mpVertices.GetComData(), drawGroupNew.mVerticeCount * sizeof(ImguiVert),
pDataOutput);
drawGroup.mpDraws.SetComDataPtr(pDataOutput);
CompressData( pDrawsPrev, drawSizePrev,
drawGroupNew.mpDraws.GetComData(), drawGroupNew.mDrawCount*sizeof(ImguiDraw),
pDataOutput);
}
// Adjust data transfert amount to memory that has been actually needed
pDrawFramePacked->mSize = static_cast<uint32_t>((pDataOutput - reinterpret_cast<ComDataType*>(pDrawFramePacked)))*static_cast<uint32_t>(sizeof(uint64_t));
return pDrawFramePacked;
}
//=================================================================================================
//
//=================================================================================================
CmdDrawFrame* DecompressCmdDrawFrame(const CmdDrawFrame* pDrawFramePrev, const CmdDrawFrame* pDrawFramePacked)
{
//-----------------------------------------------------------------------------------------
// Allocate memory for the new uncompressed compressed command
//-----------------------------------------------------------------------------------------
CmdDrawFrame* pDrawFrameNew = netImguiSizedNew<CmdDrawFrame>(pDrawFramePacked->mUncompressedSize);
*pDrawFrameNew = *pDrawFramePacked;
pDrawFrameNew->mCompressed = false;
ComDataType* pDataOutput = reinterpret_cast<ComDataType*>(&pDrawFrameNew[1]);
SetAndIncreaseDataPointer(pDrawFrameNew->mpDrawGroups, pDrawFrameNew->mDrawGroupCount * sizeof(ImguiDrawGroup), pDataOutput);
for(uint32_t n = 0; n < pDrawFrameNew->mDrawGroupCount; n++)
{
const ImguiDrawGroup& drawGroupPack = pDrawFramePacked->mpDrawGroups[n];
ImguiDrawGroup& drawGroup = pDrawFrameNew->mpDrawGroups[n];
drawGroup = drawGroupPack;
// Uncompress the 3 data streams
const ComDataType* pVerticePrev = nullptr;
const ComDataType* pIndicePrev = nullptr;
const ComDataType* pDrawsPrev = nullptr;
size_t verticeSizePrev(0), indiceSizePrev(0), drawSizePrev(0);
if (drawGroup.mDrawGroupIdxPrev < pDrawFramePrev->mDrawGroupCount) {
const ImguiDrawGroup& drawGroupPrev = pDrawFramePrev->mpDrawGroups[drawGroup.mDrawGroupIdxPrev];
pVerticePrev = reinterpret_cast<const ComDataType*>(drawGroupPrev.mpVertices.Get());
pIndicePrev = reinterpret_cast<const ComDataType*>(drawGroupPrev.mpIndices.Get());
pDrawsPrev = reinterpret_cast<const ComDataType*>(drawGroupPrev.mpDraws.Get());
verticeSizePrev = drawGroupPrev.mVerticeCount * sizeof(ImguiVert);
indiceSizePrev = drawGroupPrev.mIndiceCount*static_cast<size_t>(drawGroupPrev.mBytePerIndex);
drawSizePrev = drawGroupPrev.mDrawCount*sizeof(ImguiDraw);
}
drawGroup.mpIndices.SetComDataPtr(pDataOutput);
DecompressData( pIndicePrev, indiceSizePrev,
drawGroupPack.mpIndices.GetComData(), drawGroupPack.mIndiceCount*static_cast<size_t>(drawGroupPack.mBytePerIndex),
pDataOutput);
drawGroup.mpVertices.SetComDataPtr(pDataOutput);
DecompressData( pVerticePrev, verticeSizePrev,
drawGroupPack.mpVertices.GetComData(), drawGroupPack.mVerticeCount*sizeof(ImguiVert),
pDataOutput);
drawGroup.mpDraws.SetComDataPtr(pDataOutput);
DecompressData( pDrawsPrev, drawSizePrev,
drawGroupPack.mpDraws.GetComData(), drawGroupPack.mDrawCount*sizeof(ImguiDraw),
pDataOutput);
}
return pDrawFrameNew;
}
//=================================================================================================
// Take a regular Dear Imgui Draw Data, and convert it to a NetImgui DrawFrame Command
// It involves saving each window draw group vertex/indices/draw buffers
// and packing their data a little bit, to reduce the bandwidth usage
//=================================================================================================
CmdDrawFrame* ConvertToCmdDrawFrame(const ImDrawData* pDearImguiData, ImGuiMouseCursor mouseCursor)
{
//-----------------------------------------------------------------------------------------
// Find memory needed for entire DrawFrame Command
//-----------------------------------------------------------------------------------------
static_assert(sizeof(CmdDrawFrame) % ComDataSize == 0, "Make sure Command Data is aligned to com data type size");
size_t neededDataCount = DivUp(sizeof(CmdDrawFrame), ComDataSize);
neededDataCount += DivUp(static_cast<size_t>(pDearImguiData->CmdListsCount) * sizeof(ImguiDrawGroup), ComDataSize);
for(int n = 0; n < pDearImguiData->CmdListsCount; n++)
{
const ImDrawList* pCmdList = pDearImguiData->CmdLists[n];
bool is16Bit = pCmdList->VtxBuffer.size() <= 0xFFFF;
neededDataCount += DivUp(static_cast<size_t>(pCmdList->VtxBuffer.size()) * sizeof(ImguiVert), ComDataSize);
neededDataCount += DivUp(static_cast<size_t>(pCmdList->IdxBuffer.size()) * (is16Bit ? 2 : 4), ComDataSize);
neededDataCount += DivUp(static_cast<size_t>(pCmdList->CmdBuffer.size()) * sizeof(ImguiDraw), ComDataSize);
}
//-----------------------------------------------------------------------------------------
// Allocate Data and initialize general frame information
//-----------------------------------------------------------------------------------------
CmdDrawFrame* pDrawFrame = netImguiSizedNew<CmdDrawFrame>(neededDataCount*ComDataSize);
ComDataType* pDataOutput = reinterpret_cast<ComDataType*>(&pDrawFrame[1]);
pDrawFrame->mMouseCursor = static_cast<uint32_t>(mouseCursor);
pDrawFrame->mDisplayArea[0] = pDearImguiData->DisplayPos.x;
pDrawFrame->mDisplayArea[1] = pDearImguiData->DisplayPos.y;
pDrawFrame->mDisplayArea[2] = pDearImguiData->DisplayPos.x + pDearImguiData->DisplaySize.x;
pDrawFrame->mDisplayArea[3] = pDearImguiData->DisplayPos.y + pDearImguiData->DisplaySize.y;
pDrawFrame->mDrawGroupCount = static_cast<uint32_t>(pDearImguiData->CmdListsCount);
SetAndIncreaseDataPointer(pDrawFrame->mpDrawGroups, static_cast<uint32_t>(pDrawFrame->mDrawGroupCount * sizeof(ImguiDrawGroup)), pDataOutput);
//-----------------------------------------------------------------------------------------
// Copy draw data (vertices, indices, drawcall info, ...)
//-----------------------------------------------------------------------------------------
for(size_t n = 0; n < pDrawFrame->mDrawGroupCount; n++)
{
ImguiDrawGroup& drawGroup = pDrawFrame->mpDrawGroups[n];
const ImDrawList* pCmdList = pDearImguiData->CmdLists[static_cast<int>(n)];
drawGroup = ImguiDrawGroup();
drawGroup.mGroupID = PointerCast<uint64_t>(pCmdList->_OwnerName); // Use the name string pointer as a unique ID (seems to remain the same between frame)
ImGui_ExtractIndices(*pCmdList, drawGroup, pDataOutput);
ImGui_ExtractVertices(*pCmdList,drawGroup, pDataOutput);
ImGui_ExtractDraws(*pCmdList, drawGroup, pDataOutput);
pDrawFrame->mTotalVerticeCount += drawGroup.mVerticeCount;
pDrawFrame->mTotalIndiceCount += drawGroup.mIndiceCount;
pDrawFrame->mTotalDrawCount += drawGroup.mDrawCount;
}
pDrawFrame->mSize = static_cast<uint32_t>(pDataOutput - reinterpret_cast<const ComDataType*>(pDrawFrame)) * ComDataSize;
pDrawFrame->mUncompressedSize = pDrawFrame->mSize; // No compression with this item, so same value
return pDrawFrame;
}
}} // namespace NetImgui::Internal
#include "NetImgui_WarningReenable.h"
#endif //#if NETIMGUI_ENABLED
@@ -0,0 +1,50 @@
#pragma once
#include "NetImgui_Shared.h"
namespace NetImgui { namespace Internal
{
struct ImguiVert
{
//Note: If updating this, increase 'CmdVersion::eVersion'
enum Constants{ kUvRange_Min=0, kUvRange_Max=1, kPosRange_Min=-8192, kPosRange_Max=8192};
uint16_t mPos[2];
uint16_t mUV[2];
uint32_t mColor;
};
struct ImguiDraw
{
uint64_t mTextureId;
uint32_t mIdxCount; // Drawcall number of indices (3 indices per triangles)
uint32_t mVtxOffset; // Drawcall start position in vertices buffer (considered index 0)
uint32_t mIdxOffset; // Drawcall start position in indices buffer
float mClipRect[4];
uint8_t PADDING[4]={};
};
// Each DearImgui window has its own vertex/index buffers with multiple drawcalls
struct alignas(8) ImguiDrawGroup
{
static constexpr uint32_t kInvalidDrawGroup = 0xFFFFFFFF;
uint64_t mGroupID = 0; // Unique ID to recognize DrawGroup between 2 frames
uint32_t mVerticeCount = 0;
uint32_t mIndiceCount = 0;
uint32_t mDrawCount = 0;
uint32_t mDrawGroupIdxPrev = kInvalidDrawGroup;// Group index in previous DrawFrame (kInvalidDrawGroup when not using delta compression)
uint8_t mBytePerIndex = 2; // 2, 4 bytes
uint8_t PADDING[7] = {};
float mReferenceCoord[2] = {}; // Reference position for the encoded vertices offsets (1st vertice top/left position)
OffsetPointer<uint8_t> mpIndices;
OffsetPointer<ImguiVert> mpVertices;
OffsetPointer<ImguiDraw> mpDraws;
inline void ToPointers();
inline void ToOffsets();
};
struct CmdDrawFrame* ConvertToCmdDrawFrame(const ImDrawData* pDearImguiData, ImGuiMouseCursor cursor);
struct CmdDrawFrame* CompressCmdDrawFrame(const CmdDrawFrame* pDrawFramePrev, const CmdDrawFrame* pDrawFrameNew);
struct CmdDrawFrame* DecompressCmdDrawFrame(const CmdDrawFrame* pDrawFramePrev, const CmdDrawFrame* pDrawFramePacked);
}} // namespace NetImgui::Internal
@@ -0,0 +1,22 @@
#pragma once
namespace NetImgui { namespace Internal { struct PendingCom; }}
namespace NetImgui { namespace Internal { namespace Network
{
struct SocketInfo;
bool Startup (void);
void Shutdown (void);
SocketInfo* Connect (const char* ServerHost, uint32_t ServerPort); // Communication Socket expected to be blocking
SocketInfo* ListenConnect (SocketInfo* ListenSocket); // Communication Socket expected to be blocking
SocketInfo* ListenStart (uint32_t ListenPort); // Listening Socket expected to be non blocking
void Disconnect (SocketInfo* pClientSocket);
bool DataReceivePending (SocketInfo* pClientSocket); // True if some new data if waiting to be processed from remote connection
void DataReceive (SocketInfo* pClientSocket, PendingCom& PendingComRcv); // Try reading X amount of bytes from remote connection, but can fall short.
void DataSend (SocketInfo* pClientSocket, PendingCom& PendingComSend); // Try sending X amount of bytes to remote connection, but can fall short.
}}} //namespace NetImgui::Internal::Network
@@ -0,0 +1,154 @@
#include "NetImgui_Shared.h"
#if defined(_MSC_VER)
#pragma warning (disable: 4221)
#endif
#if NETIMGUI_ENABLED && NETIMGUI_POSIX_SOCKETS_ENABLED
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
//NOTE: The socket handling has been modified to improve speed, but the Posix version
// has not been updated. Please review the changes made to 'NetImgui_NetworkWin32.cpp'
// between version 1.11 and 1.12 and bring them over to this file. In particular :
// -Sockets set to non blocking and immediate sending
// -Added 'DataReceivePending' function
// -Reworked 'DataReceive' and 'DataSend' to be non blocking socket operation
static_assert(0)
namespace NetImgui { namespace Internal { namespace Network
{
struct SocketInfo
{
SocketInfo(int socket) : mSocket(socket){}
int mSocket;
};
bool Startup()
{
return true;
}
void Shutdown()
{
}
inline void SetNonBlocking(int Socket, bool bIsNonBlocking)
{
int Flags = fcntl(Socket, F_GETFL, 0);
Flags = bIsNonBlocking ? Flags | O_NONBLOCK : Flags ^ (Flags & O_NONBLOCK);
fcntl(Socket, F_SETFL, Flags);
}
SocketInfo* Connect(const char* ServerHost, uint32_t ServerPort)
{
int ConnectSocket = socket(AF_INET , SOCK_STREAM , 0 );
if(ConnectSocket == -1)
return nullptr;
char zPortName[32];
addrinfo* pResults = nullptr;
SocketInfo* pSocketInfo = nullptr;
sprintf(zPortName, "%i", ServerPort);
getaddrinfo(ServerHost, zPortName, nullptr, &pResults);
addrinfo* pResultCur = pResults;
while( pResultCur && !pSocketInfo)
{
if( connect(ConnectSocket, pResultCur->ai_addr, static_cast<int>(pResultCur->ai_addrlen)) == 0 )
{
SetNonBlocking(ConnectSocket, false);
pSocketInfo = netImguiNew<SocketInfo>(ConnectSocket);
}
pResultCur = pResultCur->ai_next;
}
freeaddrinfo(pResults);
if( !pSocketInfo )
{
close(ConnectSocket);
}
return pSocketInfo;
}
SocketInfo* ListenStart(uint32_t ListenPort)
{
addrinfo hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
addrinfo* addrInfo;
getaddrinfo(nullptr, std::to_string(ListenPort).c_str(), &hints, &addrInfo);
int ListenSocket = socket(addrInfo->ai_family, addrInfo->ai_socktype, addrInfo->ai_protocol);
if( ListenSocket != -1 )
{
#if NETIMGUI_FORCE_TCP_LISTEN_BINDING
int flag = 1;
setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEPORT, &flag, sizeof(flag));
#endif
if( bind(ListenSocket, addrInfo->ai_addr, addrInfo->ai_addrlen) != -1 &&
listen(ListenSocket, 0) != -1)
{
SetNonBlocking(ListenSocket, false);
return netImguiNew<SocketInfo>(ListenSocket);
}
close(ListenSocket);
}
return nullptr;
}
SocketInfo* ListenConnect(SocketInfo* ListenSocket)
{
if( ListenSocket )
{
sockaddr_storage ClientAddress;
socklen_t Size(sizeof(ClientAddress));
int ServerSocket = accept(ListenSocket->mSocket, (sockaddr*)&ClientAddress, &Size) ;
if (ServerSocket != -1)
{
SetNonBlocking(ServerSocket, false);
return netImguiNew<SocketInfo>(ServerSocket);
}
}
return nullptr;
}
void Disconnect(SocketInfo* pClientSocket)
{
if( pClientSocket )
{
shutdown(pClientSocket->mSocket, SHUT_RDWR);
close(pClientSocket->mSocket);
netImguiDelete(pClientSocket);
}
}
bool DataReceive(SocketInfo* pClientSocket, void* pDataIn, size_t Size)
{
int resultRcv = recv(pClientSocket->mSocket, static_cast<char*>(pDataIn), static_cast<int>(Size), MSG_WAITALL);
return static_cast<int>(Size) == resultRcv;
}
bool DataSend(SocketInfo* pClientSocket, void* pDataOut, size_t Size)
{
int resultSend = send(pClientSocket->mSocket, static_cast<char*>(pDataOut), static_cast<int>(Size), 0);
return static_cast<int>(Size) == resultSend;
}
}}} // namespace NetImgui::Internal::Network
#else
// Prevents Linker warning LNK4221 in Visual Studio (This object file does not define any previously undefined public symbols, so it will not be used by any link operation that consumes this library)
extern int sSuppresstLNK4221_NetImgui_NetworkPosix;
int sSuppresstLNK4221_NetImgui_NetworkPosix(0);
#endif // #if NETIMGUI_ENABLED && NETIMGUI_POSIX_SOCKETS_ENABLED
@@ -0,0 +1,233 @@
#include "NetImgui_Shared.h"
// Tested with Unreal Engine 4.27, 5.3, 5.4, 5.5
#if NETIMGUI_ENABLED && defined(__UNREAL__)
#include "CoreMinimal.h"
#include "Runtime/Launch/Resources/Version.h"
#include "Misc/OutputDeviceRedirector.h"
#include "SocketSubsystem.h"
#include "Sockets.h"
#include "HAL/PlatformProcess.h"
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 2
#include "IPAddressAsyncResolve.h"
#endif
namespace NetImgui { namespace Internal { namespace Network
{
//=================================================================================================
// Wrapper around native socket object and init some socket options
//=================================================================================================
struct SocketInfo
{
SocketInfo(FSocket* pSocket)
: mpSocket(pSocket)
{
if( mpSocket )
{
mpSocket->SetNonBlocking(true);
mpSocket->SetNoDelay(true);
int32 NewSize(0);
while( !mpSocket->SetSendBufferSize(2*mSendSize, NewSize) ){
mSendSize /= 2;
}
mSendSize = NewSize/2;
}
}
~SocketInfo()
{
Close();
}
void Close()
{
if(mpSocket )
{
mpSocket->Close();
ISocketSubsystem::Get()->DestroySocket(mpSocket);
mpSocket = nullptr;
}
}
FSocket* mpSocket = nullptr;
int32 mSendSize = 1024*1024; // Limit tx data to avoid socket error on large amount (texture)
};
bool Startup()
{
return true;
}
void Shutdown()
{
}
//=================================================================================================
// Try establishing a connection to a remote client at given address
//=================================================================================================
SocketInfo* Connect(const char* ServerHost, uint32_t ServerPort)
{
SocketInfo* pSocketInfo = nullptr;
ISocketSubsystem* SocketSubSystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
TSharedPtr<FInternetAddr> IpAddressFind = SocketSubSystem->GetAddressFromString((TCHAR*)StringCast<TCHAR>(static_cast<const ANSICHAR*>(ServerHost)).Get());
if(IpAddressFind)
{
TSharedRef<FInternetAddr> IpAddress = IpAddressFind->Clone();
IpAddress->SetPort(ServerPort);
if (IpAddress->IsValid())
{
FSocket* pNewSocket = SocketSubSystem->CreateSocket(NAME_Stream, "NetImgui", IpAddress->GetProtocolType());
if (pNewSocket)
{
pNewSocket->SetNonBlocking(true);
if (pNewSocket->Connect(*IpAddress))
{
bool bConnectionReady = false;
pNewSocket->WaitForPendingConnection(bConnectionReady, FTimespan::FromSeconds(1.0f));
if( bConnectionReady )
{
pSocketInfo = netImguiNew<SocketInfo>(pNewSocket);
return pSocketInfo;
}
}
}
}
}
netImguiDelete(pSocketInfo);
return nullptr;
}
//=================================================================================================
// Start waiting for connection request on this socket
//=================================================================================================
SocketInfo* ListenStart(uint32_t ListenPort)
{
ISocketSubsystem* PlatformSocketSub = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
TSharedPtr<FInternetAddr> IpAddress = PlatformSocketSub->GetLocalBindAddr(*GLog);
IpAddress->SetPort(ListenPort);
FSocket* pNewListenSocket = PlatformSocketSub->CreateSocket(NAME_Stream, "NetImguiListen", IpAddress->GetProtocolType());
if( pNewListenSocket )
{
SocketInfo* pListenSocketInfo = netImguiNew<SocketInfo>(pNewListenSocket);
#if NETIMGUI_FORCE_TCP_LISTEN_BINDING
pNewListenSocket->SetReuseAddr();
#endif
pNewListenSocket->SetNonBlocking(false);
pNewListenSocket->SetRecvErr();
if (pNewListenSocket->Bind(*IpAddress))
{
if (pNewListenSocket->Listen(1))
{
return pListenSocketInfo;
}
}
netImguiDelete(pListenSocketInfo);
}
return nullptr;
}
//=================================================================================================
// Establish a new connection to a remote request
//=================================================================================================
SocketInfo* ListenConnect(SocketInfo* pListenSocket)
{
if (pListenSocket)
{
FSocket* pNewSocket = pListenSocket->mpSocket->Accept(FString("NetImgui"));
if( pNewSocket )
{
SocketInfo* pSocketInfo = netImguiNew<SocketInfo>(pNewSocket);
return pSocketInfo;
}
}
return nullptr;
}
//=================================================================================================
// Close a connection and free allocated object
//=================================================================================================
void Disconnect(SocketInfo* pClientSocket)
{
netImguiDelete(pClientSocket);
}
//=================================================================================================
// Return true if data has been received, or there's a connection error
//=================================================================================================
bool DataReceivePending(SocketInfo* pClientSocket)
{
// Note: return true on a connection error, to exit code looping on the data wait. Will handle error after DataReceive()
uint32 PendingDataSize;
return !pClientSocket || pClientSocket->mpSocket->HasPendingData(PendingDataSize) || (pClientSocket->mpSocket->GetConnectionState() != ESocketConnectionState::SCS_Connected);
}
//=================================================================================================
// Receive as much as possible a command and keep track of transfer status
//=================================================================================================
void DataReceive(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& PendingComRcv)
{
// Invalid command
if( !pClientSocket || !PendingComRcv.pCommand || !pClientSocket->mpSocket || (pClientSocket->mpSocket->GetConnectionState() != ESocketConnectionState::SCS_Connected)){
PendingComRcv.bError = true;
return;
}
int32 sizeRcv(0);
if( pClientSocket->mpSocket->Recv( &reinterpret_cast<uint8*>(PendingComRcv.pCommand)[PendingComRcv.SizeCurrent],
static_cast<int>(PendingComRcv.pCommand->mSize-PendingComRcv.SizeCurrent),
sizeRcv,
ESocketReceiveFlags::None) )
{
PendingComRcv.SizeCurrent += static_cast<size_t>(sizeRcv);
PendingComRcv.bError |= sizeRcv <= 0; // Error if no data read since DataReceivePending() said there was some available
}
else
{
// Connection error, abort transmission
const ESocketErrors SocketError = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLastErrorCode();
PendingComRcv.bError = SocketError != SE_NO_ERROR && SocketError != ESocketErrors::SE_EWOULDBLOCK;
}
}
//=================================================================================================
// Receive as much as possible a command and keep track of transfer status
//=================================================================================================
void DataSend(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& PendingComSend)
{
// Invalid command
if( !pClientSocket || !PendingComSend.pCommand || !pClientSocket->mpSocket || (pClientSocket->mpSocket->GetConnectionState() != ESocketConnectionState::SCS_Connected) ){
PendingComSend.bError = true;
return;
}
int32 sizeSent = 0;
int32 sizeToSend = PendingComSend.pCommand->mSize-PendingComSend.SizeCurrent;
sizeToSend = sizeToSend > pClientSocket->mSendSize ? pClientSocket->mSendSize : sizeToSend;
if( pClientSocket->mpSocket->Send( &reinterpret_cast<uint8*>(PendingComSend.pCommand)[PendingComSend.SizeCurrent],
static_cast<int>(sizeToSend),
sizeSent) )
{
PendingComSend.SizeCurrent += static_cast<size_t>(sizeSent);
}
else
{
// Connection error, abort transmission
const ESocketErrors SocketError = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLastErrorCode();
PendingComSend.bError = SocketError != SE_NO_ERROR && SocketError != ESocketErrors::SE_EWOULDBLOCK;
}
}
}}} // namespace NetImgui::Internal::Network
#else
// Prevents Linker warning LNK4221 in Visual Studio (This object file does not define any previously undefined public symbols, so it will not be used by any link operation that consumes this library)
extern int sSuppresstLNK4221_NetImgui_NetworkUE4;
int sSuppresstLNK4221_NetImgui_NetworkUE4(0);
#endif // #if NETIMGUI_ENABLED && defined(__UNREAL__)
@@ -0,0 +1,253 @@
#include "NetImgui_Shared.h"
#if NETIMGUI_ENABLED && NETIMGUI_WINSOCKET_ENABLED
#include "NetImgui_WarningDisableStd.h"
#include <WinSock2.h>
#include <WS2tcpip.h>
#if defined(_MSC_VER)
#pragma comment(lib, "ws2_32")
#endif
#include "NetImgui_CmdPackets.h"
namespace NetImgui { namespace Internal { namespace Network
{
//=================================================================================================
// Wrapper around native socket object and init some socket options
//=================================================================================================
struct SocketInfo
{
SocketInfo(SOCKET socket)
: mSocket(socket)
{
u_long kNonBlocking = true;
ioctlsocket(mSocket, static_cast<long>(FIONBIO), &kNonBlocking);
constexpr DWORD kComsNoDelay = 1;
setsockopt(mSocket, SOL_SOCKET, TCP_NODELAY, reinterpret_cast<const char*>(&kComsNoDelay), sizeof(kComsNoDelay));
const int kComsSendBuffer = 2*mSendSizeMax;
setsockopt(mSocket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<const char*>(&kComsSendBuffer), sizeof(kComsSendBuffer));
//constexpr int kComsRcvBuffer = 1014*1024;
//setsockopt(mSocket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<const char*>(&kComsRcvBuffer), sizeof(kComsRcvBuffer));
}
SOCKET mSocket;
int mSendSizeMax = 1024*1024; // Limit tx data to avoid socket error on large amount (texture)
};
bool Startup()
{
WSADATA wsa;
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
return false;
return true;
}
void Shutdown()
{
WSACleanup();
}
//=================================================================================================
// Try establishing a connection to a remote client at given address
//=================================================================================================
SocketInfo* Connect(const char* ServerHost, uint32_t ServerPort)
{
const timeval kConnectTimeout = {1, 0}; // Waiting 1 seconds before failing connection attempt
u_long kNonBlocking = true;
SOCKET ClientSocket = socket(AF_INET , SOCK_STREAM , 0);
if(ClientSocket == INVALID_SOCKET)
return nullptr;
char zPortName[32] = {};
addrinfo* pResults = nullptr;
SocketInfo* pSocketInfo = nullptr;
NetImgui::Internal::StringFormat(zPortName, "%i", ServerPort);
getaddrinfo(ServerHost, zPortName, nullptr, &pResults);
addrinfo* pResultCur = pResults;
fd_set SocketSet;
ioctlsocket(ClientSocket, static_cast<long>(FIONBIO), &kNonBlocking);
while( pResultCur && !pSocketInfo )
{
int Result = connect(ClientSocket, pResultCur->ai_addr, static_cast<int>(pResultCur->ai_addrlen));
bool Connected = Result != SOCKET_ERROR;
// Not connected yet, wait some time before bailing out
if( Result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK )
{
FD_ZERO(&SocketSet);
FD_SET(ClientSocket, &SocketSet);
Result = select(0, nullptr, &SocketSet, nullptr, &kConnectTimeout);
Connected = Result == 1; // when 1 socket ready for write, otherwise it's -1 or 0
}
if( Connected )
{
pSocketInfo = netImguiNew<SocketInfo>(ClientSocket);
}
pResultCur = pResultCur->ai_next;
}
freeaddrinfo(pResults);
if( !pSocketInfo )
{
closesocket(ClientSocket);
}
return pSocketInfo;
}
//=================================================================================================
// Start waiting for connection request on this socket
//=================================================================================================
SocketInfo* ListenStart(uint32_t ListenPort)
{
SOCKET ListenSocket = INVALID_SOCKET;
if( (ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != INVALID_SOCKET )
{
sockaddr_in server;
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(static_cast<USHORT>(ListenPort));
#if NETIMGUI_FORCE_TCP_LISTEN_BINDING
constexpr BOOL ReUseAdrValue(true);
setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&ReUseAdrValue), sizeof(ReUseAdrValue));
#endif
if( bind(ListenSocket, reinterpret_cast<sockaddr*>(&server), sizeof(server)) != SOCKET_ERROR &&
listen(ListenSocket, 0) != SOCKET_ERROR )
{
u_long kIsNonBlocking = false;
ioctlsocket(ListenSocket, static_cast<long>(FIONBIO), &kIsNonBlocking);
return netImguiNew<SocketInfo>(ListenSocket);
}
closesocket(ListenSocket);
}
return nullptr;
}
//=================================================================================================
// Establish a new connection to a remote request
//=================================================================================================
SocketInfo* ListenConnect(SocketInfo* ListenSocket)
{
if( ListenSocket )
{
sockaddr ClientAddress;
int Size(sizeof(ClientAddress));
SOCKET ClientSocket = accept(ListenSocket->mSocket, &ClientAddress, &Size) ;
if (ClientSocket != INVALID_SOCKET)
{
return netImguiNew<SocketInfo>(ClientSocket);
}
}
return nullptr;
}
//=================================================================================================
// Close a connection and free allocated object
//=================================================================================================
void Disconnect(SocketInfo* pClientSocket)
{
if( pClientSocket )
{
shutdown(pClientSocket->mSocket, SD_BOTH);
closesocket(pClientSocket->mSocket);
netImguiDelete(pClientSocket);
}
}
//=================================================================================================
// Return true if data has been received, or there's a connection error
//=================================================================================================
bool DataReceivePending(SocketInfo* pClientSocket)
{
const timeval kConnectTimeout = {0, 0}; // No wait time
if( pClientSocket )
{
fd_set fdSetRead;
fd_set fdSetErr;
FD_ZERO(&fdSetRead);
FD_ZERO(&fdSetErr);
FD_SET(pClientSocket->mSocket, &fdSetRead);
FD_SET(pClientSocket->mSocket, &fdSetErr);
// Note: return true if data ready or connection error (to exit parent loop waiting on data)
int result = select(0, &fdSetRead, nullptr, &fdSetErr, &kConnectTimeout);
return result != 0;
}
return true;
}
//=================================================================================================
// Receive as much as possible a command and keep track of transfer status
//=================================================================================================
void DataReceive(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& PendingComRcv)
{
// Invalid command
if( !pClientSocket || !PendingComRcv.pCommand || !pClientSocket->mSocket ){
PendingComRcv.bError = true;
return;
}
// Receive data from remote connection
int resultRcv = recv( pClientSocket->mSocket,
&reinterpret_cast<char*>(PendingComRcv.pCommand)[PendingComRcv.SizeCurrent],
static_cast<int>(PendingComRcv.pCommand->mSize-PendingComRcv.SizeCurrent),
0);
// Note: 'DataReceive' is called after pending data has been confirm.
// 0 received data means connection lost
if( resultRcv != SOCKET_ERROR ){
PendingComRcv.SizeCurrent += static_cast<size_t>(resultRcv);
PendingComRcv.bError |= resultRcv <= 0; // Error if no data read since DataReceivePending() said there was some available
}
// Connection error, abort transmission
else if( WSAGetLastError() != WSAEWOULDBLOCK ){
PendingComRcv.bError = true;
}
}
//=================================================================================================
// Receive as much as possible a command and keep track of transfer status
//=================================================================================================
void DataSend(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& PendingComSend)
{
// Invalid command
if( !pClientSocket || !PendingComSend.pCommand || !pClientSocket->mSocket ){
PendingComSend.bError = true;
return;
}
// Send data to remote connection
int sizeToSend = static_cast<int>(PendingComSend.pCommand->mSize-PendingComSend.SizeCurrent);
sizeToSend = sizeToSend > pClientSocket->mSendSizeMax ? pClientSocket->mSendSizeMax : sizeToSend;
int resultSent = send( pClientSocket->mSocket,
&reinterpret_cast<char*>(PendingComSend.pCommand)[PendingComSend.SizeCurrent],
sizeToSend,
0);
if( resultSent != SOCKET_ERROR ){
PendingComSend.SizeCurrent += static_cast<size_t>(resultSent);
}
// Connection error, abort transmission
else if( WSAGetLastError() != WSAEWOULDBLOCK ){
PendingComSend.bError = true;
}
}
}}} // namespace NetImgui::Internal::Network
#include "NetImgui_WarningReenable.h"
#else
// Prevents Linker warning LNK4221 in Visual Studio (This object file does not define any previously undefined public symbols, so it will not be used by any link operation that consumes this library)
extern int sSuppresstLNK4221_NetImgui_NetworkWin23;
int sSuppresstLNK4221_NetImgui_NetworkWin23(0);
#endif // #if NETIMGUI_ENABLED && NETIMGUI_WINSOCKET_ENABLED
@@ -0,0 +1,200 @@
#pragma once
//=================================================================================================
// Include NetImgui_Api.h with almost no warning suppression.
// this is to make sure library user does not need to suppress any
#if defined(_MSC_VER)
#pragma warning (disable: 4464) // warning C4464: relative include path contains '..'
#endif
#ifndef NETIMGUI_INTERNAL_INCLUDE
#define NETIMGUI_INTERNAL_INCLUDE 1
#include "NetImgui_Api.h"
#undef NETIMGUI_INTERNAL_INCLUDE
#else
#include "NetImgui_Api.h"
#endif
#if NETIMGUI_ENABLED
//=================================================================================================
// Include a few standard c++ header, with additional warning suppression
#include "NetImgui_WarningDisableStd.h"
#include <atomic>
#include <thread>
#include <chrono>
#include <vector>
#include "NetImgui_WarningReenable.h"
//=================================================================================================
//=================================================================================================
#include "NetImgui_WarningDisable.h"
namespace NetImgui { namespace Internal
{
using ComDataType = uint64_t;
constexpr size_t ComDataSize = sizeof(ComDataType);
//=============================================================================
// All allocations made by netImgui goes through here.
// Relies in ImGui allocator
//=============================================================================
template <typename TType, typename... Args> TType* netImguiNew(Args... args);
template <typename TType> TType* netImguiSizedNew(size_t placementSize);
template <typename TType> void netImguiDelete(TType* pData);
template <typename TType> void netImguiDeleteSafe(TType*& pData);
class ScopedImguiContext
{
public:
ScopedImguiContext(ImGuiContext* pNewContext) : mpSavedContext(ImGui::GetCurrentContext()){ ImGui::SetCurrentContext(pNewContext); }
~ScopedImguiContext() { ImGui::SetCurrentContext(mpSavedContext); }
protected:
ImGuiContext* mpSavedContext;
};
template<typename TType>
class ScopedValue
{
public:
ScopedValue(TType& ValueRef, TType Value)
: mValueRef(ValueRef)
, mValueRestore(ValueRef)
{
mValueRef = Value;
}
~ScopedValue()
{
mValueRef = mValueRestore;
}
protected:
TType& mValueRef;
TType mValueRestore;
uint8_t mPadding[sizeof(void*)-(sizeof(TType)%8)]={};
// Prevents warning about implicitly delete functions
ScopedValue(const ScopedValue&) = delete;
ScopedValue(const ScopedValue&&) = delete;
void operator=(const ScopedValue&) = delete;
};
using ScopedBool = ScopedValue<bool>;
//=============================================================================
// Class to safely exchange a pointer between two threads
//=============================================================================
template <typename TType>
class ExchangePtr
{
public:
ExchangePtr():mpData(nullptr){}
~ExchangePtr();
inline TType* Release();
inline void Assign(TType*& pNewData);
inline void Free();
inline bool IsNull()const { return mpData.load() == nullptr; }
private:
std::atomic<TType*> mpData;
// Prevents warning about implicitly delete functions
private:
ExchangePtr(const ExchangePtr&) = delete;
ExchangePtr(const ExchangePtr&&) = delete;
void operator=(const ExchangePtr&) = delete;
};
//=============================================================================
// Make data serialization easier
//=============================================================================
template <typename TType>
struct OffsetPointer
{
inline OffsetPointer();
inline explicit OffsetPointer(TType* pPointer);
inline explicit OffsetPointer(uint64_t offset);
inline bool IsPointer()const;
inline bool IsOffset()const;
inline TType* ToPointer();
inline uint64_t ToOffset();
inline TType* operator->();
inline const TType* operator->()const;
inline TType& operator[](size_t index);
inline const TType& operator[](size_t index)const;
inline TType* Get();
inline const TType* Get()const;
inline const ComDataType* GetComData()const;
inline uint64_t GetOff()const;
inline void SetPtr(TType* pPointer);
inline void SetComDataPtr(ComDataType* pPointer);
inline void SetOff(uint64_t offset);
private:
union
{
uint64_t mOffset;
TType* mPointer;
};
};
//=============================================================================
//=============================================================================
template <typename TType, size_t TCount>
class Ringbuffer
{
public:
Ringbuffer():mPosCur(0),mPosLast(0){}
void AddData(const TType* pData, size_t& count);
bool ReadData(TType* pData);
private:
TType mBuffer[TCount] = {0};
std::atomic_uint64_t mPosCur;
std::atomic_uint64_t mPosLast;
// Prevents warning about implicitly delete functions
private:
Ringbuffer(const Ringbuffer&) = delete;
Ringbuffer(const Ringbuffer&&) = delete;
void operator=(const Ringbuffer&) = delete;
};
template <typename T, std::size_t N>
constexpr std::size_t ArrayCount(T const (&)[N]) noexcept
{
return N;
}
//=============================================================================
//=============================================================================
template <size_t charCount>
inline void StringCopy(char (&output)[charCount], const char* pSrc, size_t srcCharCount=0xFFFFFFFE);
template <size_t charCount>
int StringFormat(char(&output)[charCount], char const* const format, ...);
//=============================================================================
// Get the (value / Denominator) rounded up to the next int value big enough
//=============================================================================
template <typename IntType>
IntType DivUp(IntType Value, IntType Denominator);
//=============================================================================
// Get the rounded up value
//=============================================================================
template <typename IntType>
IntType RoundUp(IntType Value, IntType Round);
inline uint64_t TextureCastFromID(ImTextureID textureID);
inline ImTextureID TextureCastFromPtr(void* pTexture);
inline ImTextureID TextureCastFromUInt(uint64_t textureID);
}} //namespace NetImgui::Internal
#include "NetImgui_Shared.inl"
#include "NetImgui_WarningReenable.h"
#endif //NETIMGUI_ENABLED
@@ -0,0 +1,319 @@
#pragma once
#include <assert.h>
#include <string.h>
namespace NetImgui { namespace Internal
{
template <typename TType, typename... Args>
TType* netImguiNew(Args... args)
{
return new( ImGui::MemAlloc(sizeof(TType)) ) TType(args...);
}
template <typename TType>
TType* netImguiSizedNew(size_t placementSize)
{
return new( ImGui::MemAlloc(placementSize > sizeof(TType) ? placementSize : sizeof(TType)) ) TType();
}
template <typename TType>
void netImguiDelete(TType* pData)
{
if( pData )
{
pData->~TType();
ImGui::MemFree(pData);
}
}
template <typename TType>
void netImguiDeleteSafe(TType*& pData)
{
netImguiDelete(pData);
pData = nullptr;
}
//=============================================================================
// Acquire ownership of the resource
//=============================================================================
template <typename TType>
TType* ExchangePtr<TType>::Release()
{
return mpData.exchange(nullptr);
}
//-----------------------------------------------------------------------------
// Take ownership of the provided data.
// If there's a previous unclaimed pointer to some data, release it
//-----------------------------------------------------------------------------
template <typename TType>
void ExchangePtr<TType>::Assign(TType*& pNewData)
{
netImguiDelete( mpData.exchange(pNewData) );
pNewData = nullptr;
}
template <typename TType>
void ExchangePtr<TType>::Free()
{
TType* pNull(nullptr);
Assign(pNull);
}
template <typename TType>
ExchangePtr<TType>::~ExchangePtr()
{
Free();
}
//=============================================================================
//
//=============================================================================
template <typename TType>
OffsetPointer<TType>::OffsetPointer()
: mOffset(0)
{
SetOff(0);
}
template <typename TType>
OffsetPointer<TType>::OffsetPointer(TType* pPointer)
{
SetPtr(pPointer);
}
template <typename TType>
OffsetPointer<TType>::OffsetPointer(uint64_t offset)
{
SetOff(offset);
}
template <typename TType>
void OffsetPointer<TType>::SetPtr(TType* pPointer)
{
mOffset = 0; // Remove 'offset flag' that can be left active on non 64bits pointer
mPointer = pPointer;
}
template <typename TType>
void OffsetPointer<TType>::SetComDataPtr(ComDataType* pPointer)
{
SetPtr(reinterpret_cast<TType*>(pPointer));
}
template <typename TType>
void OffsetPointer<TType>::SetOff(uint64_t offset)
{
mOffset = offset | 0x0000000000000001u;
}
template <typename TType>
uint64_t OffsetPointer<TType>::GetOff()const
{
return mOffset & ~0x0000000000000001u;
}
template <typename TType>
bool OffsetPointer<TType>::IsOffset()const
{
return (mOffset & 0x0000000000000001u) != 0;
}
template <typename TType>
bool OffsetPointer<TType>::IsPointer()const
{
return !IsOffset();
}
template <typename TType>
TType* OffsetPointer<TType>::ToPointer()
{
assert(IsOffset());
SetPtr( reinterpret_cast<TType*>( reinterpret_cast<uint64_t>(&mPointer) + GetOff() ) );
return mPointer;
}
template <typename TType>
uint64_t OffsetPointer<TType>::ToOffset()
{
assert(IsPointer());
SetOff( reinterpret_cast<uint64_t>(mPointer) - reinterpret_cast<uint64_t>(&mPointer) );
return mOffset;
}
template <typename TType>
TType* OffsetPointer<TType>::operator->()
{
assert(IsPointer());
return mPointer;
}
template <typename TType>
const TType* OffsetPointer<TType>::operator->()const
{
assert(IsPointer());
return mPointer;
}
template <typename TType>
TType* OffsetPointer<TType>::Get()
{
assert(IsPointer());
return mPointer;
}
template <typename TType>
const TType* OffsetPointer<TType>::Get()const
{
assert(IsPointer());
return mPointer;
}
template <typename TType>
const ComDataType* OffsetPointer<TType>::GetComData()const
{
return reinterpret_cast<const ComDataType*>(Get());
}
template <typename TType>
TType& OffsetPointer<TType>::operator[](size_t index)
{
assert(IsPointer());
return mPointer[index];
}
template <typename TType>
const TType& OffsetPointer<TType>::operator[](size_t index)const
{
assert(IsPointer());
return mPointer[index];
}
//=============================================================================
template <typename TType, size_t TCount>
void Ringbuffer<TType,TCount>::AddData(const TType* pData, size_t& count)
//=============================================================================
{
size_t i(0);
while (i < count && (mPosLast - mPosCur < TCount)) {
mBuffer[mPosLast % TCount] = pData[i];
mPosLast++;
i++;
}
count = i;
}
//=============================================================================
template <typename TType, size_t TCount>
bool Ringbuffer<TType,TCount>::ReadData(TType* pData)
//=============================================================================
{
if (mPosCur < mPosLast)
{
*pData = mBuffer[mPosCur % TCount];
mPosCur++;
return true;
}
return false;
}
//=============================================================================
// The _s string functions are a mess. There's really no way to do this right
// in a cross-platform way. Best solution I've found is to set just use
// strncpy, infer the buffer length, and null terminate. Still need to suppress
// the warning on Windows.
// See https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/
// and many other discussions online on the topic.
//=============================================================================
template <size_t charCount>
void StringCopy(char (&output)[charCount], const char* pSrc, size_t srcCharCount)
{
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#elif defined(_MSC_VER)
#pragma warning (push)
#pragma warning (disable: 4996) // warning C4996: 'strncpy': This function or variable may be unsafe.
#endif
size_t charToCopyCount = charCount < srcCharCount + 1 ? charCount : srcCharCount + 1;
strncpy(output, pSrc, charToCopyCount - 1);
output[charCount - 1] = 0;
#if defined(_MSC_VER) && defined(__clang__)
#pragma clang diagnostic pop
#elif defined(_MSC_VER)
#pragma warning (pop)
#endif
}
template <size_t charCount>
int StringFormat(char(&output)[charCount], char const* const format, ...)
{
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
#endif
va_list args;
va_start(args, format);
int w = vsnprintf(output, charCount, format, args);
va_end(args);
output[charCount - 1] = 0;
return w;
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
//=============================================================================
//=============================================================================
template <typename IntType>
IntType DivUp(IntType Value, IntType Denominator)
{
return (Value + Denominator - 1) / Denominator;
}
template <typename IntType>
IntType RoundUp(IntType Value, IntType Round)
{
return DivUp(Value, Round) * Round;
}
union TextureCastHelperUnion
{
ImTextureID TextureID;
uint64_t TextureUint;
const void* TexturePtr;
};
uint64_t TextureCastFromID(ImTextureID textureID)
{
TextureCastHelperUnion textureUnion;
textureUnion.TextureUint = 0;
textureUnion.TextureID = textureID;
return textureUnion.TextureUint;
}
ImTextureID TextureCastFromPtr(void* pTexture)
{
TextureCastHelperUnion textureUnion;
textureUnion.TextureUint = 0;
textureUnion.TexturePtr = pTexture;
return textureUnion.TextureID;
}
#ifndef IS_NETIMGUISERVER
ImTextureID TextureCastFromUInt(uint64_t textureID)
{
TextureCastHelperUnion textureUnion;
textureUnion.TextureUint = textureID;
return textureUnion.TextureID;
}
#endif
}} //namespace NetImgui::Internal
@@ -0,0 +1,33 @@
#pragma once
//
// Deactivate a few warnings to allow internal netImgui code to compile
// with 'Warning as error' and '-Wall' compile actions enabled
//
//=================================================================================================
// Clang
//=================================================================================================
#if defined (__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
#pragma clang diagnostic ignored "-Wmissing-prototypes"
//=================================================================================================
// Visual Studio warnings
//=================================================================================================
#elif defined(_MSC_VER)
#pragma warning (disable: 5032) // detected #pragma warning(push) with no corresponding #pragma warning(pop)
#pragma warning (push)
#pragma warning (disable: 4365) // conversion from 'long' to 'unsigned int', signed/unsigned mismatch for <atomic>
#pragma warning (disable: 4464) // relative include path contains '..'
#pragma warning (disable: 4514) // unreferenced inline function has been removed
#pragma warning (disable: 4577) // 'noexcept' used with no exception handling mode specified; termination on exception is not guaranteed. Specify
#pragma warning (disable: 4710) // 'xxx': function not inlined
#pragma warning (disable: 4711) // function 'xxx' selected for automatic inline expansion
#pragma warning (disable: 4826) // Conversion from 'TType *' to 'uint64_t' is sign-extended. This may cause unexpected runtime behavior.
#pragma warning (disable: 5031) // #pragma warning(pop): likely mismatch, popping warning state pushed in different file
#pragma warning (disable: 5045) // Compiler will insert Spectre mitigation for memory load if / Qspectre switch specified
#pragma warning (disable: 5264) // 'xxx': 'const' variable is not used
#endif
@@ -0,0 +1,33 @@
#pragma once
//
// Deactivate a few warnings to allow Imgui header includes,
// without generating warnings in '-Wall' compile actions enabled
//
//=================================================================================================
// Clang
//=================================================================================================
#if defined (__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
#pragma clang diagnostic ignored "-Wnonportable-include-path" // Sharpmake convert include path to lowercase, avoid warning
#pragma clang diagnostic ignored "-Wreserved-identifier" // Enuma values using '__' or member starting with '_' in imgui.h
//=================================================================================================
// Visual Studio warnings
//=================================================================================================
#elif defined(_MSC_VER)
#pragma warning (push)
#pragma warning (disable: 4514) // 'xxx': unreferenced inline function has been removed
#pragma warning (disable: 4365) // '=': conversion from 'ImGuiTabItemFlags' to 'ImGuiID', signed/unsigned mismatch
#pragma warning (disable: 4710) // 'xxx': function not inlined
#pragma warning (disable: 4820) // 'xxx': 'yyy' bytes padding added after data member 'zzz'
#pragma warning (disable: 5031) // #pragma warning(pop): likely mismatch, popping warning state pushed in different file
#pragma warning (disable: 5045) // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
#if _MSC_VER >= 1920
#pragma warning (disable: 5219) // implicit conversion from 'int' to 'float', possible loss of data
#endif
#pragma warning (disable: 26495) // Code Analysis warning : Variable 'ImGuiStorage::ImGuiStoragePair::<unnamed-tag>::val_p' is uninitialized. Always initialize a member variable (type.6).
#endif
@@ -0,0 +1,25 @@
#pragma once
//
// Deactivate a few more warnings to allow standard header includes,
// without generating warnings in '-Wall' compile actions enabled
//
#include "NetImgui_WarningDisable.h"
//=================================================================================================
// Clang
//=================================================================================================
#if defined (__clang__)
//=================================================================================================
// Visual Studio warnings
//=================================================================================================
#elif defined(_MSC_VER)
#pragma warning (disable: 4061) // enumerator xxx in switch of enum yyy is not explicitly handled by a case label (d3d11.h)
#pragma warning (disable: 4548) // expression before comma has no effect; expected expression with side - effect (malloc.h VS2017)
#pragma warning (disable: 4668) // xxx is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' (winsock2.h)
#pragma warning (disable: 4574) // xxx is defined to be '0': did you mean to use yyy (winsock2.h VS2017)
#pragma warning (disable: 4820) // xxx : yyy bytes padding added after data member zzz
#endif
@@ -0,0 +1,18 @@
#pragma once
//=================================================================================================
// Clang
//=================================================================================================
#if defined(__clang__)
#pragma clang diagnostic pop
//=================================================================================================
// Visual Studio warnings
//=================================================================================================
#elif defined(_MSC_VER)
#pragma warning(pop)
#endif
@@ -269,68 +269,66 @@ void FCogAIWindow_BehaviorTree::RenderNode(UBehaviorTreeComponent& BehaviorTreeC
//------------------------
// Tooltip
//------------------------
if (ImGui::IsItemHovered())
if (FCogWindowWidgets::BeginItemTableTooltip())
{
FCogWindowWidgets::BeginTableTooltip();
if (ImGui::BeginTable("Effect", 2, ImGuiTableFlags_Borders))
{
ImGui::TableSetupColumn("Property");
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
if (ImGui::BeginTable("Effect", 2, ImGuiTableFlags_Borders))
{
ImGui::TableSetupColumn("Property");
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
const ImVec4 TextColor(1.0f, 1.0f, 1.0f, 0.5f);
const ImVec4 TextColor(1.0f, 1.0f, 1.0f, 0.5f);
//------------------------
// Name
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Name");
ImGui::TableNextColumn();
ImGui::Text("%s", NodeName.Get());
//------------------------
// Name
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Name");
ImGui::TableNextColumn();
ImGui::Text("%s", NodeName.Get());
//------------------------
// Static Description
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Description");
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*Node->GetStaticDescription()));
//------------------------
// Static Description
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Description");
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*Node->GetStaticDescription()));
//------------------------
// Runtime Values
//------------------------
TArray<FString> RunTimeValues;
uint8* NodeMemory = BehaviorTreeComponent.GetNodeMemory(Node, BehaviorTreeComponent.GetActiveInstanceIdx());
Node->DescribeRuntimeValues(BehaviorTreeComponent, NodeMemory, EBTDescriptionVerbosity::Detailed, RunTimeValues);
//------------------------
// Runtime Values
//------------------------
TArray<FString> RunTimeValues;
uint8* NodeMemory = BehaviorTreeComponent.GetNodeMemory(Node, BehaviorTreeComponent.GetActiveInstanceIdx());
Node->DescribeRuntimeValues(BehaviorTreeComponent, NodeMemory, EBTDescriptionVerbosity::Detailed, RunTimeValues);
for (const FString& RuntimeValue : RunTimeValues)
{
ImGui::TableNextRow();
for (const FString& RuntimeValue : RunTimeValues)
{
ImGui::TableNextRow();
FString Left, Right;
if (RuntimeValue.Split(TEXT(": "), &Left, &Right))
{
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "%s", TCHAR_TO_ANSI(*Left));
FString Left, Right;
if (RuntimeValue.Split(TEXT(": "), &Left, &Right))
{
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "%s", TCHAR_TO_ANSI(*Left));
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*Right));
}
else
{
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Value");
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*RuntimeValue));
}
}
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*Right));
}
else
{
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Value");
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*RuntimeValue));
}
}
ImGui::EndTable();
}
ImGui::EndTable();
}
FCogWindowWidgets::EndTableTooltip();
FCogWindowWidgets::EndItemTableTooltip();
}
//------------------------
@@ -1,5 +1,4 @@
using UnrealBuildTool;
using UnrealBuildTool.Rules;
public class CogAbility : ModuleRules
{
@@ -26,10 +25,11 @@ public class CogAbility : ModuleRules
"CogImgui",
"CogCommon",
"CogDebug",
"CogEngine",
"CogWindow",
"GameplayAbilities",
"GameplayTags",
"NetCore",
"NetCore",
}
);
@@ -0,0 +1,111 @@
#include "CogAbilityCheat_Execution_ApplyEffect.h"
#include "CogAbilityDataAsset.h"
#include "AbilitySystemComponent.h"
#include "AbilitySystemGlobals.h"
#include "CogAbilityConfig_Alignment.h"
#include "CogImguiHelper.h"
#include "CogWindow.h"
//--------------------------------------------------------------------------------------------------------------------------
void UCogAbilityCheat_Execution_ApplyEffect::Execute_Implementation(const AActor* Instigator, const TArray<AActor*>& Targets) const
{
UAbilitySystemComponent* DefaultInstigatorAbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Instigator, true);
for (AActor* Target : Targets)
{
UAbilitySystemComponent* TargetAbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Target, true);
if (TargetAbilitySystem == nullptr)
{
UE_LOG(LogCogImGui, Warning, TEXT("ACogAbilityReplicator::Server_ApplyCheat_Implementation | Target:%s | Invalid Target AbilitySystem"), *GetNameSafe(Target));
continue;
}
if (TargetAbilitySystem->GetGameplayEffectCount(Effect, nullptr) > 0)
{
TargetAbilitySystem->RemoveActiveGameplayEffectBySourceEffect(Effect, nullptr);
}
else
{
//-----------------------------------------------------------------------------------
// When executing a cheat directly on the game server, there is not an obvious
// local player to use as the instigator. Instead, we use the target ability system.
//-----------------------------------------------------------------------------------
UAbilitySystemComponent* InstigatorAbilitySystem = (DefaultInstigatorAbilitySystem != nullptr) ? DefaultInstigatorAbilitySystem : TargetAbilitySystem;
FGameplayEffectContextHandle ContextHandle = InstigatorAbilitySystem->MakeEffectContext();
ContextHandle.AddSourceObject(InstigatorAbilitySystem);
FGameplayEffectSpecHandle SpecHandle = InstigatorAbilitySystem->MakeOutgoingSpec(Effect, 1, ContextHandle);
if (const FGameplayEffectSpec* EffectSpec = SpecHandle.Data.Get())
{
FHitResult HitResult;
HitResult.HitObjectHandle = FActorInstanceHandle(Target);
HitResult.Normal = FVector::ForwardVector;
HitResult.ImpactNormal = FVector::ForwardVector;
HitResult.Location = Target->GetActorLocation();
HitResult.ImpactPoint = Target->GetActorLocation();
HitResult.PhysMaterial = nullptr;
ContextHandle.AddHitResult(HitResult, true);
InstigatorAbilitySystem->ApplyGameplayEffectSpecToTarget(*EffectSpec, TargetAbilitySystem);
}
}
}
}
//--------------------------------------------------------------------------------------------------------------------------
ECogEngineCheat_ActiveState UCogAbilityCheat_Execution_ApplyEffect::IsActiveOnTargets_Implementation(const TArray<AActor*>& Targets) const
{
if (Effect == nullptr)
{
return ECogEngineCheat_ActiveState::Inactive;
}
int32 NumWithEffect = 0;
for (const AActor* Target : Targets)
{
const UAbilitySystemComponent* AbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Target, true);
if (AbilitySystem == nullptr)
{
continue;
}
const int32 Count = AbilitySystem->GetGameplayEffectCount(Effect, nullptr);
if (Count > 0)
{
NumWithEffect++;
}
}
if (NumWithEffect == 0)
{
return ECogEngineCheat_ActiveState::Inactive;
}
if (NumWithEffect == Targets.Num())
{
return ECogEngineCheat_ActiveState::Active;
}
return ECogEngineCheat_ActiveState::Partial;
}
//--------------------------------------------------------------------------------------------------------------------------
bool UCogAbilityCheat_Execution_ApplyEffect::GetColor(const FCogWindow& InCallingWindow, FLinearColor& OutColor) const
{
if (Effect == nullptr)
{ return false; }
const UGameplayEffect* GameplayEffect = Effect->GetDefaultObject<UGameplayEffect>();
if (GameplayEffect == nullptr)
{ return false; }
const UCogAbilityConfig_Alignment* Config = InCallingWindow.GetConfig<UCogAbilityConfig_Alignment>();
const UCogAbilityDataAsset* Asset = InCallingWindow.GetAsset<UCogAbilityDataAsset>();
OutColor = Config->GetEffectColor(Asset, *GameplayEffect);
return true;
}
@@ -3,6 +3,7 @@
#include "AbilitySystemComponent.h"
#include "AbilitySystemGlobals.h"
#include "CogAbilityDataAsset.h"
#include "CogImguiHelper.h"
#include "CogWindowHelper.h"
#include "Components/SceneComponent.h"
#include "Engine/World.h"
@@ -30,8 +31,10 @@ ACogAbilityReplicator* ACogAbilityReplicator::GetFirstReplicator(const UWorld& W
{
for (TActorIterator<ACogAbilityReplicator> It(&World, StaticClass()); It; ++It)
{
ACogAbilityReplicator* Replicator = *It;
return Replicator;
if (ACogAbilityReplicator* Replicator = *It)
{
return Replicator;
}
}
return nullptr;
@@ -95,17 +98,14 @@ void ACogAbilityReplicator::EndPlay(const EEndPlayReason::Type EndPlayReason)
//--------------------------------------------------------------------------------------------------------------------------
void ACogAbilityReplicator::Server_ApplyCheat_Implementation(const AActor* CheatInstigator, const TArray<AActor*>& Targets, const FCogAbilityCheat& Cheat) const
{
UAbilitySystemComponent* InstigatorAbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(CheatInstigator, true);
if (InstigatorAbilitySystem == nullptr)
{
return;
}
UAbilitySystemComponent* DefaultInstigatorAbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(CheatInstigator, true);
for (AActor* Target : Targets)
{
UAbilitySystemComponent* TargetAbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Target, true);
if (TargetAbilitySystem == nullptr)
{
UE_LOG(LogCogImGui, Warning, TEXT("ACogAbilityReplicator::Server_ApplyCheat_Implementation | Target:%s | Invalid Target AbilitySystem"), *GetNameSafe(Target));
continue;
}
@@ -115,6 +115,12 @@ void ACogAbilityReplicator::Server_ApplyCheat_Implementation(const AActor* Cheat
}
else
{
//-----------------------------------------------------------------------------------
// When executing a cheat directly on the game server, there is not an obvious
// local player to use as the instigator. Instead, we use the target ability system.
//-----------------------------------------------------------------------------------
UAbilitySystemComponent* InstigatorAbilitySystem = (DefaultInstigatorAbilitySystem != nullptr) ? DefaultInstigatorAbilitySystem : TargetAbilitySystem;
FGameplayEffectContextHandle ContextHandle = InstigatorAbilitySystem->MakeEffectContext();
ContextHandle.AddSourceObject(InstigatorAbilitySystem);
FGameplayEffectSpecHandle SpecHandle = InstigatorAbilitySystem->MakeOutgoingSpec(Cheat.Effect, 1, ContextHandle);
@@ -155,15 +155,13 @@ void FCogAbilityWindow_Abilities::RenderAbilitiesMenu(AActor* Selection)
ImGui::Separator();
RenderAbilitiesMenuFilters();
ImGui::Separator();
ImGui::Checkbox("Sort by Name", &Config->SortByName);
RenderAbilitiesMenuColorSettings();
ImGui::Separator();
if (ImGui::MenuItem("Reset"))
if (ImGui::MenuItem("Reset Settings"))
{
ResetConfig();
}
@@ -171,6 +169,12 @@ void FCogAbilityWindow_Abilities::RenderAbilitiesMenu(AActor* Selection)
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Filters"))
{
RenderAbilitiesMenuFilters();
ImGui::EndMenu();
}
FCogWindowWidgets::SearchBar(Filter);
ImGui::EndMenuBar();
@@ -180,11 +184,10 @@ void FCogAbilityWindow_Abilities::RenderAbilitiesMenu(AActor* Selection)
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Abilities::RenderAbilitiesMenuFilters()
{
ImGui::Checkbox("Sort by Name", &Config->SortByName);
ImGui::Checkbox("Show Active", &Config->ShowActive);
ImGui::Checkbox("Show Inactive", &Config->ShowInactive);
ImGui::Checkbox("Show Pressed", &Config->ShowPressed);
ImGui::Checkbox("Show Blocked", &Config->ShowBlocked);
ImGui::Checkbox("Active", &Config->ShowActive);
ImGui::Checkbox("Inactive", &Config->ShowInactive);
ImGui::Checkbox("Pressed", &Config->ShowPressed);
ImGui::Checkbox("Blocked", &Config->ShowBlocked);
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -201,13 +204,13 @@ void FCogAbilityWindow_Abilities::RenderAbilitiesMenuColorSettings()
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Abilities::RenderAbilityActivation(FGameplayAbilitySpec& Spec)
{
FCogWindowWidgets::PushStyleCompact();
bool IsActive = Spec.IsActive();
if (ImGui::Checkbox("##Activation", &IsActive))
{
AbilityHandleToActivate = Spec.Handle;
}
FCogWindowWidgets::PopStyleCompact();
FCogWindowWidgets::PushStyleCompact();
bool IsActive = Spec.IsActive();
if (ImGui::Checkbox("##Activation", &IsActive))
{
AbilityHandleToActivate = Spec.Handle;
}
FCogWindowWidgets::PopStyleCompact();
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -216,15 +219,15 @@ void FCogAbilityWindow_Abilities::RenderAbilitiesTableAbilityName(UAbilitySystem
const ImVec4 Color = GetAbilityColor(AbilitySystemComponent, Spec);
ImGui::PushStyleColor(ImGuiCol_Text, Color);
if (ImGui::Selectable(TCHAR_TO_ANSI(*GetAbilityName(Ability)), SelectedIndex == Index, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_AllowDoubleClick))
{
SelectedIndex = Index;
if (ImGui::Selectable(TCHAR_TO_ANSI(*GetAbilityName(Ability)), SelectedIndex == Index, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_AllowDoubleClick))
{
SelectedIndex = Index;
if (ImGui::IsMouseDoubleClicked(0))
{
OpenAbility(Spec.Handle);
}
}
if (ImGui::IsMouseDoubleClicked(0))
{
OpenAbilityDetails(Spec.Handle);
}
}
ImGui::PopStyleColor(1);
}
@@ -232,26 +235,26 @@ void FCogAbilityWindow_Abilities::RenderAbilitiesTableAbilityName(UAbilitySystem
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Abilities::RenderAbilitiesTableAbilityBlocking(UAbilitySystemComponent& AbilitySystemComponent, UGameplayAbility* Ability)
{
if (Ability->DoesAbilitySatisfyTagRequirements(AbilitySystemComponent) == false)
{
FGameplayTagContainer OwnedGameplayTags;
AbilitySystemComponent.GetOwnedGameplayTags(OwnedGameplayTags);
if (Ability->DoesAbilitySatisfyTagRequirements(AbilitySystemComponent) == false)
{
FGameplayTagContainer OwnedGameplayTags;
AbilitySystemComponent.GetOwnedGameplayTags(OwnedGameplayTags);
if (const FGameplayTagContainer* ActivationBlockedTags = &PRIVATE_ACCESS_PTR(Ability, GameplayAbility_ActivationBlockedTags))
{
FGameplayTagContainer AllBlockingTags;
AbilitySystemComponent.GetBlockedAbilityTags(AllBlockingTags);
AllBlockingTags.AppendTags(OwnedGameplayTags);
if (const FGameplayTagContainer* ActivationBlockedTags = &PRIVATE_ACCESS_PTR(Ability, GameplayAbility_ActivationBlockedTags))
{
FGameplayTagContainer AllBlockingTags;
AbilitySystemComponent.GetBlockedAbilityTags(AllBlockingTags);
AllBlockingTags.AppendTags(OwnedGameplayTags);
FCogAbilityHelper::RenderTagContainer(*ActivationBlockedTags, AllBlockingTags, false, true, true, ImVec4(0, 0, 0, 0), FCogImguiHelper::ToImVec4(Config->BlockedTagsColor));
}
FCogAbilityHelper::RenderTagContainer(*ActivationBlockedTags, AllBlockingTags, false, true, true, ImVec4(0, 0, 0, 0), FCogImguiHelper::ToImVec4(Config->BlockedTagsColor));
}
ImGui::SameLine();
if (const FGameplayTagContainer* ActivationRequiredTags = &PRIVATE_ACCESS_PTR(Ability, GameplayAbility_ActivationRequiredTags))
{
FCogAbilityHelper::RenderTagContainer(*ActivationRequiredTags, OwnedGameplayTags, true, true, true, ImVec4(0, 0, 0, 0), FCogImguiHelper::ToImVec4(Config->BlockedTagsColor));
}
}
ImGui::SameLine();
if (const FGameplayTagContainer* ActivationRequiredTags = &PRIVATE_ACCESS_PTR(Ability, GameplayAbility_ActivationRequiredTags))
{
FCogAbilityHelper::RenderTagContainer(*ActivationRequiredTags, OwnedGameplayTags, true, true, true, ImVec4(0, 0, 0, 0), FCogImguiHelper::ToImVec4(Config->BlockedTagsColor));
}
}
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -259,7 +262,7 @@ void FCogAbilityWindow_Abilities::RenderAbilitiesTable(UAbilitySystemComponent&
{
TArray<FGameplayAbilitySpec>& Abilities = AbilitySystemComponent.GetActivatableAbilities();
TArray<FGameplayAbilitySpec> FitleredAbilities;
TArray<FGameplayAbilitySpec> FilteredAbilities;
for (FGameplayAbilitySpec& Spec : Abilities)
{
@@ -271,8 +274,8 @@ void FCogAbilityWindow_Abilities::RenderAbilitiesTable(UAbilitySystemComponent&
if (ShouldShowAbility(AbilitySystemComponent, Spec, Ability) == false)
{
continue;
}
continue;
}
const auto AbilityName = StringCast<ANSICHAR>(*GetAbilityName(Ability));
if (Filter.PassFilter(AbilityName.Get()) == false)
@@ -280,12 +283,12 @@ void FCogAbilityWindow_Abilities::RenderAbilitiesTable(UAbilitySystemComponent&
continue;
}
FitleredAbilities.Add(Spec);
FilteredAbilities.Add(Spec);
}
if (Config->SortByName)
{
FitleredAbilities.Sort([](const FGameplayAbilitySpec& Lhs, const FGameplayAbilitySpec& Rhs)
FilteredAbilities.Sort([](const FGameplayAbilitySpec& Lhs, const FGameplayAbilitySpec& Rhs)
{
return Lhs.Ability.GetName().Compare(Rhs.Ability.GetName()) < 0;
});
@@ -296,7 +299,7 @@ void FCogAbilityWindow_Abilities::RenderAbilitiesTable(UAbilitySystemComponent&
static int SelectedIndex = -1;
int Index = 0;
for (FGameplayAbilitySpec& Spec : FitleredAbilities)
for (FGameplayAbilitySpec& Spec : FilteredAbilities)
{
UGameplayAbility* Ability = Spec.GetPrimaryInstance();
if (Ability == nullptr)
@@ -357,7 +360,7 @@ bool FCogAbilityWindow_Abilities::RenderAbilitiesTableHeader(UAbilitySystemCompo
| ImGuiTableFlags_Reorderable
| ImGuiTableFlags_Hideable))
{
ImGui::TableSetupColumn("##Activation", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##ActivationCol", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("Ability");
ImGui::TableSetupColumn("Level");
@@ -390,11 +393,10 @@ void FCogAbilityWindow_Abilities::RenderAbilitiesTableRow(UAbilitySystemComponen
//------------------------
// Popup
//------------------------
if (ImGui::IsItemHovered())
if (FCogWindowWidgets::BeginItemTableTooltip())
{
FCogWindowWidgets::BeginTableTooltip();
RenderAbilityInfo(AbilitySystemComponent, Spec);
FCogWindowWidgets::EndTableTooltip();
FCogWindowWidgets::EndItemTableTooltip();
}
//------------------------
@@ -475,36 +477,45 @@ void FCogAbilityWindow_Abilities::RenderAbilityContextMenu(UAbilitySystemCompone
if (ImGui::BeginPopupContextItem())
{
bool bOpen = OpenedAbilities.Contains(Spec.Handle);
if (ImGui::Checkbox("Open", &bOpen))
if (ImGui::Checkbox("Open Details", &bOpen))
{
if (bOpen)
{
OpenAbility(Spec.Handle);
OpenAbilityDetails(Spec.Handle);
}
else
{
CloseAbility(Spec.Handle);
CloseAbilityDetails(Spec.Handle);
}
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Remove"))
const ImVec2 ButtonsSize = ImVec2(ImGui::GetFontSize() * 10, 0);
if (ImGui::Button("Cancel", ButtonsSize))
{
AbilitySystemComponent.CancelAbilityHandle(Spec.Handle);
}
if (ImGui::Button("Remove", ButtonsSize))
{
AbilityHandleToRemove = Spec.Handle;
}
FCogWindowWidgets::OpenObjectAssetButton(Spec.Ability, ButtonsSize);
ImGui::EndPopup();
}
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Abilities::OpenAbility(const FGameplayAbilitySpecHandle& Handle)
void FCogAbilityWindow_Abilities::OpenAbilityDetails(const FGameplayAbilitySpecHandle& Handle)
{
OpenedAbilities.AddUnique(Handle);
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Abilities::CloseAbility(const FGameplayAbilitySpecHandle& Handle)
void FCogAbilityWindow_Abilities::CloseAbilityDetails(const FGameplayAbilitySpecHandle& Handle)
{
OpenedAbilities.Remove(Handle);
}
@@ -535,9 +546,9 @@ void FCogAbilityWindow_Abilities::RenderAbilityInfo(const UAbilitySystemComponen
return;
}
if (ImGui::BeginTable("Ability", 2, ImGuiTableFlags_Borders))
if (ImGui::BeginTable("Ability", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable))
{
constexpr ImVec4 TextColor(1.0f, 1.0f, 1.0f, 0.5f);
constexpr ImVec4 TextColor(1.0f, 1.0f, 1.0f, 0.5f);
ImGui::TableSetupColumn("Property");
ImGui::TableSetupColumn("Value");
@@ -631,7 +642,7 @@ void FCogAbilityWindow_Abilities::RenderAbilityInfo(const UAbilitySystemComponen
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Ability Tags");
ImGui::TableNextColumn();
FCogAbilityHelper::RenderTagContainer(Ability->AbilityTags);
FCogAbilityHelper::RenderTagContainer(Ability->GetAssetTags());
//------------------------
// RequiredTags
@@ -665,8 +676,8 @@ void FCogAbilityWindow_Abilities::RenderAbilityInfo(const UAbilitySystemComponen
}
//------------------------
// Additional info
//------------------------
// Additional info
//------------------------
RenderAbilityAdditionalInfo(AbilitySystemComponent, Spec, *Ability, TextColor);
ImGui::EndTable();
@@ -8,6 +8,8 @@
#include "CogImguiHelper.h"
#include "CogWindowWidgets.h"
#include "AttributeSet.h"
#include "CogAbilityWindow_Abilities.h"
#include "imgui_internal.h"
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Attributes::Initialize()
@@ -41,6 +43,32 @@ void FCogAbilityWindow_Attributes::ResetConfig()
Config->Reset();
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Attributes::RenderTick(float DeltaTime)
{
Super::RenderTick(DeltaTime);
RenderOpenAttributes();
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Attributes::FormatAttributeSetName(FString& AttributeSetName)
{
if (Config->AttributeSetPrefixes.IsEmpty() == false)
{
TArray<FString> Prefixes;
Config->AttributeSetPrefixes.ParseIntoArray(Prefixes, TEXT(";"));
for (const FString& Prefix : Prefixes)
{
if (AttributeSetName.RemoveFromStart(Prefix, ESearchCase::IgnoreCase))
{
break;
}
}
}
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Attributes::RenderContent()
{
@@ -61,10 +89,17 @@ void FCogAbilityWindow_Attributes::RenderContent()
ImGui::Checkbox("Group by Attribute Set", &Config->GroupByAttributeSet);
ImGui::Checkbox("Group by Category", &Config->GroupByCategory);
ImGui::Checkbox("Show Only Modified", &Config->ShowOnlyModified);
FCogWindowWidgets::SetNextItemToShortWidth();
FCogWindowWidgets::InputText("Attribute Set Prefixes", Config->AttributeSetPrefixes);
ImGui::SetItemTooltip("Prefixes to remove from the attribute set name. Separate multiple prefixes with the semicolon character ';'");
ImGui::Separator();
ImGui::ColorEdit4("Positive Color", (float*)&AlignmentConfig->PositiveColor, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_AlphaPreviewHalf);
ImGui::ColorEdit4("Negative Color", (float*)&AlignmentConfig->NegativeColor, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_AlphaPreviewHalf);
ImGui::ColorEdit4("Neutral Color", (float*)&AlignmentConfig->NeutralColor, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_AlphaPreviewHalf);
ImGui::ColorEdit4("AttributeSet Color", (float*)&Config->AttributeSetColor, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_AlphaPreviewHalf);
ImGui::ColorEdit4("Category Color", (float*)&Config->CategoryColor, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_AlphaPreviewHalf);
ImGui::Separator();
if (ImGui::MenuItem("Reset"))
{
@@ -78,10 +113,12 @@ void FCogAbilityWindow_Attributes::RenderContent()
ImGui::EndMenuBar();
}
bool bGroupByAttributeSetValue = Filter.IsActive() == false && Config->ShowOnlyModified == false && Config->GroupByAttributeSet;
bool bGroupByCategoryValue = Filter.IsActive() == false && Config->ShowOnlyModified == false && Config->GroupByCategory;
const bool bGroupByAttributeSetValue = Filter.IsActive() == false && Config->ShowOnlyModified == false && Config->GroupByAttributeSet;
const bool bGroupByCategoryValue = Filter.IsActive() == false && Config->ShowOnlyModified == false && Config->GroupByCategory;
const float bShowGroup = bGroupByAttributeSetValue | bGroupByCategoryValue;
const float FirstColWidth = ((int32)bGroupByAttributeSetValue + (int32)bGroupByCategoryValue) * ImGui::GetFontSize() * 2;
if (ImGui::BeginTable("Attributes", 3, ImGuiTableFlags_SizingFixedFit
if (ImGui::BeginTable("Attributes", 5, ImGuiTableFlags_SizingFixedFit
| ImGuiTableFlags_Resizable
| ImGuiTableFlags_NoBordersInBodyUntilResize
| ImGuiTableFlags_ScrollY
@@ -91,6 +128,8 @@ void FCogAbilityWindow_Attributes::RenderContent()
| ImGuiTableFlags_Hideable))
{
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("-", ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoHeaderLabel | ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, FirstColWidth);
ImGui::TableSetupColumn("Set", bShowGroup ? ImGuiTableColumnFlags_DefaultHide : ImGuiTableColumnFlags_None);
ImGui::TableSetupColumn("Attribute");
ImGui::TableSetupColumn("Base");
ImGui::TableSetupColumn("Current");
@@ -102,23 +141,36 @@ void FCogAbilityWindow_Attributes::RenderContent()
//------------------------------------------------------------------------------------------
// Draw all the attribute sets
//------------------------------------------------------------------------------------------
for (const UAttributeSet* Set : AbilitySystemComponent->GetSpawnedAttributes())
for (const UAttributeSet* AttributeSet : AbilitySystemComponent->GetSpawnedAttributes())
{
if (AttributeSet == nullptr)
{ continue; }
ImGui::PushID(TCHAR_TO_ANSI(*AttributeSet->GetName()));
FString AttributeSetName = AttributeSet->GetName();
FormatAttributeSetName(AttributeSetName);
const auto AttributeSetNameStr = StringCast<ANSICHAR>(*AttributeSetName);
//------------------------------------------------------------------------------------------
// Add an tree node categories are shown
// Add a tree node with the name of the attribute set if grouping by attribute set
//------------------------------------------------------------------------------------------
bool bOpenAttributeSet = true;
if (bGroupByAttributeSetValue)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
bOpenAttributeSet = ImGui::TreeNodeEx(TCHAR_TO_ANSI(*Set->GetName()), ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::PushStyleColor(ImGuiCol_Text, FCogImguiHelper::ToImVec4(Config->AttributeSetColor));
bOpenAttributeSet = ImGui::TreeNodeEx(AttributeSetNameStr.Get(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_LabelSpanAllColumns);
ImGui::PopStyleColor();
}
if (bOpenAttributeSet)
{
TArray<FGameplayAttribute> AllAttributes;
for (TFieldIterator<FProperty> It(Set->GetClass()); It; ++It)
for (TFieldIterator<FProperty> It(AttributeSet->GetClass()); It; ++It)
{
FGameplayAttribute Attribute = *It;
if (Attribute.IsValid())
@@ -176,7 +228,9 @@ void FCogAbilityWindow_Attributes::RenderContent()
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
bOpenCategory = ImGui::TreeNodeEx(TCHAR_TO_ANSI(*It.Key), ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::PushStyleColor(ImGuiCol_Text, FCogImguiHelper::ToImVec4(Config->CategoryColor));
bOpenCategory = ImGui::TreeNodeEx(TCHAR_TO_ANSI(*It.Key), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_LabelSpanAllColumns);
ImGui::PopStyleColor();
}
if (bOpenCategory)
@@ -201,51 +255,66 @@ void FCogAbilityWindow_Attributes::RenderContent()
for (const FGameplayAttribute& Attribute : AttributesInCategory)
{
if (!Attribute.IsValid())
{
continue;
}
{ continue; }
const auto AttributeName = StringCast<ANSICHAR>(*Attribute.GetName());
const auto AttributeNameStr = StringCast<ANSICHAR>(*Attribute.GetName());
if (Filter.PassFilter(AttributeName.Get()) == false)
{
continue;
}
if (Filter.PassFilter(AttributeNameStr.Get()) == false)
{ continue; }
const float BaseValue = AbilitySystemComponent->GetNumericAttributeBase(Attribute);
const float CurrentValue = AbilitySystemComponent->GetNumericAttribute(Attribute);
if (Config->ShowOnlyModified && FMath::IsNearlyEqual(CurrentValue, BaseValue))
{
continue;
}
{ continue; }
ImGui::PushID(AttributeNameStr.Get());
ImGui::TableNextRow();
//------------------------
// Selectable
//------------------------
ImGui::TableNextColumn();
if (ImGui::Selectable("", Selected == Index, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick))
{
Selected = Index;
if (ImGui::IsMouseDoubleClicked(0))
{
OpenAttributeDetails(Attribute);
}
}
//------------------------
// Popup
//------------------------
if (FCogWindowWidgets::BeginItemTableTooltip())
{
RenderAttributeDetails(*AbilitySystemComponent, AttributeSetNameStr.Get(), Attribute, true);
FCogWindowWidgets::EndItemTableTooltip();
}
//------------------------
// ContextMenu
//------------------------
RenderAttributeContextMenu(*AbilitySystemComponent, Attribute, Index);
const ImVec4 Color = FCogImguiHelper::ToImVec4(AlignmentConfig->GetAttributeColor(*AbilitySystemComponent, Attribute));
ImGui::PushStyleColor(ImGuiCol_Text, Color);
//------------------------
// Name
// Attribute Set
//------------------------
ImGui::TableNextColumn();
ImGui::Text("");
ImGui::SameLine();
if (ImGui::Selectable(AttributeName.Get(), Selected == Index, ImGuiSelectableFlags_SpanAllColumns))
{
Selected = Index;
}
ImGui::PopStyleColor(1);
ImGui::Text("%s", AttributeSetNameStr.Get());
//------------------------
// Popup
// Attribute Name
//------------------------
if (ImGui::IsItemHovered())
{
FCogWindowWidgets::BeginTableTooltip();
DrawAttributeInfo(*AbilitySystemComponent, Attribute);
FCogWindowWidgets::EndTableTooltip();
}
ImGui::TableNextColumn();
ImGui::Text("%s", AttributeNameStr.Get());
ImGui::PopStyleColor(1);
ImGui::PushStyleColor(ImGuiCol_Text, Color);
@@ -263,6 +332,8 @@ void FCogAbilityWindow_Attributes::RenderContent()
ImGui::PopStyleColor(1);
ImGui::PopID();
Index++;
}
}
@@ -274,10 +345,15 @@ void FCogAbilityWindow_Attributes::RenderContent()
}
}
if (bOpenAttributeSet && bGroupByAttributeSetValue)
if (bGroupByAttributeSetValue)
{
ImGui::TreePop();
if (bOpenAttributeSet)
{
ImGui::TreePop();
}
}
ImGui::PopID();
}
ImGui::EndTable();
@@ -285,17 +361,34 @@ void FCogAbilityWindow_Attributes::RenderContent()
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Attributes::DrawAttributeInfo(const UAbilitySystemComponent& AbilitySystemComponent, const FGameplayAttribute& Attribute)
void FCogAbilityWindow_Attributes::RenderAttributeDetails(const UAbilitySystemComponent& AbilitySystemComponent, const char* AttributeSetName, const FGameplayAttribute& Attribute, bool IsForTooltip)
{
if (ImGui::BeginTable("Attribute", 2, ImGuiTableFlags_Borders))
ImGuiTableFlags TableFlags = IsForTooltip ? ImGuiTableFlags_Borders
: ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBodyUntilResize | ImGuiTableFlags_Resizable;
const float BaseValue = AbilitySystemComponent.GetNumericAttributeBase(Attribute);
const float CurrentValue = AbilitySystemComponent.GetNumericAttribute(Attribute);
if (ImGui::BeginTable("Details", 2, TableFlags))
{
const ImVec4 TextColor(1.0f, 1.0f, 1.0f, 0.5f);
constexpr ImVec4 TextColor(1.0f, 1.0f, 1.0f, 0.5f);
ImGui::TableSetupColumn("Property");
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
const float BaseValue = AbilitySystemComponent.GetNumericAttributeBase(Attribute);
const float CurrentValue = AbilitySystemComponent.GetNumericAttribute(Attribute);
if (IsForTooltip == false)
{
ImGui::TableHeadersRow();
}
//------------------------
// Attribute Set
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Attribute Set");
ImGui::TableNextColumn();
ImGui::Text("%s", AttributeSetName);
//------------------------
// Name
@@ -326,38 +419,197 @@ void FCogAbilityWindow_Attributes::DrawAttributeInfo(const UAbilitySystemCompone
ImGui::Text("%0.2f", CurrentValue);
ImGui::PopStyleColor(1);
//------------------------
// Modifiers
//------------------------
FGameplayEffectQuery Query;
for (const FActiveGameplayEffectHandle& ActiveHandle : AbilitySystemComponent.GetActiveEffects(Query))
{
const FActiveGameplayEffect* ActiveEffect = AbilitySystemComponent.GetActiveGameplayEffect(ActiveHandle);
if (ActiveEffect == nullptr)
{
continue;
}
for (int32 i = 0; i < ActiveEffect->Spec.Modifiers.Num(); ++i)
{
const FModifierSpec& ModSpec = ActiveEffect->Spec.Modifiers[i];
const FGameplayModifierInfo& ModInfo = ActiveEffect->Spec.Def->Modifiers[i];
if (ModInfo.Attribute == Attribute)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Effect");
ImGui::TextColored(TextColor, "Operation");
ImGui::TextColored(TextColor, "Magnitude");
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*FCogAbilityHelper::CleanupName(GetNameSafe(ActiveEffect->Spec.Def))));
ImGui::Text("%s", TCHAR_TO_ANSI(*EGameplayModOpToString(ModInfo.ModifierOp)));
ImGui::TextColored(FCogImguiHelper::ToImVec4(AlignmentConfig->GetEffectModifierColor(ModSpec, ModInfo, BaseValue)), "%0.2f", ModSpec.GetEvaluatedMagnitude());
}
}
}
ImGui::EndTable();
}
//------------------------
// Modifiers
//------------------------
int32 ModifierIndex = 1;
const FGameplayEffectQuery Query;
for (const FActiveGameplayEffectHandle& ActiveHandle : AbilitySystemComponent.GetActiveEffects(Query))
{
const FActiveGameplayEffect* ActiveEffect = AbilitySystemComponent.GetActiveGameplayEffect(ActiveHandle);
if (ActiveEffect == nullptr)
{
continue;
}
for (int32 i = 0; i < ActiveEffect->Spec.Modifiers.Num(); ++i)
{
const FModifierSpec& ModSpec = ActiveEffect->Spec.Modifiers[i];
const FGameplayModifierInfo& ModInfo = ActiveEffect->Spec.Def->Modifiers[i];
if (ModInfo.Attribute == Attribute)
{
char Buffer[128];
ImFormatString(Buffer, IM_ARRAYSIZE(Buffer), "Modifier %d", ModifierIndex);
if (FCogWindowWidgets::DarkCollapsingHeader(Buffer, ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_DefaultOpen))
{
if (ImGui::BeginTable("Details", 2, TableFlags))
{
constexpr ImVec4 TextColor(1.0f, 1.0f, 1.0f, 0.5f);
ImGui::TableSetupColumn("Property");
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Effect");
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*FCogAbilityHelper::CleanupName(GetNameSafe(ActiveEffect->Spec.Def))));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Operation");
ImGui::TableNextColumn();
ImGui::Text("%s", TCHAR_TO_ANSI(*EGameplayModOpToString(ModInfo.ModifierOp)));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Magnitude");
ImGui::TableNextColumn();
ImGui::TextColored(FCogImguiHelper::ToImVec4(AlignmentConfig->GetEffectModifierColor(ModSpec, ModInfo, BaseValue)), "%0.2f", ModSpec.GetEvaluatedMagnitude());
if (ModInfo.SourceTags.RequireTags.IsEmpty() == false)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "SourceTags - Require");
ImGui::TableNextColumn();
FCogAbilityHelper::RenderTagContainer(ModInfo.SourceTags.RequireTags, false);
}
if (ModInfo.SourceTags.IgnoreTags.IsEmpty() == false)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "SourceTags - Ignore");
ImGui::TableNextColumn();
FCogAbilityHelper::RenderTagContainer(ModInfo.SourceTags.IgnoreTags, false);
}
if (ModInfo.SourceTags.TagQuery.IsEmpty() == false)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "SourceTags - Query");
ImGui::TableNextColumn();
const auto Str = StringCast<ANSICHAR>(*ModInfo.SourceTags.TagQuery.GetDescription());
ImGui::Text("%s", Str.Get());
}
if (ModInfo.TargetTags.RequireTags.IsEmpty() == false)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "TargetTags - Require");
ImGui::TableNextColumn();
FCogAbilityHelper::RenderTagContainer(ModInfo.TargetTags.RequireTags, false);
}
if (ModInfo.TargetTags.IgnoreTags.IsEmpty() == false)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "TargetTags - Ignore");
ImGui::TableNextColumn();
FCogAbilityHelper::RenderTagContainer(ModInfo.TargetTags.IgnoreTags, false);
}
if (ModInfo.TargetTags.TagQuery.IsEmpty() == false)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "TargetTags - Query");
ImGui::TableNextColumn();
const auto Str = StringCast<ANSICHAR>(*ModInfo.TargetTags.TagQuery.GetDescription());
ImGui::Text("%s", Str.Get());
}
ImGui::EndTable();
}
}
ModifierIndex++;
}
}
}
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Attributes::RenderOpenAttributes()
{
const AActor* Selection = GetSelection();
if (Selection == nullptr)
{
return;
}
const UAbilitySystemComponent* AbilitySystemComponent = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Selection, true);
if (AbilitySystemComponent == nullptr)
{
return;
}
for (int i = OpenedAttributes.Num() - 1; i >= 0; --i)
{
const FGameplayAttribute& Attribute = OpenedAttributes[i];
FString AttributeSetName = Attribute.GetAttributeSetClass()->GetFName().ToString();
FormatAttributeSetName(AttributeSetName);
const FString WindowName = AttributeSetName + FString(" - ") + Attribute.GetName();
const auto WindowNameStr = StringCast<ANSICHAR>(*WindowName);
bool Open = true;
if (ImGui::Begin(WindowNameStr.Get(), &Open))
{
const auto AttributeSetNameStr = StringCast<ANSICHAR>(*AttributeSetName);
RenderAttributeDetails(*AbilitySystemComponent, AttributeSetNameStr.Get(), Attribute, false);
ImGui::End();
}
if (Open == false)
{
OpenedAttributes.RemoveAt(i);
}
}
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Attributes::RenderAttributeContextMenu(UAbilitySystemComponent& AbilitySystemComponent, const FGameplayAttribute& InAttribute, int Index)
{
if (ImGui::BeginPopupContextItem())
{
bool bOpen = OpenedAttributes.Contains(InAttribute);
if (ImGui::Checkbox("Open Details", &bOpen))
{
if (bOpen)
{
OpenAttributeDetails(InAttribute);
}
else
{
CloseAttributeDetails(InAttribute);
}
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Attributes::OpenAttributeDetails(const FGameplayAttribute& InAttribute)
{
OpenedAttributes.AddUnique(InAttribute);
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Attributes::CloseAttributeDetails(const FGameplayAttribute& InAttribute)
{
OpenedAttributes.Remove(InAttribute);
}
@@ -5,6 +5,7 @@
#include "CogAbilityReplicator.h"
#include "CogCommonAllegianceActorInterface.h"
#include "CogImguiHelper.h"
#include "CogWindowConsoleCommandManager.h"
#include "CogWindowWidgets.h"
#include "EngineUtils.h"
#include "GameFramework/Character.h"
@@ -21,6 +22,29 @@ void FCogAbilityWindow_Cheats::Initialize()
Asset = GetAsset<UCogAbilityDataAsset>();
Config = GetConfig<UCogAbilityConfig_Cheats>();
AlignmentConfig = GetConfig<UCogAbilityConfig_Alignment>();
FCogWindowConsoleCommandManager::RegisterWorldConsoleCommand(
TEXT("Cog.Cheat"),
TEXT("Apply a cheat to the selection. Cog.Cheat <CheatName> -Allies -Enemies -Controlled"),
GetWorld(),
FCogWindowConsoleCommandDelegate::CreateLambda([this](const TArray<FString>& InArgs, UWorld* InWorld)
{
if (InArgs.Num() > 0)
{
if (const FCogAbilityCheat* cheat = FindCheatByName(InArgs[0]))
{
const bool ApplyToEnemies = InArgs.Contains("-Enemies");
const bool ApplyToAllies = InArgs.Contains("-Allies");
const bool ApplyToControlled = InArgs.Contains("-Controlled");
RequestCheat(GetLocalPlayerPawn(), GetSelection(), *cheat, ApplyToEnemies, ApplyToAllies, ApplyToControlled);
}
else
{
UE_LOG(LogCogImGui, Warning, TEXT("Cog.Cheat %s | Cheat not found"), *InArgs[0]);
}
}
}));
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -84,8 +108,8 @@ void FCogAbilityWindow_Cheats::TryReapplyCheats()
return;
}
APawn* LocalPawn = GetLocalPlayerPawn();
if (LocalPawn == nullptr)
APawn* ControlledActor = GetLocalPlayerPawn();
if (ControlledActor == nullptr)
{
return;
}
@@ -96,7 +120,7 @@ void FCogAbilityWindow_Cheats::TryReapplyCheats()
return;
}
TArray<AActor*> Targets { LocalPawn };
TArray<AActor*> Targets { ControlledActor };
for (int32 i = Config->AppliedCheats.Num() - 1; i >= 0; i--)
{
@@ -105,7 +129,7 @@ void FCogAbilityWindow_Cheats::TryReapplyCheats()
if (const FCogAbilityCheat* Cheat = Asset->PersistentEffects.FindByPredicate(
[AppliedCheatName](const FCogAbilityCheat& Cheat) { return Cheat.Name == AppliedCheatName; }))
{
Replicator->Server_ApplyCheat(LocalPawn, Targets, *Cheat);
Replicator->Server_ApplyCheat(ControlledActor, Targets, *Cheat);
}
else
{
@@ -247,13 +271,17 @@ bool FCogAbilityWindow_Cheats::AddCheat(AActor* ControlledActor, AActor* Selecte
FCogWindowWidgets::PushBackColor(FCogImguiHelper::ToImVec4(AlignmentConfig->GetEffectColor(Asset, *EffectCDO)));
}
const bool IsShiftDown = (ImGui::GetCurrentContext()->IO.KeyMods & ImGuiMod_Shift) != 0;
const bool IsAltDown = (ImGui::GetCurrentContext()->IO.KeyMods & ImGuiMod_Alt) != 0;
const bool IsControlDown = (ImGui::GetCurrentContext()->IO.KeyMods & ImGuiMod_Ctrl) != 0;
bool bIsPressed = false;
if (IsPersistent)
{
bool isEnabled = ACogAbilityReplicator::IsCheatActive(SelectedActor, Cheat);
if (ImGui::Checkbox(TCHAR_TO_ANSI(*Cheat.Name), &isEnabled))
{
RequestCheat(ControlledActor, SelectedActor, Cheat);
RequestCheat(ControlledActor, SelectedActor, Cheat, IsShiftDown, IsAltDown, IsControlDown);
bIsPressed = true;
}
}
@@ -261,22 +289,18 @@ bool FCogAbilityWindow_Cheats::AddCheat(AActor* ControlledActor, AActor* Selecte
{
if (ImGui::Button(TCHAR_TO_ANSI(*Cheat.Name), ImVec2(-1, 0)))
{
RequestCheat(ControlledActor, SelectedActor, Cheat);
RequestCheat(ControlledActor, SelectedActor, Cheat, IsShiftDown, IsAltDown, IsControlDown);
bIsPressed = true;
}
}
if (ImGui::IsItemHovered())
{
const bool IsShiftDown = (ImGui::GetCurrentContext()->IO.KeyMods & ImGuiMod_Shift) != 0;
const bool IsAltDown = (ImGui::GetCurrentContext()->IO.KeyMods & ImGuiMod_Alt) != 0;
const bool IsControlDown = (ImGui::GetCurrentContext()->IO.KeyMods & ImGuiMod_Ctrl) != 0;
ImGui::BeginTooltip();
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, IsShiftDown || IsAltDown || IsControlDown ? 0.5f : 1.0f), "On Selection");
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, IsShiftDown ? 1.0f : 0.5f), "On Enemies [SHIFT]");
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, IsAltDown ? 1.0f : 0.5f), "On Allies [ALT]");
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, IsControlDown ? 1.0f : 0.5f), "On Controlled [CTRL]");
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, IsShiftDown || IsAltDown || IsControlDown ? 0.5f : 1.0f), "On Selection");
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, IsShiftDown ? 1.0f : 0.5f), "On Enemies [SHIFT]");
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, IsAltDown ? 1.0f : 0.5f), "On Allies [ALT]");
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, IsControlDown ? 1.0f : 0.5f), "On Controlled [CTRL]");
ImGui::EndTooltip();
}
@@ -289,20 +313,16 @@ bool FCogAbilityWindow_Cheats::AddCheat(AActor* ControlledActor, AActor* Selecte
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Cheats::RequestCheat(AActor* ControlledActor, AActor* SelectedActor, const FCogAbilityCheat& Cheat)
void FCogAbilityWindow_Cheats::RequestCheat(AActor* ControlledActor, AActor* SelectedActor, const FCogAbilityCheat& Cheat, bool ApplyToEnemies, bool ApplyToAllies, bool ApplyToControlled)
{
const bool IsShiftDown = (ImGui::GetCurrentContext()->IO.KeyMods & ImGuiMod_Shift) != 0;
const bool IsAltDown = (ImGui::GetCurrentContext()->IO.KeyMods & ImGuiMod_Alt) != 0;
const bool IsControlDown = (ImGui::GetCurrentContext()->IO.KeyMods & ImGuiMod_Ctrl) != 0;
TArray<AActor*> Actors;
if (IsControlDown)
if (ApplyToControlled)
{
Actors.Add(ControlledActor);
}
if (IsShiftDown || IsAltDown)
if (ApplyToEnemies || ApplyToAllies)
{
for (TActorIterator<ACharacter> It(GetWorld(), ACharacter::StaticClass()); It; ++It)
{
@@ -315,8 +335,8 @@ void FCogAbilityWindow_Cheats::RequestCheat(AActor* ControlledActor, AActor* Sel
Allegiance = AllegianceInterface->GetAllegianceWithOtherActor(ControlledActor);
}
if ((IsShiftDown && (Allegiance == ECogCommonAllegiance::Enemy))
|| (IsAltDown && (Allegiance == ECogCommonAllegiance::Friendly)))
if ((ApplyToEnemies && (Allegiance == ECogCommonAllegiance::Enemy))
|| (ApplyToAllies && (Allegiance == ECogCommonAllegiance::Friendly)))
{
Actors.Add(OtherActor);
}
@@ -324,7 +344,7 @@ void FCogAbilityWindow_Cheats::RequestCheat(AActor* ControlledActor, AActor* Sel
}
}
if ((IsControlDown || IsShiftDown || IsAltDown) == false)
if ((ApplyToControlled || ApplyToEnemies || ApplyToAllies) == false)
{
Actors.Add(SelectedActor);
}
@@ -333,4 +353,30 @@ void FCogAbilityWindow_Cheats::RequestCheat(AActor* ControlledActor, AActor* Sel
{
Replicator->Server_ApplyCheat(ControlledActor, Actors, Cheat);
}
else
{
UE_LOG(LogCogImGui, Warning, TEXT("FCogAbilityWindow_Cheats::RequestCheat | Replicator not found"));
}
}
//--------------------------------------------------------------------------------------------------------------------------
const FCogAbilityCheat* FCogAbilityWindow_Cheats::FindCheatByName(const FString& CheatName)
{
for (const FCogAbilityCheat& cheat : Asset->PersistentEffects)
{
if (cheat.Name == CheatName)
{
return &cheat;
}
}
for (const FCogAbilityCheat& cheat : Asset->InstantEffects)
{
if (cheat.Name == CheatName)
{
return &cheat;
}
}
return nullptr;
}
@@ -42,9 +42,9 @@ void FCogAbilityWindow_Effects::ResetConfig()
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Effects::RenderTick(float DetlaTime)
void FCogAbilityWindow_Effects::RenderTick(float DeltaTime)
{
Super::RenderTick(DetlaTime);
Super::RenderTick(DeltaTime);
RenderOpenEffects();
}
@@ -85,7 +85,7 @@ void FCogAbilityWindow_Effects::RenderContent()
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Effects::RenderEffectsTable()
{
const UAbilitySystemComponent* AbilitySystemComponent = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(GetSelection(), true);
UAbilitySystemComponent* AbilitySystemComponent = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(GetSelection(), true);
if (AbilitySystemComponent == nullptr)
{
ImGui::TextDisabled("Selection has no ability system component");
@@ -170,7 +170,7 @@ void FCogAbilityWindow_Effects::RenderEffectsTable()
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Effects::RenderEffectRow(const UAbilitySystemComponent& AbilitySystemComponent, const FActiveGameplayEffectHandle& ActiveHandle, int32 Index, int32& Selected)
void FCogAbilityWindow_Effects::RenderEffectRow(UAbilitySystemComponent& AbilitySystemComponent, const FActiveGameplayEffectHandle& ActiveHandle, int32 Index, int32& Selected)
{
const FActiveGameplayEffect* ActiveEffectPtr = AbilitySystemComponent.GetActiveGameplayEffect(ActiveHandle);
if (ActiveEffectPtr == nullptr)
@@ -213,11 +213,10 @@ void FCogAbilityWindow_Effects::RenderEffectRow(const UAbilitySystemComponent& A
//------------------------
// Popup
//------------------------
if (ImGui::IsItemHovered())
if (FCogWindowWidgets::BeginItemTableTooltip())
{
FCogWindowWidgets::BeginTableTooltip();
RenderEffectInfo(AbilitySystemComponent, ActiveEffect, Effect);
FCogWindowWidgets::EndTableTooltip();
FCogWindowWidgets::EndItemTableTooltip();
}
//------------------------
@@ -225,8 +224,10 @@ void FCogAbilityWindow_Effects::RenderEffectRow(const UAbilitySystemComponent& A
//------------------------
if (ImGui::BeginPopupContextItem())
{
const ImVec2 ButtonsSize = ImVec2(ImGui::GetFontSize() * 10, 0);
bool bOpen = OpenedEffects.Contains(ActiveHandle);
if (ImGui::Checkbox("Open", &bOpen))
if (ImGui::Checkbox("Open Details", &bOpen))
{
if (bOpen)
{
@@ -238,6 +239,15 @@ void FCogAbilityWindow_Effects::RenderEffectRow(const UAbilitySystemComponent& A
}
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Remove", ButtonsSize))
{
AbilitySystemComponent.RemoveActiveGameplayEffect(ActiveHandle);
ImGui::CloseCurrentPopup();
}
FCogWindowWidgets::OpenObjectAssetButton(EffectPtr, ButtonsSize);
ImGui::EndPopup();
}
@@ -270,7 +280,7 @@ void FCogAbilityWindow_Effects::RenderEffectRow(const UAbilitySystemComponent& A
//--------------------------------------------------------------------------------------------------------------------------
void FCogAbilityWindow_Effects::RenderEffectInfo(const UAbilitySystemComponent& AbilitySystemComponent, const FActiveGameplayEffect& ActiveEffect, const UGameplayEffect& Effect)
{
if (ImGui::BeginTable("Effect", 2, ImGuiTableFlags_Borders))
if (ImGui::BeginTable("Effect", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable))
{
constexpr ImVec4 TextColor(1.0f, 1.0f, 1.0f, 0.5f);
@@ -134,11 +134,10 @@ void FCogAbilityWindow_Tags::RenderTagContainer(const UAbilitySystemComponent& A
//------------------------
// Tooltip
//------------------------
if (ImGui::IsItemHovered())
if (FCogWindowWidgets::BeginItemTableTooltip())
{
FCogWindowWidgets::BeginTableTooltip();
RenderTag(AbilitySystemComponent, Tag);
FCogWindowWidgets::EndTableTooltip();
FCogWindowWidgets::EndItemTableTooltip();
}
ImGui::PopID();
@@ -2,11 +2,8 @@
#include "AbilitySystemGlobals.h"
#include "AbilitySystemComponent.h"
#include "CogAbilityDataAsset.h"
#include "CogAbilityHelper.h"
#include "CogAbilityReplicator.h"
#include "CogImguiHelper.h"
#include "CogWindowHelper.h"
#include "CogWindowWidgets.h"
#include "imgui.h"
@@ -160,15 +157,15 @@ void FCogAbilityWindow_Tasks::RenderTasksTable(UAbilitySystemComponent& AbilityS
continue;
}
const char* TaskName = StringCast<ANSICHAR>(*Task->GetName()).Get();
bool PassFilter = Filter.PassFilter(TaskName);
const auto& TaskName = StringCast<ANSICHAR>(*Task->GetName());
bool PassFilter = Filter.PassFilter(TaskName.Get());
if (PassFilter == false)
{
if (const UGameplayAbility* Ability = Cast<UGameplayAbility>(Task->GetTaskOwner()))
{
const char* AbilityName = StringCast<ANSICHAR>(*FCogAbilityHelper::CleanupName(Ability->GetName())).Get();
PassFilter = Filter.PassFilter(AbilityName);
const auto& AbilityName = StringCast<ANSICHAR>(*FCogAbilityHelper::CleanupName(Ability->GetName()));
PassFilter = Filter.PassFilter(AbilityName.Get());
}
}
@@ -177,7 +174,7 @@ void FCogAbilityWindow_Tasks::RenderTasksTable(UAbilitySystemComponent& AbilityS
continue;
}
FilteredTasks.Add(Task);
FilteredTasks.Add(Task);
}
if (Config->SortByName)
@@ -228,8 +225,8 @@ void FCogAbilityWindow_Tasks::RenderTasksTable(UAbilitySystemComponent& AbilityS
//------------------------
ImGui::TableNextColumn();
const char* TaskName = StringCast<ANSICHAR>(*Task->GetName()).Get();
if (ImGui::Selectable(TaskName, SelectedIndex == LineIndex, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_AllowDoubleClick))
const auto& TaskName = StringCast<ANSICHAR>(*Task->GetName());
if (ImGui::Selectable(TaskName.Get(), SelectedIndex == LineIndex, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_AllowDoubleClick))
{
SelectedIndex = LineIndex;
}
@@ -237,11 +234,10 @@ void FCogAbilityWindow_Tasks::RenderTasksTable(UAbilitySystemComponent& AbilityS
//------------------------
// Popup
//------------------------
if (ImGui::IsItemHovered())
if (FCogWindowWidgets::BeginItemTableTooltip())
{
FCogWindowWidgets::BeginTableTooltip();
RenderTaskInfo(Task);
FCogWindowWidgets::EndTableTooltip();
FCogWindowWidgets::EndItemTableTooltip();
}
//------------------------
@@ -254,7 +250,7 @@ void FCogAbilityWindow_Tasks::RenderTasksTable(UAbilitySystemComponent& AbilityS
// IsTicking
//------------------------
ImGui::TableNextColumn();
ImGui::Text(Task->IsTickingTask() ? "Yes" : "No");
ImGui::Text(Task->IsTickingTask() ? "Yes" : "No");
ImGui::PopID();
}
@@ -275,7 +271,7 @@ void FCogAbilityWindow_Tasks::RenderTaskInfo(const UGameplayTask* Task)
if (ImGui::BeginTable("Task", 2, ImGuiTableFlags_Borders))
{
constexpr ImVec4 TextColor(1.0f, 1.0f, 1.0f, 0.5f);
constexpr ImVec4 TextColor(1.0f, 1.0f, 1.0f, 0.5f);
ImGui::TableSetupColumn("Property");
ImGui::TableSetupColumn("Value");
@@ -287,20 +283,20 @@ void FCogAbilityWindow_Tasks::RenderTaskInfo(const UGameplayTask* Task)
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Name");
ImGui::TableNextColumn();
ImGui::Text(StringCast<ANSICHAR>(*Task->GetName()).Get());
ImGui::Text("%s", StringCast<ANSICHAR>(*Task->GetName()).Get());
//------------------------
// Instance Name
//------------------------
// Instance Name
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Instance Name");
ImGui::TableNextColumn();
ImGui::Text(StringCast<ANSICHAR>(*Task->GetInstanceName().ToString()).Get());
ImGui::Text("%s", StringCast<ANSICHAR>(*Task->GetInstanceName().ToString()).Get());
//------------------------
// Owner
//------------------------
// Owner
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Ability");
@@ -316,9 +312,9 @@ void FCogAbilityWindow_Tasks::RenderTaskInfo(const UGameplayTask* Task)
ImGui::TableNextColumn();
RenderTaskState(Task);
//------------------------
// Priority
//------------------------
//------------------------
// Priority
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Priority");
@@ -326,17 +322,17 @@ void FCogAbilityWindow_Tasks::RenderTaskInfo(const UGameplayTask* Task)
ImGui::Text("%d", (int32)Task->GetPriority());
//------------------------
// IsTicking
//------------------------
// IsTicking
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Is Ticking");
ImGui::TableNextColumn();
ImGui::Text(Task->IsTickingTask() ? "Yes" : "No");
//------------------------
// IsSimulated
//------------------------
//------------------------
// IsSimulated
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Is Simulated");
@@ -344,8 +340,8 @@ void FCogAbilityWindow_Tasks::RenderTaskInfo(const UGameplayTask* Task)
ImGui::Text(Task->IsSimulatedTask() ? "Yes" : "No");
//------------------------
// IsSimulating
//------------------------
// IsSimulating
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Is Simulating");
@@ -353,15 +349,15 @@ void FCogAbilityWindow_Tasks::RenderTaskInfo(const UGameplayTask* Task)
ImGui::Text(Task->IsSimulating() ? "Yes" : "No");
//------------------------
// Debug
//------------------------
// Debug
//------------------------
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(TextColor, "Debug");
ImGui::TableNextColumn();
ImGui::PushTextWrapPos(FCogWindowWidgets::GetFontWidth() * 80);
ImGui::Text(StringCast<ANSICHAR>(*Task->GetDebugString()).Get());
ImGui::PopTextWrapPos();
ImGui::Text("%s", StringCast<ANSICHAR>(*Task->GetDebugString()).Get());
ImGui::PopTextWrapPos();
ImGui::EndTable();
}
@@ -383,7 +379,7 @@ void FCogAbilityWindow_Tasks::RenderTaskOwner(const UGameplayTask* Task)
OwnerName = GetNameSafe(Object);
}
ImGui::Text(StringCast<ANSICHAR>(*OwnerName).Get());
ImGui::Text("%s", StringCast<ANSICHAR>(*OwnerName).Get());
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,24 @@
#pragma once
#include "CoreMinimal.h"
#include "CogEngineDataAsset.h"
#include "GameplayEffect.h"
#include "CogAbilityCheat_Execution_ApplyEffect.generated.h"
//--------------------------------------------------------------------------------------------------------------------------
UCLASS(DisplayName = "Apply Effect")
class COGABILITY_API UCogAbilityCheat_Execution_ApplyEffect
: public UCogEngineCheat_Execution
{
GENERATED_BODY()
public:
void Execute_Implementation(const AActor* Instigator, const TArray<AActor*>& Targets) const override;
ECogEngineCheat_ActiveState IsActiveOnTargets_Implementation(const TArray<AActor*>& Targets) const override;
virtual bool GetColor(const FCogWindow& InCallingWindow, FLinearColor& OutColor) const override;
UPROPERTY(EditAnywhere)
TSubclassOf<UGameplayEffect> Effect;
};
@@ -73,9 +73,9 @@ protected:
virtual void DeactivateAbility(UAbilitySystemComponent& AbilitySystemComponent, FGameplayAbilitySpec& Spec);
virtual void OpenAbility(const FGameplayAbilitySpecHandle& Handle);
virtual void OpenAbilityDetails(const FGameplayAbilitySpecHandle& Handle);
virtual void CloseAbility(const FGameplayAbilitySpecHandle& Handle);
virtual void CloseAbilityDetails(const FGameplayAbilitySpecHandle& Handle);
virtual ImVec4 GetAbilityColor(const UAbilitySystemComponent& AbilitySystemComponent, FGameplayAbilitySpec& Spec);
@@ -2,13 +2,14 @@
#include "CoreMinimal.h"
#include "CogCommonConfig.h"
#include "AttributeSet.h"
#include "CogWindow.h"
#include "CogAbilityWindow_Attributes.generated.h"
class UAttributeSet;
class UAbilitySystemComponent;
class UCogAbilityConfig_Attributes;
class UCogAbilityConfig_Alignment;
struct FGameplayAttribute;
struct FModifierSpec;
struct FGameplayModifierInfo;
@@ -27,9 +28,21 @@ protected:
virtual void RenderHelp() override;
virtual void RenderTick(float DeltaTime);
virtual void RenderContent() override;
virtual void DrawAttributeInfo(const UAbilitySystemComponent& AbilitySystemComponent, const FGameplayAttribute& Attribute);
virtual void RenderAttributeDetails(const UAbilitySystemComponent& AbilitySystemComponent, const char* AttributeSetName, const FGameplayAttribute& Attribute, bool IsForTooltip);
virtual void RenderOpenAttributes();
virtual void FormatAttributeSetName(FString& AttributeSetName);
virtual void OpenAttributeDetails(const FGameplayAttribute& InAttribute);
virtual void CloseAttributeDetails(const FGameplayAttribute& InAttribute);
virtual void RenderAttributeContextMenu(UAbilitySystemComponent& AbilitySystemComponent, const FGameplayAttribute& InAttribute, int Index);
private:
@@ -38,6 +51,8 @@ private:
TObjectPtr<UCogAbilityConfig_Attributes> Config = nullptr;
TObjectPtr<UCogAbilityConfig_Alignment> AlignmentConfig = nullptr;
TArray<FGameplayAttribute> OpenedAttributes;
};
//--------------------------------------------------------------------------------------------------------------------------
@@ -60,6 +75,15 @@ public:
UPROPERTY(Config)
bool ShowOnlyModified = false;
UPROPERTY(Config)
FString AttributeSetPrefixes;
UPROPERTY(Config)
FVector4f CategoryColor = FVector4f(1.0f, 1.0f, 1.0f, 0.5f);
UPROPERTY(Config)
FVector4f AttributeSetColor = FVector4f(1.0f, 1.0f, 1.0f, 0.5f);
virtual void Reset() override
{
Super::Reset();
@@ -68,5 +92,8 @@ public:
GroupByAttributeSet = false;
GroupByCategory = false;
ShowOnlyModified = false;
AttributeSetPrefixes = FString();
CategoryColor = FVector4f(1.0f, 1.0f, 1.0f, 0.5f);
AttributeSetColor = FVector4f(1.0f, 1.0f, 1.0f, 0.5f);
}
};
@@ -31,10 +31,13 @@ protected:
virtual void RenderContent() override;
virtual void TryReapplyCheats();
APawn* GetCheatInstigator();
virtual bool AddCheat(AActor* ControlledActor, AActor* TargetActor, const FCogAbilityCheat& CheatEffect, bool IsPersistent);
virtual void RequestCheat(AActor* ControlledActor, AActor* TargetActor, const FCogAbilityCheat& CheatEffect);
virtual void RequestCheat(AActor* ControlledActor, AActor* SelectedActor, const FCogAbilityCheat& Cheat, bool ApplyToEnemies, bool ApplyToAllies, bool ApplyToControlled);
virtual const FCogAbilityCheat* FindCheatByName(const FString& CheatName);
TObjectPtr<const UCogAbilityDataAsset> Asset = nullptr;
@@ -32,13 +32,13 @@ protected:
virtual void RenderContent() override;
virtual void RenderTick(float DetlaTime) override;
virtual void RenderTick(float DeltaTime) override;
virtual void ResetConfig() override;
virtual void RenderEffectsTable();
virtual void RenderEffectRow(const UAbilitySystemComponent& AbilitySystemComponent, const FActiveGameplayEffectHandle& ActiveHandle, int32 Index, int32& Selected);
virtual void RenderEffectRow(UAbilitySystemComponent& AbilitySystemComponent, const FActiveGameplayEffectHandle& ActiveHandle, int32 Index, int32& Selected);
virtual void RenderEffectInfo(const UAbilitySystemComponent& AbilitySystemComponent, const FActiveGameplayEffect& ActiveEffect, const UGameplayEffect& Effect);
@@ -2,7 +2,6 @@
#include "CogAbilityWindow_Abilities.h"
#include "CogAbilityWindow_Attributes.h"
#include "CogAbilityWindow_Cheats.h"
#include "CogAbilityWindow_Effects.h"
#include "CogAbilityWindow_Pools.h"
#include "CogAbilityWindow_Tags.h"
@@ -10,6 +9,7 @@
#include "CogAbilityWindow_Tweaks.h"
#include "CogAIWindow_BehaviorTree.h"
#include "CogAIWindow_Blackboard.h"
#include "CogEngineWindow_Cheats.h"
#include "CogEngineWindow_CollisionTester.h"
#include "CogEngineWindow_CollisionViewer.h"
#include "CogEngineWindow_CommandBindings.h"
@@ -18,6 +18,7 @@
#include "CogEngineWindow_Inspector.h"
#include "CogEngineWindow_LogCategories.h"
#include "CogEngineWindow_Metrics.h"
#include "CogEngineWindow_NetImgui.h"
#include "CogEngineWindow_NetEmulation.h"
#include "CogEngineWindow_OutputLog.h"
#include "CogEngineWindow_Plots.h"
@@ -65,11 +66,13 @@ void Cog::AddAllWindows(UCogWindowManager& CogWindowManager)
CogWindowManager.AddWindow<FCogEngineWindow_LogCategories>("Engine.Log Categories");
CogWindowManager.AddWindow<FCogEngineWindow_Metrics>("Engine.Metrics");
CogWindowManager.AddWindow<FCogEngineWindow_NetEmulation>("Engine.Net Emulation");
CogWindowManager.AddWindow<FCogEngineWindow_OutputLog>("Engine.Output Log");
CogWindowManager.AddWindow<FCogEngineWindow_NetImgui>("Engine.Net ImGui");
CogWindowManager.AddWindow<FCogEngineWindow_Metrics>("Engine.Metrics");
CogWindowManager.AddWindow<FCogEngineWindow_OutputLog>("Engine.Output Log");
CogWindowManager.AddWindow<FCogEngineWindow_Plots>("Engine.Plots");
@@ -100,7 +103,9 @@ void Cog::AddAllWindows(UCogWindowManager& CogWindowManager)
CogWindowManager.AddWindow<FCogAbilityWindow_BlockedTags>("Gameplay.Blocking Tags");
CogWindowManager.AddWindow<FCogAbilityWindow_Cheats>("Gameplay.Cheats");
//CogWindowManager.AddWindow<FCogEngineWindow_Cheats>("Gameplay.Cheats");
CogWindowManager.AddWindow<FCogEngineWindow_Cheats>("Gameplay.Cheats");
CogWindowManager.AddWindow<FCogAbilityWindow_Effects>("Gameplay.Effects");
@@ -1,9 +1,9 @@
#include "CogInjectActionInfo.h"
#include "CogInputActionInfo.h"
#include "EnhancedInputSubsystems.h"
//--------------------------------------------------------------------------------------------------------------------------
void FCogInjectActionInfo::Inject(UEnhancedInputLocalPlayerSubsystem& EnhancedInput, bool IsTimeToRepeat)
void FCogInputActionInfo::Inject(UEnhancedInputLocalPlayerSubsystem& EnhancedInput, bool IsTimeToRepeat)
{
if (Action == nullptr)
{
@@ -1,12 +1,11 @@
#include "CogInputWindow_Actions.h"
#include "CogInputDataAsset.h"
#include "CogWindowHelper.h"
#include "CogWindowWidgets.h"
#include "Engine/LocalPlayer.h"
#include "Engine/World.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/Pawn.h"
#include "imgui_internal.h"
#include "InputAction.h"
#include "InputMappingContext.h"
@@ -17,7 +16,6 @@ void FCogInputWindow_Actions::Initialize()
bHasMenu = true;
Asset = GetAsset<UCogInputDataAsset>();
Config = GetConfig<UCogInputConfig_Actions>();
}
@@ -26,10 +24,7 @@ void FCogInputWindow_Actions::RenderHelp()
{
ImGui::Text(
"This window displays the current state of each Input Action. "
"It can also be used to inject inputs to help debugging. "
"The input action are read from a Input Mapping Context defined in '%s' data asset. "
, TCHAR_TO_ANSI(*GetNameSafe(Asset.Get()))
);
"It can also be used to inject inputs to help debugging.");
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -45,12 +40,6 @@ void FCogInputWindow_Actions::RenderContent()
{
Super::RenderContent();
if (Asset == nullptr)
{
ImGui::TextDisabled("Invalid Asset");
return;
}
ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
if (LocalPlayer == nullptr)
{
@@ -65,122 +54,226 @@ void FCogInputWindow_Actions::RenderContent()
return;
}
if(EnhancedInputSubsystem->GetPlayerInput() == nullptr)
UEnhancedPlayerInput* PlayerInput = EnhancedInputSubsystem->GetPlayerInput();
if(PlayerInput == nullptr)
{
ImGui::TextDisabled("No Player Input");
return;
}
const bool IsControlDown = (ImGui::GetCurrentContext()->IO.KeyMods & ImGuiMod_Ctrl) != 0;
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("Options"))
{
ImGui::Checkbox("Show Header", &Config->ShowHeader);
ImGui::SliderFloat("##Repeat", &Config->RepeatPeriod, 0.1f, 10.0f, "Repeat: %0.1fs");
ImGui::EndMenu();
}
if (ImGui::MenuItem("Reset"))
{
for (FCogInjectActionInfo& ActionInfo : Actions)
for (FCogInputMappingContextInfo Mapping : Mappings)
{
ActionInfo.Reset();
for (FCogInputActionInfo& Action : Mapping.Actions)
{
Action.Reset();
}
}
}
ImGui::EndMenuBar();
}
if (Actions.Num() == 0)
//--------------------------------------------------------------------------
// Get all the existing mapping. This is not publicly exposed so we rob it.
//--------------------------------------------------------------------------
TArray<FCogInputMappingContextInfo, TInlineAllocator<8>> AllAppliedMappings;
if (const CogInputMappingContextMap* AppliedMappingContexts = &PRIVATE_ACCESS_PTR(PlayerInput, UEnhancedPlayerInput_AppliedInputContexts))
{
for (TObjectPtr<const UInputMappingContext> MappingContext : Asset->MappingContexts)
for (auto& kv : *AppliedMappingContexts)
{
for (const FEnhancedActionKeyMapping& Mapping : MappingContext->GetMappings())
{
if (Mapping.Action != nullptr && Actions.ContainsByPredicate([&Mapping](const FCogInjectActionInfo& ActionInfo) { return Mapping.Action == ActionInfo.Action; }) == false)
{
FCogInjectActionInfo& ActionInfo = Actions.AddDefaulted_GetRef();
ActionInfo.Action = Mapping.Action;
}
}
FCogInputMappingContextInfo& MappingInfo = AllAppliedMappings.AddDefaulted_GetRef();
MappingInfo.MappingContext = kv.Key;
MappingInfo.Priority = kv.Value;
}
Actions.Sort([](const FCogInjectActionInfo& Lhs, const FCogInjectActionInfo& Rhs)
{
return GetNameSafe(Lhs.Action).Compare(GetNameSafe(Rhs.Action)) < 0;
});
}
if (ImGui::BeginTable("Actions", 3, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_NoBordersInBodyUntilResize))
//--------------------------------------------------------------------------
// Remove mappings that are not present anymore.
//--------------------------------------------------------------------------
for (int32 i = Mappings.Num() - 1; i >= 0; i--)
{
ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Inject", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableHeadersRow();
int Index = 0;
for (FCogInjectActionInfo& ActionInfo : Actions)
{
ImGui::PushID(Index);
const auto ActionName = StringCast<ANSICHAR>(*ActionInfo.Action->GetName());
FInputActionValue ActionValue = EnhancedInputSubsystem->GetPlayerInput()->GetActionValue(ActionInfo.Action);
switch (ActionInfo.Action->ValueType)
const FCogInputMappingContextInfo& MappingInfo = Mappings[i];
if (AllAppliedMappings.ContainsByPredicate([&MappingInfo](const FCogInputMappingContextInfo& m)
{
case EInputActionValueType::Boolean:
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%s", ActionName.Get());
return m.MappingContext == MappingInfo.MappingContext;
}))
{
continue;
}
const ImVec4 ActiveColor(1, 0.8f, 0, 1);
const ImVec4 InnactiveColor(0.3f, 0.3f, 0.3f, 1);
const ImVec2 ButtonSize(FCogWindowWidgets::GetFontWidth() * 10, 0);
Mappings.RemoveAt(i);
}
ImGui::TableNextColumn();
ImGui::BeginDisabled();
bool Value = ActionValue.Get<bool>();
FCogWindowWidgets::ToggleButton(&Value, "Pressed##Value", "Released##Value", ActiveColor, InnactiveColor, ButtonSize);
ImGui::EndDisabled();
//--------------------------------------------------------------------------
// Add mappings that were not yet added.
//--------------------------------------------------------------------------
for (FCogInputMappingContextInfo& MappingInfo : AllAppliedMappings)
{
//----------------------------------------------------
// The mapping was already added
//----------------------------------------------------
if (Mappings.ContainsByPredicate([&MappingInfo](const FCogInputMappingContextInfo& m) { return m.MappingContext == MappingInfo.MappingContext; }))
{
continue;
}
ImGui::TableNextColumn();
FCogWindowWidgets::ToggleButton(&ActionInfo.bPressed, "Pressed##Inject", "Released##Inject", ActiveColor, InnactiveColor, ButtonSize);
ImGui::SameLine();
FCogWindowWidgets::ToggleButton(&ActionInfo.bRepeat, "Repeat", "Repeat", ActiveColor, InnactiveColor, ButtonSize);
break;
}
//----------------------------------------------------
// Add the mapping
//----------------------------------------------------
FCogInputMappingContextInfo& NewMappingInfo = Mappings.AddDefaulted_GetRef();
NewMappingInfo.MappingContext = MappingInfo.MappingContext;
NewMappingInfo.Priority = MappingInfo.Priority;
case EInputActionValueType::Axis1D:
{
const float Value = ActionValue.Get<float>();
DrawAxis("%s", ActionName.Get(), Value, &ActionInfo.X);
break;
}
case EInputActionValueType::Axis2D:
{
const FVector2D Value = ActionValue.Get<FVector2D>();
DrawAxis("%s X", ActionName.Get(), Value.X, &ActionInfo.X);
DrawAxis("%s Y", ActionName.Get(), Value.Y, &ActionInfo.Y);
break;
}
case EInputActionValueType::Axis3D:
{
const FVector Value = ActionValue.Get<FVector>();
DrawAxis("%s X", ActionName.Get(), Value.X, &ActionInfo.X);
DrawAxis("%s Y", ActionName.Get(), Value.Y, &ActionInfo.Y);
DrawAxis("%s Z", ActionName.Get(), Value.Z, &ActionInfo.Z);
break;
}
//----------------------------------------------------
// Add all the mapping actions
//----------------------------------------------------
for (const FEnhancedActionKeyMapping& Mapping : NewMappingInfo.MappingContext->GetMappings())
{
TObjectPtr<const UInputAction> Action = Mapping.Action;
if (Action == nullptr)
{
continue;
}
ImGui::PopID();
Index++;
//----------------------------------------------------
// Actions are present multiple time, as many times
// as they are mapped to key. We only want to display
// the same action once per mapping
//----------------------------------------------------
if (NewMappingInfo.Actions.ContainsByPredicate([&Action](const FCogInputActionInfo& m){ return m.Action == Action; }))
{
continue;
}
FCogInputActionInfo& ActionInfo = NewMappingInfo.Actions.AddDefaulted_GetRef();
ActionInfo.Action = Mapping.Action;
}
Mappings.Sort([](const FCogInputMappingContextInfo& Lhs, const FCogInputMappingContextInfo& Rhs)
{
return Lhs.Priority < Rhs.Priority;
});
}
int Index = 0;
for (FCogInputMappingContextInfo& Mapping : Mappings)
{
const auto MappingName = StringCast<ANSICHAR>(*Mapping.MappingContext.GetName());
const bool Open = ImGui::CollapsingHeader(MappingName.Get(), ImGuiTreeNodeFlags_DefaultOpen);
if (Open && ImGui::BeginTable("Actions", 3, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_NoBordersInBodyUntilResize))
{
ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Current", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Inject", ImGuiTableColumnFlags_WidthStretch);
if (Config->ShowHeader)
{
ImGui::TableHeadersRow();
}
for (FCogInputActionInfo& ActionInfo : Mapping.Actions)
{
ImGui::PushID(Index);
const auto ActionName = StringCast<ANSICHAR>(*ActionInfo.Action->GetName());
FInputActionValue ActionValue = PlayerInput->GetActionValue(ActionInfo.Action);
switch (ActionInfo.Action->ValueType)
{
case EInputActionValueType::Boolean:
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%s", ActionName.Get());
ImGui::TableNextColumn();
ImGui::BeginDisabled();
bool Value = ActionValue.Get<bool>();
FCogWindowWidgets::PushBackColor(ImVec4(0.8f, 0.8f, 0.8f, 1));
ImGui::Checkbox("##Current", &Value);
FCogWindowWidgets::PopBackColor();
ImGui::EndDisabled();
ImGui::TableNextColumn();
ECheckBoxState State = ActionInfo.bRepeat ? ECheckBoxState::Undetermined : ActionInfo.bPressed ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
if (FCogWindowWidgets::CheckBoxState("##Inject", State, false))
{
if (IsControlDown)
{
ActionInfo.bRepeat = !ActionInfo.bRepeat;
ActionInfo.bPressed = false;
}
else
{
ActionInfo.bPressed = !ActionInfo.bPressed;
ActionInfo.bRepeat = false;
}
}
if (ImGui::BeginPopupContextItem())
{
ImGui::Checkbox("Repeat", &ActionInfo.bRepeat);
ImGui::EndPopup();
}
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort))
{
ImGui::BeginTooltip();
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, IsControlDown ? 1.0f : 0.5f), "Repeat [CTRL]");
ImGui::EndTooltip();
}
break;
}
case EInputActionValueType::Axis1D:
{
const float Value = ActionValue.Get<float>();
DrawAxis("%s", ActionName.Get(), Value, &ActionInfo.X);
break;
}
case EInputActionValueType::Axis2D:
{
const FVector2D Value = ActionValue.Get<FVector2D>();
DrawAxis("%s X", ActionName.Get(), Value.X, &ActionInfo.X);
DrawAxis("%s Y", ActionName.Get(), Value.Y, &ActionInfo.Y);
break;
}
case EInputActionValueType::Axis3D:
{
const FVector Value = ActionValue.Get<FVector>();
DrawAxis("%s X", ActionName.Get(), Value.X, &ActionInfo.X);
DrawAxis("%s Y", ActionName.Get(), Value.Y, &ActionInfo.Y);
DrawAxis("%s Z", ActionName.Get(), Value.Z, &ActionInfo.Z);
break;
}
}
ImGui::PopID();
Index++;
}
ImGui::EndTable();
}
ImGui::EndTable();
}
}
@@ -214,9 +307,12 @@ void FCogInputWindow_Actions::RenderTick(float DeltaSeconds)
IsTimeToRepeat = true;
}
for (FCogInjectActionInfo& ActionInfo : Actions)
for (FCogInputMappingContextInfo Mapping : Mappings)
{
ActionInfo.Inject(*EnhancedInput, IsTimeToRepeat);
for (FCogInputActionInfo& ActionInfo : Mapping.Actions)
{
ActionInfo.Inject(*EnhancedInput, IsTimeToRepeat);
}
}
}
@@ -232,7 +328,9 @@ void FCogInputWindow_Actions::DrawAxis(const char* Format, const char* ActionNam
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(-1);
ImGui::BeginDisabled();
FCogWindowWidgets::PushBackColor(ImVec4(0.8f, 0.8f, 0.8f, 1));
ImGui::SliderFloat("##Value", &CurrentValue, -1.0f, 1.0f, "%0.2f");
FCogWindowWidgets::PopBackColor();
ImGui::EndDisabled();
ImGui::TableNextColumn();
@@ -1,7 +1,6 @@
#include "CogInputWindow_Gamepad.h"
#include "CogImguiHelper.h"
#include "CogInputDataAsset.h"
#include "CogWindowWidgets.h"
#include "Engine/LocalPlayer.h"
#include "Engine/World.h"
@@ -14,7 +13,6 @@ void FCogInputWindow_Gamepad::Initialize()
{
Super::Initialize();
Asset = GetAsset<UCogInputDataAsset>();
Config = GetConfig<UCogInputConfig_Gamepad>();
}
@@ -43,7 +41,7 @@ void FCogInputWindow_Gamepad::ResetConfig()
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogInputWindow_Gamepad::RenderButtonContextMenu(const FKey& Key, FCogInjectActionInfo* ActionInfoButton)
void FCogInputWindow_Gamepad::RenderButtonContextMenu(const FKey& Key, FCogInputActionInfo* ActionInfoButton)
{
if (ActionInfoButton != nullptr)
{
@@ -66,7 +64,7 @@ void FCogInputWindow_Gamepad::RenderButtonContextMenu(const FKey& Key, FCogInjec
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogInputWindow_Gamepad::RenderStickContextMenu(const FKey& Key, FCogInjectActionInfo* ActionInfo2D, bool& InvertY, float& Sensitivity)
void FCogInputWindow_Gamepad::RenderStickContextMenu(const FKey& Key, FCogInputActionInfo* ActionInfo2D, bool& InvertY, float& Sensitivity)
{
if (ActionInfo2D != nullptr)
{
@@ -83,7 +81,7 @@ void FCogInputWindow_Gamepad::RenderStickContextMenu(const FKey& Key, FCogInject
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogInputWindow_Gamepad::OnButtonClicked(FCogInjectActionInfo* ActionInfo)
void FCogInputWindow_Gamepad::OnButtonClicked(FCogInputActionInfo* ActionInfo)
{
if (ActionInfo == nullptr)
{
@@ -116,7 +114,7 @@ void FCogInputWindow_Gamepad::RenderButton(const FKey& Key, const ImVec2& Relati
const ImVec2 Position = (CanvasMin + CanvasSize * RelativePosition) - Alignment * Size;
bool IsPressed = false;
FCogInjectActionInfo* ActionInfo = Actions.Find(Key);
FCogInputActionInfo* ActionInfo = Actions.Find(Key);
if (ActionInfo != nullptr)
{
if (ActionInfo->Action != nullptr)
@@ -169,8 +167,8 @@ void FCogInputWindow_Gamepad::RenderStick(const FKey& Key2D, const FKey& KeyBool
{
ImGui::PushID((void*)(&Key2D));
FCogInjectActionInfo* ActionInfoBool = Actions.Find(KeyBool);
FCogInjectActionInfo* ActionInfo2D = Actions.Find(Key2D);
FCogInputActionInfo* ActionInfoBool = Actions.Find(KeyBool);
FCogInputActionInfo* ActionInfo2D = Actions.Find(Key2D);
FVector Value = Input->GetRawVectorKeyValue(Key2D);
if (ActionInfo2D != nullptr && ActionInfo2D->Action != nullptr)
@@ -288,48 +286,44 @@ void FCogInputWindow_Gamepad::RenderContent()
return;
}
if (Asset == nullptr)
if (Actions.Num() == 0)
{
ImGui::TextDisabled("Invalid Asset");
}
else
{
if (Actions.Num() == 0)
{
Actions.FindOrAdd(EKeys:: Gamepad_Left2D);
Actions.FindOrAdd(EKeys:: Gamepad_LeftX);
Actions.FindOrAdd(EKeys:: Gamepad_LeftY);
Actions.FindOrAdd(EKeys:: Gamepad_Right2D);
Actions.FindOrAdd(EKeys:: Gamepad_RightX);
Actions.FindOrAdd(EKeys:: Gamepad_RightY);
Actions.FindOrAdd(EKeys:: Gamepad_LeftTriggerAxis);
Actions.FindOrAdd(EKeys:: Gamepad_RightTriggerAxis);
Actions.FindOrAdd(EKeys:: Gamepad_LeftThumbstick);
Actions.FindOrAdd(EKeys:: Gamepad_RightThumbstick);
Actions.FindOrAdd(EKeys:: Gamepad_Special_Left);
Actions.FindOrAdd(EKeys:: Gamepad_Special_Left_X);
Actions.FindOrAdd(EKeys:: Gamepad_Special_Left_Y);
Actions.FindOrAdd(EKeys:: Gamepad_Special_Right);
Actions.FindOrAdd(EKeys:: Gamepad_FaceButton_Bottom);
Actions.FindOrAdd(EKeys:: Gamepad_FaceButton_Right);
Actions.FindOrAdd(EKeys:: Gamepad_FaceButton_Left);
Actions.FindOrAdd(EKeys:: Gamepad_FaceButton_Top);
Actions.FindOrAdd(EKeys:: Gamepad_LeftShoulder);
Actions.FindOrAdd(EKeys:: Gamepad_RightShoulder);
Actions.FindOrAdd(EKeys:: Gamepad_LeftTrigger);
Actions.FindOrAdd(EKeys:: Gamepad_RightTrigger);
Actions.FindOrAdd(EKeys:: Gamepad_DPad_Up);
Actions.FindOrAdd(EKeys:: Gamepad_DPad_Down);
Actions.FindOrAdd(EKeys:: Gamepad_DPad_Right);
Actions.FindOrAdd(EKeys:: Gamepad_DPad_Left);
Actions.FindOrAdd(EKeys:: Gamepad_Left2D);
Actions.FindOrAdd(EKeys:: Gamepad_LeftX);
Actions.FindOrAdd(EKeys:: Gamepad_LeftY);
Actions.FindOrAdd(EKeys:: Gamepad_Right2D);
Actions.FindOrAdd(EKeys:: Gamepad_RightX);
Actions.FindOrAdd(EKeys:: Gamepad_RightY);
Actions.FindOrAdd(EKeys:: Gamepad_LeftTriggerAxis);
Actions.FindOrAdd(EKeys:: Gamepad_RightTriggerAxis);
Actions.FindOrAdd(EKeys:: Gamepad_LeftThumbstick);
Actions.FindOrAdd(EKeys:: Gamepad_RightThumbstick);
Actions.FindOrAdd(EKeys:: Gamepad_Special_Left);
Actions.FindOrAdd(EKeys:: Gamepad_Special_Left_X);
Actions.FindOrAdd(EKeys:: Gamepad_Special_Left_Y);
Actions.FindOrAdd(EKeys:: Gamepad_Special_Right);
Actions.FindOrAdd(EKeys:: Gamepad_FaceButton_Bottom);
Actions.FindOrAdd(EKeys:: Gamepad_FaceButton_Right);
Actions.FindOrAdd(EKeys:: Gamepad_FaceButton_Left);
Actions.FindOrAdd(EKeys:: Gamepad_FaceButton_Top);
Actions.FindOrAdd(EKeys:: Gamepad_LeftShoulder);
Actions.FindOrAdd(EKeys:: Gamepad_RightShoulder);
Actions.FindOrAdd(EKeys:: Gamepad_LeftTrigger);
Actions.FindOrAdd(EKeys:: Gamepad_RightTrigger);
Actions.FindOrAdd(EKeys:: Gamepad_DPad_Up);
Actions.FindOrAdd(EKeys:: Gamepad_DPad_Down);
Actions.FindOrAdd(EKeys:: Gamepad_DPad_Right);
Actions.FindOrAdd(EKeys:: Gamepad_DPad_Left);
for (TObjectPtr<const UInputMappingContext> MappingContext : Asset->MappingContexts)
if (const CogInputMappingContextMap* AppliedMappingContexts = &PRIVATE_ACCESS_PTR(Input, UEnhancedPlayerInput_AppliedInputContexts))
{
for (auto& kv : *AppliedMappingContexts)
{
for (const FEnhancedActionKeyMapping& Mapping : MappingContext->GetMappings())
for (const FEnhancedActionKeyMapping& Mapping : kv.Key->GetMappings())
{
if (Mapping.Action != nullptr)
{
FCogInjectActionInfo& ActionInfo = Actions.FindOrAdd(Mapping.Key);
FCogInputActionInfo& ActionInfo = Actions.FindOrAdd(Mapping.Key);
ActionInfo.Action = Mapping.Action;
}
}
@@ -337,7 +331,6 @@ void FCogInputWindow_Gamepad::RenderContent()
}
}
constexpr float AspectRatio = 0.55f;
constexpr float StickAmplitude = 0.04f;
constexpr float StickRadius = 0.08f;
@@ -345,8 +338,8 @@ void FCogInputWindow_Gamepad::RenderContent()
constexpr float GamepadRound = 0.15f;
constexpr float HandleWidth = 0.26f;
constexpr ImVec2 DPadButtonSize(0.08f, 0.08f);
constexpr float DpadButtonDistance = 0.075f;
constexpr float DpadButtonRounding = 0.1f;
constexpr float DPadButtonDistance = 0.075f;
constexpr float DPadButtonRounding = 0.1f;
constexpr ImVec2 SpecialButtonSize(0.08f, 0.04f);
constexpr float SpecialButtonRound = 0.05f;
constexpr float SpecialButtonDistance = 0.15f;
@@ -420,19 +413,19 @@ void FCogInputWindow_Gamepad::RenderContent()
// DPad Buttons
//------------------------------
constexpr ImVec2 DPad_Pos(0.15f, 0.44f);
RenderButton(EKeys::Gamepad_DPad_Up, DPad_Pos + ImVec2(0.0f, -DpadButtonDistance / AspectRatio), DPadButtonSize, ImVec2(0.5f, 0.5f), DpadButtonRounding);
RenderButton(EKeys::Gamepad_DPad_Down, DPad_Pos + ImVec2(0.0f, DpadButtonDistance / AspectRatio), DPadButtonSize, ImVec2(0.5f, 0.5f), DpadButtonRounding);
RenderButton(EKeys::Gamepad_DPad_Left, DPad_Pos + ImVec2(-DpadButtonDistance, 0.0f), DPadButtonSize, ImVec2(0.5f, 0.5f), DpadButtonRounding);
RenderButton(EKeys::Gamepad_DPad_Right, DPad_Pos + ImVec2(DpadButtonDistance, 0.0f), DPadButtonSize, ImVec2(0.5f, 0.5f), DpadButtonRounding);
RenderButton(EKeys::Gamepad_DPad_Up, DPad_Pos + ImVec2(0.0f, -DPadButtonDistance / AspectRatio), DPadButtonSize, ImVec2(0.5f, 0.5f), DPadButtonRounding);
RenderButton(EKeys::Gamepad_DPad_Down, DPad_Pos + ImVec2(0.0f, DPadButtonDistance / AspectRatio), DPadButtonSize, ImVec2(0.5f, 0.5f), DPadButtonRounding);
RenderButton(EKeys::Gamepad_DPad_Left, DPad_Pos + ImVec2(-DPadButtonDistance, 0.0f), DPadButtonSize, ImVec2(0.5f, 0.5f), DPadButtonRounding);
RenderButton(EKeys::Gamepad_DPad_Right, DPad_Pos + ImVec2(DPadButtonDistance, 0.0f), DPadButtonSize, ImVec2(0.5f, 0.5f), DPadButtonRounding);
//------------------------------
// Face Buttons
//------------------------------
constexpr ImVec2 Face_Pos(1.0f - 0.15f, 0.44f);
RenderButton(EKeys::Gamepad_FaceButton_Top, Face_Pos + ImVec2(0.0f, -DpadButtonDistance / AspectRatio), DPadButtonSize, ImVec2(0.5f, 0.5f), DpadButtonRounding);
RenderButton(EKeys::Gamepad_FaceButton_Bottom, Face_Pos + ImVec2(0.0f, DpadButtonDistance / AspectRatio), DPadButtonSize, ImVec2(0.5f, 0.5f), DpadButtonRounding);
RenderButton(EKeys::Gamepad_FaceButton_Left, Face_Pos + ImVec2(-DpadButtonDistance, 0.0f), DPadButtonSize, ImVec2(0.5f, 0.5f), DpadButtonRounding);
RenderButton(EKeys::Gamepad_FaceButton_Right, Face_Pos + ImVec2(DpadButtonDistance, 0.0f), DPadButtonSize, ImVec2(0.5f, 0.5f), DpadButtonRounding);
RenderButton(EKeys::Gamepad_FaceButton_Top, Face_Pos + ImVec2(0.0f, -DPadButtonDistance / AspectRatio), DPadButtonSize, ImVec2(0.5f, 0.5f), DPadButtonRounding);
RenderButton(EKeys::Gamepad_FaceButton_Bottom, Face_Pos + ImVec2(0.0f, DPadButtonDistance / AspectRatio), DPadButtonSize, ImVec2(0.5f, 0.5f), DPadButtonRounding);
RenderButton(EKeys::Gamepad_FaceButton_Left, Face_Pos + ImVec2(-DPadButtonDistance, 0.0f), DPadButtonSize, ImVec2(0.5f, 0.5f), DPadButtonRounding);
RenderButton(EKeys::Gamepad_FaceButton_Right, Face_Pos + ImVec2(DPadButtonDistance, 0.0f), DPadButtonSize, ImVec2(0.5f, 0.5f), DPadButtonRounding);
//------------------------------
// Special Buttons
@@ -475,7 +468,7 @@ void FCogInputWindow_Gamepad::RenderTick(float DeltaSeconds)
for (auto& Entry : Actions)
{
FCogInjectActionInfo& ActionInfo = Entry.Value;
FCogInputActionInfo& ActionInfo = Entry.Value;
ActionInfo.Inject(*EnhancedInput, IsTimeToRepeat);
}
}
@@ -1,13 +1,19 @@
#pragma once
#include "CoreMinimal.h"
#include "CogDebugRob.h"
#include "EnhancedPlayerInput.h"
class UInputAction;
class UInputMappingContext;
class UEnhancedInputLocalPlayerSubsystem;
struct FCogInjectActionInfo
typedef TMap<TObjectPtr<const UInputMappingContext>, int32> CogInputMappingContextMap;
DEFINE_PRIVATE_ACCESSOR_VARIABLE(UEnhancedPlayerInput_AppliedInputContexts, UEnhancedPlayerInput, CogInputMappingContextMap, AppliedInputContexts);
struct FCogInputActionInfo
{
const UInputAction* Action = nullptr;
TObjectPtr<const UInputAction> Action;
bool bPressed = false;
@@ -36,3 +42,12 @@ struct FCogInjectActionInfo
void Inject(UEnhancedInputLocalPlayerSubsystem& EnhancedInput, bool IsTimeToRepeat);
};
struct FCogInputMappingContextInfo
{
TObjectPtr<const UInputMappingContext> MappingContext;
int32 Priority = 0;
TArray<FCogInputActionInfo> Actions;
};
@@ -1,20 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "CogInputDataAsset.generated.h"
class UInputAction;
class UInputMappingContext;
UCLASS(Blueprintable)
class COGINPUT_API UCogInputDataAsset : public UPrimaryDataAsset
{
GENERATED_BODY()
public:
UCogInputDataAsset() {}
UPROPERTY(EditAnywhere, Category="Cog")
TArray<TObjectPtr<const UInputMappingContext>> MappingContexts;
};
@@ -2,13 +2,12 @@
#include "CoreMinimal.h"
#include "CogCommonConfig.h"
#include "CogInjectActionInfo.h"
#include "CogInputActionInfo.h"
#include "CogWindow.h"
#include "CogInputWindow_Actions.generated.h"
class UInputAction;
class UCogInputConfig_Actions;
class UCogInputDataAsset;
//--------------------------------------------------------------------------------------------------------------------------
class COGINPUT_API FCogInputWindow_Actions : public FCogWindow
@@ -35,11 +34,9 @@ private:
float RepeatTime = 0.0f;
TObjectPtr<const UCogInputDataAsset> Asset = nullptr;
TObjectPtr<UCogInputConfig_Actions> Config = nullptr;
TArray<FCogInjectActionInfo> Actions;
TArray<FCogInputMappingContextInfo> Mappings;
};
//--------------------------------------------------------------------------------------------------------------------------
@@ -53,10 +50,14 @@ public:
UPROPERTY(Config)
float RepeatPeriod = 0.5f;
UPROPERTY(Config)
bool ShowHeader = true;
virtual void Reset() override
{
Super::Reset();
RepeatPeriod = 0.5f;
ShowHeader = true;
}
};
@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "CogCommonConfig.h"
#include "CogInjectActionInfo.h"
#include "CogInputActionInfo.h"
#include "CogWindow.h"
#include "imgui.h"
#include "InputCoreTypes.h"
@@ -37,19 +37,17 @@ protected:
virtual void RenderStick(const FKey& Key2D, const FKey& KeyBool, bool& InvertY, float& Sensitivity, float Amplitude, const ImVec2& Position, float Radius);
virtual void OnButtonClicked(FCogInjectActionInfo* ActionInfo);
virtual void OnButtonClicked(FCogInputActionInfo* ActionInfo);
virtual void RenderMainContextMenu();
virtual void RenderButtonContextMenu(const FKey& Key, FCogInjectActionInfo* ActionInfoButton);
virtual void RenderButtonContextMenu(const FKey& Key, FCogInputActionInfo* ActionInfoButton);
virtual void RenderStickContextMenu(const FKey& Key, FCogInjectActionInfo* ActionInfo2D, bool& InvertY, float& Sensitivity);
TObjectPtr<const UCogInputDataAsset> Asset;
virtual void RenderStickContextMenu(const FKey& Key, FCogInputActionInfo* ActionInfo2D, bool& InvertY, float& Sensitivity);
TObjectPtr<UCogInputConfig_Gamepad> Config;
TMap<FKey, FCogInjectActionInfo> Actions;
TMap<FKey, FCogInputActionInfo> Actions;
UEnhancedPlayerInput* Input = nullptr;