2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
# include "GraphEditorCommon.h"
2014-05-22 11:33:54 -04:00
# include "Materials/MaterialExpression.h"
2014-09-11 08:13:08 -04:00
# include "Materials/MaterialFunction.h"
2014-03-14 14:13:41 -04:00
# include "SGraphNodeMaterialBase.h"
# include "ScopedTransaction.h"
# include "Runtime/Engine/Public/Slate/SceneViewport.h"
2014-09-11 08:13:08 -04:00
# include "TutorialMetaData.h"
2015-04-10 03:30:54 -04:00
# include "CanvasTypes.h"
# include "CanvasItem.h"
2014-09-11 08:13:08 -04:00
2014-03-14 14:13:41 -04:00
/**
* Simple representation of the backbuffer that the preview canvas renders to
* This class may only be accessed from the render thread
*/
2014-06-21 13:09:21 -04:00
class FSlateMaterialPreviewRenderTarget : public FRenderTarget
2014-03-14 14:13:41 -04:00
{
public :
/** FRenderTarget interface */
virtual FIntPoint GetSizeXY ( ) const
{
return ClippingRect . Size ( ) ;
}
/** Sets the texture that this target renders to */
void SetRenderTargetTexture ( FTexture2DRHIRef & InRHIRef )
{
RenderTargetTextureRHI = InRHIRef ;
}
/** Clears the render target texture */
void ClearRenderTargetTexture ( )
{
RenderTargetTextureRHI . SafeRelease ( ) ;
}
/** Sets the viewport rect for the render target */
void SetViewRect ( const FIntRect & InViewRect )
{
ViewRect = InViewRect ;
}
/** Gets the viewport rect for the render target */
const FIntRect & GetViewRect ( ) const
{
return ViewRect ;
}
/** Sets the clipping rect for the render target */
void SetClippingRect ( const FIntRect & InClippingRect )
{
ClippingRect = InClippingRect ;
}
/** Gets the clipping rect for the render target */
const FIntRect & GetClippingRect ( ) const
{
return ClippingRect ;
}
private :
FIntRect ViewRect ;
FIntRect ClippingRect ;
} ;
/*-----------------------------------------------------------------------------
FPreviewViewport
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
FPreviewViewport : : FPreviewViewport ( class UMaterialGraphNode * InNode )
: MaterialNode ( InNode )
, PreviewElement ( new FPreviewElement )
{
}
FPreviewViewport : : ~ FPreviewViewport ( )
{
// Pass the preview element to the render thread so that it's deleted after it's shown for the last time
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER
(
SafeDeletePreviewElement ,
FThreadSafePreviewPtr , PreviewElementPtr , PreviewElement ,
{
PreviewElementPtr . Reset ( ) ;
}
) ;
}
void FPreviewViewport : : OnDrawViewport ( const FGeometry & AllottedGeometry , const FSlateRect & MyClippingRect , class FSlateWindowElementList & OutDrawElements , int32 LayerId , const FWidgetStyle & InWidgetStyle , bool bParentEnabled )
{
FSlateRect SlateCanvasRect = AllottedGeometry . GetClippingRect ( ) ;
FSlateRect ClippedCanvasRect = SlateCanvasRect . IntersectionWith ( MyClippingRect ) ;
FIntRect CanvasRect (
2014-05-06 06:26:25 -04:00
FMath : : TruncToInt ( FMath : : Max ( 0.0f , SlateCanvasRect . Left ) ) ,
FMath : : TruncToInt ( FMath : : Max ( 0.0f , SlateCanvasRect . Top ) ) ,
FMath : : TruncToInt ( FMath : : Max ( 0.0f , SlateCanvasRect . Right ) ) ,
FMath : : TruncToInt ( FMath : : Max ( 0.0f , SlateCanvasRect . Bottom ) ) ) ;
2014-03-14 14:13:41 -04:00
FIntRect ClippingRect (
2014-05-06 06:26:25 -04:00
FMath : : TruncToInt ( FMath : : Max ( 0.0f , ClippedCanvasRect . Left ) ) ,
FMath : : TruncToInt ( FMath : : Max ( 0.0f , ClippedCanvasRect . Top ) ) ,
FMath : : TruncToInt ( FMath : : Max ( 0.0f , ClippedCanvasRect . Right ) ) ,
FMath : : TruncToInt ( FMath : : Max ( 0.0f , ClippedCanvasRect . Bottom ) ) ) ;
2014-03-14 14:13:41 -04:00
bool bIsRealtime = MaterialNode - > RealtimeDelegate . IsBound ( ) ? MaterialNode - > RealtimeDelegate . Execute ( ) : false ;
if ( PreviewElement - > BeginRenderingCanvas ( CanvasRect , ClippingRect , MaterialNode - > GetExpressionPreview ( ) , bIsRealtime ) )
{
// Draw above everything else
2014-09-23 17:25:46 -04:00
uint32 PreviewLayer = LayerId + 1 ;
2014-03-14 14:13:41 -04:00
FSlateDrawElement : : MakeCustom ( OutDrawElements , PreviewLayer , PreviewElement ) ;
}
}
FIntPoint FPreviewViewport : : GetSize ( ) const
{
return FIntPoint ( 96 , 96 ) ;
}
/////////////////////////////////////////////////////
// FPreviewElement
FPreviewElement : : FPreviewElement ( )
2014-07-30 14:51:27 -04:00
: RenderTarget ( new FSlateMaterialPreviewRenderTarget )
, ExpressionPreview ( NULL )
2014-03-14 14:13:41 -04:00
, bIsRealtime ( false )
{
}
FPreviewElement : : ~ FPreviewElement ( )
{
delete RenderTarget ;
}
bool FPreviewElement : : BeginRenderingCanvas ( const FIntRect & InCanvasRect , const FIntRect & InClippingRect , FMaterialRenderProxy * InExpressionPreview , bool bInIsRealtime )
{
if ( InCanvasRect . Size ( ) . X > 0 & & InCanvasRect . Size ( ) . Y > 0 & & InClippingRect . Size ( ) . X > 0 & & InClippingRect . Size ( ) . Y > 0 & & InExpressionPreview ! = NULL )
{
/**
* Struct to contain all info that needs to be passed to the render thread
*/
struct FPreviewRenderInfo
{
/** Size of the Canvas tile */
FIntRect CanvasRect ;
/** How to clip the canvas tile */
FIntRect ClippingRect ;
/** Render proxy for the expression preview */
FMaterialRenderProxy * ExpressionPreview ;
/** Whether preview is using realtime values */
bool bIsRealtime ;
} ;
FPreviewRenderInfo RenderInfo ;
RenderInfo . CanvasRect = InCanvasRect ;
RenderInfo . ClippingRect = InClippingRect ;
RenderInfo . ExpressionPreview = InExpressionPreview ;
RenderInfo . bIsRealtime = bInIsRealtime ;
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER
(
BeginRenderingPreviewCanvas ,
FPreviewElement * , PreviewElement , this ,
FPreviewRenderInfo , InRenderInfo , RenderInfo ,
{
PreviewElement - > RenderTarget - > SetViewRect ( InRenderInfo . CanvasRect ) ;
PreviewElement - > RenderTarget - > SetClippingRect ( InRenderInfo . ClippingRect ) ;
PreviewElement - > ExpressionPreview = InRenderInfo . ExpressionPreview ;
PreviewElement - > bIsRealtime = InRenderInfo . bIsRealtime ;
}
) ;
return true ;
}
return false ;
}
2014-06-27 11:07:13 -04:00
void FPreviewElement : : DrawRenderThread ( FRHICommandListImmediate & RHICmdList , const void * InWindowBackBuffer )
2014-03-14 14:13:41 -04:00
{
// Clip the canvas to avoid having to set UV values
FIntRect ClippingRect = RenderTarget - > GetClippingRect ( ) ;
2014-06-27 11:07:13 -04:00
RHICmdList . SetScissorRect ( true ,
2014-03-14 14:13:41 -04:00
ClippingRect . Min . X ,
ClippingRect . Min . Y ,
ClippingRect . Max . X ,
ClippingRect . Max . Y ) ;
RenderTarget - > SetRenderTargetTexture ( * ( FTexture2DRHIRef * ) InWindowBackBuffer ) ;
{
// Check realtime mode for whether to pass current time to canvas
2014-04-23 18:12:58 -04:00
float CurrentTime = bIsRealtime ? ( FApp : : GetCurrentTime ( ) - GStartTime ) : 0.0f ;
float DeltaTime = bIsRealtime ? FApp : : GetDeltaTime ( ) : 0.0f ;
2014-03-14 14:13:41 -04:00
2014-08-19 10:41:34 -04:00
FCanvas Canvas ( RenderTarget , NULL , CurrentTime , CurrentTime , DeltaTime , GMaxRHIFeatureLevel ) ;
2014-03-14 14:13:41 -04:00
{
Canvas . SetAllowedModes ( 0 ) ;
Canvas . SetRenderTargetRect ( RenderTarget - > GetViewRect ( ) ) ;
FCanvasTileItem TileItem ( FVector2D : : ZeroVector , ExpressionPreview , RenderTarget - > GetSizeXY ( ) ) ;
Canvas . DrawItem ( TileItem ) ;
}
2014-07-02 14:13:59 -04:00
Canvas . Flush_RenderThread ( RHICmdList , true ) ;
2014-03-14 14:13:41 -04:00
}
RenderTarget - > ClearRenderTargetTexture ( ) ;
2014-06-27 11:07:13 -04:00
RHICmdList . SetScissorRect ( false , 0 , 0 , 0 , 0 ) ;
2014-03-14 14:13:41 -04:00
}
/////////////////////////////////////////////////////
// SGraphNodeMaterialBase
void SGraphNodeMaterialBase : : Construct ( const FArguments & InArgs , UMaterialGraphNode * InNode )
{
this - > GraphNode = InNode ;
this - > MaterialNode = InNode ;
this - > SetCursor ( EMouseCursor : : CardinalCross ) ;
2014-10-15 11:42:39 -04:00
this - > UpdateGraphNode ( ) ;
2014-03-14 14:13:41 -04:00
}
void SGraphNodeMaterialBase : : CreatePinWidgets ( )
{
// Create Pin widgets for each of the pins.
for ( int32 PinIndex = 0 ; PinIndex < GraphNode - > Pins . Num ( ) ; + + PinIndex )
{
UEdGraphPin * CurPin = GraphNode - > Pins [ PinIndex ] ;
bool bHideNoConnectionPins = false ;
if ( OwnerGraphPanelPtr . IsValid ( ) )
{
bHideNoConnectionPins = OwnerGraphPanelPtr . Pin ( ) - > GetPinVisibility ( ) = = SGraphEditor : : Pin_HideNoConnection ;
}
const bool bPinHasConections = CurPin - > LinkedTo . Num ( ) > 0 ;
const bool bPinDesiresToBeHidden = CurPin - > bHidden | | ( bHideNoConnectionPins & & ! bPinHasConections ) ;
if ( ! bPinDesiresToBeHidden )
{
TSharedPtr < SGraphPin > NewPin = CreatePinWidget ( CurPin ) ;
check ( NewPin . IsValid ( ) ) ;
NewPin - > SetIsEditable ( IsEditable ) ;
this - > AddPin ( NewPin . ToSharedRef ( ) ) ;
}
}
}
2014-05-06 04:45:44 -04:00
void SGraphNodeMaterialBase : : MoveTo ( const FVector2D & NewPosition , FNodeSet & NodeFilter )
2014-03-14 14:13:41 -04:00
{
2014-05-06 04:45:44 -04:00
SGraphNode : : MoveTo ( NewPosition , NodeFilter ) ;
2014-03-14 14:13:41 -04:00
MaterialNode - > MaterialExpression - > MaterialExpressionEditorX = MaterialNode - > NodePosX ;
MaterialNode - > MaterialExpression - > MaterialExpressionEditorY = MaterialNode - > NodePosY ;
MaterialNode - > MaterialExpression - > MarkPackageDirty ( ) ;
MaterialNode - > MaterialDirtyDelegate . ExecuteIfBound ( ) ;
}
void SGraphNodeMaterialBase : : AddPin ( const TSharedRef < SGraphPin > & PinToAdd )
{
PinToAdd - > SetOwner ( SharedThis ( this ) ) ;
if ( PinToAdd - > GetDirection ( ) = = EEdGraphPinDirection : : EGPD_Input )
{
2014-09-15 21:48:46 -04:00
FMargin Padding = Settings - > GetInputPinPadding ( ) ;
Padding . Left * = 0.5f ;
Padding . Right = 0.0f ;
2014-03-14 14:13:41 -04:00
LeftNodeBox - > AddSlot ( )
. AutoHeight ( )
. HAlign ( HAlign_Left )
. VAlign ( VAlign_Center )
2014-09-15 21:48:46 -04:00
. Padding ( Padding )
2014-03-14 14:13:41 -04:00
[
PinToAdd
] ;
InputPins . Add ( PinToAdd ) ;
}
else // Direction == EEdGraphPinDirection::EGPD_Output
{
2014-09-15 21:48:46 -04:00
FMargin Padding = Settings - > GetOutputPinPadding ( ) ;
Padding . Left = 0.0f ;
Padding . Right * = 0.5f ;
2014-03-14 14:13:41 -04:00
RightNodeBox - > AddSlot ( )
. AutoHeight ( )
. HAlign ( HAlign_Right )
. VAlign ( VAlign_Center )
2014-09-15 21:48:46 -04:00
. Padding ( Padding )
2014-03-14 14:13:41 -04:00
[
PinToAdd
] ;
OutputPins . Add ( PinToAdd ) ;
}
}
void SGraphNodeMaterialBase : : CreateBelowPinControls ( TSharedPtr < SVerticalBox > MainBox )
{
if ( GraphNode & & MainBox . IsValid ( ) )
{
int32 LeftPinCount = InputPins . Num ( ) ;
int32 RightPinCount = OutputPins . Num ( ) ;
2014-09-15 21:48:46 -04:00
const float NegativeHPad = FMath : : Max < float > ( - Settings - > PaddingTowardsNodeEdge , 0.0f ) ;
const float ExtraPad = 0.0f ;
2014-03-14 14:13:41 -04:00
// Place preview widget based on where the least pins are
2014-09-15 21:48:46 -04:00
if ( ( LeftPinCount < RightPinCount ) | | ( RightPinCount = = 0 ) )
2014-03-14 14:13:41 -04:00
{
LeftNodeBox - > AddSlot ( )
2014-09-15 21:48:46 -04:00
. Padding ( FMargin ( NegativeHPad + ExtraPad , 0.0f , 0.0f , 0.0f ) )
2014-03-14 14:13:41 -04:00
. AutoHeight ( )
[
CreatePreviewWidget ( )
] ;
}
else if ( LeftPinCount > RightPinCount )
{
RightNodeBox - > AddSlot ( )
2014-09-15 21:48:46 -04:00
. Padding ( FMargin ( NegativeHPad + ExtraPad , 0.0f , 0.0f , 0.0f ) )
2014-03-14 14:13:41 -04:00
. AutoHeight ( )
[
CreatePreviewWidget ( )
] ;
}
else
{
MainBox - > AddSlot ( )
2014-09-15 21:48:46 -04:00
. Padding ( Settings - > GetNonPinNodeBodyPadding ( ) )
2014-03-14 14:13:41 -04:00
. AutoHeight ( )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
[
CreatePreviewWidget ( )
]
] ;
}
}
}
void SGraphNodeMaterialBase : : SetDefaultTitleAreaWidget ( TSharedRef < SOverlay > DefaultTitleAreaWidget )
{
if ( ! MaterialNode - > MaterialExpression - > bHidePreviewWindow )
{
DefaultTitleAreaWidget - > AddSlot ( )
. HAlign ( HAlign_Right )
. VAlign ( VAlign_Center )
. Padding ( FMargin ( 5 ) )
[
SNew ( SCheckBox )
. OnCheckStateChanged ( this , & SGraphNodeMaterialBase : : OnExpressionPreviewChanged )
. IsChecked ( IsExpressionPreviewChecked ( ) )
. Cursor ( EMouseCursor : : Default )
. Style ( FEditorStyle : : Get ( ) , " Graph.Node.AdvancedView " )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. VAlign ( VAlign_Center )
. HAlign ( HAlign_Center )
[
SNew ( SImage )
. Image ( GetExpressionPreviewArrow ( ) )
]
]
] ;
}
}
TSharedRef < SWidget > SGraphNodeMaterialBase : : CreateNodeContentArea ( )
{
// NODE CONTENT AREA
return SNew ( SBorder )
. BorderImage ( FEditorStyle : : GetBrush ( " NoBorder " ) )
. HAlign ( HAlign_Fill )
. VAlign ( VAlign_Fill )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. HAlign ( HAlign_Left )
. FillWidth ( 1.0f )
[
// LEFT
SAssignNew ( LeftNodeBox , SVerticalBox )
]
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. HAlign ( HAlign_Right )
[
// RIGHT
SAssignNew ( RightNodeBox , SVerticalBox )
]
] ;
}
TSharedRef < SWidget > SGraphNodeMaterialBase : : CreatePreviewWidget ( )
{
PreviewViewport . Reset ( ) ;
// if this node should currently show a preview
if ( ! MaterialNode - > MaterialExpression - > bHidePreviewWindow & & ! MaterialNode - > MaterialExpression - > bCollapsed )
{
const float ExpressionPreviewSize = 106.0f ;
const float CentralPadding = 5.0f ;
TSharedPtr < SViewport > ViewportWidget =
SNew ( SViewport )
. EnableGammaCorrection ( false ) ;
PreviewViewport = MakeShareable ( new FPreviewViewport ( MaterialNode ) ) ;
// The viewport widget needs an interface so it knows what should render
ViewportWidget - > SetViewportInterface ( PreviewViewport . ToSharedRef ( ) ) ;
return SNew ( SBox )
. WidthOverride ( ExpressionPreviewSize )
. HeightOverride ( ExpressionPreviewSize )
. Visibility ( ExpressionPreviewVisibility ( ) )
[
SNew ( SBorder )
. Padding ( CentralPadding )
. BorderImage ( FEditorStyle : : GetBrush ( " NoBorder " ) )
[
ViewportWidget . ToSharedRef ( )
]
] ;
}
return SNullWidget : : NullWidget ;
}
EVisibility SGraphNodeMaterialBase : : ExpressionPreviewVisibility ( ) const
{
UMaterialExpression * MaterialExpression = MaterialNode - > MaterialExpression ;
const bool bShowPreview = ! MaterialExpression - > bHidePreviewWindow & & ! MaterialExpression - > bCollapsed ;
return bShowPreview ? EVisibility : : Visible : EVisibility : : Collapsed ;
}
2014-12-10 14:24:09 -05:00
void SGraphNodeMaterialBase : : OnExpressionPreviewChanged ( const ECheckBoxState NewCheckedState )
2014-03-14 14:13:41 -04:00
{
UMaterialExpression * MaterialExpression = MaterialNode - > MaterialExpression ;
2014-12-10 14:24:09 -05:00
const bool bCollapsed = ( NewCheckedState ! = ECheckBoxState : : Checked ) ;
2014-03-14 14:13:41 -04:00
if ( MaterialExpression - > bCollapsed ! = bCollapsed )
{
UMaterialGraph * MaterialGraph = CastChecked < UMaterialGraph > ( MaterialNode - > GetGraph ( ) ) ;
MaterialGraph - > ToggleCollapsedDelegate . ExecuteIfBound ( MaterialExpression ) ;
// Update the graph node so that preview viewport is created
UpdateGraphNode ( ) ;
}
}
2014-12-10 14:24:09 -05:00
ECheckBoxState SGraphNodeMaterialBase : : IsExpressionPreviewChecked ( ) const
2014-03-14 14:13:41 -04:00
{
2014-12-10 14:24:09 -05:00
return MaterialNode - > MaterialExpression - > bCollapsed ? ECheckBoxState : : Unchecked : ECheckBoxState : : Checked ;
2014-03-14 14:13:41 -04:00
}
const FSlateBrush * SGraphNodeMaterialBase : : GetExpressionPreviewArrow ( ) const
{
return FEditorStyle : : GetBrush ( MaterialNode - > MaterialExpression - > bCollapsed ? TEXT ( " Kismet.TitleBarEditor.ArrowDown " ) : TEXT ( " Kismet.TitleBarEditor.ArrowUp " ) ) ;
}
2014-09-11 08:13:08 -04:00
void SGraphNodeMaterialBase : : PopulateMetaTag ( FGraphNodeMetaData * TagMeta ) const
{
if ( GraphNode ! = nullptr )
{
UMaterialGraph * OuterGraph = MaterialNode - > GetTypedOuter < UMaterialGraph > ( ) ;
if ( ( OuterGraph ! = nullptr ) & & ( MaterialNode - > MaterialExpression ! = nullptr ) )
{
TagMeta - > OuterName = OuterGraph - > OriginalMaterialFullName ;
TagMeta - > GUID = MaterialNode - > MaterialExpression - > MaterialExpressionGuid ;
TagMeta - > Tag = FName ( * FString : : Printf ( TEXT ( " MaterialExprNode_%s_%s " ) , * TagMeta - > OuterName , * TagMeta - > GUID . ToString ( ) ) ) ;
}
TagMeta - > FriendlyName = FString : : Printf ( TEXT ( " %s expression node in %s " ) , * GraphNode - > GetNodeTitle ( ENodeTitleType : : FullTitle ) . ToString ( ) , * TagMeta - > OuterName ) ;
}
}