Files
UnrealEngine/Engine/Source/Runtime/UMG/Private/Slate/SMeshWidget.cpp
2025-05-18 13:04:45 +08:00

266 lines
8.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Slate/SMeshWidget.h"
#include "Rendering/DrawElements.h"
#include "Modules/ModuleManager.h"
#include "Materials/MaterialInterface.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Framework/Application/SlateApplication.h"
#include "RHI.h"
#include "UMGPrivate.h"
#include "SlateMaterialBrush.h"
#include "Interfaces/ISlateRHIRendererModule.h"
#include "Slate/SlateVectorArtData.h"
#include "Slate/SlateVectorArtInstanceData.h"
/** Populate OutSlateVerts and OutIndexes with data from this static mesh such that Slate can render it. */
static void SlateMeshToSlateRenderData(const USlateVectorArtData& DataSource, TArray<FSlateVertex>& OutSlateVerts, TArray<SlateIndex>& OutIndexes)
{
// Populate Index data
{
// Note that we do a slow copy because on some platforms the SlateIndex is
// a 16-bit value, so we cannot do a memcopy.
const TArray<uint32>& IndexDataSource = DataSource.GetIndexData();
const int32 NumIndexes = IndexDataSource.Num();
OutIndexes.Empty();
OutIndexes.Reserve(NumIndexes);
for (int32 i = 0; i < NumIndexes; ++i)
{
OutIndexes.Add(IndexDataSource[i]);
}
}
// Populate Vertex Data
{
const TArray<FSlateMeshVertex> VertexDataSource = DataSource.GetVertexData();
const uint32 NumVerts = VertexDataSource.Num();
OutSlateVerts.Empty();
OutSlateVerts.Reserve(NumVerts);
for (uint32 i = 0; i < NumVerts; ++i)
{
const FSlateMeshVertex& SourceVertex = VertexDataSource[i];
FSlateVertex& NewVert = OutSlateVerts[OutSlateVerts.AddUninitialized()];
// Copy Position
{
NewVert.Position[0] = SourceVertex.Position.X;
NewVert.Position[1] = SourceVertex.Position.Y;
}
// Copy Color
{
NewVert.Color = SourceVertex.Color;
}
// Copy all the UVs that we have, and as many as we can fit.
{
NewVert.TexCoords[0] = SourceVertex.UV0.X;
NewVert.TexCoords[1] = SourceVertex.UV0.Y;
NewVert.TexCoords[2] = SourceVertex.UV1.X;
NewVert.TexCoords[3] = SourceVertex.UV1.Y;
NewVert.MaterialTexCoords[0] = SourceVertex.UV2.X;
NewVert.MaterialTexCoords[1] = SourceVertex.UV2.Y;
}
}
}
}
void SMeshWidget::Construct(const FArguments& Args)
{
if (Args._MeshData != nullptr)
{
AddMesh(*Args._MeshData);
}
}
static const FVector2D DontCare(FVector2D(64, 64));
uint32 SMeshWidget::AddMesh(USlateVectorArtData& InMeshData)
{
InMeshData.EnsureValidData();
FRenderData& NewRenderData = RenderData[RenderData.Add(FRenderData())];
UMaterialInterface* MaterialFromMesh = InMeshData.GetMaterial();
if (MaterialFromMesh != nullptr)
{
NewRenderData.Brush = MakeShareable(new FSlateMaterialBrush(*MaterialFromMesh, DontCare));
NewRenderData.RenderingResourceHandle = FSlateApplication::Get().GetRenderer()->GetResourceHandle( *NewRenderData.Brush );
}
SlateMeshToSlateRenderData(InMeshData, NewRenderData.VertexData, NewRenderData.IndexData);
return RenderData.Num()-1;
}
uint32 SMeshWidget::AddMeshWithInstancing(USlateVectorArtData& InMeshData, int32 InitialBufferSize)
{
const uint32 NewMeshId = AddMesh(InMeshData);
EnableInstancing(NewMeshId, InitialBufferSize);
return NewMeshId;
}
UMaterialInstanceDynamic* SMeshWidget::ConvertToMID(uint32 MeshId)
{
FRenderData& MeshRenderData = RenderData[MeshId];
UObject* ResourceObject = MeshRenderData.Brush->GetResourceObject();
UMaterialInterface* Material = Cast<UMaterialInterface>(ResourceObject);
UMaterialInstanceDynamic* ExistingMID = Cast<UMaterialInstanceDynamic>(Material);
if (ExistingMID == nullptr)
{
UMaterialInstanceDynamic* NewMID = UMaterialInstanceDynamic::Create(Material, nullptr);
MeshRenderData.Brush->SetResourceObject(NewMID);
MeshRenderData.RenderingResourceHandle = FSlateApplication::Get().GetRenderer()->GetResourceHandle(*MeshRenderData.Brush);
return NewMID;
}
else
{
return ExistingMID;
}
}
void SMeshWidget::ClearRuns(int32 NumRuns)
{
RenderRuns.Reset(NumRuns);
}
static const FName SlateRHIModuleName("SlateRHIRenderer");
void SMeshWidget::EnableInstancing(uint32 MeshId, int32 InitialSize)
{
if (!RenderData[MeshId].PerInstanceBuffer.IsValid())
{
RenderData[MeshId].PerInstanceBuffer = FModuleManager::Get().GetModuleChecked<ISlateRHIRendererModule>(SlateRHIModuleName).CreateInstanceBuffer(InitialSize);
}
}
void SMeshWidget::UpdatePerInstanceBuffer(uint32 MeshId, FSlateInstanceBufferData& Data)
{
EnableInstancing(MeshId, Data.Num() * Data.GetTypeSize());
RenderData[MeshId].PerInstanceBuffer->Update(Data);
}
int32 SMeshWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
if (RenderRuns.Num() > 0)
{
// We have multiple render runs. Assume that we have per-instance data.
for (int RunIndex = 0; RunIndex < RenderRuns.Num(); ++RunIndex)
{
const FRenderRun& Run = RenderRuns[RunIndex];
const FRenderData& RunRenderData = RenderData[Run.GetMeshIndex()];
if (RunRenderData.RenderingResourceHandle.IsValid() && RunRenderData.VertexData.Num() > 0 && RunRenderData.IndexData.Num() > 0 && RunRenderData.PerInstanceBuffer.IsValid())
{
ensure(Run.GetInstanceOffset() + Run.GetNumInstances() <= RunRenderData.PerInstanceBuffer->GetNumInstances());
FSlateDrawElement::MakeCustomVerts(OutDrawElements,
LayerId,
RunRenderData.RenderingResourceHandle,
RunRenderData.VertexData,
RunRenderData.IndexData,
RunRenderData.PerInstanceBuffer.Get(),
Run.GetInstanceOffset(),
Run.GetNumInstances());
}
else
{
if( !GUsingNullRHI )
{
UE_LOG(LogUMG, Warning, TEXT("SMeshWidget did not render a run because of one of these Brush: %s, InstanceBuffer: %s, NumVertexes: %d, NumIndexes: %d"),
RunRenderData.RenderingResourceHandle.IsValid() ? TEXT("valid") : TEXT("nullptr"),
RunRenderData.PerInstanceBuffer.IsValid() ? TEXT("valid") : TEXT("nullptr"),
RunRenderData.VertexData.Num(),
RunRenderData.IndexData.Num());
}
}
}
}
else
{
// We have no render runs. Render all the meshes in order they were added
for (int i = 0; i < RenderData.Num(); ++i)
{
const FRenderData& RunRenderData = RenderData[i];
if (RunRenderData.RenderingResourceHandle.IsValid() && RunRenderData.VertexData.Num() > 0 && RunRenderData.IndexData.Num() > 0)
{
if (RunRenderData.PerInstanceBuffer.IsValid())
{
// Drawing instanced widgets
const int32 NumInstances = RunRenderData.PerInstanceBuffer->GetNumInstances();
if (NumInstances > 0)
{
FSlateDrawElement::MakeCustomVerts(OutDrawElements, LayerId, RunRenderData.RenderingResourceHandle, RunRenderData.VertexData, RunRenderData.IndexData, RunRenderData.PerInstanceBuffer.Get(), 0, NumInstances);
}
}
else
{
// Drawing a single widget, no instancing
FSlateDrawElement::MakeCustomVerts(OutDrawElements, LayerId, RunRenderData.RenderingResourceHandle, RunRenderData.VertexData, RunRenderData.IndexData, nullptr, 0, 0);
}
}
else
{
if( !GUsingNullRHI )
{
UE_LOG(LogUMG, Warning, TEXT("SMeshWidget did not render a run because of one of these Brush: %s, NumVertexes: %d, NumIndexes: %d"),
RunRenderData.RenderingResourceHandle.IsValid() ? TEXT("valid") : TEXT("nullptr"),
RunRenderData.VertexData.Num(),
RunRenderData.IndexData.Num());
}
}
}
}
return LayerId;
}
FVector2D SMeshWidget::ComputeDesiredSize(float) const
{
return FVector2D(256,256);
}
void SMeshWidget::AddReferencedObjects(FReferenceCollector& Collector)
{
for (const FRenderData& SomeRenderData : RenderData)
{
if (SomeRenderData.Brush.IsValid())
{
SomeRenderData.Brush->AddReferencedObjects(Collector);
}
}
}
FString SMeshWidget::GetReferencerName() const
{
return TEXT("SMeshWidget");
}
void SMeshWidget::PushUpdate(uint32 VectorArtId, SMeshWidget& Widget, const FVector2D& Position, float Scale, uint32 BaseAddress)
{
PushUpdate(VectorArtId, Widget, Position, Scale, static_cast<float>(BaseAddress));
}
void SMeshWidget::PushUpdate(uint32 VectorArtId, SMeshWidget& Widget, const FVector2D& Position, float Scale, float OptionalFloat /*= 0*/)
{
FSlateVectorArtInstanceData Data;
Data.SetPosition(Position);
Data.SetScale(Scale);
Data.SetBaseAddress(OptionalFloat);
{
FSlateInstanceBufferData PerInstanceData;
PerInstanceData.Add((FVector4f)Data.GetData()); // LWC_TODO: precision loss
Widget.UpdatePerInstanceBuffer(VectorArtId, PerInstanceData);
}
}