Adds plugins

This commit is contained in:
2021-11-18 15:24:24 +01:00
parent 748b0804b8
commit b94590ab4f
529 changed files with 10429 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
[FilterPlugin]
/Config/
/Resources/
/Source/

View 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

Binary file not shown.

View File

@@ -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[]
{ }
);
}
}

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -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;
};

View File

@@ -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;
}
}

View File

@@ -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;
};

View File

@@ -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)

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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 = "";
};

View File

@@ -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());
}

View File

@@ -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);
};

View File

@@ -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
};

View File

@@ -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
};

View File

@@ -0,0 +1,3 @@
[/Script/ElectronicNodes.ENUpdateConfig]
PluginVersionUpdate=3.5