Files
Andron666 9c38e93fa4 part7
2022-12-05 20:31:35 +05:00

553 lines
18 KiB
C++

/*
* Copyright (c) <2021> Side Effects Software Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. The name of Side Effects Software may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "HoudiniStaticMeshSceneProxy.h"
#include "Async/ParallelFor.h"
#include "Materials/Material.h"
#include "PrimitiveViewRelevance.h"
#include "Engine/Engine.h"
#include "ProfilingDebugging/CpuProfilerTrace.h"
#include "HoudiniStaticMeshComponent.h"
#include "HoudiniStaticMesh.h"
// Based on: Plugins\Experimental\MeshModelingToolset\Source\ModelingComponents\Private\BaseDynamicMeshSceneProxy.h
//
// FHoudiniStaticMeshRenderBufferSet
//
FHoudiniStaticMeshRenderBufferSet::FHoudiniStaticMeshRenderBufferSet(ERHIFeatureLevel::Type InFeatureLevel)
: LocalVertexFactory(InFeatureLevel, "FHoudiniStaticMeshRenderBufferSet")
{
}
FHoudiniStaticMeshRenderBufferSet::~FHoudiniStaticMeshRenderBufferSet()
{
check(IsInRenderingThread());
if (NumTriangles > 0)
{
PositionVertexBuffer.ReleaseResource();
ColorVertexBuffer.ReleaseResource();
StaticMeshVertexBuffer.ReleaseResource();
LocalVertexFactory.ReleaseResource();
if (TriangleIndexBuffer.IsInitialized())
{
TriangleIndexBuffer.ReleaseResource();
}
}
}
void FHoudiniStaticMeshRenderBufferSet::CopyBuffers()
{
check(IsInRenderingThread());
if (NumTriangles == 0)
{
return;
}
InitOrUpdateResource(&PositionVertexBuffer);
InitOrUpdateResource(&ColorVertexBuffer);
InitOrUpdateResource(&StaticMeshVertexBuffer);
FLocalVertexFactory::FDataType Data;
PositionVertexBuffer.BindPositionVertexBuffer(&LocalVertexFactory, Data);
StaticMeshVertexBuffer.BindTangentVertexBuffer(&LocalVertexFactory, Data);
StaticMeshVertexBuffer.BindPackedTexCoordVertexBuffer(&LocalVertexFactory, Data);
ColorVertexBuffer.BindColorVertexBuffer(&LocalVertexFactory, Data);
LocalVertexFactory.SetData(Data);
InitOrUpdateResource(&LocalVertexFactory);
if (TriangleIndexBuffer.Indices.Num() > 0)
{
TriangleIndexBuffer.InitResource();
}
}
void FHoudiniStaticMeshRenderBufferSet::InitOrUpdateResource(FRenderResource* Resource)
{
check(IsInRenderingThread());
if (Resource->IsInitialized())
Resource->UpdateRHI();
else
Resource->InitResource();
}
void FHoudiniStaticMeshRenderBufferSet::DestroyRenderBufferSet(FHoudiniStaticMeshRenderBufferSet* BufferSet)
{
if (BufferSet->NumTriangles == 0)
{
return;
}
ENQUEUE_RENDER_COMMAND(FMeshRenderBufferSetDestroy)(
[BufferSet](FRHICommandListImmediate& RHICmdList)
{
delete BufferSet;
});
}
//
// End - FHoudiniStaticMeshRenderBufferSet
//
//
// FHoudiniStaticMeshSceneProxy
//
FHoudiniStaticMeshSceneProxy::FHoudiniStaticMeshSceneProxy(UHoudiniStaticMeshComponent* InComponent, ERHIFeatureLevel::Type InFeatureLevel)
: FPrimitiveSceneProxy(InComponent)
, DefaultVertexColor(255, 255, 255)
, FeatureLevel(InFeatureLevel)
, Component(InComponent)
, MaterialRelevance(InComponent ? InComponent->GetMaterialRelevance(InFeatureLevel) : FMaterialRelevance())
#if STATICMESH_ENABLE_DEBUG_RENDERING
, Owner(InComponent ? InComponent->GetOwner() : nullptr)
#endif
{
}
FHoudiniStaticMeshSceneProxy::~FHoudiniStaticMeshSceneProxy()
{
check(IsInRenderingThread());
for (FHoudiniStaticMeshRenderBufferSet* BufferSet : BufferSets)
{
FHoudiniStaticMeshRenderBufferSet::DestroyRenderBufferSet(BufferSet);
}
}
uint32 FHoudiniStaticMeshSceneProxy::AllocateNewRenderBufferSet(FHoudiniStaticMeshRenderBufferSet*& OutBufferSet)
{
OutBufferSet = MakeNewBufferSet();
OutBufferSet->Material = UMaterial::GetDefaultMaterial(MD_Surface);
BufferSetsLock.Lock();
const uint32 NewIndex = BufferSets.Add(OutBufferSet);
BufferSetsLock.Unlock();
return NewIndex;
}
void FHoudiniStaticMeshSceneProxy::ReleaseRenderBufferSet(uint32 InIndex)
{
FHoudiniStaticMeshRenderBufferSet *BufferSet = nullptr;
BufferSetsLock.Lock();
check(BufferSets.IsValidIndex(InIndex));
BufferSet = BufferSets[InIndex];
BufferSets.RemoveAt(InIndex);
BufferSetsLock.Unlock();
FHoudiniStaticMeshRenderBufferSet::DestroyRenderBufferSet(BufferSet);
}
void FHoudiniStaticMeshSceneProxy::UpdatedReferencedMaterials()
{
// copied from FPrimitiveSceneProxy::FPrimitiveSceneProxy()
#if WITH_EDITOR
TArray<UMaterialInterface*> Materials;
Component->GetUsedMaterials(Materials, true);
ENQUEUE_RENDER_COMMAND(FHoudiniStaticMeshRenderBufferSetDestroy)(
[this, Materials](FRHICommandListImmediate& RHICmdList)
{
this->SetUsedMaterialForVerification(Materials);
});
#endif
}
void FHoudiniStaticMeshSceneProxy::Build()
{
TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("FHoudiniStaticMeshSceneProxy::Build"));
// Allocate a buffer set per material
const uint32 NumMaterials = GetNumMaterials();
if (NumMaterials == 0)
{
// No materials, allocate a singel buffer set using the default material
FHoudiniStaticMeshRenderBufferSet *BufferSet = nullptr;
AllocateNewRenderBufferSet(BufferSet);
BufferSet->Material = UMaterial::GetDefaultMaterial(MD_Surface);
}
else
{
for (uint32 MaterialIdx = 0; MaterialIdx < NumMaterials; ++MaterialIdx)
{
FHoudiniStaticMeshRenderBufferSet *BufferSet = nullptr;
AllocateNewRenderBufferSet(BufferSet);
BufferSet->Material = GetMaterial(MaterialIdx);
}
}
if (Component)
{
UHoudiniStaticMesh *Mesh = Component->GetMesh();
if (Mesh)
{
if (NumMaterials > 1 && Mesh->HasPerFaceMaterials())
{
BuildBufferSetsByMaterial();
}
else
{
BuildSingleBufferSet();
}
}
}
}
void FHoudiniStaticMeshSceneProxy::GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const
{
const FEngineShowFlags EngineShowFlags = ViewFamily.EngineShowFlags;
const bool bRenderAsWireframe = (AllowDebugViewmodes() && EngineShowFlags.Wireframe);
// Set up the wireframe material
FMaterialRenderProxy *WireframeMaterialProxy = nullptr;
if (bRenderAsWireframe)
{
FColoredMaterialRenderProxy *WireframeMaterialInstance = new FColoredMaterialRenderProxy(
GEngine->WireframeMaterial ? GEngine->WireframeMaterial->GetRenderProxy() : nullptr,
FLinearColor(0.6f, 0.6f, 0.6f)
);
Collector.RegisterOneFrameMaterialProxy(WireframeMaterialInstance);
WireframeMaterialProxy = WireframeMaterialInstance;
}
const ESceneDepthPriorityGroup DepthPriority = SDPG_World;
const int32 NumViews = Views.Num();
for (int32 ViewIdx = 0; ViewIdx < NumViews; ++ViewIdx)
{
if (!(VisibilityMap & (1 << ViewIdx)))
continue;
const FSceneView *View = Views[ViewIdx];
bool bHasPrecomputedVolumetricLightmap;
FMatrix PreviousLocalToWorld;
int32 SingleCaptureIndex;
bool bOutputVelocity;
GetScene().GetPrimitiveUniformShaderParameters_RenderThread(
GetPrimitiveSceneInfo(), bHasPrecomputedVolumetricLightmap, PreviousLocalToWorld, SingleCaptureIndex, bOutputVelocity);
const uint32 NumBufferSets = BufferSets.Num();
for (uint32 BufferSetIdx = 0; BufferSetIdx < NumBufferSets; ++BufferSetIdx)
{
FHoudiniStaticMeshRenderBufferSet *BufferSet = BufferSets[BufferSetIdx];
UMaterialInterface *Material = BufferSet->Material;
FMaterialRenderProxy *MaterialProxy = Material->GetRenderProxy();
if (BufferSet->NumTriangles == 0)
continue;
FDynamicPrimitiveUniformBuffer &DynamicPrimitiveUniformBuffer = Collector.AllocateOneFrameResource<FDynamicPrimitiveUniformBuffer>();
DynamicPrimitiveUniformBuffer.Set(
GetLocalToWorld(), PreviousLocalToWorld, GetBounds(), GetLocalBounds(), true, bHasPrecomputedVolumetricLightmap, DrawsVelocity(), bOutputVelocity);
if (BufferSet->TriangleIndexBuffer.Indices.Num() > 0)
{
FMeshBatch& Mesh = Collector.AllocateMesh();
if (PopulateMeshElement(Mesh, *BufferSet, MaterialProxy, false, DepthPriority, ViewIdx, DynamicPrimitiveUniformBuffer))
{
Collector.AddMesh(ViewIdx, Mesh);
}
if (bRenderAsWireframe)
{
FMeshBatch& WireframeMesh = Collector.AllocateMesh();
if (PopulateMeshElement(WireframeMesh, *BufferSet, WireframeMaterialProxy, true, DepthPriority, ViewIdx, DynamicPrimitiveUniformBuffer))
{
Collector.AddMesh(ViewIdx, WireframeMesh);
}
}
}
}
#if STATICMESH_ENABLE_DEBUG_RENDERING
if (EngineShowFlags.StaticMeshes)
{
RenderBounds(Collector.GetPDI(ViewIdx), EngineShowFlags, GetBounds(), !Owner || IsSelected());
}
#endif // STATICMESH_ENABLE_DEBUG_RENDERING
}
}
bool FHoudiniStaticMeshSceneProxy::PopulateMeshElement(
FMeshBatch &InMeshBatch,
const FHoudiniStaticMeshRenderBufferSet& Buffers,
FMaterialRenderProxy* Material,
bool bRenderAsWireframe,
ESceneDepthPriorityGroup DepthPriority,
int ViewIndex,
FDynamicPrimitiveUniformBuffer& DynamicPrimitiveUniformBuffer) const
{
FMeshBatchElement& BatchElement = InMeshBatch.Elements[0];
BatchElement.IndexBuffer = &Buffers.TriangleIndexBuffer;
InMeshBatch.bWireframe = bRenderAsWireframe;
InMeshBatch.VertexFactory = &Buffers.LocalVertexFactory;
InMeshBatch.MaterialRenderProxy = Material;
BatchElement.PrimitiveUniformBufferResource = &DynamicPrimitiveUniformBuffer.UniformBuffer;
BatchElement.FirstIndex = 0;
BatchElement.NumPrimitives = Buffers.NumTriangles;
BatchElement.MinVertexIndex = 0;
BatchElement.MaxVertexIndex = Buffers.PositionVertexBuffer.GetNumVertices() - 1;
InMeshBatch.ReverseCulling = IsLocalToWorldDeterminantNegative();
InMeshBatch.Type = PT_TriangleList;
InMeshBatch.DepthPriorityGroup = DepthPriority;
InMeshBatch.bCanApplyViewModeOverrides = false;
return true;
}
FPrimitiveViewRelevance FHoudiniStaticMeshSceneProxy::GetViewRelevance(const FSceneView* View) const
{
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = IsShown(View);
Result.bDynamicRelevance = true;
Result.bRenderCustomDepth = ShouldRenderCustomDepth();
Result.bRenderInMainPass = ShouldRenderInMainPass();
Result.bShadowRelevance = IsShadowCast(View);
Result.bTranslucentSelfShadow = bCastVolumetricTranslucentShadow;
Result.bUsesLightingChannels = GetLightingChannelMask() != GetDefaultLightingChannelMask();
MaterialRelevance.SetPrimitiveViewRelevance(Result);
Result.bVelocityRelevance = IsMovable() && Result.bOpaque && Result.bRenderInMainPass;
return Result;
}
bool FHoudiniStaticMeshSceneProxy::CanBeOccluded() const
{
return !MaterialRelevance.bDisableDepthTest;
}
void FHoudiniStaticMeshSceneProxy::PopulateBuffers(const UHoudiniStaticMesh *InMesh, FHoudiniStaticMeshRenderBufferSet *InBuffers, const TArray<uint32>* InTriangleIDs, uint32 InTriangleGroupStartIdx, uint32 InNumTrianglesInGroup)
{
TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("FHoudiniStaticMeshSceneProxy::PopulateBuffers"));
check(InMesh);
check(InBuffers);
const uint32 NumTriangles = InTriangleIDs ? InNumTrianglesInGroup : InMesh->GetNumTriangles();
InBuffers->NumTriangles = NumTriangles;
if (NumTriangles == 0)
return;
const uint32 NumVertices = NumTriangles * 3;
const uint32 NumUVLayers = InMesh->GetNumUVLayers();
InBuffers->PositionVertexBuffer.Init(NumVertices);
// There must be at least one UV layer
// TODO: Would it be possible to have no UV layers and bind to a dummy 0/black SRV?
InBuffers->StaticMeshVertexBuffer.Init(NumVertices, NumUVLayers > 0 ? NumUVLayers : 1);
InBuffers->ColorVertexBuffer.Init(NumVertices);
InBuffers->TriangleIndexBuffer.Indices.AddUninitialized(NumTriangles * 3);
const TArray<FVector>& VertexPositions = InMesh->GetVertexPositions();
const TArray<FIntVector>& TriangleIndices = InMesh->GetTriangleIndices();
const TArray<FColor>& VertexInstanceColors = InMesh->GetVertexInstanceColors();
const TArray<FVector>& VertexInstanceNormals = InMesh->GetVertexInstanceNormals();
const TArray<FVector>& VertexInstanceUTangents = InMesh->GetVertexInstanceUTangents();
const TArray<FVector>& VertexInstanceVTangents = InMesh->GetVertexInstanceVTangents();
const TArray<FVector2D>& VertexInstanceUVs = InMesh->GetVertexInstanceUVs();
const bool bHasColors = InMesh->HasColors();
const bool bHasNormals = InMesh->HasNormals();
const bool bHasTangents = InMesh->HasTangents();
FThreadSafeCounter VertCounter(0);
//for (uint32 TriangleIDIdx = 0; TriangleIDIdx < NumTriangles; ++TriangleIDIdx)
ParallelFor(NumTriangles, [&](uint32 TriangleIDIdx)
{
const uint32 TriangleID = InTriangleIDs ? (*InTriangleIDs)[InTriangleGroupStartIdx + TriangleIDIdx] : TriangleIDIdx;
const FIntVector &TriIndices = TriangleIndices[TriangleID];
FVector TangentU;
FVector TangentV;
uint32 VertIdx = VertCounter.Add(3);
for (uint8 TriVertIdx = 0; TriVertIdx < 3; ++TriVertIdx)
{
const uint32 MeshVtxIdx = TriIndices[TriVertIdx];
const uint32 MeshVtxInstanceIdx = TriangleID * 3 + TriVertIdx;
InBuffers->PositionVertexBuffer.VertexPosition(VertIdx) = VertexPositions[TriIndices[TriVertIdx]];
FVector Normal = bHasNormals ? VertexInstanceNormals[MeshVtxInstanceIdx] : FVector(0, 0, 1);
if (bHasTangents)
{
TangentU = VertexInstanceUTangents[MeshVtxInstanceIdx];
TangentV = VertexInstanceVTangents[MeshVtxInstanceIdx];
}
else
{
Normal.FindBestAxisVectors(TangentU, TangentV);
}
InBuffers->StaticMeshVertexBuffer.SetVertexTangents(VertIdx, TangentU, TangentV, Normal);
if (NumUVLayers > 0)
{
for (uint8 UVLayerIdx = 0; UVLayerIdx < NumUVLayers; ++UVLayerIdx)
{
InBuffers->StaticMeshVertexBuffer.SetVertexUV(VertIdx, UVLayerIdx, VertexInstanceUVs[MeshVtxInstanceIdx]);
}
}
else
{
InBuffers->StaticMeshVertexBuffer.SetVertexUV(VertIdx, 0, FVector2D::ZeroVector);
}
InBuffers->ColorVertexBuffer.VertexColor(VertIdx) = bHasColors ? VertexInstanceColors[MeshVtxInstanceIdx] : DefaultVertexColor;
InBuffers->TriangleIndexBuffer.Indices[VertIdx] = VertIdx;
VertIdx++;
}
});
}
void FHoudiniStaticMeshSceneProxy::BuildSingleBufferSet()
{
TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("FHoudiniStaticMeshSceneProxy::BuildSingleBufferSet"));
if (!Component)
return;
UHoudiniStaticMesh *Mesh = Component->GetMesh();
if (!Mesh)
return;
if (BufferSets.Num() == 0)
return;
FHoudiniStaticMeshRenderBufferSet *Buffers = BufferSets.Last();
PopulateBuffers(Mesh, Buffers);
ENQUEUE_RENDER_COMMAND(FHoudiniStaticMeshSceneProxy_BuildSingleBufferSet)(
[Buffers](FRHICommandListImmediate& RHICMdList)
{
Buffers->CopyBuffers();
});
}
void FHoudiniStaticMeshSceneProxy::BuildBufferSetsByMaterial()
{
TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("FHoudiniStaticMeshSceneProxy::BuildBufferSetsByMaterial"));
// We need to group tris by which material they use, and populate a buffer set for each group
if (!Component)
return;
UHoudiniStaticMesh *Mesh = Component->GetMesh();
if (!Mesh)
return;
if (BufferSets.Num() == 0)
return;
const TArray<int32>& MaterialIDsPerTriangle = Mesh->GetMaterialIDsPerTriangle();
const uint32 NumTriangles = MaterialIDsPerTriangle.Num();
const uint32 NumMaterials = GetNumMaterials();
TArray<FThreadSafeCounter> TriCountPerMaterialSafe;
TriCountPerMaterialSafe.Init(FThreadSafeCounter(0), NumMaterials);
ParallelFor(NumTriangles, [&](uint32 TriangleID)
{
const int32 MatID = MaterialIDsPerTriangle[TriangleID];
if (MatID >= 0 && (uint32) MatID < NumMaterials)
{
TriCountPerMaterialSafe[MatID].Increment();
}
});
TArray<uint32> TriCountPerMaterial;
TArray<uint32> OffsetPerMaterial;
TArray<FThreadSafeCounter> WrittenPerMaterial;
TriCountPerMaterial.Init(0, NumMaterials);
OffsetPerMaterial.Init(0, NumMaterials);
WrittenPerMaterial.Init(FThreadSafeCounter(0), NumMaterials);
for (int32 MatID = 0; (uint32) MatID < NumMaterials; ++MatID)
{
const uint32 Count = TriCountPerMaterialSafe[MatID].GetValue();
TriCountPerMaterial[MatID] = Count;
if (MatID > 0)
{
OffsetPerMaterial[MatID] = OffsetPerMaterial[MatID - 1] + TriCountPerMaterial[MatID - 1];
}
}
TArray<uint32> GroupTriangleIDs;
GroupTriangleIDs.Init(0, NumTriangles);
ParallelFor(NumTriangles, [&](uint32 TriangleID)
{
const int32 MatID = MaterialIDsPerTriangle[TriangleID];
if (MatID >= 0 && (uint32) MatID < NumMaterials)
{
GroupTriangleIDs[OffsetPerMaterial[MatID] + WrittenPerMaterial[MatID].Add(1)] = TriangleID;
}
});
for (int32 MatID = 0; (uint32) MatID < NumMaterials; ++MatID)
{
if (TriCountPerMaterial[MatID] == 0)
continue;
FHoudiniStaticMeshRenderBufferSet *Buffers = BufferSets[MatID];
PopulateBuffers(
Mesh, Buffers,
&GroupTriangleIDs, OffsetPerMaterial[MatID], TriCountPerMaterial[MatID]
);
ENQUEUE_RENDER_COMMAND(FHoudiniStaticMeshSceneProxy_BuildSingleBufferSet)(
[Buffers](FRHICommandListImmediate& RHICMdList)
{
Buffers->CopyBuffers();
});
}
}
UMaterialInterface* FHoudiniStaticMeshSceneProxy::GetMaterial(uint32 InMaterialIdx) const
{
if (!Component)
return UMaterial::GetDefaultMaterial(MD_Surface);
UMaterialInterface *Material = Component->GetMaterial(InMaterialIdx);
return Material ? Material : UMaterial::GetDefaultMaterial(MD_Surface);
}
//
// End - FHoudiniStaticMeshSceneProxy
//