// Copyright Epic Games, Inc. All Rights Reserved. #include "GroomAssetTerminalNode.h" #include "AssetCompilingManager.h" #include "GroomCollectionFacades.h" #include "GroomEdit.h" #include "Dataflow/DataflowObjectInterface.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(GroomAssetTerminalNode) namespace UE::Groom::Private { FORCEINLINE void BuildEditableGuides(const UE::Groom::FGroomGuidesFacade& GuidesFacade, FEditableGroom& EditGroom) { int32 ObjectIndex = 0, CurveIndex = 0; int32 PrevCurve = 0, PrevPoint = 0;; for (FEditableGroomGroup& Group : EditGroom.Groups) { const int32 NextCurve = GuidesFacade.GetObjectCurveOffsets()[ObjectIndex]; Group.Guides.SetNum(NextCurve-PrevCurve); // Only rebuild the guides since the strands are the same for (FEditableHairGuide& Guide : Group.Guides) { const int32 NextPoint = GuidesFacade.GetCurvePointOffsets()[CurveIndex]; const int32 StrandIndex = GuidesFacade.GetCurveStrandIndices()[CurveIndex]; Guide.ControlPoints.Reset(); for(int32 PointIndex = PrevPoint; PointIndex < NextPoint; ++PointIndex) { Guide.ControlPoints.Add({GuidesFacade.GetPointRestPositions()[PointIndex], FMath::Clamp(static_cast(PointIndex-PrevPoint) / (NextPoint-PrevPoint-1), 0.f, 1.f)}); } if((StrandIndex != INDEX_NONE) && (StrandIndex < Group.Strands.Num())) { Guide.GuideID = CurveIndex; Guide.RootUV = Group.Strands[StrandIndex].RootUV; } PrevPoint = NextPoint; ++CurveIndex; } PrevCurve = NextCurve; ++ObjectIndex; } } FORCEINLINE void CopyCollectionAttributes(const FManagedArrayCollection* InputCollection, FManagedArrayCollection* OutputCollection, const TArray> AttributesToCopy = TArray>()) { for(const TTuple& AttributeToCopy : AttributesToCopy) { const FName& AttributeName = AttributeToCopy.Value; const FName& GroupName = AttributeToCopy.Key; if (InputCollection->HasGroup(GroupName)) { if(!OutputCollection->HasGroup(GroupName)) { OutputCollection->AddGroup(GroupName); } if (InputCollection->NumElements(GroupName) != OutputCollection->NumElements(GroupName)) { OutputCollection->EmptyGroup(GroupName); OutputCollection->AddElements(InputCollection->NumElements(GroupName), GroupName); } OutputCollection->CopyAttribute(*InputCollection, AttributeName, GroupName); } } } template static void BuildVerticesAttribute(const FManagedArrayCollection& InCollection, FManagedArrayCollection* OutCollection, const int32 NumPoints, const FName& AttributeName) { const TManagedArray& VerticesAttribute = InCollection.GetAttribute(AttributeName, FacadeType::VerticesGroup); if(OutCollection->NumElements(FacadeType::PointsGroup) != NumPoints) { if(OutCollection->NumElements(FacadeType::PointsGroup) > 0) { OutCollection->EmptyGroup(FacadeType::PointsGroup); } OutCollection->AddElements(NumPoints, FacadeType::PointsGroup); } TManagedArray& PointsAttribute = OutCollection->AddAttribute(AttributeName, FacadeType::PointsGroup); for(int32 PointIndex = 0; PointIndex < NumPoints; ++PointIndex) { PointsAttribute[PointIndex] = VerticesAttribute[2*PointIndex]; } } template static void TransferVerticesAttributes(const FManagedArrayCollection& InCollection, FManagedArrayCollection* OutCollection, const int32 NumPoints, const TArray& AttributesToSkip) { // Transfer vertices weight maps onto the points to be stored onto the rest collection const TArray AttributeNames = InCollection.AttributeNames(FacadeType::VerticesGroup); for(const FName& AttributeName : AttributeNames) { if(!AttributesToSkip.Contains(AttributeName)) { if(InCollection.GetAttributeType(AttributeName, FacadeType::VerticesGroup) == EManagedArrayType::FFloatType) { BuildVerticesAttribute(InCollection, OutCollection, NumPoints, AttributeName); } else if(InCollection.GetAttributeType(AttributeName, FacadeType::VerticesGroup) == EManagedArrayType::FVector4fType) { BuildVerticesAttribute(InCollection, OutCollection, NumPoints, AttributeName); } else if(InCollection.GetAttributeType(AttributeName, FacadeType::VerticesGroup) == EManagedArrayType::FVectorType) { BuildVerticesAttribute(InCollection, OutCollection, NumPoints, AttributeName); } else if(InCollection.GetAttributeType(AttributeName, FacadeType::VerticesGroup) == EManagedArrayType::FVector2DType) { BuildVerticesAttribute(InCollection, OutCollection, NumPoints, AttributeName); } if(InCollection.GetAttributeType(AttributeName, FacadeType::VerticesGroup) == EManagedArrayType::FInt32Type) { BuildVerticesAttribute(InCollection, OutCollection, NumPoints, AttributeName); } else if(InCollection.GetAttributeType(AttributeName, FacadeType::VerticesGroup) == EManagedArrayType::FIntVector4Type) { BuildVerticesAttribute(InCollection, OutCollection, NumPoints, AttributeName); } else if(InCollection.GetAttributeType(AttributeName, FacadeType::VerticesGroup) == EManagedArrayType::FIntVectorType) { BuildVerticesAttribute(InCollection, OutCollection, NumPoints, AttributeName); } else if(InCollection.GetAttributeType(AttributeName, FacadeType::VerticesGroup) == EManagedArrayType::FIntVector2Type) { BuildVerticesAttribute(InCollection, OutCollection, NumPoints, AttributeName); } else if(InCollection.GetAttributeType(AttributeName, FacadeType::VerticesGroup) == EManagedArrayType::FBoolType) { BuildVerticesAttribute(InCollection, OutCollection, NumPoints, AttributeName); } else if(InCollection.GetAttributeType(AttributeName, FacadeType::VerticesGroup) == EManagedArrayType::FLinearColorType) { BuildVerticesAttribute(InCollection, OutCollection, NumPoints, AttributeName); } else if(InCollection.GetAttributeType(AttributeName, FacadeType::VerticesGroup) == EManagedArrayType::FQuatType) { BuildVerticesAttribute(InCollection, OutCollection, NumPoints, AttributeName); } else if(InCollection.GetAttributeType(AttributeName, FacadeType::VerticesGroup) == EManagedArrayType::FTransform3fType) { BuildVerticesAttribute(InCollection, OutCollection, NumPoints, AttributeName); } } } } template FORCEINLINE void RegisterSkeletalMeshes(const FManagedArrayCollection& InCollection, const FacadeType& GroomFacade, UGroomAsset* GroomAsset) { const TManagedArray>& ObjectSkeletalMeshes = InCollection.GetAttribute>(UE::Groom::FGroomGuidesFacade::ObjectSkeletalMeshesAttribute, FacadeType::ObjectsGroup); const TManagedArray& ObjectMeshLODs = InCollection.GetAttribute(UE::Groom::FGroomGuidesFacade::ObjectMeshLODsAttribute, FacadeType::ObjectsGroup); for(int32 GroupIndex = 0; GroupIndex < GroomFacade.GetNumObjects(); ++GroupIndex) { GroomAsset->GetDataflowSettings().SetSkeletalMesh(GroupIndex, Cast(ObjectSkeletalMeshes[GroupIndex]), ObjectMeshLODs[GroupIndex]); } } } void FGroomAssetTerminalDataflowNode::SetAssetValue(TObjectPtr Asset, UE::Dataflow::FContext& Context) const { if (UGroomAsset* GroomAsset = Cast(Asset.Get())) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); UE::Groom::FGroomGuidesFacade GuidesFacade(InCollection); UE::Groom::FGroomStrandsFacade StrandsFacade(InCollection); FManagedArrayCollection* OutCollection = (GuidesFacade.IsValid() || StrandsFacade.IsValid()) ? new FManagedArrayCollection() : nullptr; if(StrandsFacade.IsValid()) { // Skip default vertex attributes as not defined by the user const TArray AttributesToSkip = {UE::Groom::FGroomStrandsFacade::VertexLinearColorsAttribute}; // Transfer vertices weight maps onto the points to be stored onto the rest collection UE::Groom::Private::TransferVerticesAttributes(InCollection, OutCollection, StrandsFacade.GetNumPoints(), AttributesToSkip); } if(GuidesFacade.IsValid()) { // Only copy the attributes not stored already in the asset const TArray> AttributesToCopy = { {UE::Groom::FGroomGuidesFacade::CurvesGroup, UE::Groom::FGroomGuidesFacade::CurveParentIndicesAttribute}, {UE::Groom::FGroomGuidesFacade::CurvesGroup, UE::Groom::FGroomGuidesFacade::CurveLodIndicesAttribute}}; // Copy attributes from input collection UE::Groom::Private::CopyCollectionAttributes(&InCollection, OutCollection, AttributesToCopy); // Skip default vertex attributes as not defined by the user const TArray AttributesToSkip = {UE::Groom::FGroomGuidesFacade::VertexLinearColorsAttribute}; // Transfer vertices weight maps onto the points to be stored onto the rest collection (VertexKinematicWeights should be included here) UE::Groom::Private::TransferVerticesAttributes(InCollection, OutCollection, GuidesFacade.GetNumPoints(), AttributesToSkip); // Build an editable groom asset for the strands FEditableGroom EditGroom; ConvertFromGroomAsset(const_cast(GroomAsset), &EditGroom, false, false, false); if(GuidesFacade.GetNumObjects() == EditGroom.Groups.Num()) { // Build the editable guides UE::Groom::Private::BuildEditableGuides(GuidesFacade,EditGroom); // Ensure compilation dependent assets is done FAssetCompilingManager::Get().FinishCompilationForObjects({ GroomAsset }); // Convert to groom asset ConvertToGroomAsset(const_cast(GroomAsset), &EditGroom, EEditableGroomOperations::ControlPoints_Modified); } // To prevent future reconstruction in the BuildData we set the type to be imported for(FHairGroupsInterpolation& GroupInterpolation : GroomAsset->GetHairGroupsInterpolation()) { GroupInterpolation.InterpolationSettings.GuideType = EGroomGuideType::Imported; } } if(OutCollection) { GroomAsset->GetDataflowSettings().SetRestCollection(OutCollection); GroomAsset->GetDataflowSettings().InitSkeletalMeshes(GuidesFacade.GetNumObjects()); if(InCollection.HasAttribute(UE::Groom::FGroomGuidesFacade::ObjectSkeletalMeshesAttribute, UE::Groom::FGroomGuidesFacade::ObjectsGroup)) { UE::Groom::Private::RegisterSkeletalMeshes(InCollection, GuidesFacade, GroomAsset); } else if(InCollection.HasAttribute(UE::Groom::FGroomGuidesFacade::ObjectSkeletalMeshesAttribute, UE::Groom::FGroomStrandsFacade::ObjectsGroup)) { UE::Groom::Private::RegisterSkeletalMeshes(InCollection, StrandsFacade, GroomAsset); } } } } void FGroomAssetTerminalDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); SetValue(Context, InCollection, &Collection); }