// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= GeometryCollection.cpp: FGeometryCollection methods. =============================================================================*/ #include "GeometryCollection/GeometryCollection.h" #include "GeometryCollection/GeometryCollectionAlgo.h" #include "GeometryCollection/GeometryCollectionUtility.h" #include "GeometryCollection/GeometryCollectionProximityUtility.h" #include "GeometryCollection/GeometryCollectionClusteringUtility.h" #include "GeometryCollection/GeometryCollectionConvexUtility.h" #include "GeometryCollection/Facades/CollectionHierarchyFacade.h" #include "GeometryCollection/Facades/CollectionRenderingFacade.h" #include "UObject/FortniteSeasonBranchObjectVersion.h" #include "UObject/FortniteMainBranchObjectVersion.h" #include #include #include "Chaos/ChaosArchive.h" #include "Voronoi/Voronoi.h" bool bChaosGeometryCollectionEnableCollisionParticles = true; FAutoConsoleVariableRef CVarChaosGeometryCollectionEnableCollisionParticles( TEXT("p.Chaos.GC.EnableCollisionParticles"), bChaosGeometryCollectionEnableCollisionParticles, TEXT("Enable use of collision particles for collision [def:true]")); DEFINE_LOG_CATEGORY_STATIC(FGeometryCollectionLogging, Log, All); // @todo: update names const FName FGeometryCollection::FacesGroup = "Faces"; const FName FGeometryCollection::GeometryGroup = "Geometry"; const FName FGeometryCollection::VerticesGroup = "Vertices"; const FName FGeometryCollection::BreakingGroup = "Breaking"; const FName FGeometryCollection::MaterialGroup = "Material"; // Attributes const FName FGeometryCollection::SimulatableParticlesAttribute("SimulatableParticlesAttribute"); const FName FGeometryCollection::SimulationTypeAttribute("SimulationType"); const FName FGeometryCollection::StatusFlagsAttribute("StatusFlags"); const FName FGeometryCollection::ExternalCollisionsAttribute("ExternalCollisions"); const FName FGeometryCollection::ColorAttribute("Color"); const FName FGeometryCollection::BoneColorAttribute("BoneColor"); const FName FGeometryCollection::TransformToGeometryIndexAttribute("TransformToGeometryIndex"); const FName FGeometryCollection::BoundingBoxAttribute("BoundingBox"); const FName FGeometryCollection::TransformIndexAttribute("TransformIndex"); const FName FGeometryCollection::VertexStartAttribute("VertexStart"); const FName FGeometryCollection::VertexCountAttribute("VertexCount"); bool FGeometryCollection::AreCollisionParticlesEnabled() { return bChaosGeometryCollectionEnableCollisionParticles; } FGeometryCollection::FGeometryCollection(FGeometryCollectionDefaults InDefaults) : FTransformCollection() , FGeometryCollectionConvexPropertiesInterface(this) , FGeometryCollectionProximityPropertiesInterface(this) , Defaults(InDefaults) { Construct(); } FGeometryCollection::~FGeometryCollection() = default; void FGeometryCollection::DefineGeometrySchema(FManagedArrayCollection& InCollection) { FTransformCollection::DefineTransformSchema(InCollection); FManagedArrayCollection::FConstructionParameters TransformDependency(FTransformCollection::TransformGroup); FManagedArrayCollection::FConstructionParameters VerticesDependency(FGeometryCollection::VerticesGroup); FManagedArrayCollection::FConstructionParameters FacesDependency(FGeometryCollection::FacesGroup); // Transform Group InCollection.AddAttribute("TransformToGeometryIndex", FTransformCollection::TransformGroup); InCollection.AddAttribute("SimulationType", FTransformCollection::TransformGroup); InCollection.AddAttribute("StatusFlags", FTransformCollection::TransformGroup); InCollection.AddAttribute("InitialDynamicState", FTransformCollection::TransformGroup); InCollection.AddAttribute("ExemplarIndex", FTransformCollection::TransformGroup); // Vertices Group InCollection.AddAttribute("Vertex", FGeometryCollection::VerticesGroup); InCollection.AddAttribute("Normal", FGeometryCollection::VerticesGroup); GeometryCollection::UV::DefineUVSchema(InCollection); InCollection.AddAttribute(FGeometryCollection::ColorAttribute, FGeometryCollection::VerticesGroup); InCollection.AddAttribute("TangentU", FGeometryCollection::VerticesGroup); InCollection.AddAttribute("TangentV", FGeometryCollection::VerticesGroup); InCollection.AddAttribute("BoneMap", FGeometryCollection::VerticesGroup, TransformDependency); // Faces Group InCollection.AddAttribute("Indices", FGeometryCollection::FacesGroup, VerticesDependency); InCollection.AddAttribute("Visible", FGeometryCollection::FacesGroup); InCollection.AddAttribute("MaterialIndex", FGeometryCollection::FacesGroup); InCollection.AddAttribute("MaterialID", FGeometryCollection::FacesGroup); // Geometry Group InCollection.AddAttribute("TransformIndex", FGeometryCollection::GeometryGroup, TransformDependency); InCollection.AddAttribute("BoundingBox", FGeometryCollection::GeometryGroup); InCollection.AddAttribute("InnerRadius", FGeometryCollection::GeometryGroup); InCollection.AddAttribute("OuterRadius", FGeometryCollection::GeometryGroup); InCollection.AddAttribute("VertexStart", FGeometryCollection::GeometryGroup, VerticesDependency); InCollection.AddAttribute("VertexCount", FGeometryCollection::GeometryGroup); InCollection.AddAttribute("FaceStart", FGeometryCollection::GeometryGroup, FacesDependency); InCollection.AddAttribute("FaceCount", FGeometryCollection::GeometryGroup); // Material Group InCollection.AddAttribute("Sections", FGeometryCollection::MaterialGroup, FacesDependency); } void FGeometryCollection::Construct() { Version = GetLatestVersionNumber(); FManagedArrayCollection::FConstructionParameters TransformDependency(FTransformCollection::TransformGroup); FManagedArrayCollection::FConstructionParameters VerticesDependency(FGeometryCollection::VerticesGroup); FManagedArrayCollection::FConstructionParameters FacesDependency(FGeometryCollection::FacesGroup); // Transform Group AddExternalAttribute("TransformToGeometryIndex", FTransformCollection::TransformGroup, TransformToGeometryIndex); AddExternalAttribute("SimulationType", FTransformCollection::TransformGroup, SimulationType); AddExternalAttribute("StatusFlags", FTransformCollection::TransformGroup, StatusFlags); AddExternalAttribute("InitialDynamicState", FTransformCollection::TransformGroup, InitialDynamicState); AddExternalAttribute("ExemplarIndex", FTransformCollection::TransformGroup, ExemplarIndex); // Vertices Group AddExternalAttribute("Vertex", FGeometryCollection::VerticesGroup, Vertex); AddExternalAttribute("Normal", FGeometryCollection::VerticesGroup, Normal); AddExternalAttribute(FGeometryCollection::ColorAttribute, FGeometryCollection::VerticesGroup, Color); AddExternalAttribute("TangentU", FGeometryCollection::VerticesGroup, TangentU); AddExternalAttribute("TangentV", FGeometryCollection::VerticesGroup, TangentV); AddExternalAttribute("BoneMap", FGeometryCollection::VerticesGroup, BoneMap, TransformDependency); GeometryCollection::UV::DefineUVSchema(*this); // Faces Group AddExternalAttribute("Indices", FGeometryCollection::FacesGroup, Indices, VerticesDependency); AddExternalAttribute("Visible", FGeometryCollection::FacesGroup, Visible); AddExternalAttribute("MaterialIndex", FGeometryCollection::FacesGroup, MaterialIndex); AddExternalAttribute("MaterialID", FGeometryCollection::FacesGroup, MaterialID); AddExternalAttribute("Internal", FGeometryCollection::FacesGroup, Internal); // Geometry Group AddExternalAttribute("TransformIndex", FGeometryCollection::GeometryGroup, TransformIndex, TransformDependency); AddExternalAttribute("BoundingBox", FGeometryCollection::GeometryGroup, BoundingBox); AddExternalAttribute("InnerRadius", FGeometryCollection::GeometryGroup, InnerRadius); AddExternalAttribute("OuterRadius", FGeometryCollection::GeometryGroup, OuterRadius); AddExternalAttribute("VertexStart", FGeometryCollection::GeometryGroup, VertexStart, VerticesDependency); AddExternalAttribute("VertexCount", FGeometryCollection::GeometryGroup, VertexCount); AddExternalAttribute("FaceStart", FGeometryCollection::GeometryGroup, FaceStart, FacesDependency); AddExternalAttribute("FaceCount", FGeometryCollection::GeometryGroup, FaceCount); // Material Group AddExternalAttribute("Sections", FGeometryCollection::MaterialGroup, Sections, FacesDependency); InitializeInterfaces(); } void FGeometryCollection::SetDefaults(FName Group, uint32 StartSize, uint32 NumElements) { Super::SetDefaults(Group, StartSize, NumElements); if (Group == FTransformCollection::TransformGroup) { for (uint32 Idx = StartSize; Idx < StartSize + NumElements; ++Idx) { TransformToGeometryIndex[Idx] = FGeometryCollection::Invalid; Parent[Idx] = FGeometryCollection::Invalid; SimulationType[Idx] = FGeometryCollection::ESimulationTypes::FST_None; StatusFlags[Idx] = 0; InitialDynamicState[Idx] = static_cast(Chaos::EObjectStateType::Uninitialized); ExemplarIndex[Idx] = INDEX_NONE; } FGeometryCollectionConvexUtility::SetDefaults(this, Group, StartSize, NumElements); } else if (Group == FGeometryCollection::VerticesGroup) { for (uint32 Idx = StartSize; Idx < StartSize + NumElements; ++Idx) { Color[Idx] = Defaults.DefaultVertexColor; } } } void FGeometryCollection::Append(const FManagedArrayCollection& InCollection) { /* InCollection data is appended to the front*/ Super::Append(InCollection); if (const FGeometryCollection* InTypedCollection = InCollection.Cast()) { const int32 Offset = InTypedCollection->NumElements(GeometryGroup); const int32 OtherSize = InTypedCollection->NumElements(TransformGroup); const int32 Size = NumElements(TransformGroup); /*TransformToGeometryIndex does not have GeometryGroup dependency, update manually*/ for (int32 Idx = OtherSize; Idx < Size; ++Idx) { if (TransformToGeometryIndex[Idx] != INDEX_NONE) { TransformToGeometryIndex[Idx] += Offset; } } } } void FGeometryCollection::AppendCollection(const FGeometryCollection& InCollection) { Append(InCollection); } // MaterialIDOffset is based on the number of materials added by this append geometry call int32 FGeometryCollection::AppendGeometry(const FGeometryCollection & Element, int32 MaterialIDOffset, bool ReindexAllMaterials, const FTransform& TransformRoot) { // until we support a transform hierarchy this is just one. if (Element.NumElements(FGeometryCollection::TransformGroup) == 0) { return INDEX_NONE; } int NumTransforms = NumElements(FTransformCollection::TransformGroup); int NumNewTransforms = Element.NumElements(FTransformCollection::TransformGroup); int32 StartTransformIndex = Super::AppendTransform(Element, TransformRoot); check(NumTransforms == StartTransformIndex); check(Element.NumElements(FGeometryCollection::FacesGroup) > 0); check(Element.NumElements(FGeometryCollection::VerticesGroup) > 0); int NumNewVertices = Element.NumElements(FGeometryCollection::VerticesGroup); const TManagedArray& ElementVertices = Element.Vertex; const TManagedArray& ElementNormals = Element.Normal; const TManagedArray& ElementColors = Element.Color; const TManagedArray& ElementTangentUs = Element.TangentU; const TManagedArray& ElementTangentVs = Element.TangentV; const TManagedArray& ElementBoneMap = Element.BoneMap; const TManagedArray& ElementIndices = Element.Indices; const TManagedArray& ElementVisible = Element.Visible; const TManagedArray& ElementMaterialIndex = Element.MaterialIndex; const TManagedArray& ElementMaterialID = Element.MaterialID; const TManagedArray& ElementInternal = Element.Internal; const TManagedArray& ElementTransformIndex = Element.TransformIndex; const TManagedArray& ElementBoundingBox = Element.BoundingBox; const TManagedArray& ElementInnerRadius = Element.InnerRadius; const TManagedArray& ElementOuterRadius = Element.OuterRadius; const TManagedArray& ElementVertexStart = Element.VertexStart; const TManagedArray& ElementVertexCount = Element.VertexCount; const TManagedArray& ElementFaceStart = Element.FaceStart; const TManagedArray& ElementFaceCount = Element.FaceCount; const TManagedArray& ElementBoneName = Element.BoneName; const TManagedArray& ElementSections = Element.Sections; const TManagedArray& ElementSimulationType = Element.SimulationType; const TManagedArray& ElementStatusFlags = Element.StatusFlags; const TManagedArray& ElementInitialDynamicState = Element.InitialDynamicState; const TManagedArray& ElementExemplarIndex = Element.ExemplarIndex; // --- TRANSFORM --- for (int TransformIdx = 0; TransformIdx < NumNewTransforms; TransformIdx++) { SimulationType[TransformIdx + StartTransformIndex] = ElementSimulationType[TransformIdx]; StatusFlags[TransformIdx + StartTransformIndex] = ElementStatusFlags[TransformIdx]; InitialDynamicState[TransformIdx + StartTransformIndex] = ElementInitialDynamicState[TransformIdx]; ExemplarIndex[TransformIdx + StartTransformIndex] = ElementExemplarIndex[TransformIdx]; } // --- VERTICES GROUP --- int NumVertices = NumElements(FGeometryCollection::VerticesGroup); int VerticesIndex = AddElements(NumNewVertices, FGeometryCollection::VerticesGroup); TManagedArray& Vertices = Vertex; TManagedArray& Normals = Normal; TManagedArray& Colors = Color; TManagedArray& TangentUs = TangentU; TManagedArray& TangentVs = TangentV; TManagedArray& BoneMaps = BoneMap; TManagedArray& FaceIndices = Indices; // Make sure we have enough UV layers to copy all the Element's UVs int32 ExistingUVLayers = NumUVLayers(); int32 ElementUVLayers = Element.NumUVLayers(); if (ElementUVLayers > ExistingUVLayers) { SetNumUVLayers(ElementUVLayers); } for (int vdx = 0; vdx < NumNewVertices; vdx++) { Vertices[VerticesIndex + vdx] = ElementVertices[vdx]; Normals[VerticesIndex + vdx] = ElementNormals[vdx]; for (int UVLayerIndex = 0; UVLayerIndex < ElementUVLayers; UVLayerIndex++) { ModifyUV(VerticesIndex + vdx, UVLayerIndex) = Element.GetUV(vdx, UVLayerIndex); } Colors[VerticesIndex + vdx] = ElementColors[vdx]; TangentUs[VerticesIndex + vdx] = ElementTangentUs[vdx]; TangentVs[VerticesIndex + vdx] = ElementTangentVs[vdx]; BoneMaps[VerticesIndex + vdx] = ElementBoneMap[vdx] + StartTransformIndex; } // --- FACES GROUP --- int NumIndices = NumElements(FGeometryCollection::FacesGroup); int NumNewIndices = ElementIndices.Num(); int IndicesIndex = AddElements(NumNewIndices, FGeometryCollection::FacesGroup); for (int32 tdx = 0; tdx < NumNewIndices; tdx++) { Indices[IndicesIndex + tdx] = FIntVector(VerticesIndex, VerticesIndex, VerticesIndex) + ElementIndices[tdx]; Visible[IndicesIndex + tdx] = ElementVisible[tdx]; Internal[IndicesIndex + tdx] = ElementInternal[tdx]; MaterialIndex[IndicesIndex + tdx] = ElementMaterialIndex[tdx]; // MaterialIDs need to be incremented MaterialID[IndicesIndex + tdx] = MaterialIDOffset + ElementMaterialID[tdx]; } // --- GEOMETRY GROUP --- int NumNewGeometryGroups = Element.NumElements(FGeometryCollection::GeometryGroup); NumNewGeometryGroups = (NumNewGeometryGroups == 0) ? 1 : NumNewGeometryGroups; // add one if Element input failed to create a geometry group int GeometryIndex = AddElements(NumNewGeometryGroups, FGeometryCollection::GeometryGroup); if (ElementTransformIndex.Num() > 0) { for (int32 tdx = 0; tdx < NumNewGeometryGroups; tdx++) { BoundingBox[GeometryIndex + tdx] = ElementBoundingBox[tdx]; InnerRadius[GeometryIndex + tdx] = ElementInnerRadius[tdx]; OuterRadius[GeometryIndex + tdx] = ElementOuterRadius[tdx]; FaceStart[GeometryIndex + tdx] = NumIndices + ElementFaceStart[tdx]; FaceCount[GeometryIndex + tdx] = ElementFaceCount[tdx]; VertexStart[GeometryIndex + tdx] = NumVertices + ElementVertexStart[tdx]; VertexCount[GeometryIndex + tdx] = ElementVertexCount[tdx]; TransformIndex[GeometryIndex + tdx] = BoneMaps[VertexStart[GeometryIndex + tdx]]; TransformToGeometryIndex[TransformIndex[GeometryIndex + tdx]] = GeometryIndex + tdx; } } else // Element input failed to create a geometry group { // Compute BoundingBox BoundingBox[GeometryIndex] = FBox(ForceInitToZero); TransformIndex[GeometryIndex] = BoneMaps[VerticesIndex]; VertexStart[GeometryIndex] = VerticesIndex; VertexCount[GeometryIndex] = NumNewVertices; FaceStart[GeometryIndex] = IndicesIndex; FaceCount[GeometryIndex] = NumNewIndices; TransformToGeometryIndex[TransformIndex[GeometryIndex]] = GeometryIndex; // Bounding Box for (int vdx = VerticesIndex; vdx < VerticesIndex+NumNewVertices; vdx++) { BoundingBox[GeometryIndex] += FVector(Vertices[vdx]); } // Find average particle // @todo (CenterOfMass) : This need to be the center of mass instead FVector3f Center(0); for (int vdx = VerticesIndex; vdx < VerticesIndex + NumNewVertices; vdx++) { Center += Vertices[vdx]; } if(NumNewVertices) { Center /= static_cast(NumNewVertices); } // // Inner/Outer Radius // { TManagedArray& InnerR = InnerRadius; TManagedArray& OuterR = OuterRadius; // init the radius arrays InnerR[GeometryIndex] = FLT_MAX; OuterR[GeometryIndex] = -FLT_MAX; // Vertices for (int vdx = VerticesIndex; vdx < VerticesIndex + NumNewVertices; vdx++) { float Delta = (Center - Vertices[vdx]).Size(); InnerR[GeometryIndex] = FMath::Min(InnerR[GeometryIndex], Delta); OuterR[GeometryIndex] = FMath::Max(OuterR[GeometryIndex], Delta); } // Inner/Outer centroid for (int fdx = IndicesIndex; fdx < IndicesIndex+NumNewIndices; fdx++) { FVector3f Centroid(0); for (int e = 0; e < 3; e++) { Centroid += Vertices[FaceIndices[fdx][e]]; } Centroid /= 3.0f; float Delta = (Center - Centroid).Size(); InnerR[GeometryIndex] = FMath::Min(InnerR[GeometryIndex], Delta); OuterR[GeometryIndex] = FMath::Max(OuterR[GeometryIndex], Delta); } // Inner/Outer edges for (int fdx = IndicesIndex; fdx < IndicesIndex + NumNewIndices; fdx++) { for (int e = 0; e < 3; e++) { int i = e, j = (e + 1) % 3; FVector3f Edge = Vertices[FaceIndices[fdx][i]] + 0.5*(Vertices[FaceIndices[fdx][j]] - Vertices[FaceIndices[fdx][i]]); float Delta = (Center - Edge).Size(); InnerR[GeometryIndex] = FMath::Min(InnerR[GeometryIndex], Delta); OuterR[GeometryIndex] = FMath::Max(OuterR[GeometryIndex], Delta); } } } } // --- MATERIAL GROUP --- // note for now, we rely on rebuilding mesh sections rather than passing them through. We know // that MaterialID is set correctly to correspond with the material index that will be rendered if (ReindexAllMaterials) { ReindexMaterials(); } return StartTransformIndex; } bool FGeometryCollection::AppendEmbeddedInstance(int32 InExemplarIndex, int32 InParentIndex, const FTransform& InTransform) { if (InParentIndex == INDEX_NONE || InParentIndex >= NumElements(FGeometryCollection::TransformGroup)) { return false; } // add a new embedded instance int32 Element = AddElements(1, FGeometryCollection::TransformGroup); Transform[Element] = FTransform3f(InTransform); Parent[Element] = InParentIndex; Children[InParentIndex].Add(Element); SimulationType[Element] = FST_None; ExemplarIndex[Element] = InExemplarIndex; TransformToGeometryIndex[Element] = INDEX_NONE; return true; } void FGeometryCollection::ReindexExemplarIndices(TArray& SortedRemovedIndices) { for (int32 Index = 0; Index < NumElements(TransformGroup); ++Index) { if (ExemplarIndex[Index] > INDEX_NONE) { for (int32 RemovalIndex = SortedRemovedIndices.Num()-1; RemovalIndex >= 0; --RemovalIndex) { if (ExemplarIndex[Index] == SortedRemovedIndices[RemovalIndex]) { ExemplarIndex[Index] = INDEX_NONE; break; } else if (ExemplarIndex[Index] > SortedRemovedIndices[RemovalIndex]) { ExemplarIndex[Index] -= (RemovalIndex + 1); break; } } } } } // Input assumes that each face has a materialID that corresponds with a render material // This will rebuild all mesh sections void FGeometryCollection::ReindexMaterials() { FGeometryCollection::ReindexMaterials(*this); } void FGeometryCollection::ReindexMaterials(FManagedArrayCollection& InCollection) { if (!InCollection.HasAttribute("MaterialID", FGeometryCollection::FacesGroup) || !InCollection.HasAttribute("Sections", FGeometryCollection::MaterialGroup) || !InCollection.HasAttribute("MaterialIndex", FGeometryCollection::FacesGroup)) { return; } TManagedArray& MaterialID = InCollection.ModifyAttribute("MaterialID", FGeometryCollection::FacesGroup); TManagedArray& Sections = InCollection.ModifyAttribute("Sections", FGeometryCollection::MaterialGroup); TManagedArray& MaterialIndex = InCollection.ModifyAttribute("MaterialIndex", FGeometryCollection::FacesGroup); FManagedArrayCollection::FProcessingParameters ProcessingParams; ProcessingParams.bDoValidation = false; // disable validation as this can be very costly ( only in editor ) // clear all sections TArray DelSections; GeometryCollectionAlgo::ContiguousArray(DelSections, InCollection.NumElements(FGeometryCollection::MaterialGroup)); InCollection.RemoveElements(FGeometryCollection::MaterialGroup, DelSections, ProcessingParams); DelSections.Reset(0); // rebuild sections // count the number of triangles for each material section, adding a new section if the material ID is higher than the current number of sections for (int FaceElement = 0, nf = InCollection.NumElements(FGeometryCollection::FacesGroup); FaceElement < nf ; ++FaceElement) { int32 Section = MaterialID[FaceElement]; while (Section + 1 > InCollection.NumElements(FGeometryCollection::MaterialGroup)) { // add a new material section int32 Element = InCollection.AddElements(1, FGeometryCollection::MaterialGroup); Sections[Element].MaterialID = Element; Sections[Element].FirstIndex = -1; Sections[Element].NumTriangles = 0; Sections[Element].MinVertexIndex = 0; Sections[Element].MaxVertexIndex = 0; } Sections[Section].NumTriangles++; } // fixup the section FirstIndex and MaxVertexIndex for (int SectionElement = 0; SectionElement < InCollection.NumElements(FGeometryCollection::MaterialGroup); SectionElement++) { if (SectionElement == 0) { Sections[SectionElement].FirstIndex = 0; } else { // Each subsequent section has an index that starts after the last one // note the NumTriangles*3 - this is because indices are sent to the renderer in a flat array Sections[SectionElement].FirstIndex = Sections[SectionElement - 1].FirstIndex + Sections[SectionElement - 1].NumTriangles * 3; } Sections[SectionElement].MaxVertexIndex = InCollection.NumElements(FGeometryCollection::VerticesGroup) - 1; // if a material group no longer has any triangles in it then add material section for removal if (Sections[SectionElement].NumTriangles == 0) { DelSections.Push(SectionElement); } } // remap indices so the materials appear to be grouped const int32 NumSections = InCollection.NumElements(FGeometryCollection::MaterialGroup); const int32 NumFaceElements = InCollection.NumElements(FGeometryCollection::FacesGroup); // since we know the number of triangles per section we can precompute the start offset of each sections // this avoid nested loop that result in N * M operations (N=sections M=faces) and we can process it in (N + M) operations instead TArray OffsetPerSection; OffsetPerSection.AddUninitialized(NumSections); int32 SectionOffset = 0; for (int Section = 0; Section < NumSections; Section++) { OffsetPerSection[Section] = SectionOffset; SectionOffset += Sections[Section].NumTriangles; } for (int FaceElement = 0; FaceElement < NumFaceElements; FaceElement++) { const int32 SectionID = MaterialID[FaceElement]; int32& SectionOffsetRef = OffsetPerSection[SectionID]; //ensure(MaterialIndex[SectionOffsetRef] == FaceElement); MaterialIndex[SectionOffsetRef++] = FaceElement; } // delete unused material sections if (DelSections.Num()) { InCollection.RemoveElements(FGeometryCollection::MaterialGroup, DelSections, ProcessingParams); } } TArray FGeometryCollection::BuildMeshSections(const TArray& InputIndices, const TArray& BaseMeshOriginalIndicesIndex, TArray& RetIndices) const { return FGeometryCollectionSection::BuildMeshSections(*this, InputIndices, BaseMeshOriginalIndicesIndex, RetIndices); } void FGeometryCollection::RemoveElements(const FName & Group, const TArray& SortedDeletionList, FProcessingParameters Params) { if (SortedDeletionList.Num()) { GeometryCollectionAlgo::ValidateSortedList(SortedDeletionList, NumElements(Group)); if (Group == FTransformCollection::TransformGroup) { // Find geometry connected to the transform TArray GeometryIndices; const TManagedArray& GeometryToTransformIndex = TransformIndex; for (int i = 0; i < GeometryToTransformIndex.Num(); i++) { if (SortedDeletionList.Contains(GeometryToTransformIndex[i])) { GeometryIndices.Add(i); } } RemoveGeometryElements(GeometryIndices); // Find convex hulls connected to transform FGeometryCollectionConvexUtility::RemoveConvexHulls(this, SortedDeletionList); Super::RemoveElements(Group, SortedDeletionList); } else if (Group == FGeometryCollection::GeometryGroup) { RemoveGeometryElements(SortedDeletionList); } else if( Group == FGeometryCollection::FacesGroup) { BuildFaceToGeometryMapping(); Super::RemoveElements(Group, SortedDeletionList); UpdateFaceGroupElements(); } else if (Group == FGeometryCollection::VerticesGroup) { BuildVertexToGeometryMapping(); Super::RemoveElements(Group, SortedDeletionList); UpdateVerticesGroupElements(); } else { Super::RemoveElements(Group, SortedDeletionList); } #if WITH_EDITOR if (Params.bDoValidation) { ensure(HasContiguousFaces()); ensure(HasContiguousVertices()); ensure(GeometryCollectionAlgo::HasValidGeometryReferences(this)); } #endif } } void FGeometryCollection::RemoveGeometryElements(const TArray& SortedGeometryIndicesToDelete) { if (SortedGeometryIndicesToDelete.Num()) { GeometryCollectionAlgo::ValidateSortedList(SortedGeometryIndicesToDelete, NumElements(FGeometryCollection::GeometryGroup)); // // Find transform connected to the geometry [But don't delete them] // TArray TransformIndices; for (int i = 0; i < SortedGeometryIndicesToDelete.Num(); i++) { int32 GeometryIndex = SortedGeometryIndicesToDelete[i]; if (0 <= GeometryIndex && GeometryIndex < TransformIndex.Num() && TransformIndex[GeometryIndex] != INDEX_NONE) { TransformIndices.Add(TransformIndex[GeometryIndex]); } } TArray Mask; // // Delete Vertices // GeometryCollectionAlgo::BuildLookupMask(TransformIndices, NumElements(FGeometryCollection::TransformGroup), Mask); TArray DelVertices; for (int32 Index = 0; Index < BoneMap.Num(); Index++) { if (BoneMap[Index] != Invalid && BoneMap[Index] < Mask.Num() && Mask[BoneMap[Index]]) { DelVertices.Add(Index); } } DelVertices.Sort(); // // Delete Faces // GeometryCollectionAlgo::BuildLookupMask(DelVertices, NumElements(FGeometryCollection::VerticesGroup), Mask); TManagedArray& Tris = Indices; TArray DelFaces; for (int32 Index = 0; Index < Tris.Num(); Index++) { const FIntVector & Face = Tris[Index]; for (int i = 0; i < 3; i++) { ensure(Face[i] < Mask.Num()); if (Mask[Face[i]]) { DelFaces.Add(Index); break; } } } DelFaces.Sort(); Super::RemoveElements(FGeometryCollection::GeometryGroup, SortedGeometryIndicesToDelete); Super::RemoveElements(FGeometryCollection::VerticesGroup, DelVertices); Super::RemoveElements(FGeometryCollection::FacesGroup, DelFaces); for (int32 DeleteIdx = SortedGeometryIndicesToDelete.Num()-1; DeleteIdx >=0; DeleteIdx--) { int32 GeoIndex = SortedGeometryIndicesToDelete[DeleteIdx]; for (int Idx = 0; Idx < TransformToGeometryIndex.Num(); Idx++) { if (TransformToGeometryIndex[Idx] > GeoIndex) { TransformToGeometryIndex[Idx]--; } else if (TransformToGeometryIndex[Idx] == GeoIndex) { TransformToGeometryIndex[Idx] = INDEX_NONE; } } } ReindexMaterials(); } } void FGeometryCollection::Empty() { for (const FName& GroupName : GroupNames()) { EmptyGroup(GroupName); } // re-initialize interfaces InitializeInterfaces(); SetNumUVLayers(1); } void FGeometryCollection::Reset() { Super::Reset(); Construct(); } void FGeometryCollection::InitializeInterfaces() { FGeometryCollectionConvexPropertiesInterface::InitializeInterface(); FGeometryCollectionProximityPropertiesInterface::InitializeInterface(); } void FGeometryCollection::ReorderElements(FName Group, const TArray& NewOrder) { if (Group == FTransformCollection::TransformGroup) { ReorderTransformElements(NewOrder); } else if (Group == FGeometryCollection::GeometryGroup) { ReorderGeometryElements(NewOrder); } else { Super::ReorderElements(Group, NewOrder); } } void FGeometryCollection::ReorderTransformElements(const TArray& NewOrder) { struct FTransformGeomPair { FTransformGeomPair(int32 InTransformIdx, int32 InGeomIdx) : TransformIdx(InTransformIdx), GeomIdx(InGeomIdx) {} int32 TransformIdx; int32 GeomIdx; bool operator<(const FTransformGeomPair& Other) const { return TransformIdx < Other.TransformIdx; } }; const int32 NumGeometries = TransformIndex.Num(); TArray Pairs; Pairs.Reserve(NumGeometries); for (int32 GeomIdx = 0; GeomIdx < NumGeometries; ++GeomIdx) { if (TransformIndex[GeomIdx] != INDEX_NONE) { Pairs.Emplace(NewOrder[TransformIndex[GeomIdx]], GeomIdx); } } Pairs.Sort(); TArray NewGeomOrder; NewGeomOrder.Reserve(NumGeometries); for (const FTransformGeomPair& Pair : Pairs) { NewGeomOrder.Add(Pair.GeomIdx); } ReorderGeometryElements(NewGeomOrder); for (int32 Index = 0; Index < Parent.Num(); Index++) { // remap the parents (-1 === Invalid ) if (Parent[Index] != -1) { Parent[Index] = NewOrder[Parent[Index]]; } // remap children TSet ChildrenCopy = Children[Index]; Children[Index].Empty(); for (int32 ChildID : ChildrenCopy) { if (ChildID >= 0) { Children[Index].Add(NewOrder[ChildID]); } else { Children[Index].Add(ChildID); //not remap so just leave as was } } } Super::ReorderElements(FTransformCollection::TransformGroup, NewOrder); } void FGeometryCollection::ReorderGeometryElements(const TArray& NewOrder) { const int32 NumGeometry = NumElements(GeometryGroup); check(NumGeometry == NewOrder.Num()); //Compute new order for vertices group and faces group TArray NewVertOrder; NewVertOrder.Reserve(NumElements(VerticesGroup)); int32 TotalVertOffset = 0; TArray NewFaceOrder; NewFaceOrder.Reserve(NumElements(FacesGroup)); int32 TotalFaceOffset = 0; for (int32 OldGeomIdx = 0; OldGeomIdx < NumGeometry; ++OldGeomIdx) { const int32 NewGeomIdx = NewOrder[OldGeomIdx]; //verts const int32 VertStartIdx = VertexStart[NewGeomIdx]; const int32 NumVerts = VertexCount[NewGeomIdx]; for (int32 VertIdx = VertStartIdx; VertIdx < VertStartIdx + NumVerts; ++VertIdx) { NewVertOrder.Add(VertIdx); } TotalVertOffset += NumVerts; //faces const int32 FaceStartIdx = FaceStart[NewGeomIdx]; const int32 NumFaces = FaceCount[NewGeomIdx]; for (int32 FaceIdx = FaceStartIdx; FaceIdx < FaceStartIdx + NumFaces; ++FaceIdx) { NewFaceOrder.Add(FaceIdx); } TotalFaceOffset += NumFaces; } //we must now reorder according to dependencies Super::ReorderElements(VerticesGroup, NewVertOrder); Super::ReorderElements(FacesGroup, NewFaceOrder); Super::ReorderElements(GeometryGroup, NewOrder); } bool FGeometryCollection::BuildVertexToGeometryMapping(bool InSaved ) { bool AttributeAdded = false; if (!FindAttribute("VertexToGeometryIndex", VerticesGroup)) { FConstructionParameters NotSaved = { FName(""), InSaved }; AddAttribute("VertexToGeometryIndex", VerticesGroup, NotSaved); AttributeAdded = true; } TManagedArray* VertexGeometryMap = FindAttribute("VertexToGeometryIndex", VerticesGroup); if (ensure(VertexGeometryMap)) { for (int32 GeometryIndex = NumElements(GeometryGroup) - 1; GeometryIndex >= 0; GeometryIndex--) { int VertexEnd = VertexStart[GeometryIndex] + VertexCount[GeometryIndex]; for (int32 VertexIndex = VertexStart[GeometryIndex]; VertexIndex < VertexEnd; VertexIndex++) { (*VertexGeometryMap)[VertexIndex] = GeometryIndex; } } } return AttributeAdded; } void FGeometryCollection::UpdateVerticesGroupElements() { // // Reset the VertexCount array // TManagedArray* VertexGeometryMap = FindAttribute("VertexToGeometryIndex", VerticesGroup); if(ensure(VertexGeometryMap)) { const int32 NumGeometryElements = NumElements(FGeometryCollection::GeometryGroup); for (int32 GeometryIndex = NumGeometryElements - 1; GeometryIndex >= 0; GeometryIndex--) { VertexStart[GeometryIndex] = INT_MAX; VertexCount[GeometryIndex] = 0; } const int32 NumVertexElements = NumElements(VerticesGroup); for (int32 VertexIndex = NumVertexElements - 1; VertexIndex >= 0; VertexIndex--) { VertexStart[ (*VertexGeometryMap)[VertexIndex] ] = FMath::Min(VertexStart[ (*VertexGeometryMap)[VertexIndex] ], VertexIndex); VertexCount[ (*VertexGeometryMap)[VertexIndex] ]++; } // final pass through to fix any groups with zero vertices, which will not have had their start/count updated by the above loop // note: technically if count is zero, start does not matter, but other code assumes the starts are in monotonic order int32 LastVertexStart = 0, LastVertexCount = 0; for (int32 GeometryIndex = 0; GeometryIndex < NumGeometryElements; ++GeometryIndex) { if (VertexCount[GeometryIndex] == 0) { VertexStart[GeometryIndex] = LastVertexStart + LastVertexCount; } else { LastVertexStart = VertexStart[GeometryIndex]; LastVertexCount = VertexCount[GeometryIndex]; } } } ensure(HasContiguousVertices()); } bool FGeometryCollection::BuildFaceToGeometryMapping(bool InSaved) { bool AttributeAdded = false; if (!FindAttribute("FaceToGeometryIndex", FacesGroup)) { FConstructionParameters NotSaved = { FName(""), InSaved }; AddAttribute("FaceToGeometryIndex", FacesGroup, NotSaved); AttributeAdded = true; } TManagedArray* FaceGeometryMap = FindAttribute("FaceToGeometryIndex", FacesGroup); if (ensure(FaceGeometryMap)) { for (int32 GeometryIndex = NumElements(GeometryGroup) - 1; GeometryIndex >= 0; GeometryIndex--) { int FaceEnd = FaceStart[GeometryIndex] + FaceCount[GeometryIndex]; for (int32 FaceIndex = FaceStart[GeometryIndex]; FaceIndex < FaceEnd; FaceIndex++) { (*FaceGeometryMap)[FaceIndex] = GeometryIndex; } } } return AttributeAdded; } void FGeometryCollection::UpdateFaceGroupElements() { // // Reset the FaceCount and NumFaces array // TManagedArray* FaceGeometryMap = FindAttribute("FaceToGeometryIndex", FacesGroup); if (ensure(FaceGeometryMap)) { const int32 NumGeometryElements = NumElements(FGeometryCollection::GeometryGroup); for (int32 GeometryIndex = NumGeometryElements - 1; GeometryIndex >= 0; GeometryIndex--) { FaceStart[GeometryIndex] = INT_MAX; FaceCount[GeometryIndex] = 0; } const int32 NumFaceElements = NumElements(FacesGroup); for (int32 FaceIndex = NumFaceElements - 1; FaceIndex >= 0; FaceIndex--) { FaceStart[(*FaceGeometryMap)[FaceIndex]] = FMath::Min(FaceStart[(*FaceGeometryMap)[FaceIndex]], FaceIndex); FaceCount[(*FaceGeometryMap)[FaceIndex]]++; } // final pass through to fix any groups with zero faces, which will not have had their start/count updated by the above loop // note: technically if count is zero, start does not matter, but other code assumes the starts are in monotonic order int32 LastFaceStart = 0, LastFaceCount = 0; for (int32 GeometryIndex = 0; GeometryIndex < NumGeometryElements; ++GeometryIndex) { if (FaceCount[GeometryIndex] == 0) { FaceStart[GeometryIndex] = LastFaceStart + LastFaceCount; } else { LastFaceStart = FaceStart[GeometryIndex]; LastFaceCount = FaceCount[GeometryIndex]; } } } ensure(HasContiguousVertices()); } void FGeometryCollection::UpdateGeometryVisibility(const TArray& NodeList, bool VisibilityState) { for (int32 Idx = 0; Idx < Visible.Num(); Idx++) { for (int32 Node : NodeList) { if (BoneMap[Indices[Idx][0]] == Node) { Visible[Idx] = VisibilityState; } } } } bool FGeometryCollection::HasVisibleGeometry() const { bool bHasVisibleGeometry = false; const TManagedArray& VisibleIndices = Visible; for (int32 fdx = 0; fdx < VisibleIndices.Num(); fdx++) { if (VisibleIndices[fdx]) { bHasVisibleGeometry = true; break; } } return bHasVisibleGeometry; } void FGeometryCollection::UpdateBoundingBox() { FGeometryCollection::UpdateBoundingBox(*this, /*bSkipCheck*/ true); } void FGeometryCollection::UpdateBoundingBox(FManagedArrayCollection& InCollection, bool bSkipCheck) { if (!bSkipCheck && ( !InCollection.HasAttribute("BoundingBox", FGeometryCollection::GeometryGroup) || !InCollection.HasAttribute("Vertex", FGeometryCollection::VerticesGroup) || !InCollection.HasAttribute("BoneMap", FGeometryCollection::VerticesGroup) || !InCollection.HasAttribute("TransformToGeometryIndex", FTransformCollection::TransformGroup))) { return; } TManagedArray& BoundingBox = InCollection.ModifyAttribute("BoundingBox", FGeometryCollection::GeometryGroup); const TManagedArray& Vertex = InCollection.GetAttribute("Vertex", FGeometryCollection::VerticesGroup); const TManagedArray& BoneMap = InCollection.GetAttribute("BoneMap", FGeometryCollection::VerticesGroup); const TManagedArray& TransformToGeometryIndex = InCollection.GetAttribute("TransformToGeometryIndex", FTransformCollection::TransformGroup); if (BoundingBox.Num()) { // Initialize BoundingBox for (int32 Idx = 0; Idx < BoundingBox.Num(); ++Idx) { BoundingBox[Idx].Init(); } // Compute BoundingBox for (int32 Idx = 0; Idx < Vertex.Num(); ++Idx) { const int32 TransformIndexValue = BoneMap[Idx]; if (TransformIndexValue != INDEX_NONE) { const int32 GeometryIndex = TransformToGeometryIndex[TransformIndexValue]; if (GeometryIndex != INDEX_NONE) { BoundingBox[GeometryIndex] += FVector(Vertex[Idx]); } } } } } FBoxSphereBounds FGeometryCollection::GetBoundingBox() const { TArray GlobalTransformArray; GeometryCollectionAlgo::GlobalMatrices(Transform, Parent, GlobalTransformArray); FBox CombinedBounds(EForceInit::ForceInit); for (int32 GeoIdx = 0; GeoIdx < NumElements(FGeometryCollection::GeometryGroup); ++GeoIdx) { int32 TransformIdx = TransformIndex[GeoIdx]; CombinedBounds += BoundingBox[GeoIdx].TransformBy(GlobalTransformArray[TransformIdx]); } FBoxSphereBounds CombinedBoxSphereBounds(CombinedBounds); return CombinedBoxSphereBounds; } void FGeometryCollection::Serialize(Chaos::FChaosArchive& Ar) { if (Ar.IsCooking()) { FGeometryCollectionConvexPropertiesInterface::CleanInterfaceForCook(); FGeometryCollectionProximityPropertiesInterface::CleanInterfaceForCook(); } Ar.UsingCustomVersion(FFortniteSeasonBranchObjectVersion::GUID); Ar.UsingCustomVersion(FFortniteMainBranchObjectVersion::GUID); Super::Serialize(Ar); if (Ar.IsLoading()) { // Versioning - correct assets that were saved before material sections were introduced if (NumElements(FGeometryCollection::MaterialGroup) == 0) { int SectionIndex = AddElements(1, FGeometryCollection::MaterialGroup); Sections[SectionIndex].MaterialID = 0; Sections[SectionIndex].FirstIndex = 0; Sections[SectionIndex].NumTriangles = Indices.Num(); Sections[SectionIndex].MinVertexIndex = 0; Sections[SectionIndex].MaxVertexIndex = Vertex.Num(); } // Recompute TransformToGroupIndex map int NumGeoms = NumElements(FGeometryCollection::GeometryGroup); int NumTransforms = NumElements(FGeometryCollection::TransformGroup); for (int32 Idx = 0; Idx < NumGeoms; ++Idx) { if (0<=TransformIndex[Idx]&&TransformIndex[Idx] < NumTransforms) { TransformToGeometryIndex[TransformIndex[Idx]] = Idx; } } // Add SimulationType attribute if (!(this->HasAttribute(FGeometryCollection::SimulationTypeAttribute, FTransformCollection::TransformGroup))) { TManagedArray& SimType = this->AddAttribute(FGeometryCollection::SimulationTypeAttribute, FTransformCollection::TransformGroup); for (int32 Idx = 0; Idx < NumTransforms; ++Idx) { SimType[Idx] = FGeometryCollection::ESimulationTypes::FST_None; } } // for backwards compatibility convert old BoneHierarchy struct into split out arrays enum ENodeFlags : uint32 { // additional flags FS_Clustered = 0x00000002, }; const TManagedArray* BoneHierarchyPtr = FindAttribute("BoneHierarchy", FTransformCollection::TransformGroup); if (BoneHierarchyPtr) { const TManagedArray& BoneHierarchy = *BoneHierarchyPtr; for (int Idx = 0; Idx < BoneHierarchy.Num(); Idx++) { if (!HasAttribute("Level", FGeometryCollection::TransformGroup)) { AddAttribute("Level", FGeometryCollection::TransformGroup); } TManagedArray& Level = ModifyAttribute("Level", FGeometryCollection::TransformGroup); Level[Idx] = BoneHierarchy[Idx].Level; SimulationType[Idx] = ESimulationTypes::FST_Rigid; StatusFlags[Idx] = FGeometryCollection::ENodeFlags::FS_None; if (BoneHierarchy[Idx].StatusFlags & ENodeFlags::FS_Clustered) { SimulationType[Idx] = ESimulationTypes::FST_Clustered; } if (BoneHierarchy[Idx].StatusFlags & FGeometryCollection::ENodeFlags::FS_RemoveOnFracture) { StatusFlags[Idx] |= FGeometryCollection::ENodeFlags::FS_RemoveOnFracture; } } } // Version 5 introduced accurate SimulationType tagging if (Version < 5) { UE_LOG(FGeometryCollectionLogging, Log, TEXT("GeometryCollection has inaccurate simulation type tags. Updating tags based on transform topology.")); const TManagedArray* SimulatableParticles = FindAttribute(FGeometryCollection::SimulatableParticlesAttribute, FTransformCollection::TransformGroup); TArray RigidChildren; RigidChildren.Init(false,NumElements(FTransformCollection::TransformGroup)); const TArray RecursiveOrder = GeometryCollectionAlgo::ComputeRecursiveOrder(*this); for (const int32 TransformGroupIndex : RecursiveOrder) { SimulationType[TransformGroupIndex] = ESimulationTypes::FST_None; if(!Children[TransformGroupIndex].Num()) { // leaf nodes if (TransformToGeometryIndex[TransformGroupIndex] > INDEX_NONE) { SimulationType[TransformGroupIndex] = ESimulationTypes::FST_Rigid; } if (SimulatableParticles && !(*SimulatableParticles)[TransformGroupIndex]) { SimulationType[TransformGroupIndex] = ESimulationTypes::FST_None; } } else { // interior nodes if (RigidChildren[TransformGroupIndex]) { SimulationType[TransformGroupIndex] = ESimulationTypes::FST_Clustered; } else if (TransformToGeometryIndex[TransformGroupIndex] > INDEX_NONE) { SimulationType[TransformGroupIndex] = ESimulationTypes::FST_Rigid; } } if (SimulationType[TransformGroupIndex] != ESimulationTypes::FST_None && Parent[TransformGroupIndex] != INDEX_NONE ) { RigidChildren[Parent[TransformGroupIndex]] = true; } } // Structure is conditioned, now considered up to date. Version = 5; } // Version 6 introduced the Exemplar Index array if (Version < 6) { ExemplarIndex.Fill(INDEX_NONE); // Structure is conditioned, now considered up to date. Version = 6; } if (Version < 7) { if (HasAttribute("TransformToConvexIndex", FTransformCollection::TransformGroup)) { TManagedArray TransformToConvexIndex = MoveTemp(ModifyAttribute("TransformToConvexIndex", FTransformCollection::TransformGroup)); RemoveAttribute("TransformToConvexIndex", FTransformCollection::TransformGroup); // if we don't already have the one-to-many version, convert the previous one-to-one mapping to the new format if (!HasAttribute("TransformToConvexIndices", FTransformCollection::TransformGroup)) { FManagedArrayCollection::FConstructionParameters ConvexDependency(FGeometryCollection::ConvexGroup); TManagedArray>& IndexSets = AddAttribute>("TransformToConvexIndices", FTransformCollection::TransformGroup, ConvexDependency); for (int32 TransformIdx = 0; TransformIdx < TransformToConvexIndex.Num(); TransformIdx++) { int32 ConvexIdx = TransformToConvexIndex[TransformIdx]; if (ConvexIdx != INDEX_NONE) { IndexSets[TransformIdx].Add(ConvexIdx); } } } } Version = 7; } // Version 8 introduced multiple UVs. if (Version < 8) { if (!HasAttribute("UVs", FGeometryCollection::VerticesGroup)) { // Note: As UVs is an external attribute that is always added by Construct, this should never be encountered UE_LOG(FGeometryCollectionLogging, Log, TEXT("GeometryCollection updated to multiple UV sets.")); AddAttribute>("UVs", FGeometryCollection::VerticesGroup); } TManagedArray>& MultipleUVs = ModifyAttribute>("UVs", FGeometryCollection::VerticesGroup); int32 MinUVLayers = 8; for (int32 VertIdx = 0; VertIdx < MultipleUVs.Num(); ++VertIdx) { MinUVLayers = FMath::Min(MultipleUVs[VertIdx].Num(), MinUVLayers); } if (MinUVLayers < 1) { for (int32 VertIdx = 0; VertIdx < MultipleUVs.Num(); ++VertIdx) { MultipleUVs[VertIdx].SetNum(1); } } if (const TManagedArray* SingleUV = FindAttribute("UV", FGeometryCollection::VerticesGroup)) { for (int32 VertIdx = 0; VertIdx < MultipleUVs.Num(); ++VertIdx) { if (SingleUV) { MultipleUVs[VertIdx][0] = (*SingleUV)[VertIdx]; } } RemoveAttribute("UV", FGeometryCollection::VerticesGroup); } // Structure is conditioned, now considered up to date. Version = 8; } // Version 9 fixed fully-invisible geometry and invalid exemplars left in the hierarchy (artifacts from old fracture + the Version 5 change) if (Version < 9) { auto HasVisibleFaces = [this](int32 TransformGroupIndex) -> bool { int32 GeometryIndex = TransformToGeometryIndex[TransformGroupIndex]; if (GeometryIndex == INDEX_NONE) { return false; } int32 Start = FaceStart[GeometryIndex], Count = FaceCount[GeometryIndex]; for (int32 FaceIndex = Start; FaceIndex < Start + Count; FaceIndex++) { if (Visible[FaceIndex]) { return true; } } return false; }; TArray InvalidTransforms, InvalidGeometry; TArray RigidChildren; RigidChildren.Init(false, NumElements(FTransformCollection::TransformGroup)); const TArray RecursiveOrder = GeometryCollectionAlgo::ComputeRecursiveOrder(*this); for (const int32 TransformGroupIndex : RecursiveOrder) { bool bHasExemplar = ExemplarIndex[TransformGroupIndex] > INDEX_NONE; bool bHasGeometry = HasVisibleFaces(TransformGroupIndex); bool bHasRigidChildren = RigidChildren[TransformGroupIndex]; bool bKeep = true; if (SimulationType[TransformGroupIndex] == ESimulationTypes::FST_None && !bHasExemplar) // handle exemplars with no exemplar (remove or convert to rigid) { if (bHasGeometry) { SimulationType[TransformGroupIndex] = ESimulationTypes::FST_Rigid; } else { InvalidTransforms.Add(TransformGroupIndex); bKeep = false; } } else if (SimulationType[TransformGroupIndex] == ESimulationTypes::FST_Rigid ) // handle internal rigids { if (bHasRigidChildren) { SimulationType[TransformGroupIndex] = ESimulationTypes::FST_Clustered; } } if (bKeep && SimulationType[TransformGroupIndex] != ESimulationTypes::FST_None && Parent[TransformGroupIndex] != INDEX_NONE) { RigidChildren[Parent[TransformGroupIndex]] = true; } } if (InvalidGeometry.Num() > 0) { UE_LOG(FGeometryCollectionLogging, Log, TEXT("Removing %d invalid, fully-invisible geometries from geometry collection."), InvalidGeometry.Num()); InvalidGeometry.Sort(); RemoveElements(GeometryGroup, InvalidGeometry); } if (InvalidTransforms.Num() > 0) { UE_LOG(FGeometryCollectionLogging, Log, TEXT("Removing %d invalid, empty transforms from geometry collection."), InvalidTransforms.Num()); InvalidTransforms.Sort(); RemoveElements(TransformGroup, InvalidTransforms); } Version = 9; } Chaos::Facades::FCollectionHierarchyFacade HierarchyFacade(*this); if (Ar.CustomVer(FFortniteSeasonBranchObjectVersion::GUID) < FFortniteSeasonBranchObjectVersion::ChaosGeometryCollectionSaveLevelsAttribute || !HierarchyFacade.HasLevelAttribute() || !HierarchyFacade.IsLevelAttributePersistent() ) { // Level attribute previously serialized with bSave = false, so was not serializing level data. // We now compute this during cook and need to serialize, so convert attribute to bSave = true // this is handled by the facade HierarchyFacade.GenerateLevelAttribute(); } if (Version < 10) { if (!HasAttribute(GeometryCollection::UV::UVLayerNames[0], VerticesGroup) || HasAttribute("UVs", VerticesGroup)) { TManagedArray>* OrigUVs = FindAttributeTyped>("UVs", VerticesGroup); if (OrigUVs) { // Note: We take the max of the num layers because in practice there have been some vertices with inconsistent layer counts // and it seems better to transfer all the data (with missing data left as zeros) than to potentially lose data int32 NumLayers = 1; for (int32 Idx = 0; Idx < OrigUVs->Num(); ++Idx) { NumLayers = FMath::Max((*OrigUVs)[Idx].Num(), NumLayers); } NumLayers = FMath::Min((int32)GeometryCollectionUV::MAX_NUM_UV_CHANNELS, NumLayers); // make sure we never exceed max layers SetNumUVLayers(NumLayers); for (int32 Idx = 0; Idx < OrigUVs->Num(); ++Idx) { TArray& VertexLayers = (*OrigUVs)[Idx]; int32 NumVertexLayers = FMath::Min(VertexLayers.Num(), NumLayers); for (int32 LayerIdx = 0; LayerIdx < NumVertexLayers; ++LayerIdx) { ModifyUV(Idx, LayerIdx) = VertexLayers[LayerIdx]; } for (int32 LayerIdx = NumVertexLayers; LayerIdx < NumLayers; ++LayerIdx) { ModifyUV(Idx, LayerIdx) = FVector2f(0, 0); // explicitly zero UVs of any missing layers } } RemoveAttribute("UVs", VerticesGroup); } else { SetNumUVLayers(1); } } Version = 10; } if (Ar.CustomVer(FFortniteMainBranchObjectVersion::GUID) < FFortniteMainBranchObjectVersion::ChaosGeometryCollectionInternalFacesAttribute) { if (!HasAttribute("Internal", FacesGroup)) { AddExternalAttribute("Internal", FacesGroup, Internal); } for (int32 FaceIdx = 0; FaceIdx < MaterialID.Num(); ++FaceIdx) { Internal[FaceIdx] = bool(MaterialID[FaceIdx] & 1); } } if (Ar.CustomVer(FFortniteMainBranchObjectVersion::GUID) < FFortniteMainBranchObjectVersion::ChaosGeometryCollectionConnectionEdgeGroup) { // Migrate old Connections TSet data to arrays of edge data // Note we intentionally do *not* use the facade here, as the migration is specific to how the data is at the current moment, and the facade may change w/ future data changes. if (TManagedArray>* Connections = FindAttribute>("Connections", TransformGroup)) { const FName ConnectionGroupName = "ConnectionEdge"; AddGroup(ConnectionGroupName); TManagedArray& Starts = AddAttribute("ConnectionEdgeStarts", ConnectionGroupName, FConstructionParameters(FTransformCollection::TransformGroup, true)); TManagedArray& Ends = AddAttribute("ConnectionEdgeEnds", ConnectionGroupName, FConstructionParameters(FTransformCollection::TransformGroup, true)); for (int32 TransformIdx = 0; TransformIdx < NumElements(TransformGroup); ++TransformIdx) { for (int32 NbrIdx : (*Connections)[TransformIdx]) { if (TransformIdx < NbrIdx) { int32 EdgeIdx = AddElements(1, ConnectionGroupName); Starts[EdgeIdx] = TransformIdx; Ends[EdgeIdx] = NbrIdx; } } } RemoveAttribute("Connections", TransformGroup); } } // Finally, make sure expected interfaces are initialized InitializeInterfaces(); } ensure(Version == GetLatestVersionNumber()); } bool FGeometryCollection::HasContiguousVertices( ) const { int32 NumTransforms = NumElements(FGeometryCollection::TransformGroup); TSet TransformIDs; TArray RecreatedBoneIds; RecreatedBoneIds.Init(-1, NumElements(FGeometryCollection::VerticesGroup)); int32 NumTransformIndex = TransformIndex.Num(); int32 NumBoneIndex = BoneMap.Num(); for (int32 GeometryIndex = 0; GeometryIndex < NumTransformIndex; GeometryIndex++) { // for each known geometry... int32 TransformIDFromGeometry = TransformIndex[GeometryIndex]; int32 StartIndex = VertexStart[GeometryIndex]; int32 NumVertices = VertexCount[GeometryIndex]; if (TransformIDs.Contains(TransformIDFromGeometry)) { return false; } TransformIDs.Add(TransformIDFromGeometry); int32 Counter = NumVertices; for (int32 BoneIndex = 0 ; BoneIndex < NumBoneIndex; ++BoneIndex) { // for each mapping from the vertex to the transform hierarchy ... if (StartIndex <= BoneIndex && BoneIndex < (StartIndex + NumVertices)) { // process just the specified range int32 TransformIDFromBoneMap = BoneMap[BoneIndex]; RecreatedBoneIds[BoneIndex] = BoneMap[BoneIndex]; if (TransformIDFromBoneMap < 0 || NumTransforms <= TransformIDFromBoneMap) { // not contiguous if index is out of range return false; } if (TransformIDFromGeometry != TransformIDFromBoneMap) { // not contiguous if indexing into a different transform return false; } --Counter; } } if (Counter) { return false; } } for (int32 Index = 0; Index < NumElements(FGeometryCollection::VerticesGroup); ++Index) { if (RecreatedBoneIds[Index] < 0) { return false; } } return true; } bool FGeometryCollection::HasContiguousFaces() const { int32 TotalNumTransforms = NumElements(FGeometryCollection::TransformGroup); // vertices int32 TotalNumVertices = NumElements(FGeometryCollection::VerticesGroup); int32 NumIndices = Indices.Num(); int32 NumTransformIndex = TransformIndex.Num(); for (int32 GeometryIndex = 0; GeometryIndex < NumTransformIndex ; ++GeometryIndex) { // for each known geometry... int32 TransformIDFromGeometry = TransformIndex[GeometryIndex]; int32 StartIndex = FaceStart[GeometryIndex]; int32 NumFaces = FaceCount[GeometryIndex]; int32 Counter = NumFaces; for (int32 FaceIndex = 0 ; FaceIndex < NumIndices; ++FaceIndex) { // for each mapping from the vertex to the transform hierarchy ... if (StartIndex <= FaceIndex && FaceIndex < (StartIndex + NumFaces)) { // process just the specified range for (int32 i = 0; i < 3; ++i) { int32 VertexIndex = Indices[FaceIndex][i]; if (VertexIndex < 0 || TotalNumVertices <= VertexIndex) { return false; } int32 TransformIDFromBoneMap = BoneMap[VertexIndex]; if (TransformIDFromBoneMap < 0 && TotalNumTransforms < TransformIDFromBoneMap) { // not contiguous if index is out of range return false; } if (TransformIDFromGeometry != TransformIDFromBoneMap) { // not contiguous if indexing into a different transform return false; } } --Counter; } } if (Counter) { return false; } } return true; } bool FGeometryCollection::HasContiguousRenderFaces() const { // validate all remapped indexes have their materials ID's grouped an in increasing order int LastMaterialID = 0; for (int32 IndexIdx = 0, NumElementsFaceGroup = NumElements(FGeometryCollection::FacesGroup); IndexIdx < NumElementsFaceGroup ; ++IndexIdx) { if (LastMaterialID > MaterialID[MaterialIndex[IndexIdx]]) return false; LastMaterialID = MaterialID[MaterialIndex[IndexIdx]]; } // check sections ranges do all point to a single material for (int32 MaterialIdx = 0, NumElementsMaterialGroup = NumElements(FGeometryCollection::MaterialGroup) ; MaterialIdx < NumElementsMaterialGroup ; ++MaterialIdx) { int first = Sections[MaterialIdx].FirstIndex / 3; int last = first + Sections[MaterialIdx].NumTriangles; for (int32 IndexIdx = first; IndexIdx < last; ++IndexIdx) { if ( (MaterialID[MaterialIndex[IndexIdx]]) != MaterialIdx ) return false; } } return true; } int32 FGeometryCollection::NumUVLayers() const { return GeometryCollection::UV::GetNumUVLayers(*this); } bool FGeometryCollection::SetNumUVLayers(int32 NumLayers) { return GeometryCollection::UV::SetNumUVLayers(*this, NumLayers); } bool FGeometryCollection::IsVisible(int32 Element) const { if (!IsRigid(Element)) { return false; } if (TransformToGeometryIndex[Element] > INDEX_NONE) { int32 CurrFace = FaceStart[TransformToGeometryIndex[Element]]; for (int32 FaceOffset = 0; FaceOffset < FaceCount[TransformToGeometryIndex[Element]]; ++FaceOffset) { if (Visible[CurrFace + FaceOffset]) { return true; } } } return false;; } FGeometryCollection* FGeometryCollection::NewGeometryCollection(const TArray& RawVertexArray, const TArray& RawIndicesArray, bool ReverseVertexOrder, const FGeometryCollectionDefaults RawDefaults) { FGeometryCollection* Collection = new FGeometryCollection(RawDefaults); FGeometryCollection::Init(Collection, RawVertexArray, RawIndicesArray, ReverseVertexOrder); return Collection; } void FGeometryCollection::Init(FGeometryCollection* Collection, const TArray& RawVertexArray, const TArray& RawIndicesArray, bool ReverseVertexOrder) { if (Collection) { int NumNewVertices = RawVertexArray.Num() / 3; int VerticesIndex = Collection->AddElements(NumNewVertices, FGeometryCollection::VerticesGroup); int NumNewIndices = RawIndicesArray.Num() / 3; int IndicesIndex = Collection->AddElements(NumNewIndices, FGeometryCollection::FacesGroup); int NumNewParticles = 1; // 1 particle for this geometry structure int ParticlesIndex = Collection->AddElements(NumNewParticles, FGeometryCollection::TransformGroup); TManagedArray& Vertices = Collection->Vertex; TManagedArray& Normals = Collection->Normal; TManagedArray& TangentU = Collection->TangentU; TManagedArray& TangentV = Collection->TangentV; TManagedArray& Colors = Collection->Color; TManagedArray& Indices = Collection->Indices; TManagedArray& Visible = Collection->Visible; TManagedArray& MaterialID = Collection->MaterialID; TManagedArray& MaterialIndex = Collection->MaterialIndex; TManagedArray& Internal = Collection->Internal; TManagedArray& Transform = Collection->Transform; TManagedArray& BoneMap = Collection->BoneMap; Collection->SetNumUVLayers(1); // set the vertex information TManagedArray* UV0 = Collection->FindUVLayer(0); for (int32 Idx = 0; Idx < NumNewVertices; ++Idx) { Vertices[Idx] = FVector3f(RawVertexArray[3 * Idx], RawVertexArray[3 * Idx + 1], RawVertexArray[3 * Idx + 2]); (*UV0)[Idx] = FVector2f::ZeroVector; Colors[Idx] = Collection->Defaults.DefaultVertexColor; BoneMap[Idx] = 0; } Transform[0] = FTransform3f(FVector3f(0.f,0.f,0.f)); // set the index information TArray FaceNormals; FaceNormals.SetNum(NumNewIndices); for (int32 Idx = 0; Idx < NumNewIndices; ++Idx) { int32 VertexIdx1, VertexIdx2, VertexIdx3; if (!ReverseVertexOrder) { VertexIdx1 = RawIndicesArray[3 * Idx]; VertexIdx2 = RawIndicesArray[3 * Idx + 1]; VertexIdx3 = RawIndicesArray[3 * Idx + 2]; } else { VertexIdx1 = RawIndicesArray[3 * Idx]; VertexIdx2 = RawIndicesArray[3 * Idx + 2]; VertexIdx3 = RawIndicesArray[3 * Idx + 1]; } Indices[Idx] = FIntVector(VertexIdx1, VertexIdx2, VertexIdx3); Visible[Idx] = true; Internal[Idx] = false; MaterialID[Idx] = 0; MaterialIndex[Idx] = Idx; const FVector3f Edge1 = Vertices[VertexIdx1] - Vertices[VertexIdx2]; const FVector3f Edge2 = Vertices[VertexIdx1] - Vertices[VertexIdx3]; FaceNormals[Idx] = (Edge2 ^ Edge1).GetSafeNormal(); } // Compute vertexNormals TArray VertexNormals; VertexNormals.SetNum(NumNewVertices); for (int32 Idx = 0; Idx < NumNewVertices; ++Idx) { VertexNormals[Idx] = FVector3f(0.f, 0.f, 0.f); } for (int32 Idx = 0; Idx < NumNewIndices; ++Idx) { VertexNormals[Indices[Idx][0]] += FaceNormals[Idx]; VertexNormals[Indices[Idx][1]] += FaceNormals[Idx]; VertexNormals[Indices[Idx][2]] += FaceNormals[Idx]; } for (int32 Idx = 0; Idx < NumNewVertices; ++Idx) { Normals[Idx] = (VertexNormals[Idx] / 3.f).GetSafeNormal(); } for (int IndexIdx = 0; IndexIdx < NumNewIndices; IndexIdx++) { FIntVector Tri = Indices[IndexIdx]; for (int idx = 0; idx < 3; idx++) { const FVector3f Normal = Normals[Tri[idx]]; const FVector3f Edge = (Vertices[Tri[(idx + 1) % 3]] - Vertices[Tri[idx]]); TangentU[Tri[idx]] = (Edge ^ Normal).GetSafeNormal(); TangentV[Tri[idx]] = (Normal ^ TangentU[Tri[idx]]).GetSafeNormal(); } } // Build the Geometry Group GeometryCollection::AddGeometryProperties(Collection); // add a material section TManagedArray& Sections = Collection->Sections; int Element = Collection->AddElements(1, FGeometryCollection::MaterialGroup); Sections[Element].MaterialID = 0; Sections[Element].FirstIndex = 0; Sections[Element].NumTriangles = Indices.Num(); Sections[Element].MinVertexIndex = 0; Sections[Element].MaxVertexIndex = Vertices.Num() - 1; } } void FGeometryCollection::WriteDataToHeaderFile(const FString &Name, const FString &Path) { using namespace std; static const FString DataFilePath = "D:"; FString FullPath = (Path.IsEmpty() || Path.Equals(TEXT("None"))) ? DataFilePath : Path; FullPath.RemoveFromEnd("\\"); FullPath += "\\" + Name + ".h"; ofstream DataFile; DataFile.open(string(TCHAR_TO_UTF8(*FullPath))); DataFile << "// Copyright Epic Games, Inc. All Rights Reserved." << endl << endl; DataFile << "#pragma once" << endl << endl; DataFile << "class " << TCHAR_TO_UTF8(*Name) << endl; DataFile << "{" << endl; DataFile << "public:" << endl; DataFile << " " << TCHAR_TO_UTF8(*Name) << "();" << endl; DataFile << " ~" << TCHAR_TO_UTF8(*Name) << "() {};" << endl << endl; DataFile << " static const TArray RawVertexArray;" << endl; DataFile << " static const TArray RawIndicesArray;" << endl; DataFile << " static const TArray RawBoneMapArray;" << endl; DataFile << " static const TArray RawTransformArray;" << endl; DataFile << " static const TArray RawParentArray;" << endl; DataFile << " static const TArray> RawChildrenArray;" << endl; DataFile << " static const TArray RawSimulationTypeArray;" << endl; DataFile << " static const TArray RawStatusFlagsArray;" << endl; DataFile << "};" << endl << endl; DataFile << "const TArray " << TCHAR_TO_UTF8(*Name) << "::RawVertexArray = {" << endl; int32 NumVertices = NumElements(FGeometryCollection::VerticesGroup); const TManagedArray& VertexArray = Vertex; for (int32 IdxVertex = 0; IdxVertex < NumVertices; ++IdxVertex) { DataFile << " " << VertexArray[IdxVertex].X << ", " << VertexArray[IdxVertex].Y << ", " << VertexArray[IdxVertex].Z << ", " << endl; } DataFile << "};" << endl << endl; DataFile << "const TArray " << TCHAR_TO_UTF8(*Name) << "::RawIndicesArray = {" << endl; int32 NumFaces = NumElements(FGeometryCollection::FacesGroup); for (int32 IdxFace = 0; IdxFace < NumFaces; ++IdxFace) { DataFile << " " << Indices[IdxFace].X << ", " << Indices[IdxFace].Y << ", " << Indices[IdxFace].Z << ", " << endl; } DataFile << "};" << endl << endl; DataFile << "const TArray " << TCHAR_TO_UTF8(*Name) << "::RawBoneMapArray = {" << endl; for (int32 IdxVertex = 0; IdxVertex < NumVertices; ++IdxVertex) { DataFile << " " << BoneMap[IdxVertex] << ", " << endl; } DataFile << "};" << endl << endl; DataFile << "const TArray " << TCHAR_TO_UTF8(*Name) << "::RawTransformArray = {" << endl; int32 NumTransforms = NumElements(FGeometryCollection::TransformGroup); const TManagedArray& TransformArray = Transform; for (int32 IdxTransform = 0; IdxTransform < NumTransforms; ++IdxTransform) { FQuat4f Rotation = TransformArray[IdxTransform].GetRotation(); FVector3f Translation = TransformArray[IdxTransform].GetTranslation(); FVector3f Scale3D = TransformArray[IdxTransform].GetScale3D(); DataFile << " FTransform(FQuat(" << Rotation.X << ", " << Rotation.Y << ", " << Rotation.Z << ", " << Rotation.W << "), " << "FVector(" << Translation.X << ", " << Translation.Y << ", " << Translation.Z << "), " << "FVector(" << Scale3D.X << ", " << Scale3D.Y << ", " << Scale3D.Z << ")), " << endl; } DataFile << "};" << endl << endl; // Write BoneHierarchy array DataFile << "const TArray " << TCHAR_TO_UTF8(*Name) << "::RawBoneHierarchyArray = {" << endl; for (int32 IdxTransform = 0; IdxTransform < NumTransforms; ++IdxTransform) { DataFile << " FGeometryCollectionBoneNode(" << Parent[IdxTransform] << ", " << SimulationType[IdxTransform] << "), " << StatusFlags[IdxTransform] << "), " << endl; } DataFile << "};" << endl << endl; DataFile.close(); } void FGeometryCollection::WriteDataToOBJFile(const FString &Name, const FString &Path, const bool WriteTopology, const bool WriteAuxStructures) { using namespace std; static const FString DataFilePath = "D:"; int32 NumVertices = NumElements(FGeometryCollection::VerticesGroup); int32 NumFaces = NumElements(FGeometryCollection::FacesGroup); TArray GlobalTransformArray; GeometryCollectionAlgo::GlobalMatrices(Transform, Parent, GlobalTransformArray); TArray VertexInWorldArray; VertexInWorldArray.SetNum(NumVertices); for (int32 IdxVertex = 0; IdxVertex < NumVertices; ++IdxVertex) { FTransform LocalTransform = GlobalTransformArray[BoneMap[IdxVertex]]; FVector3f VertexInWorld = (FVector3f)LocalTransform.TransformPosition((FVector)Vertex[IdxVertex]); VertexInWorldArray[IdxVertex] = VertexInWorld; } ofstream DataFile; if (WriteTopology) { FString FullPath = (Path.IsEmpty() || Path.Equals(TEXT("None"))) ? DataFilePath : Path; FullPath.RemoveFromEnd("\\"); FullPath += "\\" + Name + ".obj"; DataFile.open(string(TCHAR_TO_UTF8(*FullPath))); DataFile << "# File exported from Unreal Engine" << endl; DataFile << "# " << NumVertices << " points" << endl; DataFile << "# " << NumVertices * 3 << " vertices" << endl; DataFile << "# " << NumFaces << " primitives" << endl; DataFile << "g" << endl; for (int32 IdxVertex = 0; IdxVertex < NumVertices; ++IdxVertex) { DataFile << "v " << VertexInWorldArray[IdxVertex].X << " " << VertexInWorldArray[IdxVertex].Y << " " << VertexInWorldArray[IdxVertex].Z << endl; } DataFile << "g" << endl; // FaceIndex in the OBJ format starts with 1 for (int32 IdxFace = 0; IdxFace < NumFaces; ++IdxFace) { DataFile << "f " << Indices[IdxFace].X + 1 << " " << Indices[IdxFace].Z + 1 << " " << Indices[IdxFace].Y + 1 << endl; } DataFile << endl; DataFile.close(); } if(WriteAuxStructures && HasAttribute("VertexVisibility", FGeometryCollection::VerticesGroup)) { FString FullPath = (Path.IsEmpty() || Path.Equals(TEXT("None"))) ? DataFilePath : Path; FullPath.RemoveFromEnd("\\"); FullPath += "\\" + Name + "_VertexVisibility.obj"; DataFile.open(string(TCHAR_TO_UTF8(*FullPath))); DataFile << "# Vertex Visibility - vertices whose visibility flag are true" << endl; const TManagedArray& VertexVisibility = ModifyAttribute("VertexVisibility", FGeometryCollection::VerticesGroup); int num = 0; for (int32 IdxVertex = 0; IdxVertex < NumVertices; ++IdxVertex) { if (VertexVisibility[IdxVertex]) { num++; } } DataFile << "# " << num << " Vertices" << endl; DataFile << "g" << endl; for (int32 IdxVertex = 0; IdxVertex < NumVertices; ++IdxVertex) { if(VertexVisibility[IdxVertex]) { DataFile << "v " << VertexInWorldArray[IdxVertex].X << " " << VertexInWorldArray[IdxVertex].Y << " " << VertexInWorldArray[IdxVertex].Z << endl; } } DataFile << endl; DataFile.close(); } } FGeometryCollection* FGeometryCollection::NewGeometryCollection(const TArray& RawVertexArray, const TArray& RawIndicesArray, const TArray& RawBoneMapArray, const TArray& RawTransformArray, const TManagedArray& RawLevelArray, const TManagedArray& RawParentArray, const TManagedArray>& RawChildrenArray, const TManagedArray& RawSimulationTypeArray, const TManagedArray& RawStatusFlagsArray, const FGeometryCollectionDefaults RawDefaults) { FGeometryCollection* RestCollection = new FGeometryCollection(RawDefaults); int NumNewVertices = RawVertexArray.Num() / 3; int VerticesIndex = RestCollection->AddElements(NumNewVertices, FGeometryCollection::VerticesGroup); int NumNewIndices = RawIndicesArray.Num() / 3; int IndicesIndex = RestCollection->AddElements(NumNewIndices, FGeometryCollection::FacesGroup); TManagedArray& Vertices = RestCollection->Vertex; TManagedArray& Normals = RestCollection->Normal; TManagedArray& TangentU = RestCollection->TangentU; TManagedArray& TangentV = RestCollection->TangentV; TManagedArray& Colors = RestCollection->Color; TManagedArray& BoneMap = RestCollection->BoneMap; TManagedArray& Indices = RestCollection->Indices; TManagedArray& Visible = RestCollection->Visible; TManagedArray& MaterialID = RestCollection->MaterialID; TManagedArray& MaterialIndex = RestCollection->MaterialIndex; TManagedArray& Internal = RestCollection->Internal; TManagedArray& Transform = RestCollection->Transform; TManagedArray& Parent = RestCollection->Parent; TManagedArray>& Children = RestCollection->Children; TManagedArray& SimulationType = RestCollection->SimulationType; TManagedArray& StatusFlags = RestCollection->StatusFlags; TManagedArray& InitialDynamicState = RestCollection->InitialDynamicState; RestCollection->SetNumUVLayers(1); // set the vertex information TManagedArray* UV0 = RestCollection->FindUVLayer(0); for (int32 Idx = 0; Idx < NumNewVertices; ++Idx) { Vertices[Idx] = FVector3f(RawVertexArray[3 * Idx], RawVertexArray[3 * Idx + 1], RawVertexArray[3 * Idx + 2]); BoneMap[Idx] = RawBoneMapArray[Idx]; (*UV0)[Idx] = FVector2f::ZeroVector; Colors[Idx] = RestCollection->Defaults.DefaultVertexColor; } // Transforms int NumNewTransforms = RawTransformArray.Num(); // 1 particle for this geometry structure int TransformIndex = RestCollection->AddElements(NumNewTransforms, FGeometryCollection::TransformGroup); for (int32 Idx = 0; Idx < NumNewTransforms; ++Idx) { Transform[Idx] = FTransform3f(RawTransformArray[Idx]); Transform[Idx].NormalizeRotation(); Parent[Idx] = RawParentArray[Idx]; if (RawChildrenArray.Num() > 0) { Children[Idx] = RawChildrenArray[Idx]; } SimulationType[Idx] = RawSimulationTypeArray[Idx]; StatusFlags[Idx] = RawStatusFlagsArray[Idx]; for (int32 Idx1 = 0; Idx1 < NumNewTransforms; ++Idx1) { if (RawParentArray[Idx1] == Idx) { Children[Idx].Add(Idx1); } } } // set the index information TArray FaceNormals; FaceNormals.SetNum(NumNewIndices); for (int32 Idx = 0; Idx < NumNewIndices; ++Idx) { int32 VertexIdx1, VertexIdx2, VertexIdx3; VertexIdx1 = RawIndicesArray[3 * Idx]; VertexIdx2 = RawIndicesArray[3 * Idx + 1]; VertexIdx3 = RawIndicesArray[3 * Idx + 2]; Indices[Idx] = FIntVector(VertexIdx1, VertexIdx2, VertexIdx3); Visible[Idx] = true; Internal[Idx] = false; MaterialID[Idx] = 0; MaterialIndex[Idx] = Idx; const FVector3f Edge1 = Vertices[VertexIdx1] - Vertices[VertexIdx2]; const FVector3f Edge2 = Vertices[VertexIdx1] - Vertices[VertexIdx3]; FaceNormals[Idx] = (Edge2 ^ Edge1).GetSafeNormal(); } // Compute vertexNormals TArray VertexNormals; VertexNormals.SetNum(NumNewVertices); for (int32 Idx = 0; Idx < NumNewVertices; ++Idx) { VertexNormals[Idx] = FVector3f(0.f, 0.f, 0.f); } for (int32 Idx = 0; Idx < NumNewIndices; ++Idx) { VertexNormals[Indices[Idx][0]] += FaceNormals[Idx]; VertexNormals[Indices[Idx][1]] += FaceNormals[Idx]; VertexNormals[Indices[Idx][2]] += FaceNormals[Idx]; } for (int32 Idx = 0; Idx < NumNewVertices; ++Idx) { Normals[Idx] = (VertexNormals[Idx] / 3.f).GetSafeNormal(); } for (int IndexIdx = 0; IndexIdx < NumNewIndices; IndexIdx++) { FIntVector Tri = Indices[IndexIdx]; for (int idx = 0; idx < 3; idx++) { const FVector3f Normal = Normals[Tri[idx]]; const FVector3f Edge = (Vertices[Tri[(idx + 1) % 3]] - Vertices[Tri[idx]]); TangentU[Tri[idx]] = (Edge ^ Normal).GetSafeNormal(); TangentV[Tri[idx]] = (Normal ^ TangentU[Tri[idx]]).GetSafeNormal(); } } // Build the Geometry Group GeometryCollection::AddGeometryProperties(RestCollection); FGeometryCollectionProximityUtility ProximityUtility(RestCollection); ProximityUtility.UpdateProximity(); // add a material section TManagedArray& Sections = RestCollection->Sections; int Element = RestCollection->AddElements(1, FGeometryCollection::MaterialGroup); Sections[Element].MaterialID = 0; Sections[Element].FirstIndex = 0; Sections[Element].NumTriangles = Indices.Num(); Sections[Element].MinVertexIndex = 0; Sections[Element].MaxVertexIndex = Vertices.Num() - 1; return RestCollection; } TArray> FGeometryCollection::ConnectionGraph() { int32 NumTransforms = NumElements(TransformGroup); TArray> Connectivity; Connectivity.Init(TArray(), NumTransforms); TArray GlobalMatrices; GeometryCollectionAlgo::GlobalMatrices(Transform, Parent, GlobalMatrices); TArray Pts; TMap< int32,int32> Remap; for (int32 TransformGroupIndex = 0; TransformGroupIndex < NumTransforms; ++TransformGroupIndex) { if (IsGeometry(TransformGroupIndex)) { Remap.Add(Pts.Num(), TransformGroupIndex); Pts.Add(FVector(GlobalMatrices[TransformGroupIndex].GetTranslation())); } } TArray> Neighbors; VoronoiNeighbors(Pts, Neighbors); for (int i = 0; i < Neighbors.Num(); i++) { for (int j = 0; j < Neighbors[i].Num(); j++) { Connectivity[Remap[i]].Add(Remap[Neighbors[i][j]]); } } return Connectivity; } void FGeometryCollection::UpdateOldAttributeNames() { // Faces Group int32 NumOldGeometryElements = this->NumElements("Geometry"); check(!this->NumElements(FGeometryCollection::FacesGroup)); this->AddElements(NumOldGeometryElements, FGeometryCollection::FacesGroup); TArray GeometryAttributes = this->AttributeNames("Geometry"); const TManagedArray & OldIndices = this->GetAttribute("Indices", "Geometry"); const TManagedArray & OldVisible = this->GetAttribute("Visible", "Geometry"); const TManagedArray & OldMaterialIndex = this->GetAttribute("MaterialIndex", "Geometry"); const TManagedArray & OldMaterialID = this->GetAttribute("MaterialID", "Geometry"); for (int i = NumOldGeometryElements - 1; 0 <= i; i--) { this->Indices[i] = OldIndices[i]; this->Visible[i] = OldVisible[i]; this->MaterialIndex[i] = OldMaterialIndex[i]; this->MaterialID[i] = OldMaterialID[i]; } this->RemoveAttribute("Indices", "Geometry"); this->RemoveAttribute("Visible", "Geometry"); this->RemoveAttribute("MaterialIndex", "Geometry"); this->RemoveAttribute("MaterialID", "Geometry"); // reset the geometry group TArray DelArray; GeometryCollectionAlgo::ContiguousArray(DelArray, NumOldGeometryElements); FManagedArrayCollection::FProcessingParameters Params; Params.bDoValidation = false; Params.bReindexDependentAttibutes = false; Super::RemoveElements("Geometry", DelArray, Params); // Geometry Group TArray StructureAttributes = this->AttributeNames("Structure"); int32 NumOldStructureElements = this->NumElements("Structure"); check(!this->NumElements(FGeometryCollection::GeometryGroup)); this->AddElements(NumOldStructureElements, FGeometryCollection::GeometryGroup); const TManagedArray & OldTransformIndex = this->GetAttribute("TransformIndex", "Structure"); const TManagedArray & OldBoundingBox = this->GetAttribute("BoundingBox", "Structure"); const TManagedArray & OldInnerRadius = this->GetAttribute("InnerRadius", "Structure"); const TManagedArray & OldOuterRadius = this->GetAttribute("OuterRadius", "Structure"); const TManagedArray & OldVertexStart = this->GetAttribute("VertexStart", "Structure"); const TManagedArray & OldVertexCount = this->GetAttribute("VertexCount", "Structure"); const TManagedArray & OldFaceStart = this->GetAttribute("FaceStart", "Structure"); const TManagedArray & OldFaceCount = this->GetAttribute("FaceCount", "Structure"); for (int i = NumOldStructureElements - 1; 0 <= i; i--) { this->TransformIndex[i] = OldTransformIndex[i]; this->BoundingBox[i] = OldBoundingBox[i]; this->InnerRadius[i] = OldInnerRadius[i]; this->OuterRadius[i] = OldOuterRadius[i]; this->VertexStart[i] = OldVertexStart[i]; this->VertexCount[i] = OldVertexCount[i]; this->FaceStart[i] = OldFaceStart[i]; this->FaceCount[i] = OldFaceCount[i]; } this->RemoveGroup("Structure"); } TArray FGeometryCollection::TransformSelectionToGeometryIndices(const TArray& Transforms) { TArray Geometries; for (const int32& TransformIdx : Transforms) { if (TransformToGeometryIndex.IsValidIndex(TransformIdx) && IsGeometry(TransformIdx)) { Geometries.Add(TransformToGeometryIndex[TransformIdx]); } } return Geometries; }