// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= GeometryCollection.cpp: FGeometryCollection methods. =============================================================================*/ #include "GeometryCollection/Facades/CollectionTetrahedralBindingsFacade.h" #include "GeometryCollection/GeometryCollection.h" #include "GeometryCollection/ManagedArrayAccessor.h" namespace GeometryCollection::Facades { // [ Duff et al. 2017, "Building an Orthonormal Basis, Revisited" ] // Discontinuity at TangentZ.z == 0 Chaos::PMatrix FleshDeformerImpl::GetTangentBasis(const FVector3f& TangentZ) { const float Sign = copysignf(1.0f, TangentZ.Z); const float A = -1.0f / (Sign + TangentZ.Z); const float B = TangentZ.Z * TangentZ.Y * A; FVector3f TangentX( 1.0f + Sign * TangentZ.X * TangentZ.X * A, Sign * B, -Sign * TangentZ.X); FVector3f TangentY( B, Sign + TangentZ.Y * TangentZ.Y * A, -TangentZ.Y); return Chaos::PMatrix(TangentX, TangentY, TangentZ); } Chaos::PMatrix FleshDeformerImpl::GetOrthogonalBasisVectors( const FVector3f& PtA, const FVector3f& PtB, const FVector3f& PtC) { FVector3f EdgeBA = PtB - PtA; FVector3f EdgeCA = PtC - PtA; FVector3f OrthoNorm = FVector3f::CrossProduct(EdgeBA, EdgeCA).GetSafeNormal(); return GetTangentBasis(OrthoNorm); } FVector3f FleshDeformerImpl::GetRotatedOffsetVector( const FVector3f& Offset, const FVector3f& RestPtA, const FVector3f& RestPtB, const FVector3f& RestPtC, const FVector3f& CurrPtA, const FVector3f& CurrPtB, const FVector3f& CurrPtC) { Chaos::PMatrix BasisVectors = GetOrthogonalBasisVectors(RestPtA, RestPtB, RestPtC); Chaos::PMatrix RestRotInv = BasisVectors.Inverse(); Chaos::PMatrix CurrRot = GetOrthogonalBasisVectors(CurrPtA, CurrPtB, CurrPtC); Chaos::PMatrix BasisDelta = RestRotInv * CurrRot; return BasisDelta.TransformVector(Offset); } FVector3f FleshDeformerImpl::GetRotatedOffsetVector( const FIntVector4& Parents, const FVector3f& Offset, const TManagedArray& RestVertices, const TArray>& CurrVertices) { return GetRotatedOffsetVector( Offset, RestVertices[Parents[0]], RestVertices[Parents[1]], RestVertices[Parents[2]], CurrVertices[Parents[0]], CurrVertices[Parents[1]], CurrVertices[Parents[2]]); } FString bump; FVector3f FleshDeformerImpl::GetEmbeddedPosition( const int32 SurfaceIndex, const TManagedArrayAccessor* ParentsArray, const TManagedArrayAccessor* WeightsArray, const TManagedArrayAccessor* OffsetArray, const TManagedArray& RestVertices, const TArray>& CurrVertices) { const FIntVector4& Parents = ParentsArray->Get()[SurfaceIndex]; const FVector4f& Weights = WeightsArray->Get()[SurfaceIndex]; FVector3f Pos = FVector3f::Zero(); const int32 iEnd = Parents[3] == INDEX_NONE ? 3 : 4; for (int32 i = 0; i < iEnd; i++) { Pos += CurrVertices[Parents[i]] * Weights[i]; } FVector3f Pos2 = Pos; FString bumpe = FString::Printf(TEXT("(%f,%f,%f)"), Pos.X, Pos.Y, Pos.Z); bump = bumpe; // If surface binding, the last index is -1. if (Parents[3] == INDEX_NONE) { const FVector3f& Offset = OffsetArray->Get()[SurfaceIndex]; const FVector3f RotatedOffset = GetRotatedOffsetVector(Parents, Offset, RestVertices, CurrVertices); Pos -= Offset; } return Pos; } // Groups const FName FTetrahedralBindings::MeshBindingsGroupName = "MeshBindings"; // Attributes const FName FTetrahedralBindings::MeshIdAttributeName = "MeshId"; const FName FTetrahedralBindings::ParentsAttributeName = "Parents"; const FName FTetrahedralBindings::WeightsAttributeName = "Weights"; const FName FTetrahedralBindings::OffsetsAttributeName = "Offsets"; const FName FTetrahedralBindings::MaskAttributeName = "Mask"; // Dependency const FName FTetrahedralBindings::TetrahedralGroupDependency = TEXT("Tetrahedral"); FTetrahedralBindings::FTetrahedralBindings(FManagedArrayCollection& InCollection) : MeshIdAttribute(InCollection, MeshIdAttributeName, MeshBindingsGroupName) {} FTetrahedralBindings::FTetrahedralBindings(const FManagedArrayCollection& InCollection) : MeshIdAttribute(InCollection, MeshIdAttributeName, MeshBindingsGroupName) {} FTetrahedralBindings::~FTetrahedralBindings() {} void FTetrahedralBindings::DefineSchema() { check(!IsConst()); TManagedArray& MeshIdValues = MeshIdAttribute.IsValid() ? MeshIdAttribute.Modify() : MeshIdAttribute.Add(); } bool FTetrahedralBindings::IsValid() const { return MeshIdAttribute.IsValid() && (Parents && Parents->IsValid()) && (Weights && Weights->IsValid()) && (Offsets && Offsets->IsValid()) && (Masks && Masks->IsValid()); } FName FTetrahedralBindings::GenerateMeshGroupName( const int32 TetMeshIdx, const FName& MeshId, const int32 LOD) { const FString MeshIdStr = MeshId.GetPlainNameString(); FString Str = FString::Printf(TEXT("TetrahedralBindings:TetMeshIdx:%d:%s:%d"), TetMeshIdx, *MeshIdStr, LOD); return FName(Str.Len(), *Str); } bool FTetrahedralBindings::ContainsBindingsGroup(const int32 TetMeshIdx, const FName& MeshId, const int32 LOD) const { return ContainsBindingsGroup(GenerateMeshGroupName(TetMeshIdx, MeshId, LOD)); } bool FTetrahedralBindings::ContainsBindingsGroup(const FName& GroupName) const { check(MeshIdAttribute.IsValid()); const TManagedArray* MeshIdValues = MeshIdAttribute.IsValid() ? MeshIdAttribute.Find() : nullptr; return MeshIdValues ? MeshIdValues->Contains(GroupName.ToString()) : false; } int32 FTetrahedralBindings::GetTetMeshIndex(const FName& MeshId, const int32 LOD) const { const TManagedArray* MeshIdValues = MeshIdAttribute.IsValid() ? MeshIdAttribute.Find() : nullptr; if (MeshIdValues) { FString Suffix = FString::Printf(TEXT(":%s:%d"), *MeshId.GetPlainNameString(), LOD); for (int32 i = 0; i < MeshIdValues->Num(); i++) { const FString& Entry = (*MeshIdValues)[i]; if (Entry.EndsWith(Suffix)) { FString Str = Entry; Str.RemoveAt(0, FString(TEXT("TetrahedralBindings:TetMeshIdx:")).Len(), EAllowShrinking::No); Str.RemoveAt(Str.Len() - Suffix.Len(), Suffix.Len()); return FCString::Atoi(*Str); } } } return INDEX_NONE; } void FTetrahedralBindings::AddBindingsGroup(const int32 TetMeshIdx, const FName& MeshId, const int32 LOD) { AddBindingsGroup(GenerateMeshGroupName(TetMeshIdx, MeshId, LOD)); } void FTetrahedralBindings::AddBindingsGroup(const FName& GroupName) { if (ContainsBindingsGroup(GroupName)) { ReadBindingsGroup(GroupName); return; } check(MeshIdAttribute.IsValid()); check(MeshIdAttribute.IsPersistent()); check(!IsConst()); const int32 Idx = MeshIdAttribute.AddElements(1); MeshIdAttribute.Modify()[Idx] = GroupName.ToString(); Parents.Reset(); Weights.Reset(); Offsets.Reset(); Masks.Reset(); FManagedArrayCollection& Collection = *MeshIdAttribute.GetCollection(); Parents.Reset(new TManagedArrayAccessor(Collection, ParentsAttributeName, GroupName, TetrahedralGroupDependency)); Weights.Reset(new TManagedArrayAccessor(Collection, WeightsAttributeName, GroupName)); Offsets.Reset(new TManagedArrayAccessor(Collection, OffsetsAttributeName, GroupName)); Masks.Reset(new TManagedArrayAccessor(Collection, MaskAttributeName, GroupName)); Parents->Add(ManageArrayAccessor::EPersistencePolicy::MakePersistent, FGeometryCollection::VerticesGroup); Weights->Add(); Offsets->Add(); Masks->Add(); } bool FTetrahedralBindings::ReadBindingsGroup(const int32 TetMeshIdx, const FName& MeshId, const int32 LOD) { return ReadBindingsGroup(GenerateMeshGroupName(TetMeshIdx, MeshId, LOD)); } bool FTetrahedralBindings::ReadBindingsGroup(const FName& GroupName) { check(MeshIdAttribute.IsValid()); Parents.Reset(); Weights.Reset(); Offsets.Reset(); Masks.Reset(); if (!MeshIdAttribute.Find()->Contains(GroupName.ToString())) { return false; } // This is an existing group, so find the existing bindings arrays. if (!IsConst()) { FManagedArrayCollection* Collection = MeshIdAttribute.GetCollection(); Parents.Reset(new TManagedArrayAccessor(*Collection, ParentsAttributeName, GroupName, TetrahedralGroupDependency)); Weights.Reset(new TManagedArrayAccessor(*Collection, WeightsAttributeName, GroupName)); Offsets.Reset(new TManagedArrayAccessor(*Collection, OffsetsAttributeName, GroupName)); Masks.Reset(new TManagedArrayAccessor(*Collection, MaskAttributeName, GroupName)); } else { const FManagedArrayCollection& ConstCollection = MeshIdAttribute.GetConstCollection(); Parents.Reset(new TManagedArrayAccessor(ConstCollection, ParentsAttributeName, GroupName, TetrahedralGroupDependency)); Weights.Reset(new TManagedArrayAccessor(ConstCollection, WeightsAttributeName, GroupName)); Offsets.Reset(new TManagedArrayAccessor(ConstCollection, OffsetsAttributeName, GroupName)); Masks.Reset(new TManagedArrayAccessor(ConstCollection, MaskAttributeName, GroupName)); } return Parents->IsValid() && Weights->IsValid() && Offsets->IsValid() && Masks->IsValid(); } void FTetrahedralBindings::RemoveBindingsGroup(const int32 TetMeshIdx, const FName& MeshId, const int32 LOD) { RemoveBindingsGroup(GenerateMeshGroupName(TetMeshIdx, MeshId, LOD)); } void FTetrahedralBindings::RemoveBindingsGroup(const FName& GroupName) { check(!IsConst()); TManagedArray& MeshIdValues = MeshIdAttribute.Modify(); int32 Idx = MeshIdValues.Find(GroupName.ToString()); if (Idx != INDEX_NONE) { TArray Indices; Indices.Add(Idx); MeshIdValues.RemoveElements(Indices); } FManagedArrayCollection& Collection = *MeshIdAttribute.GetCollection(); if (Parents) { Parents->Remove(); Parents.Reset(); } if (Weights) { Weights->Remove(); Weights.Reset(); } if (Offsets) { Offsets->Remove(); Offsets.Reset(); } if (Masks) { Masks->Remove(); Masks.Reset(); } // Only drop the group if it's empty at this point? if (Collection.NumAttributes(GroupName) == 0) { Collection.RemoveGroup(GroupName); } } void FTetrahedralBindings::SetBindingsData( const TArray& ParentsIn, const TArray& WeightsIn, const TArray& OffsetsIn, const TArray& MaskIn) { check(!IsConst()); check(IsValid()); check((ParentsIn.Num() == WeightsIn.Num()) && (ParentsIn.Num() == OffsetsIn.Num()) && (ParentsIn.Num() == MaskIn.Num())); const int32 Num = ParentsIn.Num(); const int32 CurrNum = Parents->Num();//Collection.NumElements(CurrGroupName); Parents->AddElements(Num - CurrNum); // Resizes the group TManagedArray& ParentsValues = Parents->Modify(); TManagedArray& WeightsValues = Weights->Modify(); TManagedArray& OffsetsValues = Offsets->Modify(); TManagedArray& MaskValues = Masks->Modify(); for (int32 i = 0; i < Num; i++) { ParentsValues[i] = ParentsIn[i]; WeightsValues[i] = WeightsIn[i]; OffsetsValues[i] = OffsetsIn[i]; MaskValues[i] = MaskIn[i]; } } };