Adds plugins
This commit is contained in:
4
Plugins/ElectronicNodes/Config/FilterPlugin.ini
Normal file
4
Plugins/ElectronicNodes/Config/FilterPlugin.ini
Normal file
@@ -0,0 +1,4 @@
|
||||
[FilterPlugin]
|
||||
/Config/
|
||||
/Resources/
|
||||
/Source/
|
||||
35
Plugins/ElectronicNodes/ElectronicNodes.uplugin
Normal file
35
Plugins/ElectronicNodes/ElectronicNodes.uplugin
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "3.5",
|
||||
"FriendlyName": "Electronic Nodes",
|
||||
"Description": "Improve the wire style of blueprints and materials editors.",
|
||||
"Category": "Editor",
|
||||
"CreatedBy": "Hugo Attal",
|
||||
"CreatedByURL": "https://twitter.com/HugoAttal",
|
||||
"DocsURL": "https://github.com/TheHerobrine/ElectronicNodes",
|
||||
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/5cb2a394d0c04e73891762be4cbd7216",
|
||||
"SupportURL": "https://forums.unrealengine.com/unreal-engine/marketplace/1647213-electronic-nodes-wiring-style-for-blueprints-and-materials-editors",
|
||||
"EngineVersion": "4.27.0",
|
||||
"CanContainContent": false,
|
||||
"Installed": true,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "ElectronicNodes",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "Default",
|
||||
"WhitelistPlatforms": [
|
||||
"Win64",
|
||||
"Win32",
|
||||
"Mac",
|
||||
"Linux"
|
||||
]
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "ControlRig",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Plugins/ElectronicNodes/Resources/Icon128.png
(Stored with Git LFS)
Normal file
BIN
Plugins/ElectronicNodes/Resources/Icon128.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@@ -0,0 +1,66 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
using System.IO;
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class ElectronicNodes : ModuleRules
|
||||
{
|
||||
public ElectronicNodes(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
string enginePath = Path.GetFullPath(Target.RelativeEnginePath);
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] { }
|
||||
);
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[]
|
||||
{
|
||||
enginePath + "Source/Editor/AnimationBlueprintEditor/Private/"
|
||||
}
|
||||
);
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core"
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"InputCore",
|
||||
"Projects",
|
||||
"UnrealEd",
|
||||
"GraphEditor",
|
||||
"BlueprintGraph",
|
||||
"AnimGraph",
|
||||
"AnimationBlueprintEditor",
|
||||
"ControlRig",
|
||||
"ControlRigDeveloper",
|
||||
"RigVM",
|
||||
"RigVMDeveloper",
|
||||
"AIGraph",
|
||||
"BehaviorTreeEditor",
|
||||
#if UE_4_26_OR_LATER
|
||||
"DeveloperSettings",
|
||||
#endif
|
||||
"EditorStyle",
|
||||
"WebBrowser"
|
||||
}
|
||||
);
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{ }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Framework/Commands/Commands.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "ENCommands"
|
||||
|
||||
class ENCommands : public TCommands<ENCommands>
|
||||
{
|
||||
public:
|
||||
ENCommands()
|
||||
: TCommands<ENCommands>(
|
||||
TEXT("ElectronicNodes"),
|
||||
FText::FromString("Electronic Nodes"),
|
||||
NAME_None,
|
||||
"ElectronicNodesStyle")
|
||||
{
|
||||
}
|
||||
|
||||
TSharedPtr<FUICommandInfo> ToggleMasterActivation;
|
||||
|
||||
virtual void RegisterCommands() override
|
||||
{
|
||||
UI_COMMAND(ToggleMasterActivation, "Toggle Master Activation", "Toggle activation of Electronic Nodes", EUserInterfaceActionType::Button, FInputChord());
|
||||
}
|
||||
};
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,591 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
#include "ENConnectionDrawingPolicy.h"
|
||||
#include "BlueprintEditorSettings.h"
|
||||
#include "ENPathDrawer.h"
|
||||
#include "SGraphPanel.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "MaterialGraph/MaterialGraphSchema.h"
|
||||
#include "Policies/ENAnimGraphConnectionDrawingPolicy.h"
|
||||
#include "Policies/ENBehaviorTreeConnectionDrawingPolicy.h"
|
||||
#include "Policies/ENControlRigConnectionDrawingPolicy.h"
|
||||
|
||||
|
||||
FConnectionDrawingPolicy* FENConnectionDrawingPolicyFactory::CreateConnectionPolicy(const class UEdGraphSchema* Schema, int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const class FSlateRect& InClippingRect, class FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const
|
||||
{
|
||||
const UElectronicNodesSettings& ElectronicNodesSettings = *GetDefault<UElectronicNodesSettings>();
|
||||
if (!ElectronicNodesSettings.MasterActivate)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const FName ClassName = Schema->GetClass()->GetFName();
|
||||
|
||||
if (ElectronicNodesSettings.DisplaySchemaName)
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("[EN] %s"), *ClassName.ToString());
|
||||
}
|
||||
|
||||
if (ElectronicNodesSettings.ActivateOnAnimation)
|
||||
{
|
||||
if (ClassName == "AnimationTransitionSchema")
|
||||
{
|
||||
return new FENConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
}
|
||||
|
||||
if (ClassName == "AnimationGraphSchema" || ClassName == "AnimationStateGraphSchema")
|
||||
{
|
||||
return new FENAnimGraphConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
}
|
||||
}
|
||||
|
||||
if (ElectronicNodesSettings.ActivateOnVoxelPlugin && ClassName == "VoxelGraphSchema")
|
||||
{
|
||||
return new FENConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
}
|
||||
|
||||
if (ElectronicNodesSettings.ActivateOnNiagara && ClassName == "EdGraphSchema_Niagara")
|
||||
{
|
||||
return new FENConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
}
|
||||
|
||||
if (ElectronicNodesSettings.ActivateOnBehaviorTree && ClassName == "EdGraphSchema_BehaviorTree")
|
||||
{
|
||||
return new FENBehaviorTreeConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
}
|
||||
|
||||
if (ElectronicNodesSettings.ActivateOnControlRig && ClassName == "ControlRigGraphSchema")
|
||||
{
|
||||
return new FENControlRigConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
}
|
||||
|
||||
if (ElectronicNodesSettings.ActivateOnReferenceViewer && ClassName == "ReferenceViewerSchema")
|
||||
{
|
||||
return new FENConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
}
|
||||
|
||||
if (ElectronicNodesSettings.ActivateOnBlueprint && Schema->IsA(UEdGraphSchema_K2::StaticClass()))
|
||||
{
|
||||
return new FENConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
}
|
||||
|
||||
if (ElectronicNodesSettings.ActivateOnMaterial && Schema->IsA(UMaterialGraphSchema::StaticClass()))
|
||||
{
|
||||
return new FENConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
}
|
||||
|
||||
for (const auto& Type : ElectronicNodesSettings.CustomGraphSchemas)
|
||||
{
|
||||
if (Schema->IsA(Type))
|
||||
{
|
||||
return new FENConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
}
|
||||
}
|
||||
|
||||
if (ElectronicNodesSettings.ActivateFallback)
|
||||
{
|
||||
return new FENConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void FENConnectionDrawingPolicy::DrawConnection(int32 LayerId, const FVector2D& Start, const FVector2D& End, const FConnectionParams& Params)
|
||||
{
|
||||
const bool RightPriority = ENIsRightPriority(Params);
|
||||
|
||||
this->_LayerId = LayerId;
|
||||
this->_Params = &Params;
|
||||
ClosestDistanceSquared = MAX_FLT;
|
||||
|
||||
FENPathDrawer PathDrawer(LayerId, ZoomFactor, RightPriority, &Params, &DrawElementsList, this);
|
||||
FVector2D StartDirection = (Params.StartDirection == EGPD_Output) ? FVector2D(1.0f, 0.0f) : FVector2D(-1.0f, 0.0f);
|
||||
FVector2D EndDirection = (Params.EndDirection == EGPD_Input) ? FVector2D(1.0f, 0.0f) : FVector2D(-1.0f, 0.0f);
|
||||
|
||||
if (FVector2D::Distance(Start, End) < ElectronicNodesSettings.MinDistanceToStyle * ZoomFactor)
|
||||
{
|
||||
PathDrawer.DrawLine(Start, End);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsTree)
|
||||
{
|
||||
StartDirection = FVector2D(0.0f, 1.0f);
|
||||
EndDirection = FVector2D(0.0f, 1.0f);
|
||||
}
|
||||
|
||||
FVector2D NewStart = Start;
|
||||
FVector2D NewEnd = End;
|
||||
|
||||
ENCorrectZoomDisplacement(NewStart, NewEnd);
|
||||
ENProcessRibbon(_LayerId, NewStart, StartDirection, NewEnd, EndDirection, Params);
|
||||
|
||||
const float Offset = ElectronicNodesSettings.HorizontalOffset * ZoomFactor;
|
||||
|
||||
if (ElectronicNodesSettings.DisablePinOffset)
|
||||
{
|
||||
if (!((Params.AssociatedPin1 != nullptr) && (Params.AssociatedPin1->GetName() == "OutputPin")))
|
||||
{
|
||||
PathDrawer.DrawOffset(NewStart, StartDirection, Offset, false);
|
||||
}
|
||||
if (!((Params.AssociatedPin2 != nullptr) && (Params.AssociatedPin2->GetName() == "InputPin")))
|
||||
{
|
||||
PathDrawer.DrawOffset(NewEnd, EndDirection, Offset, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PathDrawer.DrawOffset(NewStart, StartDirection, Offset, false);
|
||||
PathDrawer.DrawOffset(NewEnd, EndDirection, Offset, true);
|
||||
}
|
||||
|
||||
EWireStyle WireStyle = ElectronicNodesSettings.WireStyle;
|
||||
|
||||
if (ElectronicNodesSettings.OverwriteExecWireStyle)
|
||||
{
|
||||
if (((Params.AssociatedPin1 != nullptr) && Params.AssociatedPin1->PinType.PinCategory.ToString() == "exec") ||
|
||||
((Params.AssociatedPin2 != nullptr) && Params.AssociatedPin2->PinType.PinCategory.ToString() == "exec"))
|
||||
{
|
||||
if (ElectronicNodesSettings.WireStyleForExec != EWireStyle::Default)
|
||||
{
|
||||
WireStyle = ElectronicNodesSettings.WireStyleForExec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (WireStyle)
|
||||
{
|
||||
case EWireStyle::Manhattan:
|
||||
PathDrawer.DrawManhattanWire(NewStart, StartDirection, NewEnd, EndDirection);
|
||||
break;
|
||||
case EWireStyle::Subway:
|
||||
PathDrawer.DrawSubwayWire(NewStart, StartDirection, NewEnd, EndDirection);
|
||||
break;
|
||||
default:
|
||||
PathDrawer.DrawDefaultWire(NewStart, StartDirection, NewEnd, EndDirection);
|
||||
}
|
||||
|
||||
if (Settings->bTreatSplinesLikePins)
|
||||
{
|
||||
const float QueryDistanceTriggerThresholdSquared = FMath::Square(Settings->SplineHoverTolerance + Params.WireThickness * 0.5f);
|
||||
const bool bCloseToSpline = ClosestDistanceSquared < QueryDistanceTriggerThresholdSquared;
|
||||
|
||||
if (bCloseToSpline)
|
||||
{
|
||||
if (ClosestDistanceSquared < SplineOverlapResult.GetDistanceSquared())
|
||||
{
|
||||
const float SquaredDistToPin1 = (Params.AssociatedPin1 != nullptr) ? (Start - ClosestPoint).SizeSquared() : FLT_MAX;
|
||||
const float SquaredDistToPin2 = (Params.AssociatedPin2 != nullptr) ? (End - ClosestPoint).SizeSquared() : FLT_MAX;
|
||||
|
||||
#if ENGINE_MAJOR_VERSION == 5
|
||||
SplineOverlapResult = FGraphSplineOverlapResult(Params.AssociatedPin1, Params.AssociatedPin2, ClosestDistanceSquared, SquaredDistToPin1, SquaredDistToPin2, false);
|
||||
#else
|
||||
SplineOverlapResult = FGraphSplineOverlapResult(Params.AssociatedPin1, Params.AssociatedPin2, ClosestDistanceSquared, SquaredDistToPin1, SquaredDistToPin2);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FENConnectionDrawingPolicy::ENCorrectZoomDisplacement(FVector2D& Start, FVector2D& End)
|
||||
{
|
||||
if (ElectronicNodesSettings.FixZoomDisplacement)
|
||||
{
|
||||
const float ZoomDisplacement = ZoomFactor * -19.0f + 8.0f;
|
||||
if (ZoomDisplacement > 0)
|
||||
{
|
||||
Start.X += ZoomDisplacement / 2.0f;
|
||||
End.X -= ZoomDisplacement / 2.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FENConnectionDrawingPolicy::ENProcessRibbon(int32 LayerId, FVector2D& Start, FVector2D& StartDirection, FVector2D& End, FVector2D& EndDirection, const FConnectionParams& Params)
|
||||
{
|
||||
int32 DepthOffsetX = 0;
|
||||
int32 DepthOffsetY = 0;
|
||||
|
||||
if (ElectronicNodesSettings.ActivateRibbon && !IsTree)
|
||||
{
|
||||
for (ENRibbonConnection RibbonConnection : RibbonConnections)
|
||||
{
|
||||
if (RibbonConnection.Horizontal)
|
||||
{
|
||||
if (FMath::Abs(Start.Y - RibbonConnection.Main) < ElectronicNodesSettings.RibbonOffset)
|
||||
{
|
||||
const float CurrentMax = FMath::Max(Start.X, End.X);
|
||||
const float CurrentMin = FMath::Min(Start.X, End.X);
|
||||
const float RibbonMax = FMath::Max(RibbonConnection.Start, RibbonConnection.End);
|
||||
const float RibbonMin = FMath::Min(RibbonConnection.Start, RibbonConnection.End);
|
||||
|
||||
if (FMath::IsNearlyEqual(RibbonMin, CurrentMin, KINDA_SMALL_NUMBER) ||
|
||||
FMath::IsNearlyEqual(RibbonMax, CurrentMin, KINDA_SMALL_NUMBER) ||
|
||||
FMath::IsNearlyEqual(RibbonMin, CurrentMax, KINDA_SMALL_NUMBER) ||
|
||||
FMath::IsNearlyEqual(RibbonMax, CurrentMax, KINDA_SMALL_NUMBER))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FMath::Min(CurrentMax, RibbonMax) > FMath::Max(CurrentMin, RibbonMin) - 1.0f)
|
||||
{
|
||||
if (End.Y - RibbonConnection.Sub > 0)
|
||||
{
|
||||
DepthOffsetY = FMath::Max(DepthOffsetY, FMath::Max(1, RibbonConnection.Depth + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
DepthOffsetY = FMath::Min(DepthOffsetY, FMath::Min(-1, RibbonConnection.Depth - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (ENRibbonConnection RibbonConnection : RibbonConnections)
|
||||
{
|
||||
if (!RibbonConnection.Horizontal)
|
||||
{
|
||||
if (FMath::Abs(End.X - RibbonConnection.Main) < ElectronicNodesSettings.RibbonOffset)
|
||||
{
|
||||
const float CurrentMax = FMath::Max(Start.Y, End.Y);
|
||||
const float CurrentMin = FMath::Min(Start.Y, End.Y);
|
||||
const float RibbonMax = FMath::Max(RibbonConnection.Start, RibbonConnection.End);
|
||||
const float RibbonMin = FMath::Min(RibbonConnection.Start, RibbonConnection.End);
|
||||
|
||||
if (FMath::IsNearlyEqual(RibbonMin, CurrentMin, KINDA_SMALL_NUMBER) ||
|
||||
FMath::IsNearlyEqual(RibbonMax, CurrentMin, KINDA_SMALL_NUMBER) ||
|
||||
FMath::IsNearlyEqual(RibbonMin, CurrentMax, KINDA_SMALL_NUMBER) ||
|
||||
FMath::IsNearlyEqual(RibbonMax, CurrentMax, KINDA_SMALL_NUMBER))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FMath::Min(CurrentMax, RibbonMax) > FMath::Max(CurrentMin, RibbonMin) - 1.0f)
|
||||
{
|
||||
if ((Start.Y - RibbonConnection.Start) * FMath::Sign(End.Y - Start.Y) > 0 || ElectronicNodesSettings.RibbonPushOutside)
|
||||
{
|
||||
DepthOffsetX = FMath::Max(DepthOffsetX, FMath::Max(1, RibbonConnection.Depth + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
DepthOffsetX = FMath::Min(DepthOffsetX, FMath::Min(-1, RibbonConnection.Depth - 1));
|
||||
}
|
||||
|
||||
if (DepthOffsetY != 0)
|
||||
{
|
||||
DepthOffsetX = FMath::Sign(End.Y - Start.Y) * DepthOffsetY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RibbonConnections.Add(ENRibbonConnection(Start.Y, End.Y, true, Start.X, End.X, DepthOffsetY));
|
||||
RibbonConnections.Add(ENRibbonConnection(End.X, Start.X, false, Start.Y, End.Y, DepthOffsetX));
|
||||
|
||||
FVector2D StartKey(FMath::FloorToInt(Start.X), FMath::FloorToInt(Start.Y));
|
||||
FVector2D EndKey(FMath::FloorToInt(End.X), FMath::FloorToInt(End.Y));
|
||||
|
||||
FENPathDrawer PathDrawer(LayerId, ZoomFactor, true, &Params, &DrawElementsList, this);
|
||||
|
||||
if (DepthOffsetY != 0)
|
||||
{
|
||||
FVector2D NewStart = Start;
|
||||
NewStart.X += ElectronicNodesSettings.RibbonMergeOffset * ZoomFactor * StartDirection.X;
|
||||
NewStart.Y += static_cast<int32>(ElectronicNodesSettings.RibbonOffset) * DepthOffsetY * ZoomFactor;
|
||||
|
||||
PathDrawer.DrawManhattanWire(Start, StartDirection, NewStart, StartDirection);
|
||||
|
||||
Start = NewStart;
|
||||
}
|
||||
|
||||
if (DepthOffsetX != 0)
|
||||
{
|
||||
FVector2D NewEnd = End;
|
||||
NewEnd.X -= static_cast<int32>(ElectronicNodesSettings.RibbonOffset) * DepthOffsetX * ZoomFactor * EndDirection.X;
|
||||
|
||||
if (DepthOffsetX * EndDirection.X > 0)
|
||||
{
|
||||
PathDrawer.DrawManhattanWire(NewEnd, EndDirection, End, EndDirection);
|
||||
}
|
||||
|
||||
End = NewEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FENConnectionDrawingPolicy::ENIsRightPriority(const FConnectionParams& Params)
|
||||
{
|
||||
bool RightPriority = (ElectronicNodesSettings.WireAlignment == EWireAlignment::Right);
|
||||
EWirePriority WirePriority = ElectronicNodesSettings.WirePriority;
|
||||
|
||||
if (ElectronicNodesSettings.OverwriteExecWireStyle)
|
||||
{
|
||||
if (((Params.AssociatedPin1 != nullptr) && Params.AssociatedPin1->PinType.PinCategory.ToString() == "exec") ||
|
||||
((Params.AssociatedPin2 != nullptr) && Params.AssociatedPin2->PinType.PinCategory.ToString() == "exec"))
|
||||
{
|
||||
RightPriority = (ElectronicNodesSettings.WireAlignmentForExec == EWireAlignment::Right);
|
||||
WirePriority = ElectronicNodesSettings.WirePriorityForExec;
|
||||
}
|
||||
}
|
||||
|
||||
if (WirePriority != EWirePriority::None)
|
||||
{
|
||||
if ((Params.AssociatedPin1 != nullptr) && (Params.AssociatedPin2 != nullptr))
|
||||
{
|
||||
const bool IsOutputPin = (Params.AssociatedPin1->GetName() == "OutputPin");
|
||||
const bool IsInputPin = (Params.AssociatedPin2->GetName() == "InputPin");
|
||||
if (IsOutputPin ^ IsInputPin)
|
||||
{
|
||||
switch (WirePriority)
|
||||
{
|
||||
case EWirePriority::Node:
|
||||
RightPriority = IsOutputPin;
|
||||
break;
|
||||
case EWirePriority::Pin:
|
||||
RightPriority = IsInputPin;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RightPriority;
|
||||
}
|
||||
|
||||
int32 FENConnectionDrawingPolicy::ENGetZoomLevel()
|
||||
{
|
||||
const float ZoomLevels[] = {2.0f, 1.875f, 1.75f, 1.675f, 1.5f, 1.375f, 1.25f, 1.0f, 0.875f, 0.75f, 0.675f, 0.5f, 0.375f, 0.25f, 0.225f, 0.2f, 0.175f, 0.15f, 0.125f, 0.1f};
|
||||
|
||||
for (int32 i = 0; i < 20; i++)
|
||||
{
|
||||
if (ZoomFactor > ZoomLevels[i] - KINDA_SMALL_NUMBER)
|
||||
{
|
||||
return 7 - i;
|
||||
}
|
||||
}
|
||||
|
||||
return -12;
|
||||
}
|
||||
|
||||
TSharedPtr<SGraphPanel> FENConnectionDrawingPolicy::GetGraphPanel()
|
||||
{
|
||||
FSlateApplication& SlateApplication = FSlateApplication::Get();
|
||||
const TSharedPtr<SWidget> Widget = SlateApplication.GetUserFocusedWidget(0);
|
||||
if (Widget.IsValid() && Widget->GetTypeAsString() == "SGraphPanel")
|
||||
{
|
||||
return StaticCastSharedPtr<SGraphPanel>(Widget);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void FENConnectionDrawingPolicy::BuildRelatedNodes(UEdGraphNode* Node, TArray<UEdGraphNode*>& RelatedNodes, bool InputCheck = true, bool OutputCheck = true)
|
||||
{
|
||||
if (RelatedNodes.Find(Node) != INDEX_NONE && (!InputCheck || !OutputCheck))
|
||||
{
|
||||
return;
|
||||
}
|
||||
RelatedNodes.Push(Node);
|
||||
|
||||
for (auto Pin : Node->Pins)
|
||||
{
|
||||
if (InputCheck && Pin->Direction == EGPD_Input)
|
||||
{
|
||||
for (auto LinkedPin : Pin->LinkedTo)
|
||||
{
|
||||
UEdGraphNode* LinkedNode = LinkedPin->GetOwningNode();
|
||||
if (ElectronicNodesSettings.SelectionRule == ESelectionRule::Far || LinkedNode->GetName().StartsWith("K2Node_Knot_"))
|
||||
{
|
||||
BuildRelatedNodes(LinkedNode, RelatedNodes, true, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
RelatedNodes.Push(LinkedNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (OutputCheck && Pin->Direction == EGPD_Output)
|
||||
{
|
||||
for (auto LinkedPin : Pin->LinkedTo)
|
||||
{
|
||||
UEdGraphNode* LinkedNode = LinkedPin->GetOwningNode();
|
||||
if (ElectronicNodesSettings.SelectionRule == ESelectionRule::Far || LinkedNode->GetName().StartsWith("K2Node_Knot_"))
|
||||
{
|
||||
BuildRelatedNodes(LinkedNode, RelatedNodes, false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
RelatedNodes.Push(LinkedNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FENConnectionDrawingPolicy::ENDrawBubbles(const FVector2D& Start, const FVector2D& StartTangent, const FVector2D& End, const FVector2D& EndTangent)
|
||||
{
|
||||
const bool ENDrawBubbles = ElectronicNodesSettings.ForceDrawBubbles && (ElectronicNodesSettings.BubbleZoomThreshold <= ENGetZoomLevel());
|
||||
if (_Params->bDrawBubbles || ENDrawBubbles)
|
||||
{
|
||||
bool LinkedBubbles = true;
|
||||
|
||||
if (!_Params->bDrawBubbles)
|
||||
{
|
||||
LinkedBubbles = false;
|
||||
|
||||
if (ElectronicNodesSettings.BubbleDisplayRule == EBubbleDisplayRule::DisplayOnSelection ||
|
||||
ElectronicNodesSettings.BubbleDisplayRule == EBubbleDisplayRule::MoveOnSelection)
|
||||
{
|
||||
TSharedPtr<SGraphPanel> GraphPanel = this->GetGraphPanel();
|
||||
if (GraphPanel.IsValid())
|
||||
{
|
||||
if (_Params->AssociatedPin1 != nullptr && _Params->AssociatedPin2 != nullptr)
|
||||
{
|
||||
for (auto SelectedNode : GraphPanel->SelectionManager.SelectedNodes)
|
||||
{
|
||||
TArray<UEdGraphNode*> RelatedNodes;
|
||||
UEdGraphNode* SelectedGraphNode = StaticCast<UEdGraphNode*>(SelectedNode);
|
||||
this->BuildRelatedNodes(SelectedGraphNode, RelatedNodes);
|
||||
|
||||
if (RelatedNodes.Find(_Params->AssociatedPin1->GetOwningNode()) != INDEX_NONE && RelatedNodes.Find(_Params->AssociatedPin2->GetOwningNode()) != INDEX_NONE)
|
||||
{
|
||||
LinkedBubbles = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!LinkedBubbles && ElectronicNodesSettings.BubbleDisplayRule == EBubbleDisplayRule::DisplayOnSelection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FInterpCurve<float> SplineReparamTable;
|
||||
const float SplineLength = (Start - End).Size();
|
||||
int32 NumBubbles = FMath::CeilToInt(SplineLength / (ElectronicNodesSettings.BubbleSpace * ZoomFactor));
|
||||
NumBubbles = FMath::Min(NumBubbles, 1000);
|
||||
|
||||
float BubbleSpeed = ElectronicNodesSettings.BubbleSpeed;
|
||||
if (!LinkedBubbles && ElectronicNodesSettings.BubbleDisplayRule == EBubbleDisplayRule::MoveOnSelection)
|
||||
{
|
||||
BubbleSpeed = 0.0f;
|
||||
}
|
||||
FVector2D BubbleSize = BubbleImage->ImageSize * ZoomFactor * 0.1f * ElectronicNodesSettings.BubbleSize * FMath::Sqrt(_Params->WireThickness);
|
||||
if (_Params->bDrawBubbles)
|
||||
{
|
||||
BubbleSize *= 1.25f;
|
||||
}
|
||||
const float Time = (FPlatformTime::Seconds() - GStartTime);
|
||||
|
||||
const float AlphaOffset = FMath::Frac(Time * BubbleSpeed);
|
||||
|
||||
for (int32 i = 0; i < NumBubbles; ++i)
|
||||
{
|
||||
const float Alpha = (AlphaOffset + i) / NumBubbles;
|
||||
FVector2D BubblePos;
|
||||
if (StartTangent != FVector2D::ZeroVector && EndTangent != FVector2D::ZeroVector)
|
||||
{
|
||||
if ((StartTangent != EndTangent) && ((StartTangent * EndTangent) == FVector2D::ZeroVector))
|
||||
{
|
||||
BubblePos = Start + StartTangent * FMath::Sin(Alpha * PI / 2.0f) + EndTangent * (1.0f - FMath::Cos(Alpha * PI / 2.0f));
|
||||
}
|
||||
else
|
||||
{
|
||||
BubblePos = FMath::CubicInterp(Start, StartTangent, End, EndTangent, Alpha);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BubblePos = FMath::Lerp(Start, End, Alpha);
|
||||
}
|
||||
BubblePos -= (BubbleSize * 0.5f);
|
||||
|
||||
FSlateDrawElement::MakeBox(
|
||||
DrawElementsList,
|
||||
_LayerId,
|
||||
FPaintGeometry(BubblePos, BubbleSize, ZoomFactor),
|
||||
BubbleImage,
|
||||
ESlateDrawEffect::None,
|
||||
_Params->WireColor
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FENConnectionDrawingPolicy::ENDrawArrow(const FVector2D& Start, const FVector2D& End)
|
||||
{
|
||||
if (MidpointImage != nullptr && (Start - End).Size() > 4 * MinXOffset)
|
||||
{
|
||||
const FVector2D MidpointDrawPos = (Start + End) / 2.0f - MidpointRadius * 0.75f;
|
||||
const FVector2D SlopeUnnormalized = (End - Start);
|
||||
const float AngleInRadians = FMath::Atan2(SlopeUnnormalized.Y, SlopeUnnormalized.X);
|
||||
|
||||
FSlateDrawElement::MakeRotatedBox(DrawElementsList, _LayerId, FPaintGeometry(MidpointDrawPos, MidpointImage->ImageSize * ZoomFactor * 0.75f, ZoomFactor * 0.75f),
|
||||
MidpointImage, ESlateDrawEffect::None, AngleInRadians, TOptional<FVector2D>(), FSlateDrawElement::RelativeToElement, _Params->WireColor);
|
||||
}
|
||||
}
|
||||
|
||||
void FENConnectionDrawingPolicy::DrawDebugPoint(const FVector2D& Position, FLinearColor Color)
|
||||
{
|
||||
const FVector2D BubbleSize = BubbleImage->ImageSize * ZoomFactor * 0.1f * ElectronicNodesSettings.BubbleSize * FMath::Sqrt(_Params->WireThickness);
|
||||
const FVector2D BubblePos = Position - (BubbleSize * 0.5f);
|
||||
|
||||
FSlateDrawElement::MakeBox(
|
||||
DrawElementsList,
|
||||
_LayerId,
|
||||
FPaintGeometry(BubblePos, BubbleSize, ZoomFactor),
|
||||
BubbleImage,
|
||||
ESlateDrawEffect::None,
|
||||
Color
|
||||
);
|
||||
}
|
||||
|
||||
void FENConnectionDrawingPolicy::ENComputeClosestPoint(const FVector2D& Start, const FVector2D& End)
|
||||
{
|
||||
const FVector2D TemporaryPoint = FMath::ClosestPointOnSegment2D(LocalMousePosition, Start, End);
|
||||
const float TemporaryDistance = (LocalMousePosition - TemporaryPoint).SizeSquared();
|
||||
|
||||
if (TemporaryDistance < ClosestDistanceSquared)
|
||||
{
|
||||
ClosestDistanceSquared = TemporaryDistance;
|
||||
ClosestPoint = TemporaryPoint;
|
||||
}
|
||||
}
|
||||
|
||||
void FENConnectionDrawingPolicy::ENComputeClosestPointDefault(const FVector2D& Start, const FVector2D& StartTangent, const FVector2D& End, const FVector2D& EndTangent)
|
||||
{
|
||||
const float Offset = 50.0 * ZoomFactor;
|
||||
const FVector2D MinStart = FVector2D(FMath::Min(Start.X, End.X) - Offset, FMath::Min(Start.Y, End.Y));
|
||||
const FVector2D MaxEnd = FVector2D(FMath::Max(Start.X, End.X) + Offset, FMath::Max(Start.Y, End.Y));
|
||||
|
||||
const FBox2D Bounds(MinStart, MaxEnd);
|
||||
const bool bCloseToSpline = Bounds.ComputeSquaredDistanceToPoint(LocalMousePosition) < 1.0f;
|
||||
|
||||
if (bCloseToSpline)
|
||||
{
|
||||
const float StepInterval = 1.0f / 16.0f;
|
||||
const float Tangent = (End - Start).Size();
|
||||
FVector2D PointOnSpline1 = FMath::CubicInterp(Start, StartTangent * Tangent, End, EndTangent * Tangent, 0.0f);
|
||||
for (float TestAlpha = 0.0f; TestAlpha < 1.0f; TestAlpha += StepInterval)
|
||||
{
|
||||
const FVector2D PointOnSpline2 = FMath::CubicInterp(Start, StartTangent * Tangent, End, EndTangent * Tangent, TestAlpha + StepInterval);
|
||||
|
||||
const FVector2D ClosestPointToSegment = FMath::ClosestPointOnSegment2D(LocalMousePosition, PointOnSpline1, PointOnSpline2);
|
||||
const float DistanceSquared = (LocalMousePosition - ClosestPointToSegment).SizeSquared();
|
||||
|
||||
if (DistanceSquared < ClosestDistanceSquared)
|
||||
{
|
||||
ClosestDistanceSquared = DistanceSquared;
|
||||
ClosestPoint = ClosestPointToSegment;
|
||||
}
|
||||
|
||||
PointOnSpline1 = PointOnSpline2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "EdGraphUtilities.h"
|
||||
#include "ConnectionDrawingPolicy.h"
|
||||
#include "../Public/ElectronicNodesSettings.h"
|
||||
|
||||
#include "BlueprintConnectionDrawingPolicy.h"
|
||||
|
||||
struct ENRibbonConnection
|
||||
{
|
||||
float Main;
|
||||
float Sub;
|
||||
bool Horizontal;
|
||||
float Start;
|
||||
float End;
|
||||
int32 Depth = 0;
|
||||
|
||||
ENRibbonConnection(float Main, float Sub, bool Horizontal, float Start, float End, int32 Depth = 0)
|
||||
{
|
||||
this->Main = Main;
|
||||
this->Sub = Sub;
|
||||
this->Horizontal = Horizontal;
|
||||
this->Start = Start;
|
||||
this->End = End;
|
||||
this->Depth = Depth;
|
||||
}
|
||||
};
|
||||
|
||||
struct FENConnectionDrawingPolicyFactory : public FGraphPanelPinConnectionFactory
|
||||
{
|
||||
virtual ~FENConnectionDrawingPolicyFactory()
|
||||
{
|
||||
}
|
||||
|
||||
virtual class FConnectionDrawingPolicy* CreateConnectionPolicy(const class UEdGraphSchema* Schema, int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const class FSlateRect& InClippingRect, class FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const override;
|
||||
};
|
||||
|
||||
class FENConnectionDrawingPolicy : public FKismetConnectionDrawingPolicy
|
||||
{
|
||||
public:
|
||||
FENConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj, bool IsTree = false)
|
||||
: FKismetConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj), IsTree(IsTree)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void DrawConnection(int32 LayerId, const FVector2D& Start, const FVector2D& End, const FConnectionParams& Params) override;
|
||||
|
||||
void ENComputeClosestPoint(const FVector2D& Start, const FVector2D& End);
|
||||
void ENComputeClosestPointDefault(const FVector2D& Start, const FVector2D& StartTangent, const FVector2D& End, const FVector2D& EndTangent);
|
||||
void ENDrawBubbles(const FVector2D& Start, const FVector2D& StartTangent, const FVector2D& End, const FVector2D& EndTangent);
|
||||
void ENDrawArrow(const FVector2D& Start, const FVector2D& End);
|
||||
|
||||
void DrawDebugPoint(const FVector2D& Position, FLinearColor Color);
|
||||
|
||||
private:
|
||||
const UElectronicNodesSettings& ElectronicNodesSettings = *GetDefault<UElectronicNodesSettings>();
|
||||
bool ReversePins;
|
||||
float MinXOffset;
|
||||
float ClosestDistanceSquared;
|
||||
FVector2D ClosestPoint;
|
||||
TArray<ENRibbonConnection> RibbonConnections;
|
||||
TMap<FVector2D, int> PinsOffset;
|
||||
|
||||
bool IsTree = false;
|
||||
|
||||
void ENCorrectZoomDisplacement(FVector2D& Start, FVector2D& End);
|
||||
void ENProcessRibbon(int32 LayerId, FVector2D& Start, FVector2D& StartDirection, FVector2D& End, FVector2D& EndDirection, const FConnectionParams& Params);
|
||||
bool ENIsRightPriority(const FConnectionParams& Params);
|
||||
int32 ENGetZoomLevel();
|
||||
|
||||
TSharedPtr<SGraphPanel> GetGraphPanel();
|
||||
void BuildRelatedNodes(UEdGraphNode* Node, TArray<UEdGraphNode*>& RelatedNodes, bool InputCheck, bool OutputCheck);
|
||||
|
||||
int32 _LayerId;
|
||||
const FConnectionParams* _Params;
|
||||
};
|
||||
@@ -0,0 +1,549 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
#include "ENPathDrawer.h"
|
||||
|
||||
FENPathDrawer::FENPathDrawer(int32& LayerId, float& ZoomFactor, bool RightPriority, const FConnectionParams* Params, FSlateWindowElementList* DrawElementsList, FENConnectionDrawingPolicy* ConnectionDrawingPolicy)
|
||||
{
|
||||
this->LayerId = LayerId;
|
||||
this->ZoomFactor = ZoomFactor;
|
||||
this->RightPriority = RightPriority;
|
||||
this->Params = Params;
|
||||
WireColor = Params->WireColor;
|
||||
this->DrawElementsList = DrawElementsList;
|
||||
this->ConnectionDrawingPolicy = ConnectionDrawingPolicy;
|
||||
}
|
||||
|
||||
void FENPathDrawer::DrawManhattanWire(const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection)
|
||||
{
|
||||
if (!MaxDepthWire--)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (FMath::IsNearlyZero((End - Start).SizeSquared(), KINDA_SMALL_NUMBER))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool SameDirection = FVector2D::DistSquared(StartDirection, EndDirection) < KINDA_SMALL_NUMBER;
|
||||
const bool StraightDirection = FMath::IsNearlyZero(FVector2D::CrossProduct(StartDirection, EndDirection), KINDA_SMALL_NUMBER);
|
||||
const bool ForwardDirection = FVector2D::DotProduct(End - Start, StartDirection) > KINDA_SMALL_NUMBER;
|
||||
|
||||
const float DistanceOrtho = FVector2D::CrossProduct(End - Start, StartDirection);
|
||||
const float DistanceStraight = FVector2D::DotProduct(End - Start, StartDirection);
|
||||
|
||||
FVector2D NewStart = Start, NewStartDirection = StartDirection;
|
||||
FVector2D NewEnd = End, NewEndDirection = EndDirection;
|
||||
|
||||
const int32 DirectionAngle = FMath::Sign(DistanceOrtho);
|
||||
|
||||
DebugColor(FLinearColor(1.0f, 1.0f, 1.0f));
|
||||
|
||||
if (SameDirection)
|
||||
{
|
||||
if ((Start + StartDirection * FVector2D::Distance(Start, End) - End).IsNearlyZero(KINDA_SMALL_NUMBER))
|
||||
{
|
||||
DrawLine(Start, End);
|
||||
return;
|
||||
}
|
||||
else if (FMath::IsNearlyEqual(FVector2D::DotProduct((End - Start).GetSafeNormal(), StartDirection), -1.0f, KINDA_SMALL_NUMBER))
|
||||
{
|
||||
DebugColor(FLinearColor(0.5f, 0.5f, 0.5f));
|
||||
DrawSimpleRadius(Start, StartDirection, -90, NewStart, NewStartDirection, false);
|
||||
DrawSimpleRadius(End, EndDirection, 90, NewEnd, NewEndDirection, true);
|
||||
}
|
||||
else if ((FMath::Abs(End.X - Start.X) < 2 * GetRadiusOffset()) && (FMath::Abs(End.Y - Start.Y)) < 4 * GetRadiusOffset())
|
||||
{
|
||||
const float Multiplier = FVector2D::Distance(Start, End) / 32.0f;
|
||||
DebugColor(FLinearColor(0.5f, 1.0f, 0.0f));
|
||||
DrawSpline(Start, StartDirection * Multiplier, End, EndDirection * Multiplier);
|
||||
return;
|
||||
}
|
||||
else if (!ForwardDirection && (FMath::Abs(DistanceOrtho) < 4.0f * GetRadiusOffset()))
|
||||
{
|
||||
DebugColor(FLinearColor(1.0f, 0.0f, 0.0f));
|
||||
DrawUTurn(Start, StartDirection, DirectionAngle, NewStart, NewStartDirection, false);
|
||||
}
|
||||
else if (FMath::Abs(End.Y - Start.Y) < 2.0f * GetRadiusOffset())
|
||||
{
|
||||
DebugColor(FLinearColor(1.0f, 0.5f, 0.0f));
|
||||
DrawCorrectionOrtho(End, EndDirection, DistanceOrtho, NewEnd, NewEndDirection, true);
|
||||
}
|
||||
else if (FMath::Abs(End.X - Start.X) < 2.0f * GetRadiusOffset() && FMath::IsNearlyEqual(StartDirection.Y, 1.0f, KINDA_SMALL_NUMBER) && FMath::IsNearlyEqual(EndDirection.Y, 1.0f, KINDA_SMALL_NUMBER))
|
||||
{
|
||||
DebugColor(FLinearColor(1.0f, 0.5f, 0.0f));
|
||||
DrawCorrectionOrtho(End, EndDirection, DistanceOrtho, NewEnd, NewEndDirection, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugColor(FLinearColor(1.0f, 0.0f, 1.0f));
|
||||
if (DistanceStraight < 2.0f * GetRadiusOffset())
|
||||
{
|
||||
if (FMath::Abs(DistanceOrtho) < 4.0f * GetRadiusOffset())
|
||||
{
|
||||
DrawUTurn(Start, StartDirection, DirectionAngle, NewStart, NewStartDirection, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawUTurn(End, EndDirection, DirectionAngle, NewEnd, NewEndDirection, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const float Direction = -FMath::Sign(DistanceOrtho);
|
||||
|
||||
if (RightPriority)
|
||||
{
|
||||
DrawSimpleRadius(End, EndDirection, 90 * Direction, NewEnd, NewEndDirection, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawSimpleRadius(Start, StartDirection, 90 * Direction, NewStart, NewStartDirection, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!StraightDirection)
|
||||
{
|
||||
DrawIntersectionRadius(Start, StartDirection, End, EndDirection);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FMath::Sign(DistanceStraight) > 0)
|
||||
{
|
||||
DebugColor(FLinearColor(0.5f, 0, 0.5f));
|
||||
DrawSimpleRadius(End, EndDirection, 90 * DirectionAngle, NewEnd, NewEndDirection, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugColor(FLinearColor(1.0f, 0, 1.0f));
|
||||
DrawSimpleRadius(Start, StartDirection, -90 * DirectionAngle, NewStart, NewStartDirection, false);
|
||||
}
|
||||
}
|
||||
|
||||
DrawManhattanWire(NewStart, NewStartDirection, NewEnd, NewEndDirection);
|
||||
}
|
||||
|
||||
void FENPathDrawer::DrawSubwayWire(const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection)
|
||||
{
|
||||
if (!MaxDepthWire--)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (FMath::IsNearlyZero((End - Start).SizeSquared(), KINDA_SMALL_NUMBER))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool StartDirection90 = FMath::IsNearlyEqual(FMath::Abs(StartDirection.X), 1.0f, KINDA_SMALL_NUMBER) || FMath::IsNearlyEqual(FMath::Abs(StartDirection.Y), 1.0f, KINDA_SMALL_NUMBER);
|
||||
const bool EndDirection90 = FMath::IsNearlyEqual(FMath::Abs(EndDirection.X), 1.0f, KINDA_SMALL_NUMBER) || FMath::IsNearlyEqual(FMath::Abs(EndDirection.Y), 1.0f, KINDA_SMALL_NUMBER);
|
||||
|
||||
const bool SameDirection = FVector2D::DistSquared(StartDirection, EndDirection) < KINDA_SMALL_NUMBER;
|
||||
const bool StraightDirection = FMath::IsNearlyZero(FVector2D::CrossProduct(StartDirection, EndDirection), KINDA_SMALL_NUMBER);
|
||||
const bool ForwardDirection = FVector2D::DotProduct(End - Start, StartDirection) > KINDA_SMALL_NUMBER;
|
||||
|
||||
const float DistanceOrtho = FVector2D::CrossProduct(End - Start, StartDirection);
|
||||
const float DistanceStraight = FVector2D::DotProduct(End - Start, StartDirection);
|
||||
const float DirectionOffset = (FMath::Abs(End.X - Start.X) - FMath::Abs(End.Y - Start.Y)) * (FMath::IsNearlyEqual(FMath::Abs(StartDirection.X), 1.0f, KINDA_SMALL_NUMBER) ? 1 : -1);
|
||||
|
||||
const int32 DirectionAngle = FMath::Sign(DistanceOrtho);
|
||||
|
||||
FVector2D NewStart = Start, NewStartDirection = StartDirection;
|
||||
FVector2D NewEnd = End, NewEndDirection = EndDirection;
|
||||
|
||||
DebugColor(FLinearColor(1.0f, 1.0f, 1.0f));
|
||||
|
||||
if (!StraightDirection)
|
||||
{
|
||||
if (StartDirection90 && EndDirection90)
|
||||
{
|
||||
DrawIntersectionDiagRadius(Start, StartDirection, End, EndDirection);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawIntersectionRadius(Start, StartDirection, End, EndDirection);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (SameDirection)
|
||||
{
|
||||
if ((Start + StartDirection * FVector2D::Distance(Start, End) - End).IsNearlyZero(KINDA_SMALL_NUMBER))
|
||||
{
|
||||
DrawLine(Start, End);
|
||||
return;
|
||||
}
|
||||
else if (FVector2D::Distance(Start, End) < 4 * GetRadiusOffset())
|
||||
{
|
||||
DrawManhattanWire(Start, StartDirection, End, EndDirection);
|
||||
return;
|
||||
}
|
||||
else if (FMath::IsNearlyEqual(FVector2D::DotProduct((End - Start).GetSafeNormal(), StartDirection), -1.0f, KINDA_SMALL_NUMBER))
|
||||
{
|
||||
DebugColor(FLinearColor(0.5f, 0.5f, 0.5f));
|
||||
DrawSimpleRadius(Start, StartDirection, -90, NewStart, NewStartDirection, false);
|
||||
DrawSimpleRadius(End, EndDirection, 90, NewEnd, NewEndDirection, true);
|
||||
}
|
||||
else if (ForwardDirection && DirectionOffset > 2 * GetIntersectionOffset(45, false))
|
||||
{
|
||||
DebugColor(FLinearColor(0.0f, 1.0f, 0.0f));
|
||||
|
||||
if (FMath::Abs(DistanceOrtho) < 2 * GetIntersectionOffset(45, true))
|
||||
{
|
||||
if ((FMath::Abs(DistanceOrtho) < 2 * GetIntersectionOffset(45, false)) && (FMath::Abs(DistanceStraight) > 2 * GetRadiusOffset()))
|
||||
{
|
||||
DebugColor(FLinearColor(0.0f, 0.5f, 0.0f));
|
||||
DrawCorrectionOrtho(End, EndDirection, DistanceOrtho, NewEnd, NewEndDirection, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (RightPriority)
|
||||
{
|
||||
DrawSimpleRadius(End, EndDirection, -45 * DirectionAngle, NewEnd, NewEndDirection, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawSimpleRadius(Start, StartDirection, -45 * DirectionAngle, NewStart, NewStartDirection, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DistanceStraight < 2 * GetRadiusOffset())
|
||||
{
|
||||
DebugColor(FLinearColor(1.0f, 1.0f, 0.0f));
|
||||
int32 Direction = -FMath::Sign(DistanceOrtho);
|
||||
DrawSimpleRadius(Start, StartDirection, 90 * Direction, NewStart, NewStartDirection, false);
|
||||
DrawSimpleRadius(End, EndDirection, 90 * Direction, NewEnd, NewEndDirection, true);
|
||||
}
|
||||
else if (DirectionOffset > 2 * GetIntersectionOffset(45, false))
|
||||
{
|
||||
DebugColor(FLinearColor(0, 0, 1.0f));
|
||||
DrawSimpleRadius(End, EndDirection, -45 * DirectionAngle, NewEnd, NewEndDirection, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugColor(FLinearColor(0, 0.5f, 0));
|
||||
DrawSimpleRadius(End, EndDirection, -90 * DirectionAngle, NewEnd, NewEndDirection, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FMath::Sign(DistanceStraight) > 0)
|
||||
{
|
||||
DebugColor(FLinearColor(0.5f, 0, 0.5f));
|
||||
DrawSimpleRadius(End, EndDirection, 90 * DirectionAngle, NewEnd, NewEndDirection, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugColor(FLinearColor(1.0f, 0, 1.0f));
|
||||
DrawSimpleRadius(Start, StartDirection, -90 * DirectionAngle, NewStart, NewStartDirection, false);
|
||||
}
|
||||
}
|
||||
|
||||
DrawSubwayWire(NewStart, NewStartDirection, NewEnd, NewEndDirection);
|
||||
}
|
||||
|
||||
void FENPathDrawer::DrawDefaultWire(const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection)
|
||||
{
|
||||
const float Tangent = (End - Start).Size();
|
||||
|
||||
FSlateDrawElement::MakeDrawSpaceSpline(*DrawElementsList, LayerId,
|
||||
Start, StartDirection * Tangent, End, EndDirection * Tangent,
|
||||
Params->WireThickness * ElectronicNodesSettings.WireThickness, ESlateDrawEffect::None, WireColor);
|
||||
|
||||
ConnectionDrawingPolicy->ENComputeClosestPointDefault(Start, StartDirection, End, EndDirection);
|
||||
|
||||
ConnectionDrawingPolicy->ENDrawBubbles(Start, StartDirection * Tangent, End, EndDirection * Tangent);
|
||||
}
|
||||
|
||||
void FENPathDrawer::DrawIntersectionRadius(const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection)
|
||||
{
|
||||
const int32 AngleDeg = FMath::RoundToInt(FMath::UnwindDegrees(FMath::RadiansToDegrees(FMath::Atan2(StartDirection.Y, StartDirection.X) - FMath::Atan2(EndDirection.Y, EndDirection.X))));
|
||||
|
||||
const float StartOffset = GetIntersectionOffset(AngleDeg, false);
|
||||
const float EndOffset = GetIntersectionOffset(AngleDeg, true);
|
||||
|
||||
const float T = (EndDirection.X * (Start.Y - End.Y) - EndDirection.Y * (Start.X - End.X)) / (-EndDirection.X * StartDirection.Y + StartDirection.X * EndDirection.Y);
|
||||
const FVector2D Intersection = Start + T * StartDirection;
|
||||
|
||||
const FVector2D StartStop = Intersection - StartDirection * StartOffset;
|
||||
const FVector2D EndStop = Intersection + EndDirection * EndOffset;
|
||||
|
||||
DebugColor(FLinearColor(1.0f, 1.0f, 1.0f));
|
||||
DrawLine(Start, StartStop);
|
||||
|
||||
DebugColor(FLinearColor(0.0f, 0.0f, 1.0f));
|
||||
DrawRadius(StartStop, StartDirection, EndStop, EndDirection, AngleDeg);
|
||||
|
||||
DebugColor(FLinearColor(1.0f, 1.0f, 1.0f));
|
||||
DrawLine(EndStop, End);
|
||||
}
|
||||
|
||||
void FENPathDrawer::DrawIntersectionDiagRadius(const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection)
|
||||
{
|
||||
const float DirectionOffset = FMath::Abs(End.X - Start.X) - FMath::Abs(End.Y - Start.Y);
|
||||
|
||||
FVector2D NewStart = Start;
|
||||
FVector2D NewEnd = End;
|
||||
|
||||
FVector2D NewStartClose, NewStartCloseDirection;
|
||||
FVector2D NewEndClose, NewEndCloseDirection;
|
||||
|
||||
int32 Direction;
|
||||
|
||||
if (FMath::IsNearlyEqual(FMath::Abs(StartDirection.X), 1.0f, KINDA_SMALL_NUMBER))
|
||||
{
|
||||
Direction = FMath::RoundToInt(FMath::Sign(End.Y - Start.Y) * StartDirection.X);
|
||||
if (DirectionOffset > 0)
|
||||
{
|
||||
NewStart += FVector2D(1.0f, 0.0f) * DirectionOffset * StartDirection.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
NewEnd += FVector2D(0.0f, 1.0f) * DirectionOffset * FMath::Sign(End.Y - Start.Y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Direction = FMath::RoundToInt(FMath::Sign(Start.Y - End.Y) * EndDirection.X);
|
||||
|
||||
if (DirectionOffset > 0)
|
||||
{
|
||||
NewEnd -= FVector2D(1.0f, 0.0f) * DirectionOffset * EndDirection.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
NewStart -= FVector2D(0.0f, 1.0f) * DirectionOffset * FMath::Sign(End.Y - Start.Y);
|
||||
}
|
||||
}
|
||||
|
||||
DebugColor(FLinearColor(1.0f, 1.0f, 1.0f));
|
||||
DrawLine(Start, NewStart);
|
||||
DrawLine(NewEnd, End);
|
||||
|
||||
DebugColor(FLinearColor(0.0f, 0.0f, 1.0f));
|
||||
DrawSimpleRadius(NewStart, StartDirection, 45 * Direction, NewStartClose, NewStartCloseDirection, false);
|
||||
DrawSimpleRadius(NewEnd, EndDirection, -45 * Direction, NewEndClose, NewEndCloseDirection, true);
|
||||
|
||||
DebugColor(FLinearColor(1.0f, 1.0f, 1.0f));
|
||||
DrawLine(NewStartClose, NewEndClose);
|
||||
}
|
||||
|
||||
void FENPathDrawer::DrawSimpleRadius(const FVector2D& Start, const FVector2D& StartDirection, const int32& AngleDeg, FVector2D& out_End, FVector2D& out_EndDirection, bool Backward)
|
||||
{
|
||||
const float StartOffset = GetRadiusOffset(AngleDeg, false);
|
||||
const float PerpendicularOffset = GetRadiusOffset(AngleDeg, true);
|
||||
const FVector2D PerpendicularDirection = StartDirection.GetRotated(FMath::Sign(AngleDeg) * 90);
|
||||
out_EndDirection = StartDirection.GetRotated(AngleDeg);
|
||||
|
||||
if (Backward)
|
||||
{
|
||||
out_End = Start - (StartDirection * StartOffset + PerpendicularDirection * PerpendicularOffset);
|
||||
DrawRadius(out_End, out_EndDirection, Start, StartDirection, AngleDeg);
|
||||
}
|
||||
else
|
||||
{
|
||||
out_End = Start + (StartDirection * StartOffset + PerpendicularDirection * PerpendicularOffset);
|
||||
DrawRadius(Start, StartDirection, out_End, out_EndDirection, AngleDeg);
|
||||
}
|
||||
}
|
||||
|
||||
void FENPathDrawer::DrawUTurn(const FVector2D& Start, const FVector2D& StartDirection, float Direction, FVector2D& out_End, FVector2D& out_EndDirection, bool Backward)
|
||||
{
|
||||
const float BackwardDirection = Backward ? -1.0f : 1.0f;
|
||||
|
||||
const FVector2D MidDirection = StartDirection.GetRotated(FMath::Sign(Direction) * 90 * BackwardDirection);
|
||||
const FVector2D Mid = Start + (StartDirection + MidDirection) * GetRadiusOffset() * BackwardDirection;
|
||||
|
||||
out_EndDirection = -StartDirection;
|
||||
out_End = Start + MidDirection * 2.0f * GetRadiusOffset() * BackwardDirection;
|
||||
|
||||
if (Backward)
|
||||
{
|
||||
DrawRadius(out_End, out_EndDirection, Mid, MidDirection, 90);
|
||||
DrawRadius(Mid, MidDirection, Start, StartDirection, 90);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawRadius(Start, StartDirection, Mid, MidDirection, 90);
|
||||
DrawRadius(Mid, MidDirection, out_End, out_EndDirection, 90);
|
||||
}
|
||||
}
|
||||
|
||||
void FENPathDrawer::DrawCorrectionOrtho(const FVector2D& Start, const FVector2D& StartDirection, const float& Displacement, FVector2D& out_End, FVector2D& out_EndDirection, bool Backward)
|
||||
{
|
||||
out_EndDirection = StartDirection;
|
||||
const FVector2D StartDirectionOrtho = StartDirection.GetRotated(90);
|
||||
|
||||
if (Backward)
|
||||
{
|
||||
out_End = Start - 2.0f * StartDirection * GetRadiusOffset() + StartDirectionOrtho * Displacement;
|
||||
DrawSpline(out_End, out_EndDirection, Start, StartDirection);
|
||||
}
|
||||
else
|
||||
{
|
||||
out_End = Start + 2.0f * StartDirection * GetRadiusOffset() + StartDirectionOrtho * Displacement;
|
||||
DrawSpline(out_End, out_EndDirection, Start, StartDirection);
|
||||
}
|
||||
}
|
||||
|
||||
float FENPathDrawer::GetRadiusOffset(const int32& AngleDeg, bool Perpendicular)
|
||||
{
|
||||
float RadiusOffset = 1.0f;
|
||||
int32 AbsAngle = FMath::Abs(AngleDeg);
|
||||
|
||||
if (Perpendicular)
|
||||
{
|
||||
AbsAngle = 180 - AbsAngle;
|
||||
}
|
||||
|
||||
switch (AbsAngle)
|
||||
{
|
||||
case 45:
|
||||
RadiusOffset *= FMath::Sqrt(2.0f) / 2.0f;
|
||||
break;
|
||||
case 90:
|
||||
RadiusOffset *= 1.0f;
|
||||
break;
|
||||
case 135:
|
||||
RadiusOffset *= (1.0f - (FMath::Sqrt(2.0f) / 2.0f));
|
||||
break;
|
||||
case 180:
|
||||
RadiusOffset *= 2.0f;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return RadiusOffset * ZoomFactor * ElectronicNodesSettings.RoundRadius;
|
||||
}
|
||||
|
||||
float FENPathDrawer::GetRadiusTangent(const int32& AngleDeg)
|
||||
{
|
||||
float Tangent = 2 * FMath::Sqrt(2.0f) - 1;
|
||||
|
||||
switch (FMath::Abs(AngleDeg))
|
||||
{
|
||||
case 0:
|
||||
Tangent *= 4.0f / Tangent;
|
||||
break;
|
||||
case 45:
|
||||
Tangent *= 0.55166f;
|
||||
break;
|
||||
case 90:
|
||||
Tangent = 4 * (FMath::Sqrt(2.0f) - 1);
|
||||
break;
|
||||
case 135:
|
||||
Tangent *= 2.0f / Tangent;
|
||||
break;
|
||||
case 180:
|
||||
Tangent *= 4.0f / Tangent;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return Tangent * ZoomFactor * ElectronicNodesSettings.RoundRadius;
|
||||
}
|
||||
|
||||
float FENPathDrawer::GetIntersectionOffset(const int32& AngleDeg, bool Diagonal)
|
||||
{
|
||||
float IntersectionOffset = 1.0f;
|
||||
|
||||
switch (FMath::Abs(AngleDeg))
|
||||
{
|
||||
case 45:
|
||||
if (Diagonal)
|
||||
{
|
||||
IntersectionOffset *= (1.0f - FMath::Sqrt(2.0f) / 2.0f) * FMath::Sqrt(2.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
IntersectionOffset *= FMath::Sqrt(2.0f) - 1.0f;
|
||||
}
|
||||
break;
|
||||
case 90:
|
||||
IntersectionOffset *= 1.0f;
|
||||
break;
|
||||
case 135:
|
||||
//RadiusOffset *= (1.0f - (FMath::Sqrt(2.0f) / 2.0f));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return IntersectionOffset * ZoomFactor * ElectronicNodesSettings.RoundRadius;
|
||||
}
|
||||
|
||||
void FENPathDrawer::DrawOffset(FVector2D& Start, FVector2D& StartDirection, const float& Offset, bool Backward)
|
||||
{
|
||||
FVector2D NewStart = Start;
|
||||
|
||||
if (Backward)
|
||||
{
|
||||
NewStart -= StartDirection * Offset;
|
||||
DrawLine(NewStart, Start);
|
||||
}
|
||||
else
|
||||
{
|
||||
NewStart += StartDirection * Offset;
|
||||
DrawLine(Start, NewStart);
|
||||
}
|
||||
|
||||
Start = NewStart;
|
||||
}
|
||||
|
||||
void FENPathDrawer::DrawLine(const FVector2D& Start, const FVector2D& End)
|
||||
{
|
||||
if (FMath::IsNearlyZero((End - Start).SizeSquared(), KINDA_SMALL_NUMBER))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FSlateDrawElement::MakeDrawSpaceSpline(*DrawElementsList, LayerId,
|
||||
Start, FVector2D::ZeroVector, End, FVector2D::ZeroVector,
|
||||
Params->WireThickness * ElectronicNodesSettings.WireThickness, ESlateDrawEffect::None, WireColor);
|
||||
|
||||
ConnectionDrawingPolicy->ENComputeClosestPoint(Start, End);
|
||||
ConnectionDrawingPolicy->ENDrawBubbles(Start, FVector2D::ZeroVector, End, FVector2D::ZeroVector);
|
||||
if (FVector2D::DistSquared(Start, End) > 50.0f)
|
||||
{
|
||||
ConnectionDrawingPolicy->ENDrawArrow(Start, End);
|
||||
}
|
||||
}
|
||||
|
||||
void FENPathDrawer::DrawRadius(const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection, const int32& AngleDeg)
|
||||
{
|
||||
const float Tangent = GetRadiusTangent(AngleDeg);
|
||||
const float Offset = GetRadiusOffset(AngleDeg);
|
||||
|
||||
FSlateDrawElement::MakeDrawSpaceSpline(*DrawElementsList, LayerId,
|
||||
Start, StartDirection * Tangent, End, EndDirection * Tangent,
|
||||
Params->WireThickness * ElectronicNodesSettings.WireThickness, ESlateDrawEffect::None, WireColor);
|
||||
|
||||
ConnectionDrawingPolicy->ENDrawBubbles(Start, StartDirection * Offset, End, EndDirection * Offset);
|
||||
}
|
||||
|
||||
void FENPathDrawer::DrawSpline(const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection)
|
||||
{
|
||||
const float Tangent = GetRadiusTangent();
|
||||
|
||||
FSlateDrawElement::MakeDrawSpaceSpline(*DrawElementsList, LayerId,
|
||||
Start, StartDirection * Tangent, End, EndDirection * Tangent,
|
||||
Params->WireThickness * ElectronicNodesSettings.WireThickness, ESlateDrawEffect::None, WireColor);
|
||||
|
||||
ConnectionDrawingPolicy->ENComputeClosestPointDefault(Start, StartDirection * Tangent, End, EndDirection * Tangent);
|
||||
ConnectionDrawingPolicy->ENDrawBubbles(Start, StartDirection * Tangent, End, EndDirection * Tangent);
|
||||
}
|
||||
|
||||
void FENPathDrawer::DebugColor(const FLinearColor& Color)
|
||||
{
|
||||
if (ElectronicNodesSettings.Debug)
|
||||
{
|
||||
WireColor = Color;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ConnectionDrawingPolicy.h"
|
||||
#include "ENConnectionDrawingPolicy.h"
|
||||
#include "../Public/ElectronicNodesSettings.h"
|
||||
|
||||
class FENPathDrawer
|
||||
{
|
||||
public:
|
||||
FENPathDrawer(int32& LayerId, float& ZoomFactor, bool RightPriority, const FConnectionParams* Params, FSlateWindowElementList* DrawElementsList, FENConnectionDrawingPolicy* ConnectionDrawingPolicy);
|
||||
|
||||
void DrawManhattanWire(const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection);
|
||||
void DrawSubwayWire(const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection);
|
||||
void DrawDefaultWire(const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection);
|
||||
|
||||
void DrawIntersectionRadius(const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection);
|
||||
void DrawIntersectionDiagRadius(const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection);
|
||||
|
||||
void DrawSimpleRadius(const FVector2D& Start, const FVector2D& StartDirection, const int32& AngleDeg, FVector2D& out_End, FVector2D& out_EndDirection, bool Backward = false);
|
||||
void DrawUTurn(const FVector2D& Start, const FVector2D& StartDirection, float Direction, FVector2D& out_End, FVector2D& out_EndDirection, bool Backward = false);
|
||||
void DrawCorrectionOrtho(const FVector2D& Start, const FVector2D& StartDirection, const float& Displacement, FVector2D& out_End, FVector2D& out_EndDirection, bool Backward = false);
|
||||
|
||||
float GetRadiusOffset(const int32& AngleDeg = 0, bool Perpendicular = false);
|
||||
float GetRadiusTangent(const int32& AngleDeg = 0);
|
||||
float GetIntersectionOffset(const int32& AngleDeg = 0, bool Diagonal = false);
|
||||
|
||||
void DrawOffset(FVector2D& Start, FVector2D& StartDirection, const float& Offset, bool Backward = false);
|
||||
void DrawLine(const FVector2D& Start, const FVector2D& End);
|
||||
void DrawRadius(const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection, const int32& AngleDeg);
|
||||
void DrawSpline(const FVector2D& Start, const FVector2D& StartDirection, const FVector2D& End, const FVector2D& EndDirection);
|
||||
|
||||
void DebugColor(const FLinearColor& Color);
|
||||
|
||||
private:
|
||||
const UElectronicNodesSettings& ElectronicNodesSettings = *GetDefault<UElectronicNodesSettings>();
|
||||
|
||||
int32 LayerId;
|
||||
float ZoomFactor;
|
||||
bool RightPriority;
|
||||
|
||||
FLinearColor WireColor;
|
||||
const FConnectionParams* Params;
|
||||
FSlateWindowElementList* DrawElementsList;
|
||||
FENConnectionDrawingPolicy* ConnectionDrawingPolicy;
|
||||
|
||||
int32 MaxDepthWire = 5;
|
||||
};
|
||||
@@ -0,0 +1,120 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
#include "ElectronicNodes.h"
|
||||
#include "ENConnectionDrawingPolicy.h"
|
||||
#include "ENCommands.h"
|
||||
#include "NodeFactory.h"
|
||||
#include "Interfaces/IPluginManager.h"
|
||||
#include "Lib/HotPatch.h"
|
||||
#include "MainFrame/Public/Interfaces/IMainFrameModule.h"
|
||||
#include "Patch/NodeFactoryPatch.h"
|
||||
#include "Popup/ENUpdatePopup.h"
|
||||
#include "SettingsEditor/Public/ISettingsEditorModule.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FElectronicNodesModule"
|
||||
|
||||
void FElectronicNodesModule::StartupModule()
|
||||
{
|
||||
const TSharedPtr<FENConnectionDrawingPolicyFactory> ENConnectionFactory = MakeShareable(new FENConnectionDrawingPolicyFactory);
|
||||
FEdGraphUtilities::RegisterVisualPinConnectionFactory(ENConnectionFactory);
|
||||
|
||||
auto const CommandBindings = FModuleManager::LoadModuleChecked<IMainFrameModule>("MainFrame").GetMainFrameCommandBindings();
|
||||
ENCommands::Register();
|
||||
|
||||
CommandBindings->MapAction(
|
||||
ENCommands::Get().ToggleMasterActivation,
|
||||
FExecuteAction::CreateRaw(this, &FElectronicNodesModule::ToggleMasterActivation)
|
||||
);
|
||||
|
||||
PluginDirectory = IPluginManager::Get().FindPlugin(TEXT("ElectronicNodes"))->GetBaseDir();
|
||||
GlobalSettingsFile = PluginDirectory + "/Settings.ini";
|
||||
|
||||
ElectronicNodesSettings = GetMutableDefault<UElectronicNodesSettings>();
|
||||
ElectronicNodesSettings->OnSettingChanged().AddRaw(this, &FElectronicNodesModule::ReloadConfiguration);
|
||||
|
||||
if (ElectronicNodesSettings->UseGlobalSettings)
|
||||
{
|
||||
if (FPaths::FileExists(GlobalSettingsFile))
|
||||
{
|
||||
ElectronicNodesSettings->LoadConfig(nullptr, *GlobalSettingsFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (ElectronicNodesSettings->UseHotPatch)
|
||||
{
|
||||
#if PLATFORM_WINDOWS && !UE_BUILD_SHIPPING
|
||||
FHotPatch::Hook(&FNodeFactory::CreateConnectionPolicy, &FNodeFactoryPatch::CreateConnectionPolicy_Hook);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ElectronicNodesSettings->ActivatePopupOnUpdate)
|
||||
{
|
||||
ENUpdatePopup::Register();
|
||||
}
|
||||
}
|
||||
|
||||
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION <= 25
|
||||
void FElectronicNodesModule::ReloadConfiguration(FName PropertyName)
|
||||
#else
|
||||
void FElectronicNodesModule::ReloadConfiguration(UObject* Object, struct FPropertyChangedEvent& Property)
|
||||
#endif
|
||||
{
|
||||
#if (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION > 25) || (ENGINE_MAJOR_VERSION == 5)
|
||||
const FName PropertyName = Property.GetPropertyName();
|
||||
#endif
|
||||
|
||||
if (PropertyName == "UseGlobalSettings")
|
||||
{
|
||||
if (ElectronicNodesSettings->UseGlobalSettings)
|
||||
{
|
||||
if (FPaths::FileExists(GlobalSettingsFile))
|
||||
{
|
||||
ElectronicNodesSettings->LoadConfig(nullptr, *GlobalSettingsFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
ElectronicNodesSettings->SaveConfig(CPF_Config, *GlobalSettingsFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (PropertyName == "UseHotPatch")
|
||||
{
|
||||
ISettingsEditorModule* SettingsEditorModule = FModuleManager::GetModulePtr<ISettingsEditorModule>("SettingsEditor");
|
||||
if (SettingsEditorModule)
|
||||
{
|
||||
SettingsEditorModule->OnApplicationRestartRequired();
|
||||
}
|
||||
}
|
||||
|
||||
if (ElectronicNodesSettings->LoadGlobalSettings)
|
||||
{
|
||||
if (FPaths::FileExists(GlobalSettingsFile))
|
||||
{
|
||||
ElectronicNodesSettings->LoadConfig(nullptr, *GlobalSettingsFile);
|
||||
}
|
||||
ElectronicNodesSettings->LoadGlobalSettings = false;
|
||||
}
|
||||
|
||||
ElectronicNodesSettings->SaveConfig();
|
||||
|
||||
if (ElectronicNodesSettings->UseGlobalSettings)
|
||||
{
|
||||
ElectronicNodesSettings->SaveConfig(CPF_Config, *GlobalSettingsFile);
|
||||
}
|
||||
}
|
||||
|
||||
void FElectronicNodesModule::ShutdownModule()
|
||||
{
|
||||
}
|
||||
|
||||
void FElectronicNodesModule::ToggleMasterActivation() const
|
||||
{
|
||||
ElectronicNodesSettings->ToggleMasterActivation();
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FElectronicNodesModule, ElectronicNodes)
|
||||
@@ -0,0 +1,42 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WINDOWS && !UE_BUILD_SHIPPING
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
|
||||
struct FHotPatch
|
||||
{
|
||||
template <typename FunctionType>
|
||||
static bool Hook(FunctionType* From, FunctionType* To)
|
||||
{
|
||||
uint64* FromAddress = reinterpret_cast<uint64*>(From);
|
||||
uint64* ToAddress = reinterpret_cast<uint64*>(To);
|
||||
|
||||
uint8 Patch[] =
|
||||
{
|
||||
0x49, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x41, 0xFF, 0xE2
|
||||
};
|
||||
|
||||
FMemory::Memcpy(&Patch[2], &ToAddress, sizeof(ToAddress));
|
||||
|
||||
DWORD BaseProtection;
|
||||
const DWORD NewProtection = PAGE_EXECUTE_READWRITE;
|
||||
if (!VirtualProtect(FromAddress, sizeof(Patch), NewProtection, &BaseProtection))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FMemory::Memcpy(FromAddress, Patch, sizeof(Patch));
|
||||
VirtualProtect(FromAddress, sizeof(Patch), BaseProtection, &BaseProtection);
|
||||
FlushInstructionCache(GetCurrentProcess(), nullptr, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
#include "NodeFactoryPatch.h"
|
||||
|
||||
#include "ConnectionDrawingPolicy.h"
|
||||
#include "EdGraphUtilities.h"
|
||||
#include "ENConnectionDrawingPolicy.h"
|
||||
#include "AnimationStateMachineSchema.h"
|
||||
#include "AnimationGraphFactory.h"
|
||||
|
||||
FConnectionDrawingPolicy* FNodeFactoryPatch::CreateConnectionPolicy_Hook(const UEdGraphSchema* Schema, int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj)
|
||||
{
|
||||
const TSharedPtr<FENConnectionDrawingPolicyFactory> ENConnectionFactory = MakeShareable(new FENConnectionDrawingPolicyFactory);
|
||||
|
||||
FConnectionDrawingPolicy* ConnectionDrawingPolicy = ENConnectionFactory->CreateConnectionPolicy(Schema, InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
|
||||
if (!ConnectionDrawingPolicy)
|
||||
{
|
||||
ConnectionDrawingPolicy = Schema->CreateConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
}
|
||||
|
||||
if (!ConnectionDrawingPolicy && Schema->IsA(UAnimationStateMachineSchema::StaticClass()))
|
||||
{
|
||||
const TSharedPtr<FAnimationGraphPinConnectionFactory> AnimationGraphFactory = MakeShareable(new FAnimationGraphPinConnectionFactory);
|
||||
ConnectionDrawingPolicy = AnimationGraphFactory->CreateConnectionPolicy(Schema, InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
}
|
||||
|
||||
if (!ConnectionDrawingPolicy)
|
||||
{
|
||||
ConnectionDrawingPolicy = new FConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements);
|
||||
}
|
||||
|
||||
return ConnectionDrawingPolicy;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "NodeFactory.h"
|
||||
|
||||
class FNodeFactoryPatch : FNodeFactory
|
||||
{
|
||||
public:
|
||||
static FConnectionDrawingPolicy* CreateConnectionPolicy_Hook(const UEdGraphSchema* Schema, int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj);
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "AnimGraphConnectionDrawingPolicy.cpp"
|
||||
#if ENGINE_MAJOR_VERSION == 5
|
||||
#include "AnimationPins/SGraphPinPose.cpp"
|
||||
#endif
|
||||
#include "ENConnectionDrawingPolicy.h"
|
||||
|
||||
class FENAnimGraphConnectionDrawingPolicy : public FAnimGraphConnectionDrawingPolicy
|
||||
{
|
||||
public:
|
||||
FENAnimGraphConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj)
|
||||
: FAnimGraphConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj)
|
||||
{
|
||||
this->ConnectionDrawingPolicy = new FENConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
}
|
||||
|
||||
virtual void DrawConnection(int32 LayerId, const FVector2D& Start, const FVector2D& End, const FConnectionParams& Params) override
|
||||
{
|
||||
this->ConnectionDrawingPolicy->SetMousePosition(LocalMousePosition);
|
||||
this->ConnectionDrawingPolicy->DrawConnection(LayerId, Start, End, Params);
|
||||
SplineOverlapResult = FGraphSplineOverlapResult(this->ConnectionDrawingPolicy->SplineOverlapResult);
|
||||
}
|
||||
|
||||
~FENAnimGraphConnectionDrawingPolicy()
|
||||
{
|
||||
delete ConnectionDrawingPolicy;
|
||||
}
|
||||
|
||||
private:
|
||||
FENConnectionDrawingPolicy* ConnectionDrawingPolicy;
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ENConnectionDrawingPolicy.h"
|
||||
#include "BehaviorTreeEditor/Private/BehaviorTreeConnectionDrawingPolicy.h"
|
||||
|
||||
class FENBehaviorTreeConnectionDrawingPolicy : public FBehaviorTreeConnectionDrawingPolicy
|
||||
{
|
||||
public:
|
||||
FENBehaviorTreeConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj)
|
||||
: FBehaviorTreeConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj)
|
||||
{
|
||||
this->ConnectionDrawingPolicy = new FENConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj, true);
|
||||
}
|
||||
|
||||
virtual void DrawConnection(int32 LayerId, const FVector2D& Start, const FVector2D& End, const FConnectionParams& Params) override
|
||||
{
|
||||
this->ConnectionDrawingPolicy->SetMousePosition(LocalMousePosition);
|
||||
this->ConnectionDrawingPolicy->DrawConnection(LayerId, Start, End, Params);
|
||||
SplineOverlapResult = FGraphSplineOverlapResult(this->ConnectionDrawingPolicy->SplineOverlapResult);
|
||||
}
|
||||
|
||||
virtual void DrawSplineWithArrow(const FGeometry& StartGeom, const FGeometry& EndGeom, const FConnectionParams& Params) override
|
||||
{
|
||||
const FVector2D StartGeomDrawSize = StartGeom.GetDrawSize();
|
||||
const FVector2D StartCenter = FVector2D(
|
||||
StartGeom.AbsolutePosition.X + StartGeomDrawSize.X / 2,
|
||||
StartGeom.AbsolutePosition.Y + StartGeomDrawSize.Y);
|
||||
|
||||
const FVector2D EndGeomDrawSize = EndGeom.GetDrawSize();
|
||||
const FVector2D EndCenter = FVector2D(
|
||||
EndGeom.AbsolutePosition.X + EndGeomDrawSize.X / 2,
|
||||
EndGeom.AbsolutePosition.Y);
|
||||
|
||||
DrawSplineWithArrow(StartCenter, EndCenter, Params);
|
||||
}
|
||||
|
||||
virtual void DrawPreviewConnector(const FGeometry& PinGeometry, const FVector2D& StartPoint, const FVector2D& EndPoint, UEdGraphPin* Pin) override
|
||||
{
|
||||
FConnectionParams Params;
|
||||
DetermineWiringStyle(Pin, nullptr, /*inout*/ Params);
|
||||
|
||||
if (Pin->Direction == EEdGraphPinDirection::EGPD_Output)
|
||||
{
|
||||
const FVector2D GeomDrawSize = PinGeometry.GetDrawSize();
|
||||
const FVector2D Center = FVector2D(
|
||||
PinGeometry.AbsolutePosition.X + GeomDrawSize.X / 2,
|
||||
PinGeometry.AbsolutePosition.Y + GeomDrawSize.Y);
|
||||
|
||||
DrawSplineWithArrow(Center, EndPoint, Params);
|
||||
}
|
||||
else
|
||||
{
|
||||
const FVector2D GeomDrawSize = PinGeometry.GetDrawSize();
|
||||
const FVector2D Center = FVector2D(
|
||||
PinGeometry.AbsolutePosition.X + GeomDrawSize.X / 2,
|
||||
PinGeometry.AbsolutePosition.Y);
|
||||
|
||||
DrawSplineWithArrow(StartPoint, Center, Params);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void DrawSplineWithArrow(const FVector2D& StartAnchorPoint, const FVector2D& EndAnchorPoint, const FConnectionParams& Params) override
|
||||
{
|
||||
// bUserFlag1 indicates that we need to reverse the direction of connection (used by debugger)
|
||||
const FVector2D& P0 = Params.bUserFlag1 ? EndAnchorPoint : StartAnchorPoint;
|
||||
const FVector2D& P1 = Params.bUserFlag1 ? StartAnchorPoint : EndAnchorPoint;
|
||||
|
||||
Internal_DrawLineWithStraightArrow(P0, P1, Params);
|
||||
}
|
||||
|
||||
void Internal_DrawLineWithStraightArrow(const FVector2D& StartAnchorPoint, const FVector2D& EndAnchorPoint, const FConnectionParams& Params)
|
||||
{
|
||||
DrawConnection(WireLayerID, StartAnchorPoint, EndAnchorPoint, Params);
|
||||
|
||||
const FVector2D ArrowDrawPos = EndAnchorPoint - ArrowRadius;
|
||||
|
||||
FSlateDrawElement::MakeRotatedBox(
|
||||
DrawElementsList,
|
||||
ArrowLayerID,
|
||||
FPaintGeometry(ArrowDrawPos, ArrowImage->ImageSize * ZoomFactor, ZoomFactor),
|
||||
ArrowImage,
|
||||
ESlateDrawEffect::None,
|
||||
HALF_PI,
|
||||
TOptional<FVector2D>(),
|
||||
FSlateDrawElement::RelativeToElement,
|
||||
Params.WireColor
|
||||
);
|
||||
}
|
||||
|
||||
~FENBehaviorTreeConnectionDrawingPolicy()
|
||||
{
|
||||
delete ConnectionDrawingPolicy;
|
||||
}
|
||||
|
||||
private:
|
||||
FENConnectionDrawingPolicy* ConnectionDrawingPolicy;
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ENConnectionDrawingPolicy.h"
|
||||
#include "ControlRigEditor/Private/Graph/ControlRigConnectionDrawingPolicy.cpp"
|
||||
|
||||
class FENControlRigConnectionDrawingPolicy : public FControlRigConnectionDrawingPolicy
|
||||
{
|
||||
public:
|
||||
FENControlRigConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj)
|
||||
: FControlRigConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj)
|
||||
{
|
||||
this->ConnectionDrawingPolicy = new FENConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj);
|
||||
}
|
||||
|
||||
virtual void DrawConnection(int32 LayerId, const FVector2D& Start, const FVector2D& End, const FConnectionParams& Params) override
|
||||
{
|
||||
this->ConnectionDrawingPolicy->SetMousePosition(LocalMousePosition);
|
||||
this->ConnectionDrawingPolicy->DrawConnection(LayerId, Start, End, Params);
|
||||
SplineOverlapResult = FGraphSplineOverlapResult(this->ConnectionDrawingPolicy->SplineOverlapResult);
|
||||
}
|
||||
|
||||
~FENControlRigConnectionDrawingPolicy()
|
||||
{
|
||||
delete ConnectionDrawingPolicy;
|
||||
}
|
||||
|
||||
private:
|
||||
FENConnectionDrawingPolicy* ConnectionDrawingPolicy;
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/DeveloperSettings.h"
|
||||
#include "ENUpdateConfig.generated.h"
|
||||
|
||||
UCLASS(config = EditorPerProjectUserSettings)
|
||||
class ELECTRONICNODES_API UENUpdateConfig : public UDeveloperSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UENUpdateConfig()
|
||||
{
|
||||
}
|
||||
|
||||
UPROPERTY(config)
|
||||
FString PluginVersionUpdate = "";
|
||||
};
|
||||
@@ -0,0 +1,191 @@
|
||||
#include "ENUpdatePopup.h"
|
||||
|
||||
#include "ENUpdateConfig.h"
|
||||
#include "Widgets/Layout/SScrollBox.h"
|
||||
#include "Widgets/Text/SRichTextBlock.h"
|
||||
#include "SWebBrowser.h"
|
||||
#include "Interfaces/IPluginManager.h"
|
||||
|
||||
void ENUpdatePopup::OnBrowserLinkClicked(const FSlateHyperlinkRun::FMetadata& Metadata)
|
||||
{
|
||||
const FString* URL = Metadata.Find(TEXT("href"));
|
||||
|
||||
if (URL)
|
||||
{
|
||||
FPlatformProcess::LaunchURL(**URL, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void ENUpdatePopup::Register()
|
||||
{
|
||||
const FString PluginDirectory = IPluginManager::Get().FindPlugin(TEXT("ElectronicNodes"))->GetBaseDir();
|
||||
const FString UpdatedConfigFile = PluginDirectory + "/UpdateConfig.ini";
|
||||
const FString CurrentPluginVersion = "3.5";
|
||||
|
||||
UENUpdateConfig* ENUpdatePopupConfig = GetMutableDefault<UENUpdateConfig>();
|
||||
|
||||
if (FPaths::FileExists(UpdatedConfigFile))
|
||||
{
|
||||
ENUpdatePopupConfig->LoadConfig(nullptr, *UpdatedConfigFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
ENUpdatePopupConfig->SaveConfig(CPF_Config, *UpdatedConfigFile);
|
||||
}
|
||||
|
||||
if (ENUpdatePopupConfig->PluginVersionUpdate != CurrentPluginVersion)
|
||||
{
|
||||
ENUpdatePopupConfig->PluginVersionUpdate = CurrentPluginVersion;
|
||||
ENUpdatePopupConfig->SaveConfig(CPF_Config, *UpdatedConfigFile);
|
||||
|
||||
FCoreDelegates::OnPostEngineInit.AddLambda([]()
|
||||
{
|
||||
Open();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ENUpdatePopup::Open()
|
||||
{
|
||||
if (!FSlateApplication::Get().CanDisplayWindows())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TSharedRef<SBorder> WindowContent = SNew(SBorder)
|
||||
.BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder"))
|
||||
.Padding(FMargin(8.0f, 8.0f));
|
||||
|
||||
TSharedPtr<SWindow> Window = SNew(SWindow)
|
||||
.AutoCenter(EAutoCenter::PreferredWorkArea)
|
||||
.SupportsMaximize(false)
|
||||
.SupportsMinimize(false)
|
||||
.SizingRule(ESizingRule::FixedSize)
|
||||
.ClientSize(FVector2D(600, 400))
|
||||
.Title(FText::FromString("Electronic Nodes"))
|
||||
.IsTopmostWindow(true)
|
||||
[
|
||||
WindowContent
|
||||
];
|
||||
|
||||
const FSlateFontInfo HeadingFont = FCoreStyle::GetDefaultFontStyle("Regular", 24);
|
||||
const FSlateFontInfo ContentFont = FCoreStyle::GetDefaultFontStyle("Regular", 12);
|
||||
|
||||
TSharedRef<SVerticalBox> InnerContent = SNew(SVerticalBox)
|
||||
// Default settings example
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(10)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Font(HeadingFont)
|
||||
.Text(FText::FromString("Electronic Nodes v3.5"))
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.FillHeight(1.0)
|
||||
.Padding(10)
|
||||
[
|
||||
SNew(SBorder)
|
||||
.Padding(10)
|
||||
.BorderImage(FEditorStyle::GetBrush("ToolPanel.DarkGroupBorder"))
|
||||
[
|
||||
SNew(SScrollBox)
|
||||
+ SScrollBox::Slot()
|
||||
[
|
||||
SNew(SRichTextBlock)
|
||||
.Text(FText::FromString(R"(
|
||||
<LargeText>Hello and thank you for using Electronic Nodes!</>
|
||||
|
||||
First thing first, if you've been enjoying using it, it would mean a lot if you could just drop <a id="browser" href="https://bit.ly/2U1YT8O">a small review on the marketplace page</> :). I also wanted to mention that I made another plugin to update the UE4 style called <a id="browser" href="https://bit.ly/2TolSKQ">Darker Nodes</>.
|
||||
|
||||
But let's keep it short, here are the cool new features (and bugfixes) of version 3.5!
|
||||
|
||||
|
||||
<LargeText>Version 3.5</>
|
||||
|
||||
<RichTextBlock.Bold>Features</>
|
||||
|
||||
* Add support for custom graphs (<a id="browser" href="https://github.com/hugoattal/ElectronicNodes/issues/50">issue #50</>)
|
||||
|
||||
|
||||
<LargeText>Version 3.4</>
|
||||
|
||||
<RichTextBlock.Bold>Features</>
|
||||
|
||||
* Add Reference Viewer support (<a id="browser" href="https://github.com/hugoattal/ElectronicNodes/issues/45">issue #45</>)
|
||||
* Add quick restart toast when updating "Use Hot Patch" setting
|
||||
|
||||
<RichTextBlock.Bold>Bug fixes</>
|
||||
|
||||
* Fix hover on short wires (<a id="browser" href="https://github.com/hugoattal/ElectronicNodes/issues/46">issue #46</>)
|
||||
* Fix alignment bug on manhattan wires (<a id="browser" href="https://github.com/hugoattal/ElectronicNodes/issues/42">issue #42</>)
|
||||
* Fix crash on headless mode (<a id="browser" href="https://github.com/hugoattal/ElectronicNodes/issues/43">issue #43</>)
|
||||
|
||||
|
||||
<a id="browser" href="https://github.com/hugoattal/ElectronicNodes#changelog">See complete changelog</>
|
||||
)"))
|
||||
.TextStyle(FEditorStyle::Get(), "NormalText")
|
||||
.DecoratorStyleSet(&FEditorStyle::Get())
|
||||
.AutoWrapText(true)
|
||||
+ SRichTextBlock::HyperlinkDecorator(TEXT("browser"), FSlateHyperlinkRun::FOnClick::CreateStatic(&OnBrowserLinkClicked))
|
||||
]
|
||||
]
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(10)
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot().FillWidth(1.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(FText::FromString("Leave a review <3"))
|
||||
.HAlign(HAlign_Center)
|
||||
.OnClicked_Lambda([]()
|
||||
{
|
||||
const FString URL = "https://bit.ly/2RPhNPl";
|
||||
FPlatformProcess::LaunchURL(*URL, nullptr, nullptr);
|
||||
|
||||
return FReply::Handled();
|
||||
})
|
||||
]
|
||||
+ SHorizontalBox::Slot().AutoWidth()
|
||||
[
|
||||
SNew(SSpacer)
|
||||
.Size(FVector2D(20, 10))
|
||||
]
|
||||
+ SHorizontalBox::Slot().FillWidth(1.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(FText::FromString("Discover Darker Nodes"))
|
||||
.HAlign(HAlign_Center)
|
||||
.OnClicked_Lambda([]()
|
||||
{
|
||||
const FString URL = "https://bit.ly/3vqUdGE";
|
||||
FPlatformProcess::LaunchURL(*URL, nullptr, nullptr);
|
||||
|
||||
return FReply::Handled();
|
||||
})
|
||||
]
|
||||
+ SHorizontalBox::Slot().AutoWidth()
|
||||
[
|
||||
SNew(SSpacer)
|
||||
.Size(FVector2D(20, 10))
|
||||
]
|
||||
+ SHorizontalBox::Slot().FillWidth(1.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(FText::FromString("Close this window"))
|
||||
.HAlign(HAlign_Center)
|
||||
.OnClicked_Lambda([Window]()
|
||||
{
|
||||
Window->RequestDestroyWindow();
|
||||
|
||||
return FReply::Handled();
|
||||
})
|
||||
]
|
||||
];
|
||||
|
||||
WindowContent->SetContent(InnerContent);
|
||||
Window = FSlateApplication::Get().AddWindow(Window.ToSharedRef());
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include "Framework/Text/SlateHyperlinkRun.h"
|
||||
|
||||
class ENUpdatePopup
|
||||
{
|
||||
public:
|
||||
static void Register();
|
||||
static void Open();
|
||||
static void OnBrowserLinkClicked(const FSlateHyperlinkRun::FMetadata& Metadata);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
#include "ElectronicNodesSettings.h"
|
||||
#include "Modules/ModuleInterface.h"
|
||||
|
||||
class FElectronicNodesModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
void ToggleMasterActivation() const;
|
||||
|
||||
private:
|
||||
UElectronicNodesSettings* ElectronicNodesSettings = nullptr;
|
||||
FString PluginDirectory;
|
||||
FString GlobalSettingsFile;
|
||||
|
||||
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION <= 25
|
||||
void ReloadConfiguration(FName PropertyName);
|
||||
#else
|
||||
void ReloadConfiguration(UObject* Object, struct FPropertyChangedEvent& Property);
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,263 @@
|
||||
/* Copyright (C) 2021 Hugo ATTAL - All Rights Reserved
|
||||
* This plugin is downloadable from the UE4 Marketplace
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/DeveloperSettings.h"
|
||||
#include "ElectronicNodesSettings.generated.h"
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EWireStyle : uint8
|
||||
{
|
||||
Default,
|
||||
Manhattan,
|
||||
Subway
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EWireAlignment : uint8
|
||||
{
|
||||
Right,
|
||||
Left
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EWirePriority : uint8
|
||||
{
|
||||
None,
|
||||
Node,
|
||||
Pin
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EBubbleDisplayRule : uint8
|
||||
{
|
||||
Always,
|
||||
DisplayOnSelection,
|
||||
MoveOnSelection
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class ESelectionRule : uint8
|
||||
{
|
||||
Near,
|
||||
Far
|
||||
};
|
||||
|
||||
UCLASS(config = EditorPerProjectUserSettings, meta = (DisplayName = "Electronic Nodes Plugin"))
|
||||
class ELECTRONICNODES_API UElectronicNodesSettings : public UDeveloperSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UElectronicNodesSettings()
|
||||
{
|
||||
CategoryName = TEXT("Plugins");
|
||||
SectionName = TEXT("Electronic Nodes Plugin");
|
||||
}
|
||||
|
||||
/* -----[ Activation ] ----- */
|
||||
|
||||
/* Activate or deactivate the whole plugin. Default: true */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation")
|
||||
bool MasterActivate = true;
|
||||
|
||||
/* Use global settings across all your projects. When activated, it will load the global settings (overwriting this one).
|
||||
If no global settings exists, it will create it based on this one. Future updates will then be saved to global settings. */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation")
|
||||
bool UseGlobalSettings = false;
|
||||
|
||||
/* Force reload the global settings (if it was modified outside this instance for example). */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation", meta = (EditCondition = "UseGlobalSettings"))
|
||||
bool LoadGlobalSettings = false;
|
||||
|
||||
/* Display a popup with changelog on update. Default: tru */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation")
|
||||
bool ActivatePopupOnUpdate = true;
|
||||
|
||||
/* Activate Electronic Nodes on Blueprint graphs. Default: true */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation|Schema", meta = (EditCondition = "MasterActivate"))
|
||||
bool ActivateOnBlueprint = true;
|
||||
|
||||
/* Activate Electronic Nodes on Material graphs. Default: true */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation|Schema", meta = (EditCondition = "MasterActivate"))
|
||||
bool ActivateOnMaterial = true;
|
||||
|
||||
/* Activate Electronic Nodes on Animation graphs. Default: true */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation|Schema", meta = (EditCondition = "MasterActivate"))
|
||||
bool ActivateOnAnimation = true;
|
||||
|
||||
/* Activate Electronic Nodes on VoxelPlugin (available on the marketplace). Default: true */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation|Schema", meta = (EditCondition = "MasterActivate"))
|
||||
bool ActivateOnVoxelPlugin = true;
|
||||
|
||||
/* Hot patch hardcoded Unreal functions (only available on Windows) to make some more features available. NEED A RESTART OF THE ENGINE! Default: true */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation|Schema", meta = (EditCondition = "MasterActivate"))
|
||||
bool UseHotPatch = true;
|
||||
|
||||
/* Activate Electronic Nodes on Niagara. Default: true */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation|Schema", meta = (EditCondition = "MasterActivate && UseHotPatch"))
|
||||
bool ActivateOnNiagara = true;
|
||||
|
||||
/* Activate Electronic Nodes on Behavior Tree. Default: false */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation|Schema", meta = (EditCondition = "MasterActivate && UseHotPatch"))
|
||||
bool ActivateOnBehaviorTree = true;
|
||||
|
||||
/* Activate Electronic Nodes on Control Rig. Default: true */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation|Schema", meta = (EditCondition = "MasterActivate && UseHotPatch"))
|
||||
bool ActivateOnControlRig = true;
|
||||
|
||||
/* Activate Electronic Nodes on Reference Viewer. Default: true */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation|Schema", meta = (EditCondition = "MasterActivate && UseHotPatch"))
|
||||
bool ActivateOnReferenceViewer = true;
|
||||
|
||||
/* Activate Electronic Nodes on custom graphs. WARNING: some graphs might need Hot Patch, and some graphs might not work at all */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation|Schema", meta = (EditCondition = "MasterActivate"))
|
||||
TArray<TSubclassOf<UEdGraphSchema>> CustomGraphSchemas;
|
||||
|
||||
/* Activate Electronic Nodes everywhere, for debugging purpose only. Default: false */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation|Debug", meta = (EditCondition = "MasterActivate"))
|
||||
bool ActivateFallback = false;
|
||||
|
||||
/* Display schema name in log. Default: false */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Activation|Debug", meta = (EditCondition = "MasterActivate"))
|
||||
bool DisplaySchemaName = false;
|
||||
|
||||
/* -----[ Wire Style ] ----- */
|
||||
|
||||
/* Wire style of graph. "Manhattan" is for 90deg angles, "Subway" is for 45deg angles. */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Wire Style")
|
||||
EWireStyle WireStyle = EWireStyle::Subway;
|
||||
|
||||
/* Specify wire alignment. Default: right. */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Wire Style")
|
||||
EWireAlignment WireAlignment = EWireAlignment::Right;
|
||||
|
||||
/* Specify wire alignment priority (when a Node is connected to a Pin). Default: none. */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Wire Style")
|
||||
EWirePriority WirePriority = EWirePriority::None;
|
||||
|
||||
/* Round radius of the wires. Default: 10 */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Wire Style")
|
||||
uint32 RoundRadius = 10;
|
||||
|
||||
/* Thickness of the wire (multiplier). Default: 1 */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Wire Style", meta = (ClampMin = "0.0"))
|
||||
float WireThickness = 1.0f;
|
||||
|
||||
/* Bellow this distance, wires will be drawn as straight. Default: 24 */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Wire Style", meta = (ClampMin = "0.0"))
|
||||
float MinDistanceToStyle = 24.0f;
|
||||
|
||||
/* Horizontal offset of wires from nodes. Default: 16 */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Wire Style")
|
||||
uint32 HorizontalOffset = 16;
|
||||
|
||||
/* Disable the offset for pins. Default: false */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Wire Style")
|
||||
bool DisablePinOffset = false;
|
||||
|
||||
/* Fix default zoomed-out wire displacement. Default: true */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Wire Style")
|
||||
bool FixZoomDisplacement = true;
|
||||
|
||||
/* -----[ Exec Wire Style ] ----- */
|
||||
|
||||
/* Use a specific draw style for exec wires. Default: false */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Exec Wire Style")
|
||||
bool OverwriteExecWireStyle = false;
|
||||
|
||||
/* Specific wire style for exec wires. Default: Manhattan */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Exec Wire Style", meta = (EditCondition = "OverwriteExecWireStyle"))
|
||||
EWireStyle WireStyleForExec = EWireStyle::Manhattan;
|
||||
|
||||
/* Specify wire alignment for exe wires. Default: right. */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Exec Wire Style", meta = (EditCondition = "OverwriteExecWireStyle"))
|
||||
EWireAlignment WireAlignmentForExec = EWireAlignment::Right;
|
||||
|
||||
/* Specify wire alignment priority (when a Node is connected to a Pin) for exe wires. Default: node. */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Exec Wire Style", meta = (EditCondition = "OverwriteExecWireStyle"))
|
||||
EWirePriority WirePriorityForExec = EWirePriority::Node;
|
||||
|
||||
/* -----[ Ribbon Style ] ----- */
|
||||
|
||||
/* Activate ribbon cables for overlapping wires. */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Ribbon Style (experimental)")
|
||||
bool ActivateRibbon = false;
|
||||
|
||||
/* Offset between ribbon wires. Default: 4 */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Ribbon Style (experimental)", meta = (EditCondition = "ActivateRibbon"))
|
||||
uint32 RibbonOffset = 4;
|
||||
|
||||
/* Offset of wires when merge into ribbon. Default: 20 */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Ribbon Style (experimental)", meta = (EditCondition = "ActivateRibbon"))
|
||||
uint32 RibbonMergeOffset = 20;
|
||||
|
||||
/* Push the offset outside the node (instead of going for the middle). Default: false */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Ribbon Style (experimental)", meta = (EditCondition = "ActivateRibbon"))
|
||||
bool RibbonPushOutside = false;
|
||||
|
||||
/* -----[ Bubble Style ] ----- */
|
||||
|
||||
/* Show moving bubbles on the wires. Default: false */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Bubbles Style")
|
||||
bool ForceDrawBubbles = false;
|
||||
|
||||
/* Display rules to show/move bubbles only near selected nodes. Default: Always */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Bubbles Style", meta = (EditCondition = "ForceDrawBubbles"))
|
||||
EBubbleDisplayRule BubbleDisplayRule = EBubbleDisplayRule::Always;
|
||||
|
||||
/* If selection only consider close nodes (near) or every related nodes (far). Default: Near */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Bubbles Style", meta = (EditCondition = "ForceDrawBubbles"))
|
||||
ESelectionRule SelectionRule = ESelectionRule::Near;
|
||||
|
||||
/* Disable bubbles above a certain zoom level. Default: -2 */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Bubbles Style", meta = (EditCondition = "ForceDrawBubbles", ClampMin = "-12", ClampMax = "7"))
|
||||
int32 BubbleZoomThreshold = -2;
|
||||
|
||||
/* Size of bubbles on the wires. Default: 2.0 */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Bubbles Style", meta = (EditCondition = "ForceDrawBubbles", ClampMin = "1.0"))
|
||||
float BubbleSize = 2.0f;
|
||||
|
||||
/* Speed of bubbles on the wires. Default: 4.0 */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Bubbles Style", meta = (EditCondition = "ForceDrawBubbles", ClampMin = "0.0"))
|
||||
float BubbleSpeed = 4.0f;
|
||||
|
||||
/* Space between bubbles on the wires. Default: 20.0 */
|
||||
UPROPERTY(config, EditAnywhere, Category = "Bubbles Style", meta = (EditCondition = "ForceDrawBubbles", ClampMin = "10.0"))
|
||||
float BubbleSpace = 20.0f;
|
||||
|
||||
bool Debug = false;
|
||||
|
||||
/* Internal value to fix elements on plugin update. */
|
||||
UPROPERTY(config)
|
||||
FString PluginVersionUpdate = "";
|
||||
|
||||
virtual FName GetContainerName() const override
|
||||
{
|
||||
return "Editor";
|
||||
}
|
||||
|
||||
void ToggleMasterActivation()
|
||||
{
|
||||
MasterActivate = !MasterActivate;
|
||||
}
|
||||
|
||||
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION <= 25
|
||||
DECLARE_EVENT_OneParam(UDarkerNodesSettings, FSettingChangedEvent, FName);
|
||||
FSettingChangedEvent& OnSettingChanged( ) { return SettingChangedEvent; }
|
||||
|
||||
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override
|
||||
{
|
||||
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||
|
||||
const FName Name = (PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None;
|
||||
SettingChangedEvent.Broadcast(Name);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
FSettingChangedEvent SettingChangedEvent;
|
||||
#endif
|
||||
};
|
||||
3
Plugins/ElectronicNodes/UpdateConfig.ini
Normal file
3
Plugins/ElectronicNodes/UpdateConfig.ini
Normal file
@@ -0,0 +1,3 @@
|
||||
[/Script/ElectronicNodes.ENUpdateConfig]
|
||||
PluginVersionUpdate=3.5
|
||||
|
||||
Reference in New Issue
Block a user