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

175 lines
7.1 KiB
C++

// 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<FVector3f>& PointRestPositions, const TArray<int32>& CurveObjectIndices, const TArray<int32>& CurvePointOffsets,
const TArray<int32>& ObjectPointSamples, const int32 CurveIndex, const int32 PointOffset, const int32 SampleOffset,
TArray<FVector3f>& 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<float> 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<float>(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<FVector3f> PointSampledPositions;
PointSampledPositions.Init(FVector3f::Zero(), NumSamples);
TArray<int32> 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<int32> GeometryCurveOffsets = CurvesFacade.GetGeometryCurveOffsets();
const TArray<FString> GeometryGroupNames = CurvesFacade.GetGeometryGroupNames();
const TArray<float> GeometryCurveThickness = CurvesFacade.GetGeometryCurveThickness();
const TArray<int32> 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<float>(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<FManagedArrayCollection>(&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<FManagedArrayCollection>(&Collection))
{
FManagedArrayCollection GuidesCollection = GetValue<FManagedArrayCollection>(Context, &Collection);
const FDataflowCurveSelection& DataflowSelection = GetValue<FDataflowCurveSelection>(Context, &CurveSelection);
const int32 GuidePoints = IsConnected(&NumPoints) ?
UE::Groom::Private::ClosestPowerOfTwo(GetValue<int32>(Context, &NumPoints)) : static_cast<int32>(PointsCount);
GeometryCollection::Facades::FCollectionCurveGeometryFacade CurvesFacade(GuidesCollection);
if(CurvesFacade.IsValid() && GuidePoints > 0)
{
UE::Groom::Private::ResampleCurvesPoints(CurvesFacade, DataflowSelection, GuidePoints);
}
SetValue(Context, MoveTemp(GuidesCollection), &Collection);
}
}