Files
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

179 lines
7.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "BuildGuidesLODsNode.h"
#include "Dataflow/DataflowObjectInterface.h"
#include "GeometryCollection/Facades/CollectionCurveFacade.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(BuildGuidesLODsNode)
namespace UE::Groom::Private
{
FORCEINLINE float ComputeGuidesMetric(const TArray<FVector3f>& PointRestPositions, const TArray<int32>& CurvePointOffsets,const uint32 GuideIndexA, const uint32 GuideIndexB, const float& GuideLengthA, const float& GuideLengthB,
const float ShapeWeight, const float ProximityWeight)
{
const uint32 PointOffsetA = (GuideIndexA > 0) ? CurvePointOffsets[GuideIndexA-1] : 0;
const uint32 PointOffsetB = (GuideIndexB > 0) ? CurvePointOffsets[GuideIndexB-1] : 0;
const uint32 NumPointsA = CurvePointOffsets[GuideIndexA] - PointOffsetA;
const uint32 NumPointsB = CurvePointOffsets[GuideIndexB] - PointOffsetB;
if(NumPointsA == NumPointsB)
{
float ProximityMetric = 0.0f;
float ShapeMetric = 0.0f;
for(uint32 PointIndex = 0; PointIndex < NumPointsA; ++PointIndex)
{
const FVector3f& GuidePositionA = PointRestPositions[PointIndex+PointOffsetA];
const FVector3f& GuidePositionB = PointRestPositions[PointIndex+PointOffsetB];
ProximityMetric += (GuidePositionB - GuidePositionA).Size();
ShapeMetric += (GuidePositionB - PointRestPositions[PointOffsetB] - GuidePositionA + PointRestPositions[PointOffsetA]).Size();
}
const float MetricScale = 1.0f / (NumPointsA * 0.5f * (GuideLengthA+GuideLengthB));
return FMath::Exp(-ShapeWeight * ShapeMetric * MetricScale) * FMath::Exp(-ProximityWeight * ProximityMetric * MetricScale);
}
return 0.0f;
}
FORCEINLINE void BuildInternalLODs(const int32 NumObjects, const int32 NumCurves, const TArray<int32>& GeometryCurveOffsets,
const TArray<int32>& CurvePointOffsets, const TArray<FVector3f>& PointRestPositions, TArray<int32>& CurveParentIndices, TArray<int32>& CurveLodIndices,
const FDataflowCurveSelection& CurveSelection, const bool bValidSelection)
{
CurveParentIndices.Init(INDEX_NONE, NumCurves);
CurveLodIndices.Init(INDEX_NONE, NumCurves);
uint32 CurveOffset = 0, PointOffset = 0;
for(int32 ObjectIndex = 0; ObjectIndex < NumObjects; ++ObjectIndex)
{
TArray<float> GuidesLengths; GuidesLengths.Init(0.0f, GeometryCurveOffsets[ObjectIndex] - CurveOffset);
for(int32 GuideIndex = CurveOffset; GuideIndex < GeometryCurveOffsets[ObjectIndex]; ++GuideIndex)
{
for(int32 PointIndex = PointOffset+1; PointIndex < CurvePointOffsets[GuideIndex]; ++PointIndex)
{
GuidesLengths[GuideIndex-CurveOffset] += (PointRestPositions[PointIndex]-PointRestPositions[PointIndex-1]).Size();
}
PointOffset = CurvePointOffsets[GuideIndex];
}
const int32 NumObjectLODs = FMath::CeilLogTwo(GuidesLengths.Num());
ParallelFor(GuidesLengths.Num(), [CurveOffset, &GuidesLengths, &PointRestPositions, &CurvePointOffsets, &CurveParentIndices, &CurveLodIndices, &NumObjectLODs, bValidSelection, &CurveSelection](int32 CurveIndex)
{
const uint32 GuideIndex = CurveIndex + CurveOffset;
if(!bValidSelection || (bValidSelection && (CurveSelection.IsSelected(GuideIndex))))
{
const uint32 GuideLod = NumObjectLODs - 1 - FMath::FloorLog2(CurveIndex);
const uint32 LodOffset = (GuideLod == NumObjectLODs - 1) ? 0 : 1 << FMath::FloorLog2(CurveIndex);
float MinMetric = FLT_MAX;
int32 MinParent = INDEX_NONE;
for(uint32 ParentIndex = CurveOffset; ParentIndex < CurveOffset+LodOffset; ++ParentIndex)
{
const float ParentMetric = 1.0f - ComputeGuidesMetric(PointRestPositions, CurvePointOffsets,
GuideIndex, ParentIndex, GuidesLengths[CurveIndex], GuidesLengths[ParentIndex-CurveOffset], 1.0f, 1.0f);
if (ParentMetric < MinMetric)
{
MinMetric = ParentMetric;
MinParent = ParentIndex;
}
}
CurveParentIndices[GuideIndex] = (MinParent != INDEX_NONE) ? GuideIndex-MinParent : INDEX_NONE;
CurveLodIndices[GuideIndex] = GuideLod;
}
//UE_LOG(LogTemp, Log, TEXT("Guide Index = %d, Num Guides = %d, Num LODs = %d, Guide LOD = %d, LOD Offset = %d, Parent Index = %d"),
// CurveIndex, GuidesLengths.Num(), NumObjectLODs, GuideLod, LodOffset, MinParent);
}, EParallelForFlags::None);
CurveOffset = GeometryCurveOffsets[ObjectIndex];
}
}
FORCEINLINE void BuildCurvesLODs(GeometryCollection::Facades::FCollectionCurveGeometryFacade& CurvesFacade, GeometryCollection::Facades::FCollectionCurveHierarchyFacade& HierarchyFacade, const FDataflowCurveSelection& CurveSelection)
{
const uint32 NumObjects = CurvesFacade.GetNumGeometry();
TArray<int32> CurveParentIndices; CurveParentIndices.Init(INDEX_NONE, CurvesFacade.GetNumCurves());
TArray<int32> CurveLodIndices; CurveLodIndices.Init(INDEX_NONE, CurvesFacade.GetNumCurves());
const bool bValidSelection = CurveSelection.IsValidForCollection(CurvesFacade.GetManagedArrayCollection());
BuildInternalLODs(CurvesFacade.GetNumGeometry(), CurvesFacade.GetNumCurves(), CurvesFacade.GetGeometryCurveOffsets(),
CurvesFacade.GetCurvePointOffsets(), CurvesFacade.GetPointRestPositions(), CurveParentIndices, CurveLodIndices, CurveSelection, bValidSelection);
HierarchyFacade.SetCurveParentIndices(CurveParentIndices);
HierarchyFacade.SetCurveLodIndices(CurveLodIndices);
}
FCollectionAttributeKey GetCurveParentsKey()
{
FCollectionAttributeKey Key;
Key.Group = GeometryCollection::Facades::FCollectionCurveGeometryFacade::CurvesGroup.ToString();
Key.Attribute = GeometryCollection::Facades::FCollectionCurveHierarchyFacade::CurveParentIndicesAttribute.ToString();
return Key;
}
FCollectionAttributeKey GetCurveLodsKey()
{
FCollectionAttributeKey Key;
Key.Group = GeometryCollection::Facades::FCollectionCurveGeometryFacade::CurvesGroup.ToString();
Key.Attribute = GeometryCollection::Facades::FCollectionCurveHierarchyFacade::CurveLodIndicesAttribute.ToString();
return Key;
}
}
void FBuildGuidesLODsDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if (Out->IsA(&Collection))
{
SafeForwardInput(Context, &Collection, &Collection);
}
else if (Out->IsA<FCollectionAttributeKey>(&CurveParentsKey))
{
SetValue(Context, UE::Groom::Private::GetCurveParentsKey(), &CurveParentsKey);
}
else if (Out->IsA<FCollectionAttributeKey>(&CurveLodsKey))
{
SetValue(Context, UE::Groom::Private::GetCurveLodsKey(), &CurveLodsKey);
}
}
FBuildCurveLODsDataflowNode::FBuildCurveLODsDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid)
: FDataflowNode(InParam, InGuid)
{
RegisterInputConnection(&Collection);
RegisterInputConnection(&CurveSelection);
RegisterOutputConnection(&Collection, &Collection);
RegisterOutputConnection(&CurveParentsKey);
RegisterOutputConnection(&CurveLodsKey);
}
void FBuildCurveLODsDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if (Out->IsA<FManagedArrayCollection>(&Collection))
{
FManagedArrayCollection GeometryCollection = GetValue<FManagedArrayCollection>(Context, &Collection);
GeometryCollection::Facades::FCollectionCurveGeometryFacade CurvesFacade(GeometryCollection);
if(CurvesFacade.IsValid())
{
GeometryCollection::Facades::FCollectionCurveHierarchyFacade HierarchyFacade(GeometryCollection);
HierarchyFacade.DefineSchema();
UE::Groom::Private::BuildCurvesLODs(CurvesFacade, HierarchyFacade, GetValue(Context, &CurveSelection));
}
SetValue(Context, MoveTemp(GeometryCollection), &Collection);
}
else if (Out->IsA<FCollectionAttributeKey>(&CurveParentsKey))
{
SetValue(Context, UE::Groom::Private::GetCurveParentsKey(), &CurveParentsKey);
}
else if (Out->IsA<FCollectionAttributeKey>(&CurveLodsKey))
{
SetValue(Context, UE::Groom::Private::GetCurveLodsKey(), &CurveLodsKey);
}
}