Files
UnrealEngine/Engine/Plugins/FX/Niagara/Source/NiagaraEditor/Private/NiagaraBakerRendererOutputStaticMesh.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

238 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "NiagaraBakerRendererOutputStaticMesh.h"
#include "NiagaraBakerOutputStaticMesh.h"
#include "NiagaraBakerSettings.h"
#include "NiagaraComponent.h"
#include "NiagaraGpuComputeDispatchInterface.h"
#include "NiagaraRendererReadback.h"
#include "NiagaraSystemInstance.h"
#include "Engine/StaticMesh.h"
#include "Engine/TextureRenderTarget2D.h"
#include "MeshDescription.h"
#include "MeshAttributes.h"
#include "StaticMeshAttributes.h"
#include "StaticMeshOperations.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(NiagaraBakerRendererOutputStaticMesh)
UNiagaraBakerStaticMeshFactoryNew::UNiagaraBakerStaticMeshFactoryNew()
{
SupportedClass = UStaticMesh::StaticClass();
}
UObject* UNiagaraBakerStaticMeshFactoryNew::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
{
return NewObject<UStaticMesh>(InParent, InClass, InName, Flags);
}
TArray<FNiagaraBakerOutputBinding> FNiagaraBakerRendererOutputStaticMesh::GetRendererBindings(UNiagaraBakerOutput* InBakerOutput) const
{
return TArray<FNiagaraBakerOutputBinding>();
}
FIntPoint FNiagaraBakerRendererOutputStaticMesh::GetPreviewSize(UNiagaraBakerOutput* InBakerOutput, FIntPoint InAvailableSize) const
{
return InAvailableSize;
}
void FNiagaraBakerRendererOutputStaticMesh::RenderPreview(UNiagaraBakerOutput* InBakerOutput, const FNiagaraBakerRenderer& BakerRenderer, UTextureRenderTarget2D* RenderTarget, TOptional<FString>& OutErrorString) const
{
BakerRenderer.RenderSceneCapture(RenderTarget, ESceneCaptureSource::SCS_SceneColorHDR);
}
FIntPoint FNiagaraBakerRendererOutputStaticMesh::GetGeneratedSize(UNiagaraBakerOutput* InBakerOutput, FIntPoint InAvailableSize) const
{
return InAvailableSize;
}
void FNiagaraBakerRendererOutputStaticMesh::RenderGenerated(UNiagaraBakerOutput* InBakerOutput, const FNiagaraBakerRenderer& BakerRenderer, UTextureRenderTarget2D* RenderTarget, TOptional<FString>& OutErrorString) const
{
static FString StaticMeshNotFoundError(TEXT("StaticMesh asset not found.\nPlease bake to see the result."));
UNiagaraBakerOutputStaticMesh* BakerOutput = CastChecked<UNiagaraBakerOutputStaticMesh>(InBakerOutput);
UNiagaraBakerSettings* BakerGeneratedSettings = BakerOutput->GetTypedOuter<UNiagaraBakerSettings>();
const float WorldTime = BakerRenderer.GetWorldTime();
const FNiagaraBakerOutputFrameIndices FrameIndices = BakerGeneratedSettings->GetOutputFrameIndices(BakerOutput, WorldTime);
UStaticMesh* StaticMesh = BakerOutput->GetAsset<UStaticMesh>(BakerOutput->FramesAssetPathFormat, FrameIndices.FrameIndexA);
if (StaticMesh == nullptr)
{
OutErrorString = StaticMeshNotFoundError;
return;
}
BakerRenderer.RenderStaticMesh(RenderTarget, StaticMesh);
}
bool FNiagaraBakerRendererOutputStaticMesh::BeginBake(FNiagaraBakerFeedbackContext& FeedbackContext, UNiagaraBakerOutput* InBakerOutput)
{
#if WITH_NIAGARA_RENDERER_READBACK
BakeRenderTarget = NewObject<UTextureRenderTarget2D>();
BakeRenderTarget->AddToRoot();
BakeRenderTarget->ClearColor = FLinearColor::Transparent;
BakeRenderTarget->TargetGamma = 1.0f;
BakeRenderTarget->InitCustomFormat(128, 128, PF_FloatRGBA, false);
return true;
#else
FeedbackContext.Errors.Add(TEXT("Niagara Renderer Readback not enabled, failed to bake"));
return false;
#endif
}
void FNiagaraBakerRendererOutputStaticMesh::BakeFrame(FNiagaraBakerFeedbackContext& FeedbackContext, UNiagaraBakerOutput* InBakerOutput, int FrameIndex, const FNiagaraBakerRenderer& BakerRenderer)
{
UNiagaraBakerOutputStaticMesh* BakerOutput = CastChecked<UNiagaraBakerOutputStaticMesh>(InBakerOutput);
UNiagaraComponent* PreviewComponent = BakerRenderer.GetPreviewComponent();
if (!PreviewComponent || PreviewComponent->IsComplete())
{
return;
}
#if WITH_NIAGARA_RENDERER_READBACK
NiagaraRendererReadback::EnqueueReadback(
PreviewComponent,
[BakerOutput, FrameIndex](const FNiagaraRendererReadbackResult& ReadbackResult)
{
// Failed or no data
if (ReadbackResult.NumVertices == 0)
{
return;
}
// Create asset
const FString AssetFullName = BakerOutput->GetAssetPath(BakerOutput->FramesAssetPathFormat, FrameIndex);
UStaticMesh* StaticMesh = UNiagaraBakerOutput::GetOrCreateAsset<UStaticMesh, UNiagaraBakerStaticMeshFactoryNew>(AssetFullName);
if (StaticMesh == nullptr)
{
return;
}
ConvertReadbackResultsToStaticMesh(ReadbackResult, StaticMesh);
},
BakerOutput->ExportParameters
);
BakerRenderer.RenderSceneCapture(BakeRenderTarget, ESceneCaptureSource::SCS_SceneColorHDR);
#endif
}
void FNiagaraBakerRendererOutputStaticMesh::EndBake(FNiagaraBakerFeedbackContext& FeedbackContext, UNiagaraBakerOutput* InBakerOutput)
{
if (BakeRenderTarget)
{
BakeRenderTarget->RemoveFromRoot();
BakeRenderTarget->MarkAsGarbage();
BakeRenderTarget = nullptr;
}
}
bool FNiagaraBakerRendererOutputStaticMesh::ConvertReadbackResultsToStaticMesh(const FNiagaraRendererReadbackResult& ReadbackResult, UStaticMesh* StaticMesh)
{
#if WITH_NIAGARA_RENDERER_READBACK
// Failed or no data
if (ReadbackResult.NumVertices == 0 || StaticMesh == nullptr)
{
return false;
}
const uint32 NumTexCoords = ReadbackResult.VertexTexCoordNum;
const uint32 NumTriangles = ReadbackResult.NumVertices / 3;
// Create Mesh Description
FMeshDescription MeshDescription;
TVertexAttributesRef<FVector3f> VertexPositions = MeshDescription.GetVertexPositions();
TVertexInstanceAttributesRef<FVector3f> VertexInstanceNormals = MeshDescription.VertexInstanceAttributes().RegisterAttribute<FVector3f>(MeshAttribute::VertexInstance::Normal, 1, FVector3f::ZeroVector, EMeshAttributeFlags::Mandatory);
TVertexInstanceAttributesRef<FVector3f> VertexInstanceTangents = MeshDescription.VertexInstanceAttributes().RegisterAttribute<FVector3f>(MeshAttribute::VertexInstance::Tangent, 1, FVector3f::ZeroVector, EMeshAttributeFlags::Mandatory);
TVertexInstanceAttributesRef<float> VertexInstanceBinormalSigns = MeshDescription.VertexInstanceAttributes().RegisterAttribute<float>(MeshAttribute::VertexInstance::BinormalSign, 1, 1.0f, EMeshAttributeFlags::Mandatory);
TVertexInstanceAttributesRef<FVector4f> VertexInstanceColors = MeshDescription.VertexInstanceAttributes().RegisterAttribute<FVector4f>(MeshAttribute::VertexInstance::Color, 1, FVector4f(1.0f, 1.0f, 1.0f, 1.0f), EMeshAttributeFlags::Lerpable | EMeshAttributeFlags::Mandatory);
TVertexInstanceAttributesRef<FVector2f> VertexInstanceUVs = MeshDescription.VertexInstanceAttributes().RegisterAttribute<FVector2f>(MeshAttribute::VertexInstance::TextureCoordinate, NumTexCoords, FVector2f::ZeroVector, EMeshAttributeFlags::Lerpable | EMeshAttributeFlags::Mandatory);
MeshDescription.EdgeAttributes().RegisterAttribute<bool>(MeshAttribute::Edge::IsHard, 1, false, EMeshAttributeFlags::Mandatory);
TPolygonGroupAttributesRef<FName> PolygonGroupSlotNames = MeshDescription.PolygonGroupAttributes().RegisterAttribute<FName>(MeshAttribute::PolygonGroup::ImportedMaterialSlotName, 1, NAME_None, EMeshAttributeFlags::Mandatory); //The unique key to match the mesh material slot
// Reserve space
MeshDescription.ReserveNewVertices(ReadbackResult.NumVertices);
MeshDescription.ReserveNewVertexInstances(NumTriangles);
MeshDescription.ReserveNewEdges(NumTriangles);
MeshDescription.ReserveNewPolygons(NumTriangles);
MeshDescription.ReserveNewPolygonGroups(ReadbackResult.Sections.Num());
// Build vertices
TArray<FVertexInstanceID> VertexInstances;
VertexInstances.Reserve(ReadbackResult.NumVertices);
for (uint32 iVertex=0; iVertex < ReadbackResult.NumVertices; ++iVertex)
{
const FVertexID VertexID = MeshDescription.CreateVertex();
check(VertexID.GetValue() == iVertex);
VertexPositions[VertexID] = ReadbackResult.HasPosition() ? ReadbackResult.GetPosition(iVertex) : FVector3f::ZeroVector;
FVertexInstanceID VertexInstanceID = MeshDescription.CreateVertexInstance(VertexID);
VertexInstances.Add(VertexInstanceID);
const FVector3f TangentX = ReadbackResult.HasTangentBasis() ? ReadbackResult.GetTangentX(iVertex) : FVector3f::XAxisVector;
const FVector3f TangentY = ReadbackResult.HasTangentBasis() ? ReadbackResult.GetTangentY(iVertex) : FVector3f::YAxisVector;
const FVector3f TangentZ = ReadbackResult.HasTangentBasis() ? ReadbackResult.GetTangentZ(iVertex) : FVector3f::ZAxisVector;
const float TangentSign = FVector3f::DotProduct(FVector3f::CrossProduct(TangentX, TangentZ), TangentY) < 0.0f ? -1.0f : 1.0f;
VertexInstanceNormals.Set(VertexInstanceID, TangentX);
VertexInstanceTangents.Set(VertexInstanceID, TangentZ);
VertexInstanceBinormalSigns.Set(VertexInstanceID, TangentSign);
VertexInstanceColors.Set(VertexInstanceID, ReadbackResult.HasColor() ? ReadbackResult.GetColor(iVertex) : FLinearColor::White);
for (uint32 iTexCoord=0; iTexCoord < NumTexCoords; ++iTexCoord)
{
VertexInstanceUVs.Set(VertexInstanceID, iTexCoord, ReadbackResult.GetTexCoord(iVertex, iTexCoord));
}
}
// Build sections / triangles
TArray<FStaticMaterial> StaticMaterials;
for (int32 iSection=0; iSection < ReadbackResult.Sections.Num(); ++iSection)
{
const FNiagaraRendererReadbackResult::FSection& Section = ReadbackResult.Sections[iSection];
const FName MaterialSlotName(*FString::Printf(TEXT("Section%d"), iSection));
const FPolygonGroupID PolyGroupId = MeshDescription.CreatePolygonGroup();
PolygonGroupSlotNames[PolyGroupId] = MaterialSlotName;
FStaticMaterial& StaticMaterial = StaticMaterials.AddDefaulted_GetRef();
StaticMaterial.MaterialInterface = Section.WeakMaterialInterface.Get();
StaticMaterial.MaterialSlotName = MaterialSlotName;
StaticMaterial.ImportedMaterialSlotName = MaterialSlotName;
for (uint32 iTriangle=0; iTriangle < Section.NumTriangles; ++iTriangle)
{
const uint32 BaseIndex = (Section.FirstTriangle + iTriangle) * 3;
const FPolygonID PolygonID = MeshDescription.CreatePolygon(PolyGroupId, { VertexInstances[BaseIndex + 0], VertexInstances[BaseIndex + 1], VertexInstances[BaseIndex + 2] });
MeshDescription.ComputePolygonTriangulation(PolygonID);
}
}
StaticMesh->SetNumSourceModels(1);
{
FMeshBuildSettings& MeshBuildSettings = StaticMesh->GetSourceModel(0).BuildSettings;
MeshBuildSettings.bRecomputeNormals = false;
MeshBuildSettings.bRecomputeTangents = false;
}
StaticMesh->SetStaticMaterials(StaticMaterials);
// Build Mesh
UStaticMesh::FBuildMeshDescriptionsParams Params;
Params.bFastBuild = WITH_EDITOR ? false : true;
Params.bUseHashAsGuid = true;
Params.bMarkPackageDirty = false;
Params.bCommitMeshDescription = true;
Params.bAllowCpuAccess = false;
StaticMesh->BuildFromMeshDescriptions({ &MeshDescription }, Params);
return true;
#endif
}