2014-03-14 14:13:41 -04:00
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
# include "GraphEditorCommon.h"
# include "ConnectionDrawingPolicy.h"
# include "Editor/UnrealEd/Public/Kismet2/KismetDebugUtilities.h"
# include "Editor/UnrealEd/Public/Kismet2/BlueprintEditorUtils.h"
# include "KismetNodes/KismetNodeInfoContext.h"
# include "SoundDefinitions.h"
# include "AnimationStateNodes/SGraphNodeAnimTransition.h"
2014-04-24 08:49:31 -04:00
# include "AnimStateTransitionNode.h"
# include "AnimStateEntryNode.h"
# include "AnimationGraphSchema.h"
# include "AnimGraphNode_Base.h"
2014-05-29 16:47:05 -04:00
# include "Sound/SoundNode.h"
2014-03-14 14:13:41 -04:00
2014-04-23 17:45:37 -04:00
DEFINE_LOG_CATEGORY_STATIC ( LogConnectionDrawingPolicy , Log , All ) ;
2014-03-14 14:13:41 -04:00
//////////////////////////////////////////////////////////////////////////
// FGeometryHelper
FVector2D FGeometryHelper : : VerticalMiddleLeftOf ( const FGeometry & SomeGeometry )
{
const FVector2D GeometryDrawSize = SomeGeometry . GetDrawSize ( ) ;
return FVector2D (
SomeGeometry . AbsolutePosition . X ,
SomeGeometry . AbsolutePosition . Y + GeometryDrawSize . Y / 2 ) ;
}
FVector2D FGeometryHelper : : VerticalMiddleRightOf ( const FGeometry & SomeGeometry )
{
const FVector2D GeometryDrawSize = SomeGeometry . GetDrawSize ( ) ;
return FVector2D (
SomeGeometry . AbsolutePosition . X + GeometryDrawSize . X ,
SomeGeometry . AbsolutePosition . Y + GeometryDrawSize . Y / 2 ) ;
}
FVector2D FGeometryHelper : : CenterOf ( const FGeometry & SomeGeometry )
{
const FVector2D GeometryDrawSize = SomeGeometry . GetDrawSize ( ) ;
return SomeGeometry . AbsolutePosition + ( GeometryDrawSize * 0.5f ) ;
}
void FGeometryHelper : : ConvertToPoints ( const FGeometry & Geom , TArray < FVector2D > & Points )
{
const FVector2D Size = Geom . GetDrawSize ( ) ;
const FVector2D Location = Geom . AbsolutePosition ;
int32 Index = Points . AddUninitialized ( 4 ) ;
Points [ Index + + ] = Location ;
Points [ Index + + ] = Location + FVector2D ( 0.0f , Size . Y ) ;
Points [ Index + + ] = Location + FVector2D ( Size . X , Size . Y ) ;
Points [ Index + + ] = Location + FVector2D ( Size . X , 0.0f ) ;
}
/** Find the point on line segment from LineStart to LineEnd which is closest to Point */
FVector2D FGeometryHelper : : FindClosestPointOnLine ( const FVector2D & LineStart , const FVector2D & LineEnd , const FVector2D & TestPoint )
{
const FVector2D LineVector = LineEnd - LineStart ;
const float A = - FVector2D : : DotProduct ( LineStart - TestPoint , LineVector ) ;
const float B = LineVector . SizeSquared ( ) ;
const float T = FMath : : Clamp < float > ( A / B , 0.0f , 1.0f ) ;
// Generate closest point
return LineStart + ( T * LineVector ) ;
}
FVector2D FGeometryHelper : : FindClosestPointOnGeom ( const FGeometry & Geom , const FVector2D & TestPoint )
{
TArray < FVector2D > Points ;
FGeometryHelper : : ConvertToPoints ( Geom , Points ) ;
float BestDistanceSquared = MAX_FLT ;
FVector2D BestPoint ;
for ( int32 i = 0 ; i < Points . Num ( ) ; + + i )
{
const FVector2D Candidate = FindClosestPointOnLine ( Points [ i ] , Points [ ( i + 1 ) % Points . Num ( ) ] , TestPoint ) ;
const float CandidateDistanceSquared = ( Candidate - TestPoint ) . SizeSquared ( ) ;
if ( CandidateDistanceSquared < BestDistanceSquared )
{
BestPoint = Candidate ;
BestDistanceSquared = CandidateDistanceSquared ;
}
}
return BestPoint ;
}
//////////////////////////////////////////////////////////////////////////
// FKismetNodeInfoContext
// Context used to aid debugging displays for nodes
FKismetNodeInfoContext : : FKismetNodeInfoContext ( UEdGraph * SourceGraph )
: ActiveObjectBeingDebugged ( NULL )
{
// Only show pending latent actions in PIE/SIE mode
SourceBlueprint = FBlueprintEditorUtils : : FindBlueprintForGraph ( SourceGraph ) ;
if ( SourceBlueprint ! = NULL )
{
ActiveObjectBeingDebugged = SourceBlueprint - > GetObjectBeingDebugged ( ) ;
// Run thru debugged objects to see if any are objects with pending latent actions
if ( ActiveObjectBeingDebugged ! = NULL )
{
UBlueprintGeneratedClass * Class = CastChecked < UBlueprintGeneratedClass > ( ( UObject * ) ( ActiveObjectBeingDebugged - > GetClass ( ) ) ) ;
FBlueprintDebugData const & ClassDebugData = Class - > GetDebugData ( ) ;
TSet < UObject * > LatentContextObjects ;
TArray < UK2Node_CallFunction * > FunctionNodes ;
SourceGraph - > GetNodesOfClass < UK2Node_CallFunction > ( FunctionNodes ) ;
// collect all the world context objects for all of the graph's latent nodes
for ( UK2Node_CallFunction const * FunctionNode : FunctionNodes )
{
UFunction * Function = FunctionNode - > GetTargetFunction ( ) ;
if ( ( Function = = NULL ) | | ! Function - > HasMetaData ( FBlueprintMetadata : : MD_Latent ) )
{
continue ;
}
UObject * NodeWorldContext = ActiveObjectBeingDebugged ;
// if the node has a specific "world context" pin, attempt to get the value set for that first
if ( Function - > HasMetaData ( FBlueprintMetadata : : MD_WorldContext ) )
{
FString const WorldContextPinName = Function - > GetMetaData ( FBlueprintMetadata : : MD_WorldContext ) ;
if ( UEdGraphPin * ContextPin = FunctionNode - > FindPin ( WorldContextPinName ) )
{
if ( UObjectPropertyBase * ContextProperty = Cast < UObjectPropertyBase > ( ClassDebugData . FindClassPropertyForPin ( ContextPin ) ) )
{
UObject * PropertyValue = ContextProperty - > GetObjectPropertyValue_InContainer ( ActiveObjectBeingDebugged ) ;
if ( PropertyValue ! = NULL )
{
NodeWorldContext = PropertyValue ;
}
}
}
}
LatentContextObjects . Add ( NodeWorldContext ) ;
}
for ( UObject * ContextObject : LatentContextObjects )
{
if ( UWorld * World = GEngine - > GetWorldFromContextObject ( ContextObject , /*bChecked =*/ false ) )
{
FLatentActionManager & Manager = World - > GetLatentActionManager ( ) ;
TSet < int32 > UUIDSet ;
Manager . GetActiveUUIDs ( ActiveObjectBeingDebugged , /*out*/ UUIDSet ) ;
for ( TSet < int32 > : : TConstIterator IterUUID ( UUIDSet ) ; IterUUID ; + + IterUUID )
{
const int32 UUID = * IterUUID ;
if ( UEdGraphNode * ParentNode = ClassDebugData . FindNodeFromUUID ( UUID ) )
{
TArray < FObjectUUIDPair > & Pairs = NodesWithActiveLatentActions . FindOrAdd ( ParentNode ) ;
new ( Pairs ) FObjectUUIDPair ( ContextObject , UUID ) ;
}
}
}
}
}
// Covert the watched pin array into a set
for ( auto WatchedPinIt = SourceBlueprint - > PinWatches . CreateConstIterator ( ) ; WatchedPinIt ; + + WatchedPinIt )
{
UEdGraphPin * WatchedPin = * WatchedPinIt ;
UEdGraphNode * OwningNode = Cast < UEdGraphNode > ( WatchedPin - > GetOuter ( ) ) ;
if ( ! ensure ( OwningNode ! = NULL ) ) // shouldn't happen, but just in case a dead pin was added to the PinWatches array
{
continue ;
}
check ( OwningNode = = WatchedPin - > GetOwningNode ( ) ) ;
WatchedPinSet . Add ( WatchedPin ) ;
WatchedNodeSet . Add ( OwningNode ) ;
}
}
}
/////////////////////////////////////////////////////
// FConnectionDrawingPolicy
FConnectionDrawingPolicy : : FConnectionDrawingPolicy ( int32 InBackLayerID , int32 InFrontLayerID , float InZoomFactor , const FSlateRect & InClippingRect , FSlateWindowElementList & InDrawElements )
: WireLayerID ( InBackLayerID )
, ArrowLayerID ( InFrontLayerID )
, ZoomFactor ( InZoomFactor )
, ClippingRect ( InClippingRect )
, DrawElementsList ( InDrawElements )
{
ArrowImage = FEditorStyle : : GetBrush ( TEXT ( " Graph.Arrow " ) ) ;
ArrowRadius = ArrowImage - > ImageSize * ZoomFactor * 0.5f ;
MidpointImage = NULL ;
MidpointRadius = FVector2D : : ZeroVector ;
BubbleImage = FEditorStyle : : GetBrush ( TEXT ( " Graph.ExecutionBubble " ) ) ;
}
void FConnectionDrawingPolicy : : DrawSplineWithArrow ( const FVector2D & StartPoint , const FVector2D & EndPoint , const FLinearColor & WireColor , float WireThickness , bool bDrawBubbles , bool Bidirectional )
{
// Draw the spline
DrawConnection (
WireLayerID ,
StartPoint ,
EndPoint ,
WireColor ,
WireThickness ,
bDrawBubbles ) ;
// Draw the arrow
if ( ArrowImage ! = nullptr )
{
FVector2D ArrowPoint = EndPoint - ArrowRadius ;
FSlateDrawElement : : MakeBox (
DrawElementsList ,
ArrowLayerID ,
FPaintGeometry ( ArrowPoint , ArrowImage - > ImageSize * ZoomFactor , ZoomFactor ) ,
ArrowImage ,
ClippingRect ,
ESlateDrawEffect : : None ,
WireColor
) ;
}
}
void FConnectionDrawingPolicy : : DrawSplineWithArrow ( FGeometry & StartGeom , FGeometry & EndGeom , const FLinearColor & WireColor , float WireThickness , bool bDrawBubbles , bool Bidirectional )
{
const FVector2D StartPoint = FGeometryHelper : : VerticalMiddleRightOf ( StartGeom ) ;
const FVector2D EndPoint = FGeometryHelper : : VerticalMiddleLeftOf ( EndGeom ) - FVector2D ( ArrowRadius . X , 0 ) ;
DrawSplineWithArrow ( StartPoint , EndPoint , WireColor , WireThickness , bDrawBubbles , Bidirectional ) ;
}
// Update the drawing policy with the set of hovered pins (which can be empty)
void FConnectionDrawingPolicy : : SetHoveredPins ( const TSet < TWeakObjectPtr < UEdGraphPin > > & InHoveredPins , const TArray < TSharedPtr < SGraphPin > > & OverridePins , double HoverTime )
{
HoveredPins . Empty ( ) ;
LastHoverTimeEvent = ( OverridePins . Num ( ) > 0 ) ? 0.0 : HoverTime ;
for ( auto PinIt = OverridePins . CreateConstIterator ( ) ; PinIt ; + + PinIt )
{
if ( SGraphPin * GraphPin = PinIt - > Get ( ) )
{
HoveredPins . Add ( GraphPin - > GetPinObj ( ) ) ;
}
}
// Convert the widget pointer for hovered pins to be EdGraphPin pointers for their connected nets (both ends of any connection)
for ( auto PinIt = InHoveredPins . CreateConstIterator ( ) ; PinIt ; + + PinIt )
{
if ( UEdGraphPin * Pin = PinIt - > Get ( ) )
{
if ( Pin - > LinkedTo . Num ( ) > 0 )
{
HoveredPins . Add ( Pin ) ;
for ( auto LinkIt = Pin - > LinkedTo . CreateConstIterator ( ) ; LinkIt ; + + LinkIt )
{
HoveredPins . Add ( * LinkIt ) ;
}
}
}
}
}
void FConnectionDrawingPolicy : : SetMarkedPin ( TWeakPtr < SGraphPin > InMarkedPin )
{
if ( InMarkedPin . IsValid ( ) )
{
LastHoverTimeEvent = 0.0 ;
UEdGraphPin * MarkedPin = InMarkedPin . Pin ( ) - > GetPinObj ( ) ;
HoveredPins . Add ( MarkedPin ) ;
for ( auto LinkIt = MarkedPin - > LinkedTo . CreateConstIterator ( ) ; LinkIt ; + + LinkIt )
{
HoveredPins . Add ( * LinkIt ) ;
}
}
}
/** Util to make a 'distance->alpha' table and also return spline length */
float FConnectionDrawingPolicy : : MakeSplineReparamTable ( const FVector2D & P0 , const FVector2D & P0Tangent , const FVector2D & P1 , const FVector2D & P1Tangent , FInterpCurve < float > & OutReparamTable )
{
OutReparamTable . Reset ( ) ;
const int32 NumSteps = 10 ; // TODO: Make this adaptive...
// Find range of input
float Param = 0.f ;
const float MaxInput = 1.f ;
const float Interval = ( MaxInput - Param ) / ( ( float ) ( NumSteps - 1 ) ) ;
// Add first entry, using first point on curve, total distance will be 0
FVector2D OldSplinePos = FMath : : CubicInterp ( P0 , P0Tangent , P1 , P1Tangent , Param ) ;
float TotalDist = 0.f ;
OutReparamTable . AddPoint ( TotalDist , Param ) ;
Param + = Interval ;
// Then work over rest of points
for ( int32 i = 1 ; i < NumSteps ; i + + )
{
// Iterate along spline at regular param intervals
const FVector2D NewSplinePos = FMath : : CubicInterp ( P0 , P0Tangent , P1 , P1Tangent , Param ) ;
TotalDist + = ( NewSplinePos - OldSplinePos ) . Size ( ) ;
OldSplinePos = NewSplinePos ;
OutReparamTable . AddPoint ( TotalDist , Param ) ;
Param + = Interval ;
}
return TotalDist ;
}
void FConnectionDrawingPolicy : : DrawConnection ( int32 LayerId , const FVector2D & Start , const FVector2D & End , const FLinearColor & InColor , float Thickness , bool bDrawBubbles )
{
const FVector2D & P0 = Start ;
const FVector2D & P1 = End ;
const int32 Tension = FMath : : Abs < int32 > ( Start . X - End . X ) ;
const FVector2D P0Tangent = Tension * FVector2D ( 1.0f , 0 ) ;
const FVector2D P1Tangent = P0Tangent ;
// Draw the spline itself
FSlateDrawElement : : MakeDrawSpaceSpline (
DrawElementsList ,
LayerId ,
P0 , P0Tangent ,
P1 , P1Tangent ,
ClippingRect ,
Thickness ,
ESlateDrawEffect : : None ,
InColor
) ;
if ( bDrawBubbles | | ( MidpointImage ! = NULL ) )
{
// This table maps distance along curve to alpha
FInterpCurve < float > SplineReparamTable ;
float SplineLength = MakeSplineReparamTable ( P0 , P0Tangent , P1 , P1Tangent , SplineReparamTable ) ;
// Draw bubbles on the spline
if ( bDrawBubbles )
{
const float BubbleSpacing = 64.f * ZoomFactor ;
const float BubbleSpeed = 192.f * ZoomFactor ;
const FVector2D BubbleSize = BubbleImage - > ImageSize * ZoomFactor * 0.1f * Thickness ;
float Time = ( FPlatformTime : : Seconds ( ) - GStartTime ) ;
const float BubbleOffset = FMath : : Fmod ( Time * BubbleSpeed , BubbleSpacing ) ;
2014-05-06 06:26:25 -04:00
const int32 NumBubbles = FMath : : CeilToInt ( SplineLength / BubbleSpacing ) ;
2014-03-14 14:13:41 -04:00
for ( int32 i = 0 ; i < NumBubbles ; + + i )
{
const float Distance = ( ( float ) i * BubbleSpacing ) + BubbleOffset ;
if ( Distance < SplineLength )
{
const float Alpha = SplineReparamTable . Eval ( Distance , 0.f ) ;
FVector2D BubblePos = FMath : : CubicInterp ( P0 , P0Tangent , P1 , P1Tangent , Alpha ) ;
BubblePos - = ( BubbleSize * 0.5f ) ;
FSlateDrawElement : : MakeBox (
DrawElementsList ,
LayerId ,
FPaintGeometry ( BubblePos , BubbleSize , ZoomFactor ) ,
BubbleImage ,
ClippingRect ,
ESlateDrawEffect : : None ,
InColor
) ;
}
}
}
// Draw the midpoint image
if ( MidpointImage ! = NULL )
{
// Determine the spline position for the midpoint
const float MidpointAlpha = SplineReparamTable . Eval ( SplineLength * 0.5f , 0.f ) ;
const FVector2D Midpoint = FMath : : CubicInterp ( P0 , P0Tangent , P1 , P1Tangent , MidpointAlpha ) ;
// Approximate the slope at the midpoint (to orient the midpoint image to the spline)
const FVector2D MidpointPlusE = FMath : : CubicInterp ( P0 , P0Tangent , P1 , P1Tangent , MidpointAlpha + KINDA_SMALL_NUMBER ) ;
const FVector2D MidpointMinusE = FMath : : CubicInterp ( P0 , P0Tangent , P1 , P1Tangent , MidpointAlpha - KINDA_SMALL_NUMBER ) ;
const FVector2D SlopeUnnormalized = MidpointPlusE - MidpointMinusE ;
// Draw the arrow
const FVector2D MidpointDrawPos = Midpoint - MidpointRadius ;
const float AngleInRadians = SlopeUnnormalized . IsNearlyZero ( ) ? 0.0f : FMath : : Atan2 ( SlopeUnnormalized . Y , SlopeUnnormalized . X ) ;
FSlateDrawElement : : MakeRotatedBox (
DrawElementsList ,
LayerId ,
FPaintGeometry ( MidpointDrawPos , MidpointImage - > ImageSize * ZoomFactor , ZoomFactor ) ,
MidpointImage ,
ClippingRect ,
ESlateDrawEffect : : None ,
AngleInRadians ,
TOptional < FVector2D > ( ) ,
FSlateDrawElement : : RelativeToElement ,
InColor
) ;
}
}
}
void FConnectionDrawingPolicy : : DrawPreviewConnector ( const FGeometry & PinGeometry , const FVector2D & StartPoint , const FVector2D & EndPoint , UEdGraphPin * Pin )
{
float Thickness = 1.0f ;
FLinearColor WireColor = FLinearColor : : White ;
bool bDrawBubbles = false ;
bool bBiDirectional = false ;
DetermineWiringStyle ( Pin , NULL , /*inout*/ Thickness , /*inout*/ WireColor , /*inout*/ bDrawBubbles , /*inout*/ bBiDirectional ) ;
DrawSplineWithArrow ( StartPoint , EndPoint , WireColor , Thickness , bDrawBubbles , bBiDirectional ) ;
}
void FConnectionDrawingPolicy : : DetermineWiringStyle ( UEdGraphPin * OutputPin , UEdGraphPin * InputPin , /*inout*/ float & Thickness , /*inout*/ FLinearColor & WireColor , /*inout*/ bool & bDrawBubbles , /*inout*/ bool & bBidirectional )
{
}
void FConnectionDrawingPolicy : : DetermineLinkGeometry (
TMap < TSharedRef < SWidget > ,
FArrangedWidget > & PinGeometries ,
FArrangedChildren & ArrangedNodes ,
TSharedRef < SWidget > & OutputPinWidget ,
UEdGraphPin * OutputPin ,
UEdGraphPin * InputPin ,
/*out*/ FArrangedWidget * & StartWidgetGeometry ,
/*out*/ FArrangedWidget * & EndWidgetGeometry
)
{
StartWidgetGeometry = PinGeometries . Find ( OutputPinWidget ) ;
if ( TSharedRef < SGraphPin > * pTargetWidget = PinToPinWidgetMap . Find ( InputPin ) )
{
TSharedRef < SGraphPin > InputWidget = * pTargetWidget ;
EndWidgetGeometry = PinGeometries . Find ( InputWidget ) ;
}
}
void FConnectionDrawingPolicy : : Draw ( TMap < TSharedRef < SWidget > , FArrangedWidget > & PinGeometries , FArrangedChildren & ArrangedNodes )
{
PinToPinWidgetMap . Empty ( ) ;
for ( TMap < TSharedRef < SWidget > , FArrangedWidget > : : TIterator ConnectorIt ( PinGeometries ) ; ConnectorIt ; + + ConnectorIt )
{
TSharedRef < SWidget > SomePinWidget = ConnectorIt . Key ( ) ;
SGraphPin & PinWidget = static_cast < SGraphPin & > ( SomePinWidget . Get ( ) ) ;
PinToPinWidgetMap . Add ( PinWidget . GetPinObj ( ) , StaticCastSharedRef < SGraphPin > ( SomePinWidget ) ) ;
}
for ( TMap < TSharedRef < SWidget > , FArrangedWidget > : : TIterator ConnectorIt ( PinGeometries ) ; ConnectorIt ; + + ConnectorIt )
{
TSharedRef < SWidget > SomePinWidget = ConnectorIt . Key ( ) ;
SGraphPin & PinWidget = static_cast < SGraphPin & > ( SomePinWidget . Get ( ) ) ;
UEdGraphPin * ThePin = PinWidget . GetPinObj ( ) ;
if ( ThePin - > Direction = = EGPD_Output )
{
for ( int32 LinkIndex = 0 ; LinkIndex < ThePin - > LinkedTo . Num ( ) ; + + LinkIndex )
{
FArrangedWidget * LinkStartWidgetGeometry = NULL ;
FArrangedWidget * LinkEndWidgetGeometry = NULL ;
UEdGraphPin * TargetPin = ThePin - > LinkedTo [ LinkIndex ] ;
DetermineLinkGeometry ( PinGeometries , ArrangedNodes , SomePinWidget , ThePin , TargetPin , /*out*/ LinkStartWidgetGeometry , /*out*/ LinkEndWidgetGeometry ) ;
if ( ( LinkEndWidgetGeometry ! = NULL ) & & ( LinkStartWidgetGeometry ! = NULL ) )
{
float Thickness = 1.0f ;
FLinearColor WireColor = FLinearColor : : White ;
bool bDrawBubbles = false ;
bool bBidirectional = false ;
DetermineWiringStyle ( ThePin , TargetPin , /*inout*/ Thickness , /*inout*/ WireColor , /*inout*/ bDrawBubbles , /*inout*/ bBidirectional ) ;
DrawSplineWithArrow ( LinkStartWidgetGeometry - > Geometry , LinkEndWidgetGeometry - > Geometry , WireColor , Thickness , bDrawBubbles , bBidirectional ) ;
}
}
}
}
}
void FConnectionDrawingPolicy : : SetIncompatiblePinDrawState ( const TSharedPtr < SGraphPin > & StartPin , const TSet < TSharedRef < SWidget > > & VisiblePins )
{
}
void FConnectionDrawingPolicy : : ResetIncompatiblePinDrawState ( const TSet < TSharedRef < SWidget > > & VisiblePins )
{
}
void FConnectionDrawingPolicy : : ApplyHoverDeemphasis ( UEdGraphPin * OutputPin , UEdGraphPin * InputPin , /*inout*/ float & Thickness , /*inout*/ FLinearColor & WireColor )
{
const float FadeInBias = 0.75f ; // Time in seconds before the fading starts to occur
const float FadeInPeriod = 0.6f ; // Time in seconds after the bias before the fade is fully complete
const float TimeFraction = FMath : : SmoothStep ( 0.0f , FadeInPeriod , ( float ) ( FSlateApplication : : Get ( ) . GetCurrentTime ( ) - LastHoverTimeEvent - FadeInBias ) ) ;
const float DarkFraction = 0.8f ;
const float LightFraction = 0.25f ;
const FLinearColor DarkenedColor ( 0.0f , 0.0f , 0.0f , 0.5f ) ;
const FLinearColor LightenedColor ( 1.0f , 1.0f , 1.0f , 1.0f ) ;
const bool bContainsBoth = HoveredPins . Contains ( InputPin ) & & HoveredPins . Contains ( OutputPin ) ;
const bool bContainsOutput = HoveredPins . Contains ( OutputPin ) ;
const bool bEmphasize = bContainsBoth | | ( bContainsOutput & & ( InputPin = = NULL ) ) ;
if ( bEmphasize )
{
Thickness = FMath : : Lerp ( Thickness , Thickness * ( ( Thickness < 3.0f ) ? 5.0f : 3.0f ) , TimeFraction ) ;
WireColor = FMath : : Lerp < FLinearColor > ( WireColor , LightenedColor , LightFraction * TimeFraction ) ;
}
else
{
WireColor = FMath : : Lerp < FLinearColor > ( WireColor , DarkenedColor , DarkFraction * TimeFraction ) ;
}
}
/////////////////////////////////////////////////////
// FKismetConnectionDrawingPolicy
FKismetConnectionDrawingPolicy : : FKismetConnectionDrawingPolicy ( int32 InBackLayerID , int32 InFrontLayerID , float ZoomFactor , const FSlateRect & InClippingRect , FSlateWindowElementList & InDrawElements , UEdGraph * InGraphObj )
: FConnectionDrawingPolicy ( InBackLayerID , InFrontLayerID , ZoomFactor , InClippingRect , InDrawElements )
, GraphObj ( InGraphObj )
{
2014-05-20 19:00:53 -04:00
const UGraphEditorSettings * Settings = GetDefault < UGraphEditorSettings > ( ) ;
2014-03-14 14:13:41 -04:00
// Don't want to draw ending arrowheads
ArrowImage = nullptr ;
2014-05-14 14:55:31 -04:00
ArrowRadius = FVector2D : : ZeroVector ;
2014-03-14 14:13:41 -04:00
// But we do want to draw midpoint arrowheads
if ( GetDefault < UEditorExperimentalSettings > ( ) - > bDrawMidpointArrowsInBlueprints )
{
MidpointImage = FEditorStyle : : GetBrush ( TEXT ( " Graph.Arrow " ) ) ;
MidpointRadius = MidpointImage - > ImageSize * ZoomFactor * 0.5f ;
}
// Cache off the editor options
2014-05-20 19:00:53 -04:00
AttackColor = Settings - > TraceAttackColor ;
SustainColor = Settings - > TraceSustainColor ;
ReleaseColor = Settings - > TraceReleaseColor ;
2014-03-14 14:13:41 -04:00
2014-05-20 19:00:53 -04:00
AttackWireThickness = Settings - > TraceAttackWireThickness ;
SustainWireThickness = Settings - > TraceSustainWireThickness ;
ReleaseWireThickness = Settings - > TraceReleaseWireThickness ;
2014-03-14 14:13:41 -04:00
2014-05-20 19:00:53 -04:00
TracePositionBonusPeriod = Settings - > TracePositionBonusPeriod ;
TracePositionExponent = Settings - > TracePositionExponent ;
AttackHoldPeriod = Settings - > TraceAttackHoldPeriod ;
DecayPeriod = Settings - > TraceDecayPeriod ;
DecayExponent = Settings - > TraceDecayExponent ;
SustainHoldPeriod = Settings - > TraceSustainHoldPeriod ;
ReleasePeriod = Settings - > TraceReleasePeriod ;
ReleaseExponent = Settings - > TraceReleaseExponent ;
2014-03-14 14:13:41 -04:00
CurrentTime = 0.0 ;
LatestTimeDiscovered = 0.0 ;
}
void FKismetConnectionDrawingPolicy : : Draw ( TMap < TSharedRef < SWidget > , FArrangedWidget > & PinGeometries , FArrangedChildren & ArrangedNodes )
{
// Build the execution roadmap (also populates execution times)
BuildExecutionRoadmap ( ) ;
// Draw everything
FConnectionDrawingPolicy : : Draw ( PinGeometries , ArrangedNodes ) ;
}
bool FKismetConnectionDrawingPolicy : : CanBuildRoadmap ( ) const
{
UBlueprint * TargetBP = FBlueprintEditorUtils : : FindBlueprintForGraphChecked ( GraphObj ) ;
UObject * ActiveObject = TargetBP - > GetObjectBeingDebugged ( ) ;
return ActiveObject ! = NULL ;
}
void FKismetConnectionDrawingPolicy : : BuildExecutionRoadmap ( )
{
LatestTimeDiscovered = 0.0 ;
// Only do highlighting in PIE or SIE
if ( ! CanBuildRoadmap ( ) )
{
return ;
}
UBlueprint * TargetBP = FBlueprintEditorUtils : : FindBlueprintForGraphChecked ( GraphObj ) ;
UObject * ActiveObject = TargetBP - > GetObjectBeingDebugged ( ) ;
check ( ActiveObject ) ; // Due to CanBuildRoadmap
// Redirect the target Blueprint when debugging with a macro graph visible
if ( TargetBP - > BlueprintType = = BPTYPE_MacroLibrary )
{
TargetBP = Cast < UBlueprint > ( ActiveObject - > GetClass ( ) - > ClassGeneratedBy ) ;
}
TArray < UEdGraphNode * > SequentialNodesInGraph ;
TArray < double > SequentialNodeTimes ;
2014-04-23 17:45:37 -04:00
TArray < UEdGraphPin * > SequentialExecPinsInGraph ;
2014-03-14 14:13:41 -04:00
{
const TSimpleRingBuffer < FKismetTraceSample > & TraceStack = FKismetDebugUtilities : : GetTraceStack ( ) ;
UBlueprintGeneratedClass * TargetClass = Cast < UBlueprintGeneratedClass > ( TargetBP - > GeneratedClass ) ;
FBlueprintDebugData & DebugData = TargetClass - > GetDebugData ( ) ;
for ( int32 i = 0 ; i < TraceStack . Num ( ) ; + + i )
{
const FKismetTraceSample & Sample = TraceStack ( i ) ;
if ( UObject * TestObject = Sample . Context . Get ( ) )
{
if ( TestObject = = ActiveObject )
{
2014-04-23 17:45:37 -04:00
UEdGraphPin * AssociatedPin = DebugData . FindExecPinFromCodeLocation ( Sample . Function . Get ( ) , Sample . Offset ) ;
2014-03-14 14:13:41 -04:00
if ( UEdGraphNode * Node = DebugData . FindSourceNodeFromCodeLocation ( Sample . Function . Get ( ) , Sample . Offset , /*bAllowImpreciseHit=*/ false ) )
{
if ( GraphObj = = Node - > GetGraph ( ) )
{
SequentialNodesInGraph . Add ( Node ) ;
SequentialNodeTimes . Add ( Sample . ObservationTime ) ;
2014-04-23 17:45:37 -04:00
SequentialExecPinsInGraph . Add ( AssociatedPin ) ;
2014-03-14 14:13:41 -04:00
}
else
{
// If the top-level source node is a macro instance node
UK2Node_MacroInstance * MacroInstanceNode = Cast < UK2Node_MacroInstance > ( Node ) ;
if ( MacroInstanceNode )
{
// Attempt to locate the macro source node through the code mapping
UEdGraphNode * MacroSourceNode = DebugData . FindMacroSourceNodeFromCodeLocation ( Sample . Function . Get ( ) , Sample . Offset ) ;
if ( MacroSourceNode )
{
// If the macro source node is located in the current graph context
if ( GraphObj = = MacroSourceNode - > GetGraph ( ) )
{
// Add it to the sequential node list
SequentialNodesInGraph . Add ( MacroSourceNode ) ;
SequentialNodeTimes . Add ( Sample . ObservationTime ) ;
2014-04-23 17:45:37 -04:00
SequentialExecPinsInGraph . Add ( AssociatedPin ) ;
2014-03-14 14:13:41 -04:00
}
else
{
// The macro source node isn't in the current graph context, but we might have a macro instance node that is
// in the current graph context, so obtain the set of macro instance nodes that are mapped to the code here.
TArray < UEdGraphNode * > MacroInstanceNodes ;
DebugData . FindMacroInstanceNodesFromCodeLocation ( Sample . Function . Get ( ) , Sample . Offset , MacroInstanceNodes ) ;
// For each macro instance node in the set
for ( auto MacroInstanceNodeIt = MacroInstanceNodes . CreateConstIterator ( ) ; MacroInstanceNodeIt ; + + MacroInstanceNodeIt )
{
// If the macro instance node is located in the current graph context
MacroInstanceNode = Cast < UK2Node_MacroInstance > ( * MacroInstanceNodeIt ) ;
if ( MacroInstanceNode & & GraphObj = = MacroInstanceNode - > GetGraph ( ) )
{
// Add it to the sequential node list
SequentialNodesInGraph . Add ( MacroInstanceNode ) ;
SequentialNodeTimes . Add ( Sample . ObservationTime ) ;
2014-04-23 17:45:37 -04:00
SequentialExecPinsInGraph . Add ( AssociatedPin ) ;
2014-03-14 14:13:41 -04:00
// Exit the loop; we're done
break ;
}
}
}
}
}
}
}
}
}
}
}
// Run thru and apply bonus time
const float InvNumNodes = 1.0f / ( float ) SequentialNodeTimes . Num ( ) ;
for ( int32 i = 0 ; i < SequentialNodesInGraph . Num ( ) ; + + i )
{
double & ObservationTime = SequentialNodeTimes [ i ] ;
const float PositionRatio = ( SequentialNodeTimes . Num ( ) - i ) * InvNumNodes ;
const float PositionBonus = FMath : : Pow ( PositionRatio , TracePositionExponent ) * TracePositionBonusPeriod ;
ObservationTime + = PositionBonus ;
LatestTimeDiscovered = FMath : : Max < double > ( LatestTimeDiscovered , ObservationTime ) ;
}
2014-04-23 17:45:37 -04:00
UEdGraphPin * LastExecPin = NULL ;
// Record the unique exec-pin to time pairings, keeping only the most recent
// times for each pairing... reverse the "SequentialNodes" because right now
// it is in stack order (with the last executed node first)
2014-03-14 14:13:41 -04:00
for ( int32 i = SequentialNodesInGraph . Num ( ) - 1 ; i > = 1 ; - - i )
{
2014-04-23 17:45:37 -04:00
UEdGraphNode * CurNode = SequentialNodesInGraph [ i ] ;
2014-03-14 14:13:41 -04:00
UEdGraphNode * NextNode = SequentialNodesInGraph [ i - 1 ] ;
2014-04-23 17:45:37 -04:00
// keep track of the last exec-pin executed by CurNode (these tracked
// pins coincide with "WireTraceSite" op-codes that have been injected
// prior to every "goto" statement... this way we have context for which
// pin executed the jump)
if ( UEdGraphPin * AssociatedPin = SequentialExecPinsInGraph [ i ] )
2014-03-14 14:13:41 -04:00
{
2014-04-23 17:45:37 -04:00
LastExecPin = AssociatedPin ;
2014-03-14 14:13:41 -04:00
}
2014-04-23 17:45:37 -04:00
// if this statement is a jump (from one node to another)
if ( CurNode ! = NextNode )
{
// if there was a wire-trace op-code inserted before this jump
if ( LastExecPin ! = NULL )
{
//ensure(LastExecPin->GetOwningNode() == CurNode);
double NextNodeTime = SequentialNodeTimes [ i - 1 ] ;
FExecPairingMap & ExecPaths = PredecessorPins . FindOrAdd ( NextNode ) ;
FTimePair & ExecTiming = ExecPaths . FindOrAdd ( LastExecPin ) ;
// make sure that if we've already visited this exec-pin (like
// in a for-loop or something), that we're replacing it with a
// more recent execution time
//
// @TODO I don't see when this wouldn't be the case
if ( ExecTiming . ThisExecTime < NextNodeTime )
{
double CurNodeTime = SequentialNodeTimes [ i ] ;
ExecTiming . ThisExecTime = NextNodeTime ;
ExecTiming . PredExecTime = CurNodeTime ;
}
}
2014-04-23 20:43:35 -04:00
// if the nodes aren't graphically connected how could they be
// executed back-to-back? well, this could be a pop back to a
// sequence node from the end of one thread of execution, etc.
else if ( AreNodesGraphicallySequential ( CurNode , NextNode ) )
2014-04-23 17:45:37 -04:00
{
2014-04-23 20:43:35 -04:00
// only warn when the nodes are directly connected (this is all
// for execution flow visualization after all)
UE_LOG ( LogConnectionDrawingPolicy , Warning , TEXT ( " Looks like a wire-trace was not injected before the jump from '%s' to '%s'. " ) ,
2014-04-23 18:30:37 -04:00
* CurNode - > GetNodeTitle ( ENodeTitleType : : FullTitle ) . ToString ( ) , * NextNode - > GetNodeTitle ( ENodeTitleType : : FullTitle ) . ToString ( ) ) ;
2014-04-23 17:45:37 -04:00
}
// clear the exec-pin (we're moving to a new node and want to find
// it's executed out pin)
LastExecPin = NULL ;
}
// else, we're only collecting this data for tracing node-to-node
// executions (so we don't care about this sequence of statements)
2014-03-14 14:13:41 -04:00
}
2014-04-23 18:12:58 -04:00
// Fade only when free-running (since we're using FApp::GetCurrentTime(), instead of FPlatformTime::Seconds)
const double MaxTimeAhead = FMath : : Min ( FApp : : GetCurrentTime ( ) + 2 * TracePositionBonusPeriod , LatestTimeDiscovered ) ; //@TODO: Rough clamping; should be exposed as a parameter
CurrentTime = FMath : : Max ( FApp : : GetCurrentTime ( ) , MaxTimeAhead ) ;
2014-03-14 14:13:41 -04:00
}
void FKismetConnectionDrawingPolicy : : CalculateEnvelopeAlphas ( double ExecutionTime , /*out*/ float & AttackAlpha , /*out*/ float & SustainAlpha ) const
{
const float DeltaTime = ( float ) ( CurrentTime - ExecutionTime ) ;
{
const float UnclampedDecayRatio = 1.0f - ( ( DeltaTime - AttackHoldPeriod ) / DecayPeriod ) ;
const float ClampedDecayRatio = FMath : : Clamp < float > ( UnclampedDecayRatio , 0.0f , 1.0f ) ;
AttackAlpha = FMath : : Pow ( ClampedDecayRatio , DecayExponent ) ;
}
{
const float SustainEndTime = AttackHoldPeriod + DecayPeriod + SustainHoldPeriod ;
const float UnclampedReleaseRatio = 1.0f - ( ( DeltaTime - SustainEndTime ) / ReleasePeriod ) ;
const float ClampedReleaseRatio = FMath : : Clamp < float > ( UnclampedReleaseRatio , 0.0f , 1.0f ) ;
SustainAlpha = FMath : : Pow ( ClampedReleaseRatio , ReleaseExponent ) ;
}
}
bool FKismetConnectionDrawingPolicy : : TreatWireAsExecutionPin ( UEdGraphPin * InputPin , UEdGraphPin * OutputPin ) const
{
const UEdGraphSchema_K2 * Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
return ( InputPin ! = NULL ) & & ( Schema - > IsExecPin ( * OutputPin ) ) ;
}
2014-04-23 20:43:35 -04:00
bool FKismetConnectionDrawingPolicy : : AreNodesGraphicallySequential ( UEdGraphNode * InputNode , UEdGraphNode * OutputNode ) const
{
for ( UEdGraphPin * Pin : InputNode - > Pins )
{
if ( Pin - > Direction ! = EGPD_Output )
{
continue ;
}
for ( UEdGraphPin * Connection : Pin - > LinkedTo )
{
if ( ! TreatWireAsExecutionPin ( Pin , Connection ) )
{
continue ;
}
if ( Connection - > GetOwningNode ( ) = = OutputNode )
{
return true ;
}
}
}
return false ;
}
2014-03-14 14:13:41 -04:00
void FKismetConnectionDrawingPolicy : : DetermineStyleOfExecWire ( float & Thickness , FLinearColor & WireColor , bool & bDrawBubbles , const FTimePair & Times )
{
// It's a followed link, make it strong and yellowish but fading over time
const double ExecTime = Times . ThisExecTime ;
float AttackAlpha ;
float SustainAlpha ;
CalculateEnvelopeAlphas ( ExecTime , /*out*/ AttackAlpha , /*out*/ SustainAlpha ) ;
const float DecayedAttackThickness = FMath : : Lerp < float > ( SustainWireThickness , AttackWireThickness , AttackAlpha ) ;
Thickness = FMath : : Lerp < float > ( ReleaseWireThickness , DecayedAttackThickness , SustainAlpha ) ;
const FLinearColor DecayedAttackColor = FMath : : Lerp < FLinearColor > ( SustainColor , AttackColor , AttackAlpha ) ;
WireColor = WireColor * FMath : : Lerp < FLinearColor > ( ReleaseColor , DecayedAttackColor , SustainAlpha ) ;
if ( SustainAlpha > KINDA_SMALL_NUMBER )
{
bDrawBubbles = true ;
}
}
// Give specific editor modes a chance to highlight this connection or darken non-interesting connections
void FKismetConnectionDrawingPolicy : : DetermineWiringStyle ( UEdGraphPin * OutputPin , UEdGraphPin * InputPin , /*inout*/ float & Thickness , /*inout*/ FLinearColor & WireColor , /*inout*/ bool & bDrawBubbles , /*inout*/ bool & bBidirectional )
{
// Get the schema and grab the default color from it
check ( OutputPin ) ;
check ( GraphObj ) ;
const UEdGraphSchema * Schema = GraphObj - > GetSchema ( ) ;
WireColor = Schema - > GetPinTypeColor ( OutputPin - > PinType ) ;
const bool bDeemphasizeUnhoveredPins = HoveredPins . Num ( ) > 0 ;
// If this is a K2 graph, try to be a little more specific
const UEdGraphSchema_K2 * K2Schema = Cast < const UEdGraphSchema_K2 > ( Schema ) ;
if ( K2Schema ! = NULL )
{
if ( TreatWireAsExecutionPin ( InputPin , OutputPin ) )
{
if ( CanBuildRoadmap ( ) )
{
2014-04-23 17:45:37 -04:00
// track if this node connection was ran or not
2014-03-14 14:13:41 -04:00
bool bExecuted = false ;
2014-04-23 17:45:37 -04:00
UEdGraphNode * InputNode = InputPin - > GetOwningNode ( ) ;
// if the node belonging to InputPin was actually executed
if ( FExecPairingMap * ExecPaths = PredecessorPins . Find ( InputNode ) )
2014-03-14 14:13:41 -04:00
{
2014-04-23 17:45:37 -04:00
// if the output pin is one of the pins that lead to InputNode being ran
if ( FTimePair * ExecTiming = ExecPaths - > Find ( OutputPin ) )
2014-03-14 14:13:41 -04:00
{
bExecuted = true ;
2014-04-23 17:45:37 -04:00
DetermineStyleOfExecWire ( /*inout*/ Thickness , /*inout*/ WireColor , /*inout*/ bDrawBubbles , * ExecTiming ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-04-23 17:45:37 -04:00
2014-03-14 14:13:41 -04:00
if ( ! bExecuted )
{
// It's not followed, fade it and keep it thin
WireColor = ReleaseColor ;
Thickness = ReleaseWireThickness ;
}
}
else
{
// Make exec wires slightly thicker even outside of debug
Thickness = 3.0f ;
}
}
else
{
// Array types should draw thicker
if ( ( InputPin & & InputPin - > PinType . bIsArray ) | | ( OutputPin & & OutputPin - > PinType . bIsArray ) )
{
Thickness = 3.0f ;
}
}
}
if ( bDeemphasizeUnhoveredPins )
{
ApplyHoverDeemphasis ( OutputPin , InputPin , /*inout*/ Thickness , /*inout*/ WireColor ) ;
}
}
void FKismetConnectionDrawingPolicy : : SetIncompatiblePinDrawState ( const TSharedPtr < SGraphPin > & StartPin , const TSet < TSharedRef < SWidget > > & VisiblePins )
{
ResetIncompatiblePinDrawState ( VisiblePins ) ;
for ( auto VisiblePinIterator = VisiblePins . CreateConstIterator ( ) ; VisiblePinIterator ; + + VisiblePinIterator )
{
TSharedPtr < SGraphPin > CheckPin = StaticCastSharedRef < SGraphPin > ( * VisiblePinIterator ) ;
if ( CheckPin ! = StartPin )
{
const FPinConnectionResponse Response = StartPin - > GetPinObj ( ) - > GetSchema ( ) - > CanCreateConnection ( StartPin - > GetPinObj ( ) , CheckPin - > GetPinObj ( ) ) ;
if ( Response . Response = = CONNECT_RESPONSE_DISALLOW )
{
CheckPin - > SetPinColorModifier ( FLinearColor ( 0.25f , 0.25f , 0.25f , 0.5f ) ) ;
}
}
}
}
void FKismetConnectionDrawingPolicy : : ResetIncompatiblePinDrawState ( const TSet < TSharedRef < SWidget > > & VisiblePins )
{
for ( auto VisiblePinIterator = VisiblePins . CreateConstIterator ( ) ; VisiblePinIterator ; + + VisiblePinIterator )
{
TSharedPtr < SGraphPin > VisiblePin = StaticCastSharedRef < SGraphPin > ( * VisiblePinIterator ) ;
VisiblePin - > SetPinColorModifier ( FLinearColor : : White ) ;
}
}
/////////////////////////////////////////////////////
// FStateMachineConnectionDrawingPolicy
FStateMachineConnectionDrawingPolicy : : FStateMachineConnectionDrawingPolicy ( int32 InBackLayerID , int32 InFrontLayerID , float ZoomFactor , const FSlateRect & InClippingRect , FSlateWindowElementList & InDrawElements , UEdGraph * InGraphObj )
: FConnectionDrawingPolicy ( InBackLayerID , InFrontLayerID , ZoomFactor , InClippingRect , InDrawElements )
, GraphObj ( InGraphObj )
{
}
void FStateMachineConnectionDrawingPolicy : : DetermineWiringStyle ( UEdGraphPin * OutputPin , UEdGraphPin * InputPin , /*inout*/ float & Thickness , /*inout*/ FLinearColor & WireColor , /*inout*/ bool & bDrawBubbles , /*inout*/ bool & bBidirectional )
{
Thickness = 1.5f ;
if ( InputPin )
{
if ( UAnimStateTransitionNode * TransNode = Cast < UAnimStateTransitionNode > ( InputPin - > GetOwningNode ( ) ) )
{
WireColor = SGraphNodeAnimTransition : : StaticGetTransitionColor ( TransNode , HoveredPins . Contains ( InputPin ) ) ;
bBidirectional = TransNode - > Bidirectional ;
}
}
const bool bDeemphasizeUnhoveredPins = HoveredPins . Num ( ) > 0 ;
if ( bDeemphasizeUnhoveredPins )
{
ApplyHoverDeemphasis ( OutputPin , InputPin , /*inout*/ Thickness , /*inout*/ WireColor ) ;
}
}
void FStateMachineConnectionDrawingPolicy : : DetermineLinkGeometry (
TMap < TSharedRef < SWidget > , FArrangedWidget > & PinGeometries ,
FArrangedChildren & ArrangedNodes ,
TSharedRef < SWidget > & OutputPinWidget ,
UEdGraphPin * OutputPin ,
UEdGraphPin * InputPin ,
/*out*/ FArrangedWidget * & StartWidgetGeometry ,
/*out*/ FArrangedWidget * & EndWidgetGeometry
)
{
if ( UAnimStateEntryNode * EntryNode = Cast < UAnimStateEntryNode > ( OutputPin - > GetOwningNode ( ) ) )
{
//FConnectionDrawingPolicy::DetermineLinkGeometry(PinGeometries, ArrangedNodes, OutputPinWidget, OutputPin, InputPin, StartWidgetGeometry, EndWidgetGeometry);
StartWidgetGeometry = PinGeometries . Find ( OutputPinWidget ) ;
UAnimStateNodeBase * State = CastChecked < UAnimStateNodeBase > ( InputPin - > GetOwningNode ( ) ) ;
int32 StateIndex = NodeWidgetMap . FindChecked ( State ) ;
EndWidgetGeometry = & ( ArrangedNodes ( StateIndex ) ) ;
}
else if ( UAnimStateTransitionNode * TransNode = Cast < UAnimStateTransitionNode > ( InputPin - > GetOwningNode ( ) ) )
{
UAnimStateNodeBase * PrevState = TransNode - > GetPreviousState ( ) ;
UAnimStateNodeBase * NextState = TransNode - > GetNextState ( ) ;
if ( ( PrevState ! = NULL ) & & ( NextState ! = NULL ) )
{
int32 * PrevNodeIndex = NodeWidgetMap . Find ( PrevState ) ;
int32 * NextNodeIndex = NodeWidgetMap . Find ( NextState ) ;
if ( ( PrevNodeIndex ! = NULL ) & & ( NextNodeIndex ! = NULL ) )
{
StartWidgetGeometry = & ( ArrangedNodes ( * PrevNodeIndex ) ) ;
EndWidgetGeometry = & ( ArrangedNodes ( * NextNodeIndex ) ) ;
}
}
}
else
{
StartWidgetGeometry = PinGeometries . Find ( OutputPinWidget ) ;
if ( TSharedRef < SGraphPin > * pTargetWidget = PinToPinWidgetMap . Find ( InputPin ) )
{
TSharedRef < SGraphPin > InputWidget = * pTargetWidget ;
EndWidgetGeometry = PinGeometries . Find ( InputWidget ) ;
}
}
}
void FStateMachineConnectionDrawingPolicy : : Draw ( TMap < TSharedRef < SWidget > , FArrangedWidget > & PinGeometries , FArrangedChildren & ArrangedNodes )
{
// Build an acceleration structure to quickly find geometry for the nodes
NodeWidgetMap . Empty ( ) ;
for ( int32 NodeIndex = 0 ; NodeIndex < ArrangedNodes . Num ( ) ; + + NodeIndex )
{
FArrangedWidget & CurWidget = ArrangedNodes ( NodeIndex ) ;
TSharedRef < SGraphNode > ChildNode = StaticCastSharedRef < SGraphNode > ( CurWidget . Widget ) ;
NodeWidgetMap . Add ( ChildNode - > GetNodeObj ( ) , NodeIndex ) ;
}
// Now draw
FConnectionDrawingPolicy : : Draw ( PinGeometries , ArrangedNodes ) ;
}
void FStateMachineConnectionDrawingPolicy : : DrawPreviewConnector ( const FGeometry & PinGeometry , const FVector2D & StartPoint , const FVector2D & EndPoint , UEdGraphPin * Pin )
{
float Thickness = 1.0f ;
FLinearColor WireColor = FLinearColor : : White ;
bool bDrawBubbles = false ;
bool bBiDirectional = false ;
DetermineWiringStyle ( Pin , NULL , /*inout*/ Thickness , /*inout*/ WireColor , /*inout*/ bDrawBubbles , /*inout*/ bBiDirectional ) ;
const FVector2D SeedPoint = EndPoint ;
const FVector2D AdjustedStartPoint = FGeometryHelper : : FindClosestPointOnGeom ( PinGeometry , SeedPoint ) ;
DrawSplineWithArrow ( AdjustedStartPoint , EndPoint , WireColor , Thickness , bDrawBubbles , bBiDirectional ) ;
}
void FStateMachineConnectionDrawingPolicy : : DrawSplineWithArrow ( const FVector2D & StartAnchorPoint , const FVector2D & EndAnchorPoint , const FLinearColor & WireColor , float WireThickness , bool bDrawBubbles , bool Bidirectional )
{
Internal_DrawLineWithArrow ( StartAnchorPoint , EndAnchorPoint , WireColor , WireThickness , bDrawBubbles ) ;
if ( Bidirectional )
{
Internal_DrawLineWithArrow ( EndAnchorPoint , StartAnchorPoint , WireColor , WireThickness , bDrawBubbles ) ;
}
}
void FStateMachineConnectionDrawingPolicy : : Internal_DrawLineWithArrow ( const FVector2D & StartAnchorPoint , const FVector2D & EndAnchorPoint , const FLinearColor & WireColor , float WireThickness , bool bDrawBubbles )
{
//@TODO: Should this be scaled by zoom factor?
const float LineSeparationAmount = 4.5f ;
const FVector2D DeltaPos = EndAnchorPoint - StartAnchorPoint ;
const FVector2D UnitDelta = DeltaPos . SafeNormal ( ) ;
const FVector2D Normal = FVector2D ( DeltaPos . Y , - DeltaPos . X ) . SafeNormal ( ) ;
// Come up with the final start/end points
const FVector2D DirectionBias = Normal * LineSeparationAmount ;
const FVector2D LengthBias = ArrowRadius . X * UnitDelta ;
const FVector2D StartPoint = StartAnchorPoint + DirectionBias + LengthBias ;
const FVector2D EndPoint = EndAnchorPoint + DirectionBias - LengthBias ;
// Draw a line/spline
DrawConnection ( WireLayerID , StartPoint , EndPoint , WireColor , WireThickness , bDrawBubbles ) ;
// Draw the arrow
const FVector2D ArrowDrawPos = EndPoint - ArrowRadius ;
const float AngleInRadians = FMath : : Atan2 ( DeltaPos . Y , DeltaPos . X ) ;
FSlateDrawElement : : MakeRotatedBox (
DrawElementsList ,
ArrowLayerID ,
FPaintGeometry ( ArrowDrawPos , ArrowImage - > ImageSize * ZoomFactor , ZoomFactor ) ,
ArrowImage ,
ClippingRect ,
ESlateDrawEffect : : None ,
AngleInRadians ,
TOptional < FVector2D > ( ) ,
FSlateDrawElement : : RelativeToElement ,
WireColor
) ;
}
void FStateMachineConnectionDrawingPolicy : : DrawSplineWithArrow ( FGeometry & StartGeom , FGeometry & EndGeom , const FLinearColor & WireColor , float WireThickness , bool bDrawBubbles , bool Bidirectional )
{
// Get a reasonable seed point (halfway between the boxes)
const FVector2D StartCenter = FGeometryHelper : : CenterOf ( StartGeom ) ;
const FVector2D EndCenter = FGeometryHelper : : CenterOf ( EndGeom ) ;
const FVector2D SeedPoint = ( StartCenter + EndCenter ) * 0.5f ;
// Find the (approximate) closest points between the two boxes
const FVector2D StartAnchorPoint = FGeometryHelper : : FindClosestPointOnGeom ( StartGeom , SeedPoint ) ;
const FVector2D EndAnchorPoint = FGeometryHelper : : FindClosestPointOnGeom ( EndGeom , SeedPoint ) ;
DrawSplineWithArrow ( StartAnchorPoint , EndAnchorPoint , WireColor , WireThickness , bDrawBubbles , Bidirectional ) ;
}
void FStateMachineConnectionDrawingPolicy : : DrawConnection ( int32 LayerId , const FVector2D & Start , const FVector2D & End , const FLinearColor & InColor , float Thickness , bool bDrawBubbles )
{
const FVector2D & P0 = Start ;
const FVector2D & P1 = End ;
const FVector2D Delta = End - Start ;
const FVector2D NormDelta = Delta . SafeNormal ( ) ;
const FVector2D P0Tangent = NormDelta ;
const FVector2D P1Tangent = NormDelta ;
// Draw the spline itself
FSlateDrawElement : : MakeDrawSpaceSpline (
DrawElementsList ,
LayerId ,
P0 , P0Tangent ,
P1 , P1Tangent ,
ClippingRect ,
Thickness ,
ESlateDrawEffect : : None ,
InColor
) ;
//@TODO: Handle bDrawBubbles
}
/////////////////////////////////////////////////////
// FAnimGraphConnectionDrawingPolicy
FAnimGraphConnectionDrawingPolicy : : FAnimGraphConnectionDrawingPolicy ( int32 InBackLayerID , int32 InFrontLayerID , float ZoomFactor , const FSlateRect & InClippingRect , FSlateWindowElementList & InDrawElements , UEdGraph * InGraphObj )
: FKismetConnectionDrawingPolicy ( InBackLayerID , InFrontLayerID , ZoomFactor , InClippingRect , InDrawElements , InGraphObj )
{
}
bool FAnimGraphConnectionDrawingPolicy : : TreatWireAsExecutionPin ( UEdGraphPin * InputPin , UEdGraphPin * OutputPin ) const
{
const UAnimationGraphSchema * Schema = GetDefault < UAnimationGraphSchema > ( ) ;
return ( InputPin ! = NULL ) & & ( Schema - > IsPosePin ( OutputPin - > PinType ) ) ;
}
void FAnimGraphConnectionDrawingPolicy : : BuildExecutionRoadmap ( )
{
UAnimBlueprint * TargetBP = CastChecked < UAnimBlueprint > ( FBlueprintEditorUtils : : FindBlueprintForGraphChecked ( GraphObj ) ) ;
UAnimBlueprintGeneratedClass * AnimBlueprintClass = ( UAnimBlueprintGeneratedClass * ) ( * ( TargetBP - > GeneratedClass ) ) ;
if ( TargetBP - > GetObjectBeingDebugged ( ) = = NULL )
{
return ;
}
TMap < UProperty * , UObject * > PropertySourceMap ;
AnimBlueprintClass - > GetDebugData ( ) . GenerateReversePropertyMap ( /*out*/ PropertySourceMap ) ;
FAnimBlueprintDebugData & DebugInfo = AnimBlueprintClass - > GetAnimBlueprintDebugData ( ) ;
for ( auto VisitIt = DebugInfo . UpdatedNodesThisFrame . CreateIterator ( ) ; VisitIt ; + + VisitIt )
{
const FAnimBlueprintDebugData : : FNodeVisit & VisitRecord = * VisitIt ;
if ( ( VisitRecord . SourceID > = 0 ) & & ( VisitRecord . SourceID < AnimBlueprintClass - > AnimNodeProperties . Num ( ) ) & & ( VisitRecord . TargetID > = 0 ) & & ( VisitRecord . TargetID < AnimBlueprintClass - > AnimNodeProperties . Num ( ) ) )
{
if ( UAnimGraphNode_Base * SourceNode = Cast < UAnimGraphNode_Base > ( PropertySourceMap . FindRef ( AnimBlueprintClass - > AnimNodeProperties [ VisitRecord . SourceID ] ) ) )
{
if ( UAnimGraphNode_Base * TargetNode = Cast < UAnimGraphNode_Base > ( PropertySourceMap . FindRef ( AnimBlueprintClass - > AnimNodeProperties [ VisitRecord . TargetID ] ) ) )
{
2014-04-23 17:45:37 -04:00
UEdGraphPin * PoseNet = NULL ;
UAnimationGraphSchema const * AnimSchema = GetDefault < UAnimationGraphSchema > ( ) ;
for ( int32 PinIndex = 0 ; PinIndex < TargetNode - > Pins . Num ( ) ; + + PinIndex )
{
UEdGraphPin * Pin = TargetNode - > Pins [ PinIndex ] ;
if ( AnimSchema - > IsPosePin ( Pin - > PinType ) & & ( Pin - > Direction = = EGPD_Output ) )
{
PoseNet = Pin ;
break ;
}
}
if ( PoseNet ! = NULL )
{
//@TODO: Extend the rendering code to allow using the recorded blend weight instead of faked exec times
FExecPairingMap & Predecessors = PredecessorPins . FindOrAdd ( ( UEdGraphNode * ) SourceNode ) ;
FTimePair & Timings = Predecessors . FindOrAdd ( PoseNet ) ;
Timings . PredExecTime = 0.0 ;
Timings . ThisExecTime = VisitRecord . Weight ;
}
2014-03-14 14:13:41 -04:00
}
}
}
}
}
void FAnimGraphConnectionDrawingPolicy : : DetermineStyleOfExecWire ( float & Thickness , FLinearColor & WireColor , bool & bDrawBubbles , const FTimePair & Times )
{
// It's a followed link, make it strong and yellowish but fading over time
const double BlendWeight = Times . ThisExecTime ;
const float HeavyBlendThickness = AttackWireThickness ;
const float LightBlendThickness = SustainWireThickness ;
Thickness = FMath : : Lerp < float > ( LightBlendThickness , HeavyBlendThickness , BlendWeight ) ;
WireColor = WireColor * ( BlendWeight * 0.5f + 0.5f ) ; //FMath::Lerp<FLinearColor>(SustainColor, AttackColor, BlendWeight);
bDrawBubbles = true ;
}
/////////////////////////////////////////////////////
// FSoundCueGraphConnectionDrawingPolicy
FSoundCueGraphConnectionDrawingPolicy : : FSoundCueGraphConnectionDrawingPolicy ( int32 InBackLayerID , int32 InFrontLayerID , float ZoomFactor , const FSlateRect & InClippingRect , FSlateWindowElementList & InDrawElements , UEdGraph * InGraphObj )
: FConnectionDrawingPolicy ( InBackLayerID , InFrontLayerID , ZoomFactor , InClippingRect , InDrawElements )
, GraphObj ( InGraphObj )
{
// Cache off the editor options
2014-05-20 19:00:53 -04:00
const UGraphEditorSettings * Settings = GetDefault < UGraphEditorSettings > ( ) ;
2014-03-14 14:13:41 -04:00
2014-05-20 19:00:53 -04:00
ActiveColor = Settings - > TraceAttackColor ;
InactiveColor = Settings - > TraceReleaseColor ;
ActiveWireThickness = Settings - > TraceAttackWireThickness ;
InactiveWireThickness = Settings - > TraceReleaseWireThickness ;
2014-03-14 14:13:41 -04:00
// Don't want to draw ending arrowheads
ArrowImage = nullptr ;
2014-05-14 14:55:31 -04:00
ArrowRadius = FVector2D : : ZeroVector ;
2014-03-14 14:13:41 -04:00
}
void FSoundCueGraphConnectionDrawingPolicy : : Draw ( TMap < TSharedRef < SWidget > , FArrangedWidget > & PinGeometries , FArrangedChildren & ArrangedNodes )
{
// Build the execution roadmap (also populates execution times)
BuildAudioFlowRoadmap ( ) ;
// Draw everything
FConnectionDrawingPolicy : : Draw ( PinGeometries , ArrangedNodes ) ;
}
void FSoundCueGraphConnectionDrawingPolicy : : BuildAudioFlowRoadmap ( )
{
FAudioDevice * AudioDevice = GEngine - > GetAudioDevice ( ) ;
if ( AudioDevice )
{
USoundCueGraph * SoundCueGraph = CastChecked < USoundCueGraph > ( GraphObj ) ;
USoundCue * SoundCue = SoundCueGraph - > GetSoundCue ( ) ;
UAudioComponent * PreviewAudioComponent = GEditor - > GetPreviewAudioComponent ( ) ;
if ( PreviewAudioComponent & & PreviewAudioComponent - > IsPlaying ( ) & & PreviewAudioComponent - > Sound = = SoundCue )
{
TArray < FWaveInstance * > WaveInstances ;
const int32 FirstActiveIndex = AudioDevice - > GetSortedActiveWaveInstances ( WaveInstances , ESortedActiveWaveGetType : : QueryOnly ) ;
// Run through the active instances and cull out anything that isn't related to this graph
if ( FirstActiveIndex > 0 )
{
WaveInstances . RemoveAt ( 0 , FirstActiveIndex + 1 ) ;
}
for ( int32 WaveIndex = WaveInstances . Num ( ) - 1 ; WaveIndex > = 0 ; - - WaveIndex )
{
UAudioComponent * WaveInstanceAudioComponent = WaveInstances [ WaveIndex ] - > ActiveSound - > AudioComponent . Get ( ) ;
if ( WaveInstanceAudioComponent ! = PreviewAudioComponent )
{
WaveInstances . RemoveAtSwap ( WaveIndex ) ;
}
}
for ( int32 WaveIndex = 0 ; WaveIndex < WaveInstances . Num ( ) ; + + WaveIndex )
{
TArray < USoundNode * > PathToWaveInstance ;
if ( SoundCue - > FindPathToNode ( WaveInstances [ WaveIndex ] - > WaveInstanceHash , PathToWaveInstance ) )
{
TArray < USoundCueGraphNode_Root * > RootNode ;
TArray < UEdGraphNode * > GraphNodes ;
SoundCueGraph - > GetNodesOfClass < USoundCueGraphNode_Root > ( RootNode ) ;
check ( RootNode . Num ( ) = = 1 ) ;
GraphNodes . Add ( RootNode [ 0 ] ) ;
TArray < double > NodeTimes ;
2014-04-23 18:12:58 -04:00
NodeTimes . Add ( FApp : : GetCurrentTime ( ) ) ; // Time for the root node
2014-03-14 14:13:41 -04:00
for ( int32 i = 0 ; i < PathToWaveInstance . Num ( ) ; + + i )
{
2014-04-23 18:12:58 -04:00
const double ObservationTime = FApp : : GetCurrentTime ( ) + 1.f ;
2014-03-14 14:13:41 -04:00
NodeTimes . Add ( ObservationTime ) ;
GraphNodes . Add ( PathToWaveInstance [ i ] - > GraphNode ) ;
}
// Record the unique node->node pairings, keeping only the most recent times for each pairing
for ( int32 i = GraphNodes . Num ( ) - 1 ; i > = 1 ; - - i )
{
UEdGraphNode * CurNode = GraphNodes [ i ] ;
double CurNodeTime = NodeTimes [ i ] ;
UEdGraphNode * NextNode = GraphNodes [ i - 1 ] ;
double NextNodeTime = NodeTimes [ i - 1 ] ;
FExecPairingMap & Predecessors = PredecessorNodes . FindOrAdd ( NextNode ) ;
// Update the timings if this is a more recent pairing
FTimePair & Timings = Predecessors . FindOrAdd ( CurNode ) ;
if ( Timings . ThisExecTime < NextNodeTime )
{
Timings . PredExecTime = CurNodeTime ;
Timings . ThisExecTime = NextNodeTime ;
}
}
}
}
}
}
}
// Give specific editor modes a chance to highlight this connection or darken non-interesting connections
void FSoundCueGraphConnectionDrawingPolicy : : DetermineWiringStyle ( UEdGraphPin * OutputPin , UEdGraphPin * InputPin , /*inout*/ float & Thickness , /*inout*/ FLinearColor & WireColor , /*inout*/ bool & bDrawBubbles , /*inout*/ bool & bBidirectional )
{
// Get the schema and grab the default color from it
check ( OutputPin ) ;
check ( GraphObj ) ;
const UEdGraphSchema * Schema = GraphObj - > GetSchema ( ) ;
WireColor = Schema - > GetPinTypeColor ( OutputPin - > PinType ) ;
if ( InputPin = = NULL )
{
return ;
}
bool bExecuted = false ;
// Run thru the predecessors, and on
if ( FExecPairingMap * PredecessorMap = PredecessorNodes . Find ( InputPin - > GetOwningNode ( ) ) )
{
if ( FTimePair * Times = PredecessorMap - > Find ( OutputPin - > GetOwningNode ( ) ) )
{
bExecuted = true ;
Thickness = ActiveWireThickness ;
WireColor = ActiveColor ;
bDrawBubbles = true ;
}
}
if ( ! bExecuted )
{
// It's not followed, fade it and keep it thin
WireColor = InactiveColor ;
Thickness = InactiveWireThickness ;
}
}
/////////////////////////////////////////////////////
// FMaterialGraphConnectionDrawingPolicy
FMaterialGraphConnectionDrawingPolicy : : FMaterialGraphConnectionDrawingPolicy ( int32 InBackLayerID , int32 InFrontLayerID , float ZoomFactor , const FSlateRect & InClippingRect , FSlateWindowElementList & InDrawElements , UEdGraph * InGraphObj )
: FConnectionDrawingPolicy ( InBackLayerID , InFrontLayerID , ZoomFactor , InClippingRect , InDrawElements )
, MaterialGraph ( CastChecked < UMaterialGraph > ( InGraphObj ) )
, MaterialGraphSchema ( CastChecked < UMaterialGraphSchema > ( InGraphObj - > GetSchema ( ) ) )
{
// Don't want to draw ending arrowheads
ArrowImage = nullptr ;
2014-05-14 14:55:31 -04:00
ArrowRadius = FVector2D : : ZeroVector ;
2014-03-14 14:13:41 -04:00
}
void FMaterialGraphConnectionDrawingPolicy : : DetermineWiringStyle ( UEdGraphPin * OutputPin , UEdGraphPin * InputPin , /*inout*/ float & Thickness , /*inout*/ FLinearColor & WireColor , /*inout*/ bool & bDrawBubbles , /*inout*/ bool & bBidirectional )
{
WireColor = MaterialGraphSchema - > ActivePinColor ;
// Have to consider both pins as the input will be an 'output' when previewing a connection
if ( OutputPin )
{
if ( ! MaterialGraph - > IsInputActive ( OutputPin ) )
{
WireColor = MaterialGraphSchema - > InactivePinColor ;
}
}
if ( InputPin )
{
if ( ! MaterialGraph - > IsInputActive ( InputPin ) )
{
WireColor = MaterialGraphSchema - > InactivePinColor ;
}
}
const bool bDeemphasizeUnhoveredPins = HoveredPins . Num ( ) > 0 ;
if ( bDeemphasizeUnhoveredPins )
{
ApplyHoverDeemphasis ( OutputPin , InputPin , /*inout*/ Thickness , /*inout*/ WireColor ) ;
}
}