// 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& PointRestPositions, const TArray& 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& GeometryCurveOffsets, const TArray& CurvePointOffsets, const TArray& PointRestPositions, TArray& CurveParentIndices, TArray& 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 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 CurveParentIndices; CurveParentIndices.Init(INDEX_NONE, CurvesFacade.GetNumCurves()); TArray 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(&CurveParentsKey)) { SetValue(Context, UE::Groom::Private::GetCurveParentsKey(), &CurveParentsKey); } else if (Out->IsA(&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(&Collection)) { FManagedArrayCollection GeometryCollection = GetValue(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(&CurveParentsKey)) { SetValue(Context, UE::Groom::Private::GetCurveParentsKey(), &CurveParentsKey); } else if (Out->IsA(&CurveLodsKey)) { SetValue(Context, UE::Groom::Private::GetCurveLodsKey(), &CurveLodsKey); } }