// Copyright Epic Games, Inc. All Rights Reserved. #include "ResampleGuidesPointsNode.h" #include "Dataflow/DataflowObjectInterface.h" #include "GeometryCollection/Facades/CollectionCurveFacade.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(ResampleGuidesPointsNode) namespace UE::Groom::Private { FORCEINLINE int32 ComputeCurvesPoints(const GeometryCollection::Facades::FCollectionCurveGeometryFacade& CurvesFacade, const FDataflowCurveSelection& CurveSelection, const int32 GuidePoints) { const bool bValidSelection = CurveSelection.IsValidForCollection(CurvesFacade.GetManagedArrayCollection()); int32 PointOffset = 0, NumPoints = 0; for(int32 CurveIndex = 0, NumCurves = CurvesFacade.GetNumCurves(); CurveIndex < NumCurves; ++CurveIndex) { if(!bValidSelection || (bValidSelection && (CurveSelection.IsSelected(CurveIndex)))) { NumPoints += GuidePoints; } else { NumPoints += CurvesFacade.GetCurvePointOffsets()[CurveIndex] - PointOffset; } PointOffset = CurvesFacade.GetCurvePointOffsets()[CurveIndex]; } return NumPoints; } FORCEINLINE void BuildResampledPoints(const TArray& PointRestPositions, const TArray& CurveObjectIndices, const TArray& CurvePointOffsets, const TArray& ObjectPointSamples, const int32 CurveIndex, const int32 PointOffset, const int32 SampleOffset, TArray& SamplePositions, const int32 GuidePoints) { const int32 ObjectIndex = CurveObjectIndices[CurveIndex]; const int32 PointsCount = (CurvePointOffsets[CurveIndex]-PointOffset)-1; const int32 SamplesCount = (GuidePoints != 0) ? GuidePoints-1 : !ObjectPointSamples.IsEmpty() ? ObjectPointSamples[ObjectIndex]-1 : 0; float CurveLength = 0.0; TArray EdgeLengths; EdgeLengths.Init(0.0f, PointsCount); for(int32 EdgeIndex = 0; EdgeIndex < PointsCount; ++EdgeIndex) { EdgeLengths[EdgeIndex] = (PointRestPositions[EdgeIndex+PointOffset+1] - PointRestPositions[EdgeIndex+PointOffset]).Length(); CurveLength += EdgeLengths[EdgeIndex]; } SamplePositions[SampleOffset + 1] = PointRestPositions[PointOffset]; for(int32 SampleIndex = 1; SampleIndex < SamplesCount-1; ++SampleIndex) { const float SampleCoord = static_cast(SampleIndex) / (SamplesCount - 1.0f); const float SampleLength = CurveLength * SampleCoord; float LocalLength = 0.0; for(int32 EdgeIndex = 0; EdgeIndex < PointsCount; ++EdgeIndex) { LocalLength += EdgeLengths[EdgeIndex]; if(LocalLength >= SampleLength) { const int32 PrevPoint = EdgeIndex+PointOffset; const int32 NextPoint = PrevPoint+1; const float SampleAlpha = (LocalLength - SampleLength) / EdgeLengths[EdgeIndex]; SamplePositions[SampleOffset + SampleIndex + 1] = PointRestPositions[PrevPoint] * SampleAlpha + PointRestPositions[NextPoint] * (1.0-SampleAlpha); break; } } } SamplePositions[SampleOffset+SamplesCount] = PointRestPositions[PointOffset+PointsCount]; SamplePositions[SampleOffset] = 2 * SamplePositions[SampleOffset+1] - SamplePositions[SampleOffset+2]; } FORCEINLINE void ResampleCurvesPoints(GeometryCollection::Facades::FCollectionCurveGeometryFacade& CurvesFacade, const FDataflowCurveSelection& CurveSelection, const int32 GuidePoints) { if(CurvesFacade.IsValid()) { const int32 NumSamples = ComputeCurvesPoints(CurvesFacade, CurveSelection, GuidePoints); const int32 NumCurves = CurvesFacade.GetNumCurves(); TArray PointSampledPositions; PointSampledPositions.Init(FVector3f::Zero(), NumSamples); TArray CurveSampleOffsets; CurveSampleOffsets.Init(0, NumCurves); const bool bValidSelection = CurveSelection.IsValidForCollection(CurvesFacade.GetManagedArrayCollection()); int32 PointOffset = 0; int32 SampleOffset = 0; for(int32 CurveIndex = 0; CurveIndex < NumCurves; ++CurveIndex) { const int32 NumPoints = CurvesFacade.GetCurvePointOffsets()[CurveIndex]-PointOffset; if(!bValidSelection || (bValidSelection && CurveSelection.IsSelected(CurveIndex))) { UE::Groom::Private::BuildResampledPoints(CurvesFacade.GetPointRestPositions(), CurvesFacade.GetCurveGeometryIndices(), CurvesFacade.GetCurvePointOffsets(), {}, CurveIndex, PointOffset, SampleOffset, PointSampledPositions, GuidePoints); SampleOffset += GuidePoints; } else { for(int32 PointIndex = 0; PointIndex < NumPoints; ++PointIndex) { PointSampledPositions[PointIndex+PointOffset] = CurvesFacade.GetPointRestPositions()[PointIndex+PointOffset]; } SampleOffset += NumPoints; } CurveSampleOffsets[CurveIndex] = SampleOffset; PointOffset = CurvesFacade.GetCurvePointOffsets()[CurveIndex]; } const TArray GeometryCurveOffsets = CurvesFacade.GetGeometryCurveOffsets(); const TArray GeometryGroupNames = CurvesFacade.GetGeometryGroupNames(); const TArray GeometryCurveThickness = CurvesFacade.GetGeometryCurveThickness(); const TArray CurveSourceIndices = CurvesFacade.GetCurveSourceIndices(); CurvesFacade.InitCurvesCollection(PointSampledPositions, CurveSampleOffsets, GeometryCurveOffsets, GeometryGroupNames, GeometryCurveThickness, CurveSourceIndices); } } static int32 ClosestPowerOfTwo(const int32 InputValue) { if(InputValue > 0) { const float LogValue = FMath::Log2(static_cast(InputValue)); const int32 LowerExp = FMath::FloorToInt(LogValue); const int32 UpperExp = FMath::CeilToInt(LogValue); const int32 LowerValue = 1 << LowerExp; const int32 UpperValue = 1 << UpperExp; return FMath::Clamp((InputValue-LowerValue < UpperValue-InputValue) ? LowerValue : UpperValue, 4, 64); } return 2; } } void FResampleGuidesPointsDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } FResampleCurvePointsDataflowNode::FResampleCurvePointsDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid) : FDataflowNode(InParam, InGuid) { RegisterInputConnection(&Collection); RegisterInputConnection(&CurveSelection); RegisterInputConnection(&NumPoints).SetCanHidePin(true).SetPinIsHidden(true); RegisterOutputConnection(&Collection, &Collection); } void FResampleCurvePointsDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { FManagedArrayCollection GuidesCollection = GetValue(Context, &Collection); const FDataflowCurveSelection& DataflowSelection = GetValue(Context, &CurveSelection); const int32 GuidePoints = IsConnected(&NumPoints) ? UE::Groom::Private::ClosestPowerOfTwo(GetValue(Context, &NumPoints)) : static_cast(PointsCount); GeometryCollection::Facades::FCollectionCurveGeometryFacade CurvesFacade(GuidesCollection); if(CurvesFacade.IsValid() && GuidePoints > 0) { UE::Groom::Private::ResampleCurvesPoints(CurvesFacade, DataflowSelection, GuidePoints); } SetValue(Context, MoveTemp(GuidesCollection), &Collection); } }