746 lines
22 KiB
C++
746 lines
22 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MaterialEditor/MaterialNodes/SGraphNodeMaterialBase.h"
|
|
|
|
#include "CanvasItem.h"
|
|
#include "CanvasTypes.h"
|
|
#include "Containers/Array.h"
|
|
#include "Containers/EnumAsByte.h"
|
|
#include "Containers/UnrealString.h"
|
|
#include "CoreGlobals.h"
|
|
#include "Delegates/Delegate.h"
|
|
#include "EdGraph/EdGraphNode.h"
|
|
#include "EdGraph/EdGraphPin.h"
|
|
#include "Engine/World.h"
|
|
#include "GenericPlatform/ICursor.h"
|
|
#include "GraphEditor.h"
|
|
#include "GraphEditorSettings.h"
|
|
#include "HAL/PlatformCrt.h"
|
|
#include "Layout/Geometry.h"
|
|
#include "Layout/Margin.h"
|
|
#include "Layout/SlateRect.h"
|
|
#include "MaterialGraph/MaterialGraph.h"
|
|
#include "MaterialGraph/MaterialGraphNode.h"
|
|
#include "MaterialGraph/MaterialGraphSchema.h"
|
|
#include "Materials/MaterialExpression.h"
|
|
#include "Materials/MaterialExpressionBreakMaterialAttributes.h"
|
|
#include "Materials/MaterialExpressionMakeMaterialAttributes.h"
|
|
#include "Materials/MaterialExpressionMaterialFunctionCall.h"
|
|
#include "Materials/MaterialExpressionSubstrate.h"
|
|
#include "Materials/MaterialFunction.h"
|
|
#include "Math/Color.h"
|
|
#include "Math/IntPoint.h"
|
|
#include "Math/IntRect.h"
|
|
#include "Misc/App.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
#include "Misc/Attribute.h"
|
|
#include "Misc/Guid.h"
|
|
#include "Misc/Optional.h"
|
|
#include "RHI.h"
|
|
#include "RHICommandList.h"
|
|
#include "Rendering/DrawElements.h"
|
|
#include "Rendering/SlateRenderer.h"
|
|
#include "RenderingThread.h"
|
|
#include "SGraphPanel.h"
|
|
#include "SGraphPin.h"
|
|
#include "SlotBase.h"
|
|
#include "Styling/AppStyle.h"
|
|
#include "Styling/ISlateStyle.h"
|
|
#include "Templates/Casts.h"
|
|
#include "TutorialMetaData.h"
|
|
#include "Types/SlateEnums.h"
|
|
#include "Types/SlateStructs.h"
|
|
#include "UObject/NameTypes.h"
|
|
#include "UObject/ObjectPtr.h"
|
|
#include "UnrealClient.h"
|
|
#include "Widgets/Images/SImage.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "Widgets/Input/SCheckBox.h"
|
|
#include "Widgets/Layout/SBorder.h"
|
|
#include "Widgets/Layout/SBox.h"
|
|
#include "Widgets/SBoxPanel.h"
|
|
#include "Widgets/SNullWidget.h"
|
|
#include "Widgets/SOverlay.h"
|
|
#include "Widgets/SViewport.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
#include "Rendering/SubstrateMaterialShared.h"
|
|
#include "MaterialEditor/SGraphSubstrateMaterial.h"
|
|
#include "MaterialShared.h"
|
|
#include "RenderGraphBuilder.h"
|
|
|
|
class FWidgetStyle;
|
|
class SWidget;
|
|
struct FSlateBrush;
|
|
|
|
static const FName NAME_Pin_NotConnectable("Graph.Pin.Dummy");
|
|
static const FSlateBrush* CacheImg_Pin_NotConnectable = nullptr;
|
|
|
|
/**
|
|
* Simple representation of the backbuffer that the preview canvas renders to
|
|
* This class may only be accessed from the render thread
|
|
*/
|
|
class FSlateMaterialPreviewRenderTarget : public FRenderTarget
|
|
{
|
|
public:
|
|
/** FRenderTarget interface */
|
|
virtual FIntPoint GetSizeXY() const
|
|
{
|
|
return ClippingRect.Size();
|
|
}
|
|
|
|
/** Sets the texture that this target renders to */
|
|
void SetRenderTargetTexture(FRDGTexture* Texture)
|
|
{
|
|
RDGTexture = Texture;
|
|
}
|
|
|
|
/** Clears the render target texture */
|
|
void ClearRenderTargetTexture()
|
|
{
|
|
RDGTexture = nullptr;
|
|
}
|
|
|
|
FRDGTextureRef GetRenderTargetTexture(FRDGBuilder&) const override
|
|
{
|
|
return RDGTexture;
|
|
}
|
|
|
|
/** 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:
|
|
FRDGTexture* RDGTexture = nullptr;
|
|
FIntRect ViewRect;
|
|
FIntRect ClippingRect;
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
FPreviewViewport
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
FPreviewViewport::FPreviewViewport(class UMaterialGraphNode* InNode)
|
|
: MaterialNode(InNode)
|
|
, PreviewElement( new FPreviewElement )
|
|
{
|
|
if (MaterialNode)
|
|
{
|
|
MaterialNode->InvalidatePreviewMaterialDelegate.BindRaw(this, &FPreviewViewport::UpdatePreviewNodeRenderProxy);
|
|
}
|
|
}
|
|
|
|
FPreviewViewport::~FPreviewViewport()
|
|
{
|
|
if (MaterialNode)
|
|
{
|
|
MaterialNode->InvalidatePreviewMaterialDelegate.Unbind();
|
|
}
|
|
// Pass the preview element to the render thread so that it's deleted after it's shown for the last time
|
|
ENQUEUE_RENDER_COMMAND(SafeDeletePreviewElement)(
|
|
[PreviewElement = PreviewElement](FRHICommandListImmediate& RHICmdList) mutable
|
|
{
|
|
PreviewElement.Reset();
|
|
}
|
|
);
|
|
}
|
|
|
|
void FPreviewViewport::OnDrawViewport( const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, class FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled )
|
|
{
|
|
FSlateRect SlateCanvasRect = AllottedGeometry.GetLayoutBoundingRect();
|
|
FSlateRect ClippedCanvasRect = SlateCanvasRect.IntersectionWith(MyCullingRect);
|
|
|
|
FIntRect CanvasRect(
|
|
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) ) );
|
|
|
|
FIntRect ClippingRect(
|
|
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) ) );
|
|
|
|
bool bIsRealtime = MaterialNode->RealtimeDelegate.IsBound() ? MaterialNode->RealtimeDelegate.Execute() : false;
|
|
|
|
if (PreviewElement->BeginRenderingCanvas(CanvasRect, ClippingRect, MaterialNode, bIsRealtime))
|
|
{
|
|
// Draw above everything else
|
|
uint32 PreviewLayer = LayerId+1;
|
|
FSlateDrawElement::MakeCustom( OutDrawElements, PreviewLayer, PreviewElement );
|
|
}
|
|
}
|
|
|
|
FIntPoint FPreviewViewport::GetSize() const
|
|
{
|
|
return FIntPoint(96,96);
|
|
}
|
|
|
|
void FPreviewViewport::UpdatePreviewNodeRenderProxy()
|
|
{
|
|
if (PreviewElement.IsValid())
|
|
{
|
|
PreviewElement->UpdateExpressionPreview(MaterialNode);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
// FPreviewElement
|
|
|
|
FPreviewElement::FPreviewElement()
|
|
: RenderTarget(new FSlateMaterialPreviewRenderTarget)
|
|
, ExpressionPreview(nullptr)
|
|
, bIsRealtime(false)
|
|
{
|
|
}
|
|
|
|
FPreviewElement::~FPreviewElement()
|
|
{
|
|
delete RenderTarget;
|
|
}
|
|
|
|
bool FPreviewElement::BeginRenderingCanvas( const FIntRect& InCanvasRect, const FIntRect& InClippingRect, UMaterialGraphNode* InGraphNode, bool bInIsRealtime )
|
|
{
|
|
if(InCanvasRect.Size().X > 0 && InCanvasRect.Size().Y > 0 && InClippingRect.Size().X > 0 && InClippingRect.Size().Y > 0 && InGraphNode != 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* RenderProxy;
|
|
/** Whether preview is using realtime values */
|
|
bool bIsRealtime;
|
|
};
|
|
|
|
FPreviewRenderInfo RenderInfo;
|
|
RenderInfo.CanvasRect = InCanvasRect;
|
|
RenderInfo.ClippingRect = InClippingRect;
|
|
RenderInfo.RenderProxy = InGraphNode->GetExpressionPreview();
|
|
RenderInfo.bIsRealtime = bInIsRealtime;
|
|
|
|
FPreviewElement* PreviewElement = this;
|
|
ENQUEUE_RENDER_COMMAND(BeginRenderingPreviewCanvas)(
|
|
[PreviewElement, RenderInfo](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
PreviewElement->RenderTarget->SetViewRect(RenderInfo.CanvasRect);
|
|
PreviewElement->RenderTarget->SetClippingRect(RenderInfo.ClippingRect);
|
|
PreviewElement->ExpressionPreview = RenderInfo.RenderProxy;
|
|
PreviewElement->bIsRealtime = RenderInfo.bIsRealtime;
|
|
}
|
|
);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FPreviewElement::UpdateExpressionPreview(UMaterialGraphNode* MaterialNode)
|
|
{
|
|
FPreviewElement* PreviewElement = this;
|
|
FMaterialRenderProxy* InRenderProxy = MaterialNode ? MaterialNode->GetExpressionPreview() : nullptr;
|
|
ENQUEUE_RENDER_COMMAND(UpdatePreviewNodeRenderProxy)(
|
|
[PreviewElement, InRenderProxy](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
PreviewElement->ExpressionPreview = InRenderProxy;
|
|
}
|
|
);
|
|
}
|
|
|
|
void FPreviewElement::Draw_RenderThread(FRDGBuilder& GraphBuilder, const FDrawPassInputs& Inputs)
|
|
{
|
|
if(ExpressionPreview)
|
|
{
|
|
RenderTarget->SetRenderTargetTexture(Inputs.OutputTexture);
|
|
{
|
|
// Check realtime mode for whether to pass current time to canvas
|
|
double CurrentTime = bIsRealtime ? (FApp::GetCurrentTime() - GStartTime) : 0.0;
|
|
float DeltaTime = bIsRealtime ? static_cast<float>(FApp::GetDeltaTime()) : 0.0f;
|
|
|
|
FCanvas* Canvas = GraphBuilder.AllocObject<FCanvas>(RenderTarget, nullptr, FGameTime::CreateUndilated(CurrentTime, DeltaTime), GMaxRHIFeatureLevel);
|
|
{
|
|
Canvas->SetAllowedModes(0);
|
|
Canvas->SetRenderTargetRect(RenderTarget->GetViewRect());
|
|
Canvas->SetRenderTargetScissorRect(RenderTarget->GetClippingRect());
|
|
|
|
FCanvasTileItem TileItem(FVector2D::ZeroVector, ExpressionPreview, RenderTarget->GetSizeXY());
|
|
Canvas->DrawItem(TileItem);
|
|
Canvas->Flush_RenderThread(GraphBuilder, true);
|
|
}
|
|
}
|
|
RenderTarget->ClearRenderTargetTexture();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
// SGraphNodeMaterialBase
|
|
|
|
void SGraphNodeMaterialBase::Construct(const FArguments& InArgs, UMaterialGraphNode* InNode)
|
|
{
|
|
this->TitleBorderMargin = FMargin(10.f, 5.f, 50.f, 3.f);
|
|
this->GraphNode = InNode;
|
|
this->MaterialNode = InNode;
|
|
|
|
this->SetCursor(EMouseCursor::CardinalCross);
|
|
|
|
this->UpdateGraphNode();
|
|
}
|
|
|
|
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;
|
|
|
|
bool bPinDesiresToBeHidden = CurPin->bHidden || (bHideNoConnectionPins && !bPinHasConections);
|
|
|
|
UMaterialGraph* MaterialGraph = CastChecked<UMaterialGraph>(GraphNode->GetGraph());
|
|
if (MaterialNode && MaterialNode->MaterialExpression && MaterialGraph->MaterialFunction == nullptr && !MaterialGraph->MaterialInputs.IsEmpty())
|
|
{
|
|
bool bIsAMakeAttrNode = MaterialNode->MaterialExpression->IsA(UMaterialExpressionMakeMaterialAttributes::StaticClass());
|
|
bool bIsABreakAttrNode = MaterialNode->MaterialExpression->IsA(UMaterialExpressionBreakMaterialAttributes::StaticClass());
|
|
|
|
if ((bIsABreakAttrNode && CurPin->Direction == EGPD_Output) || (bIsAMakeAttrNode && CurPin->Direction == EGPD_Input))
|
|
{
|
|
if (CurPin->PinType.PinCategory != UMaterialGraphSchema::PC_Exec)
|
|
{
|
|
bPinDesiresToBeHidden |= !MaterialGraph->MaterialInputs[CurPin->SourceIndex].IsVisiblePin(MaterialGraph->Material, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bPinDesiresToBeHidden)
|
|
{
|
|
TSharedPtr<SGraphPin> NewPin = CreatePinWidget(CurPin);
|
|
check(NewPin.IsValid());
|
|
|
|
// Assign an custom icon to not connectible pins
|
|
if (CurPin->bNotConnectable)
|
|
{
|
|
if (!CacheImg_Pin_NotConnectable)
|
|
{
|
|
CacheImg_Pin_NotConnectable = FAppStyle::Get().GetBrush(NAME_Pin_NotConnectable);
|
|
}
|
|
NewPin->SetCustomPinIcon(CacheImg_Pin_NotConnectable, CacheImg_Pin_NotConnectable);
|
|
}
|
|
|
|
// Override pin color for Substrate node
|
|
if (Substrate::IsSubstrateEnabled())
|
|
{
|
|
FSubstrateWidget::GetPinColor(NewPin, MaterialNode);
|
|
}
|
|
|
|
this->AddPin(NewPin.ToSharedRef());
|
|
}
|
|
}
|
|
}
|
|
|
|
void SGraphNodeMaterialBase::MoveTo(const FVector2f& NewPosition, FNodeSet& NodeFilter, bool bMarkDirty)
|
|
{
|
|
SGraphNode::MoveTo(NewPosition, NodeFilter, bMarkDirty);
|
|
|
|
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) );
|
|
|
|
// Set visibility on advanced view pins
|
|
const UEdGraphPin* PinObj = PinToAdd->GetPinObj();
|
|
const bool bAdvancedParameter = (PinObj != nullptr) && PinObj->bAdvancedView;
|
|
if (bAdvancedParameter)
|
|
{
|
|
PinToAdd->SetVisibility(TAttribute<EVisibility>(PinToAdd, &SGraphPin::IsPinVisibleAsAdvanced));
|
|
}
|
|
|
|
if (PinToAdd->GetDirection() == EEdGraphPinDirection::EGPD_Input)
|
|
{
|
|
FMargin Padding = Settings->GetInputPinPadding();
|
|
Padding.Left *= 0.5f;
|
|
Padding.Right = 0.0f;
|
|
|
|
LeftNodeBox->AddSlot()
|
|
.AutoHeight()
|
|
.HAlign(HAlign_Left)
|
|
.VAlign(VAlign_Center)
|
|
.Padding(Padding)
|
|
[
|
|
PinToAdd
|
|
];
|
|
InputPins.Add(PinToAdd);
|
|
}
|
|
else // Direction == EEdGraphPinDirection::EGPD_Output
|
|
{
|
|
FMargin Padding = Settings->GetOutputPinPadding();
|
|
Padding.Left = 0.0f;
|
|
Padding.Right *= 0.5f;
|
|
|
|
RightNodeBox->AddSlot()
|
|
.AutoHeight()
|
|
.HAlign(HAlign_Right)
|
|
.VAlign(VAlign_Center)
|
|
.Padding(Padding)
|
|
[
|
|
PinToAdd
|
|
];
|
|
OutputPins.Add(PinToAdd);
|
|
}
|
|
}
|
|
|
|
void SGraphNodeMaterialBase::CreateBelowPinControls(TSharedPtr<SVerticalBox> MainBox)
|
|
{
|
|
if (GraphNode && MainBox.IsValid())
|
|
{
|
|
// Count the number of visible input pins on the left
|
|
int32 LeftPinCount = 0;
|
|
if (GraphNode->AdvancedPinDisplay == ENodeAdvancedPins::Hidden)
|
|
{
|
|
// Advanced view pins are hidden so exclude them from the pin count
|
|
for (int32 i = 0; i < InputPins.Num(); ++i)
|
|
{
|
|
const UEdGraphPin* PinObj = InputPins[i]->GetPinObj();
|
|
if (!PinObj->bAdvancedView)
|
|
{
|
|
LeftPinCount++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LeftPinCount = InputPins.Num();
|
|
}
|
|
|
|
int32 RightPinCount = OutputPins.Num();
|
|
|
|
const float NegativeHPad = FMath::Max<float>(-Settings->PaddingTowardsNodeEdge, 0.0f);
|
|
const float ExtraPad = 0.0f;
|
|
|
|
// Place preview widget based on where the least pins are
|
|
if ((LeftPinCount < RightPinCount) || (RightPinCount == 0))
|
|
{
|
|
LeftNodeBox->AddSlot()
|
|
.Padding(FMargin(NegativeHPad + ExtraPad, 0.0f, 0.0f, 0.0f))
|
|
.AutoHeight()
|
|
.HAlign(HAlign_Left)
|
|
[
|
|
CreatePreviewWidget()
|
|
];
|
|
}
|
|
else if (LeftPinCount > RightPinCount)
|
|
{
|
|
RightNodeBox->AddSlot()
|
|
.Padding(FMargin(NegativeHPad + ExtraPad, 0.0f, 0.0f, 0.0f))
|
|
.AutoHeight()
|
|
.HAlign(HAlign_Right)
|
|
[
|
|
CreatePreviewWidget()
|
|
];
|
|
}
|
|
else
|
|
{
|
|
MainBox->AddSlot()
|
|
.Padding(Settings->GetNonPinNodeBodyPadding())
|
|
.AutoHeight()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
CreatePreviewWidget()
|
|
]
|
|
];
|
|
}
|
|
}
|
|
|
|
// Preview of Substrate nodes topology
|
|
if (Substrate::IsSubstrateEnabled() && MaterialNode)
|
|
{
|
|
TArray<FGuid> Guids;
|
|
if (MaterialNode->MaterialExpression->IsA(UMaterialExpressionSubstrateBSDF::StaticClass()))
|
|
{
|
|
const UMaterialExpression* SubstrateExpression = (const UMaterialExpression*)MaterialNode->MaterialExpression;
|
|
Guids.Add(SubstrateExpression->MaterialExpressionGuid);
|
|
}
|
|
else if (MaterialNode->MaterialExpression->IsA(UMaterialExpressionMaterialFunctionCall::StaticClass()))
|
|
{
|
|
UMaterialExpressionMaterialFunctionCall* FunctionCall = (UMaterialExpressionMaterialFunctionCall*)MaterialNode->MaterialExpression;
|
|
const uint32 OutputCount = FunctionCall->FunctionOutputs.Num();
|
|
for (uint32 OutputIndex = 0; OutputIndex < OutputCount; ++OutputIndex)
|
|
{
|
|
if (FunctionCall->IsResultSubstrateMaterial(OutputIndex))
|
|
{
|
|
FSubstrateMaterialInfo SubstrateMaterialInfo(true/*bGatherGuids*/);
|
|
FunctionCall->GatherSubstrateMaterialInfo(SubstrateMaterialInfo, OutputIndex);
|
|
Guids = SubstrateMaterialInfo.GetGuids();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Guids.Num() > 0)
|
|
{
|
|
if (const UMaterialExpression* SubstrateExpression = (const UMaterialExpression*)MaterialNode->MaterialExpression)
|
|
{
|
|
if (UMaterial* MaterialForStats = SubstrateExpression->Material)
|
|
{
|
|
if (const FMaterialResource* MaterialResource = MaterialForStats->GetMaterialResource(GMaxRHIFeatureLevel))
|
|
{
|
|
if (FMaterialShaderMap* ShaderMap = MaterialResource->GetGameThreadShaderMap())
|
|
{
|
|
const FSubstrateMaterialCompilationOutput& CompilationOutput = ShaderMap->GetSubstrateMaterialCompilationOutput();
|
|
MainBox->AddSlot()
|
|
.Padding(Settings->GetNonPinNodeBodyPadding())
|
|
.AutoHeight()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.HAlign(HAlign_Center)
|
|
[
|
|
FSubstrateWidget::ProcessOperator(CompilationOutput, Guids)
|
|
]
|
|
];
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SGraphNodeMaterialBase::SetDefaultTitleAreaWidget(TSharedRef<SOverlay> DefaultTitleAreaWidget)
|
|
{
|
|
if (!MaterialNode->MaterialExpression->bHidePreviewWindow)
|
|
{
|
|
DefaultTitleAreaWidget->AddSlot()
|
|
.HAlign(HAlign_Right)
|
|
.VAlign(VAlign_Top)
|
|
.Padding(FMargin(5))
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SNew(SButton)
|
|
.ButtonStyle(FAppStyle::Get(), "HoverHintOnly")
|
|
.OnClicked(this, &SGraphNodeMaterialBase::OnTogglePreviewClicked)
|
|
.Cursor(EMouseCursor::Default)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.HAlign(HAlign_Center)
|
|
[
|
|
SNew(SImage)
|
|
.Image(FAppStyle::GetBrush(TEXT("Icons.Preview")))
|
|
]
|
|
]
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SNew(SCheckBox)
|
|
.OnCheckStateChanged( this, &SGraphNodeMaterialBase::OnExpressionPreviewChanged )
|
|
.IsChecked( IsExpressionPreviewChecked() )
|
|
.Cursor(EMouseCursor::Default)
|
|
.Style(FAppStyle::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( FAppStyle::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)
|
|
]
|
|
];
|
|
}
|
|
|
|
void SGraphNodeMaterialBase::OnAdvancedViewChanged(const ECheckBoxState NewCheckedState)
|
|
{
|
|
SGraphNode::OnAdvancedViewChanged(NewCheckedState);
|
|
|
|
// Update the graph node so that the preview is recreated to update its position
|
|
UpdateGraphNode();
|
|
}
|
|
|
|
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 )
|
|
.RenderDirectlyToWindow(true)
|
|
.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)
|
|
.MaxAspectRatio(1.0f)
|
|
.MaxDesiredHeight(ExpressionPreviewSize)
|
|
.Visibility(ExpressionPreviewVisibility())
|
|
[
|
|
SNew(SBorder)
|
|
.Padding(CentralPadding)
|
|
.BorderImage( FAppStyle::GetBrush("NoBorder") )
|
|
[
|
|
SNew(SOverlay)
|
|
+ SOverlay::Slot()
|
|
[
|
|
ViewportWidget.ToSharedRef()
|
|
]
|
|
+ SOverlay::Slot()
|
|
.HAlign(HAlign_Right)
|
|
.VAlign(VAlign_Bottom)
|
|
.Padding(0.0f,0.0f,8.0f,0.0f)
|
|
[
|
|
SNew(STextBlock)
|
|
.ShadowColorAndOpacity(FLinearColor::Black)
|
|
.ShadowOffset(FVector2D(1.0f, 1.0f))
|
|
.Text(this, &SGraphNodeMaterialBase::ExpressionPreviewOverlayText)
|
|
]
|
|
]
|
|
];
|
|
}
|
|
|
|
return SNullWidget::NullWidget;
|
|
}
|
|
|
|
FText SGraphNodeMaterialBase::ExpressionPreviewOverlayText() const
|
|
{
|
|
UMaterialExpression* MaterialExpression = MaterialNode->MaterialExpression;
|
|
return MaterialNode->MaterialExpression->GetPreviewOverlayText();
|
|
}
|
|
|
|
EVisibility SGraphNodeMaterialBase::ExpressionPreviewVisibility() const
|
|
{
|
|
UMaterialExpression* MaterialExpression = MaterialNode->MaterialExpression;
|
|
const bool bShowPreview = !MaterialExpression->bHidePreviewWindow && !MaterialExpression->bCollapsed;
|
|
return bShowPreview ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
|
|
FReply SGraphNodeMaterialBase::OnTogglePreviewClicked()
|
|
{
|
|
MaterialNode->ToggleNodePreview();
|
|
return FReply::Handled();
|
|
}
|
|
|
|
void SGraphNodeMaterialBase::OnExpressionPreviewChanged( const ECheckBoxState NewCheckedState )
|
|
{
|
|
UMaterialExpression* MaterialExpression = MaterialNode->MaterialExpression;
|
|
const bool bCollapsed = (NewCheckedState != ECheckBoxState::Checked);
|
|
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();
|
|
}
|
|
}
|
|
|
|
ECheckBoxState SGraphNodeMaterialBase::IsExpressionPreviewChecked() const
|
|
{
|
|
return MaterialNode->MaterialExpression->bCollapsed ? ECheckBoxState::Unchecked : ECheckBoxState::Checked;
|
|
}
|
|
|
|
const FSlateBrush* SGraphNodeMaterialBase::GetExpressionPreviewArrow() const
|
|
{
|
|
return FAppStyle::GetBrush(MaterialNode->MaterialExpression->bCollapsed ? TEXT("Icons.ChevronDown") : TEXT("Icons.ChevronUp"));
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|