// Copyright Epic Games, Inc. All Rights Reserved. #include "GeometryCollection/GeometryCollectionEngineConversion.h" #include "AssetRegistry/AssetRegistryModule.h" #include "AnimationRuntime.h" #include "Async/ParallelFor.h" #include "Components/SkeletalMeshComponent.h" #include "Components/StaticMeshComponent.h" #include "DynamicMesh/DynamicMesh3.h" #include "DynamicMesh/DynamicMeshAttributeSet.h" #include "Engine/Selection.h" #include "Engine/SkeletalMesh.h" #include "Engine/SkinnedAssetCommon.h" #include "Engine/StaticMesh.h" #include "GameFramework/Actor.h" #include "GeometryCollection/Facades/CollectionInstancedMeshFacade.h" #include "GeometryCollection/Facades/CollectionTransformFacade.h" #include "GeometryCollection/Facades/CollectionTransformSourceFacade.h" #include "GeometryCollection/GeometryCollection.h" #include "GeometryCollection/GeometryCollectionActor.h" #include "GeometryCollection/GeometryCollectionAlgo.h" #include "GeometryCollection/GeometryCollectionComponent.h" #include "GeometryCollection/GeometryCollectionClusteringUtility.h" #include "GeometryCollection/GeometryCollectionEngineUtility.h" #include "GeometryCollection/GeometryCollectionUtility.h" #include "GeometryCollectionProxyData.h" #include "IndexTypes.h" #include "Logging/LogMacros.h" #include "MaterialDomain.h" #include "Materials/Material.h" #include "MeshDescription.h" #include "MeshDescriptionBuilder.h" #include "MeshDescriptionToDynamicMesh.h" #include "Misc/ScopedSlowTask.h" #include "Physics/Experimental/ChaosInterfaceUtils.h" #include "PhysicsEngine/BodySetup.h" #include "Rendering/SkeletalMeshRenderData.h" #include "SkeletalMeshAttributes.h" #include "StaticMeshAttributes.h" #include "StaticMeshOperations.h" #include "VertexConnectedComponents.h" #include "Util/ColorConstants.h" DEFINE_LOG_CATEGORY_STATIC(UGeometryCollectionConversionLogging, Log, All); #define LOCTEXT_NAMESPACE "GeometryCollectionConversion" struct FUniqueVertex { FVector3f Normal; FVector3f Tangent; TArray UVs; bool operator==(const FUniqueVertex& Other) const { if (this->UVs.Num() != Other.UVs.Num()) { return false; } bool bEquality = true; bEquality &= (this->Normal == Other.Normal); bEquality &= (this->Tangent == Other.Tangent); for (int32 UVLayerIdx = 0; UVLayerIdx < UVs.Num(); ++UVLayerIdx) { bEquality &= (this->UVs[UVLayerIdx] == Other.UVs[UVLayerIdx]); } return bEquality; } }; FORCEINLINE uint32 GetTypeHash(const FUniqueVertex& UniqueVertex) { uint32 VertexHash = GetTypeHash(UniqueVertex.Normal); VertexHash = HashCombine(VertexHash, GetTypeHash(UniqueVertex.Tangent)); for (int32 UVLayerIdx = 0; UVLayerIdx < UniqueVertex.UVs.Num(); ++UVLayerIdx) { VertexHash = HashCombine(VertexHash, GetTypeHash(UniqueVertex.UVs[UVLayerIdx])); } return VertexHash; } static bool IsImportableImplicitObjectType(const Chaos::FImplicitObject& ImplicitObject) { const Chaos::EImplicitObjectType InnerType = ImplicitObject.GetType() & (~(Chaos::ImplicitObjectType::IsScaled | Chaos::ImplicitObjectType::IsInstanced)); if (InnerType == Chaos::ImplicitObjectType::Transformed) { const Chaos::FImplicitObjectTransformed& TransformedImplicitObject = static_cast(ImplicitObject); if (const Chaos::FImplicitObject* SubObject = TransformedImplicitObject.GetTransformedObject()) { return IsImportableImplicitObjectType(*SubObject); } } return (InnerType == Chaos::ImplicitObjectType::Box || InnerType == Chaos::ImplicitObjectType::Sphere || InnerType == Chaos::ImplicitObjectType::Capsule || InnerType == Chaos::ImplicitObjectType::Convex); } static FVector GetMeshBuildScale3D(const UStaticMesh& StaticMesh) { #if WITH_EDITOR const TArray& SourceModels = StaticMesh.GetSourceModels(); if (SourceModels.Num() > 0) { return SourceModels[0].BuildSettings.BuildScale3D; } #endif return FVector::One(); } void FGeometryCollectionEngineConversion::AppendMeshDescription( const FMeshDescription* MeshDescription, const FString& Name, int32 MaterialStartIndex, const FTransform& StaticMeshTransform, FGeometryCollection* GeometryCollection, UBodySetup* BodySetup, bool ReindexMaterials, bool bAddInternalMaterials, bool bSetInternalFromMaterialIndex) { #if WITH_EDITORONLY_DATA if (!MeshDescription) { return; } check(GeometryCollection); // prepare to tick progress per 100k vertices const int32 ReportProgressSpacing = 100000; int32 NumVertProgressSteps = int32(MeshDescription->Vertices().GetArraySize() / ReportProgressSpacing); FScopedSlowTask AppendMeshDescriptionTask(6 + 2*NumVertProgressSteps, LOCTEXT("AppendMeshDescriptionTask", "Appending Mesh Description Data")); AppendMeshDescriptionTask.EnterProgressFrame(1); // source vertex information FStaticMeshConstAttributes Attributes(*MeshDescription); TArrayView SourcePosition = Attributes.GetVertexPositions().GetRawArray(); TArrayView SourceTangent = Attributes.GetVertexInstanceTangents().GetRawArray(); TArrayView SourceBinormalSign = Attributes.GetVertexInstanceBinormalSigns().GetRawArray(); TArrayView SourceNormal = Attributes.GetVertexInstanceNormals().GetRawArray(); TArrayView SourceColor = Attributes.GetVertexInstanceColors().GetRawArray(); TVertexInstanceAttributesConstRef InstanceUVs = Attributes.GetVertexInstanceUVs(); const int32 NumUVLayers = InstanceUVs.GetNumChannels(); TArray> SourceUVArrays; SourceUVArrays.SetNum(NumUVLayers); for (int32 UVLayerIdx = 0; UVLayerIdx < NumUVLayers; ++UVLayerIdx) { SourceUVArrays[UVLayerIdx] = InstanceUVs.GetRawArray(UVLayerIdx); } // target vertex information TManagedArray& TargetVertex = GeometryCollection->Vertex; TManagedArray& TargetTangentU = GeometryCollection->TangentU; TManagedArray& TargetTangentV = GeometryCollection->TangentV; TManagedArray& TargetNormal = GeometryCollection->Normal; TManagedArray& TargetColor = GeometryCollection->Color; TManagedArray& TargetBoneMap = GeometryCollection->BoneMap; TManagedArray& TargetBoneColor = GeometryCollection->BoneColor; TManagedArray& TargetBoneName = GeometryCollection->BoneName; if (GeometryCollection->NumUVLayers() < NumUVLayers) { GeometryCollection->SetNumUVLayers(NumUVLayers); } const int32 VertexStart = GeometryCollection->NumElements(FGeometryCollection::VerticesGroup); int32 VertexCount = 0; FVector Scale = StaticMeshTransform.GetScale3D(); // We'll need to re-introduce UV seams, etc. by splitting vertices. // A new mapping of MeshDescription vertex instances to the split vertices is maintained. TMap VertexInstanceToGeometryCollectionVertex; VertexInstanceToGeometryCollectionVertex.Reserve(Attributes.GetVertexInstanceNormals().GetNumElements()); int32 LastProgress = 0; for (const FVertexID VertexIndex : MeshDescription->Vertices().GetElementIDs()) { int32 Progress = int32(VertexIndex / ReportProgressSpacing); if (Progress > LastProgress) { AppendMeshDescriptionTask.EnterProgressFrame(Progress - LastProgress); LastProgress = Progress; } TArrayView ReferencingVertexInstances = MeshDescription->GetVertexVertexInstanceIDs(VertexIndex); // Generate per instance hash of splittable attributes. TMap> SplitVertices; for (const FVertexInstanceID& InstanceID : ReferencingVertexInstances) { TArray SourceUVs; SourceUVs.SetNum(NumUVLayers); for (int32 UVLayerIdx = 0; UVLayerIdx < NumUVLayers; ++UVLayerIdx) { SourceUVs[UVLayerIdx] = SourceUVArrays[UVLayerIdx][InstanceID]; } FUniqueVertex UniqueVertex{ SourceNormal[InstanceID], SourceTangent[InstanceID], SourceUVs }; TArray& SplitVertex = SplitVertices.FindOrAdd(UniqueVertex); SplitVertex.Add(InstanceID); } int32 CurrentVertex = GeometryCollection->AddElements(SplitVertices.Num(), FGeometryCollection::VerticesGroup); // Create a new vertex for each split vertex and map the mesh description instance to it. for (const TTuple>& SplitVertex : SplitVertices) { const TArray& InstanceIDs = SplitVertex.Value; const FVertexInstanceID& ExemplarInstanceID = InstanceIDs[0]; TargetVertex[CurrentVertex] = SourcePosition[VertexIndex] * (FVector3f)Scale; TargetBoneMap[CurrentVertex] = GeometryCollection->NumElements(FGeometryCollection::TransformGroup); TargetNormal[CurrentVertex] = SourceNormal[ExemplarInstanceID]; TargetTangentU[CurrentVertex] = SourceTangent[ExemplarInstanceID]; TargetTangentV[CurrentVertex] = (FVector3f)SourceBinormalSign[ExemplarInstanceID] * FVector3f::CrossProduct(TargetNormal[CurrentVertex], TargetTangentU[CurrentVertex]); GeometryCollection::UV::SetUVs(*GeometryCollection, CurrentVertex, SplitVertex.Key.UVs); if (SourceColor.Num() > 0) { TargetColor[CurrentVertex] = FLinearColor(SourceColor[ExemplarInstanceID]); } else { TargetColor[CurrentVertex] = FLinearColor::White; } for (const FVertexInstanceID& InstanceID : InstanceIDs) { VertexInstanceToGeometryCollectionVertex.Add(InstanceID, CurrentVertex); } ++CurrentVertex; ++VertexCount; } } if (LastProgress < NumVertProgressSteps) { AppendMeshDescriptionTask.EnterProgressFrame(NumVertProgressSteps - LastProgress); LastProgress = NumVertProgressSteps; } // enter a progress frame for triangle processing w/ size equivalent to the vertex processing (as a heuristic) // (note: could instead tick this per 100k triangles as we do with vertices above, if more responsive progress tracking is desired) AppendMeshDescriptionTask.EnterProgressFrame(NumVertProgressSteps); // target triangle indices TManagedArray& TargetIndices = GeometryCollection->Indices; TManagedArray& TargetVisible = GeometryCollection->Visible; TManagedArray& TargetMaterialID = GeometryCollection->MaterialID; TManagedArray& TargetMaterialIndex = GeometryCollection->MaterialIndex; TManagedArray& TargetInternal = GeometryCollection->Internal; const int32 IndicesCount = MeshDescription->Triangles().Num(); const int32 InitialNumIndices = GeometryCollection->NumElements(FGeometryCollection::FacesGroup); const int32 IndicesStart = GeometryCollection->AddElements(IndicesCount, FGeometryCollection::FacesGroup); int32 TargetIndex = IndicesStart; for (const int32 TriangleIndex : MeshDescription->Triangles().GetElementIDs()) { TArrayView TriangleVertices = MeshDescription->GetTriangleVertexInstances(TriangleIndex); TargetIndices[TargetIndex] = FIntVector( VertexInstanceToGeometryCollectionVertex[TriangleVertices[0]], VertexInstanceToGeometryCollectionVertex[TriangleVertices[1]], VertexInstanceToGeometryCollectionVertex[TriangleVertices[2]] ); TargetVisible[TargetIndex] = true; // bAddInternalMaterials and bSetInternalFromMaterialIndex support the legacy system of odd-numbered materials indicating internal surfaces // bSetInternalFromMaterialIndex can be used to 'round trip' which faces are internal when using the 'ToMesh' tool to temporarily convert to/from static mesh. int32 MaterialIndexScale = 1 + int32(bAddInternalMaterials); int32 MaterialSourceID = MeshDescription->GetTrianglePolygonGroup(TriangleIndex); TargetMaterialID[TargetIndex] = MaterialStartIndex + (MaterialSourceID * MaterialIndexScale); bool bIsInternal = false; if (bSetInternalFromMaterialIndex && !bAddInternalMaterials) { bIsInternal = (MaterialSourceID % 2) == 1; } TargetInternal[TargetIndex] = bIsInternal; // Is this right? TargetMaterialIndex[TargetIndex] = TargetIndex; ++TargetIndex; } AppendMeshDescriptionTask.EnterProgressFrame(1); // Geometry transform TManagedArray& Transform = GeometryCollection->Transform; int32 TransformIndex1 = GeometryCollection->AddElements(1, FGeometryCollection::TransformGroup); Transform[TransformIndex1] = FTransform3f(StaticMeshTransform); Transform[TransformIndex1].SetScale3D(FVector3f(1.f, 1.f, 1.f)); // collisions if (BodySetup) { TArray Geoms; Chaos::FShapesArray Shapes; FGeometryAddParams CreateGeometryParams; CreateGeometryParams.bDoubleSided = false; CreateGeometryParams.CollisionData.CollisionFlags.bEnableQueryCollision = true; CreateGeometryParams.CollisionData.CollisionFlags.bEnableSimCollisionComplex = false; // no support for trimesh in destruction CreateGeometryParams.CollisionData.CollisionFlags.bEnableSimCollisionSimple = true; CreateGeometryParams.CollisionTraceType = ECollisionTraceFlag::CTF_UseSimpleAsComplex; CreateGeometryParams.Scale = Scale; CreateGeometryParams.LocalTransform = Chaos::FRigidTransform3::Identity; CreateGeometryParams.WorldTransform = Chaos::FRigidTransform3::Identity; CreateGeometryParams.Geometry = &BodySetup->AggGeom; CreateGeometryParams.TriMeshGeometries = MakeArrayView(BodySetup->TriMeshGeometries); // todo(chaos) : this currently also create the shape array which is unnecessary ,this could be optimized by having a common function to create only the implicits ChaosInterface::CreateGeometry(CreateGeometryParams, Geoms, Shapes); TManagedArray& ExternaCollisions = GeometryCollection->AddAttribute(FGeometryCollection::ExternalCollisionsAttribute, FGeometryCollection::TransformGroup); ExternaCollisions[TransformIndex1] = nullptr; for (int32 GeomIndex = 0; GeomIndex < Geoms.Num();) { // make sure we only import box, sphere, capsule or convex if (Geoms[GeomIndex] && IsImportableImplicitObjectType(*Geoms[GeomIndex])) { GeomIndex++; } else { Geoms.RemoveAtSwap(GeomIndex); } } if (Geoms.Num() > 0) { ExternaCollisions[TransformIndex1] = MakeImplicitObjectPtr(MoveTemp(Geoms)); } } // Bone Hierarchy - Added at root with no common parent TManagedArray& Parent = GeometryCollection->Parent; TManagedArray& SimulationType = GeometryCollection->SimulationType; Parent[TransformIndex1] = FGeometryCollection::Invalid; SimulationType[TransformIndex1] = FGeometryCollection::ESimulationTypes::FST_Rigid; const FColor RandBoneColor(FMath::Rand() % 100 + 5, FMath::Rand() % 100 + 5, FMath::Rand() % 100 + 5, 255); TargetBoneColor[TransformIndex1] = FLinearColor(RandBoneColor); TargetBoneName[TransformIndex1] = Name; // GeometryGroup int GeometryIndex = GeometryCollection->AddElements(1, FGeometryCollection::GeometryGroup); TManagedArray& TransformIndex = GeometryCollection->TransformIndex; TManagedArray& BoundingBox = GeometryCollection->BoundingBox; TManagedArray& InnerRadius = GeometryCollection->InnerRadius; TManagedArray& OuterRadius = GeometryCollection->OuterRadius; TManagedArray& VertexStartArray = GeometryCollection->VertexStart; TManagedArray& VertexCountArray = GeometryCollection->VertexCount; TManagedArray& FaceStartArray = GeometryCollection->FaceStart; TManagedArray& FaceCountArray = GeometryCollection->FaceCount; TransformIndex[GeometryIndex] = TargetBoneMap[VertexStart]; VertexStartArray[GeometryIndex] = VertexStart; VertexCountArray[GeometryIndex] = VertexCount; FaceStartArray[GeometryIndex] = InitialNumIndices; FaceCountArray[GeometryIndex] = IndicesCount; // TransformGroup TManagedArray& TransformToGeometryIndexArray = GeometryCollection->TransformToGeometryIndex; TransformToGeometryIndexArray[TransformIndex1] = GeometryIndex; FVector Center(0); for (int32 VertexIndex = VertexStart; VertexIndex < VertexStart + VertexCount; VertexIndex++) { Center += (FVector)TargetVertex[VertexIndex]; } if (VertexCount) Center /= VertexCount; AppendMeshDescriptionTask.EnterProgressFrame(1); // Inner/Outer edges, bounding box BoundingBox[GeometryIndex] = FBox(ForceInitToZero); InnerRadius[GeometryIndex] = FLT_MAX; OuterRadius[GeometryIndex] = -FLT_MAX; for (int32 VertexIndex = VertexStart; VertexIndex < VertexStart + VertexCount; VertexIndex++) { BoundingBox[GeometryIndex] += (FVector)TargetVertex[VertexIndex]; float Delta = (Center - (FVector)TargetVertex[VertexIndex]).Size(); InnerRadius[GeometryIndex] = FMath::Min(InnerRadius[GeometryIndex], Delta); OuterRadius[GeometryIndex] = FMath::Max(OuterRadius[GeometryIndex], Delta); } AppendMeshDescriptionTask.EnterProgressFrame(1); // Inner/Outer centroid for (int fdx = IndicesStart; fdx < IndicesStart + IndicesCount; fdx++) { FVector Centroid(0); for (int e = 0; e < 3; e++) { Centroid += (FVector)TargetVertex[TargetIndices[fdx][e]]; } Centroid /= 3; float Delta = (Center - Centroid).Size(); InnerRadius[GeometryIndex] = FMath::Min(InnerRadius[GeometryIndex], Delta); OuterRadius[GeometryIndex] = FMath::Max(OuterRadius[GeometryIndex], Delta); } AppendMeshDescriptionTask.EnterProgressFrame(1); // Inner/Outer edges for (int fdx = IndicesStart; fdx < IndicesStart + IndicesCount; fdx++) { for (int e = 0; e < 3; e++) { int i = e, j = (e + 1) % 3; FVector Edge = (FVector)TargetVertex[TargetIndices[fdx][i]] + 0.5 * FVector(TargetVertex[TargetIndices[fdx][j]] - TargetVertex[TargetIndices[fdx][i]]); float Delta = (Center - Edge).Size(); InnerRadius[GeometryIndex] = FMath::Min(InnerRadius[GeometryIndex], Delta); OuterRadius[GeometryIndex] = FMath::Max(OuterRadius[GeometryIndex], Delta); } } AppendMeshDescriptionTask.EnterProgressFrame(1); if (ReindexMaterials) { GeometryCollection->ReindexMaterials(); } #endif //WITH_EDITORONLY_DATA } namespace { // Based on FStaticMeshOperations::AreNormalsAndTangentsValid but tests if *any* are valid instead of if *all* are valid to avoid forcing a recomputes for any small degen tri void HasValidNormalsAndTangents(const FMeshDescription& MeshDescription, bool& bHasValidNormals, bool& bHasValidTangents) { bHasValidNormals = false; bHasValidTangents = false; FStaticMeshConstAttributes Attributes(MeshDescription); TArrayView VertexInstanceNormals = Attributes.GetVertexInstanceNormals().GetRawArray(); TArrayView VertexInstanceTangents = Attributes.GetVertexInstanceTangents().GetRawArray(); for (const FVertexInstanceID VertexInstanceID : MeshDescription.VertexInstances().GetElementIDs()) { bHasValidNormals |= (!VertexInstanceNormals[VertexInstanceID].IsNearlyZero() && !VertexInstanceNormals[VertexInstanceID].ContainsNaN()); bHasValidTangents |= (!VertexInstanceTangents[VertexInstanceID].IsNearlyZero() && !VertexInstanceTangents[VertexInstanceID].ContainsNaN()); if (bHasValidNormals && bHasValidTangents) { break; } } } // Note: This is similar to the InitializeAutoGeneratedAttributes ModelingComponents AssetUtils function, in the MeshModelingToolset plugin, // but we cannot use a plugin function from here, so have our own version. This version forces a recompute if Normals or Tangents are fully invalid, // even if the corresponding Recompute flag is not set. void InitializeNormalsAndTangentsIfNeededOrRequested(FMeshDescription& Mesh, const FMeshBuildSettings* BuildSettings) { check(BuildSettings); // Recompute according to build settings bool bShouldRecomputeNormals = BuildSettings->bRecomputeNormals; bool bShouldRecomputeTangents = BuildSettings->bRecomputeTangents; // Also recompute if normals or tangents are fully invalid // Note: We don't force a recompute if only some elements are invalid as these may just be a degenerate element on a mesh where the normals/tangents should otherwise be preserved. if (!BuildSettings->bRecomputeNormals || !BuildSettings->bRecomputeTangents) { bool bHasValidNormals = false, bHasValidTangents = false; HasValidNormalsAndTangents(Mesh, bHasValidNormals, bHasValidTangents); bShouldRecomputeNormals |= !bHasValidNormals; bShouldRecomputeTangents |= !bHasValidTangents; } // run recompute function if either normals or tangents need recompute if (bShouldRecomputeNormals || bShouldRecomputeTangents) { FStaticMeshAttributes Attributes(Mesh); if (!Attributes.GetTriangleNormals().IsValid() || !Attributes.GetTriangleTangents().IsValid()) { // If these attributes don't exist, create them and compute their values for each triangle FStaticMeshOperations::ComputeTriangleTangentsAndNormals(Mesh); } EComputeNTBsFlags ComputeNTBsOptions = EComputeNTBsFlags::BlendOverlappingNormals; ComputeNTBsOptions |= (bShouldRecomputeNormals) ? EComputeNTBsFlags::Normals : EComputeNTBsFlags::None; ComputeNTBsOptions |= (bShouldRecomputeTangents) ? EComputeNTBsFlags::Tangents : EComputeNTBsFlags::None; ComputeNTBsOptions |= BuildSettings->bUseMikkTSpace ? EComputeNTBsFlags::UseMikkTSpace : EComputeNTBsFlags::None; ComputeNTBsOptions |= BuildSettings->bComputeWeightedNormals ? EComputeNTBsFlags::WeightedNTBs : EComputeNTBsFlags::None; ComputeNTBsOptions |= BuildSettings->bRemoveDegenerates ? EComputeNTBsFlags::IgnoreDegenerateTriangles : EComputeNTBsFlags::None; FStaticMeshOperations::ComputeTangentsAndNormals(Mesh, ComputeNTBsOptions); } } } FMeshDescription* FGeometryCollectionEngineConversion::GetMaxResMeshDescriptionWithNormalsAndTangents(const UStaticMesh* StaticMesh) { if (StaticMesh == nullptr) { return nullptr; } FMeshDescription* MeshDescription = nullptr; const FStaticMeshSourceModel* SourceModel = nullptr; #if WITH_EDITORONLY_DATA // Prefer the HiRes description, although this isn't always available. if (StaticMesh->IsHiResMeshDescriptionValid()) { MeshDescription = StaticMesh->GetHiResMeshDescription(); SourceModel = &StaticMesh->GetHiResSourceModel(); } else { MeshDescription = StaticMesh->GetMeshDescription(0); SourceModel = &StaticMesh->GetSourceModel(0); } InitializeNormalsAndTangentsIfNeededOrRequested(*MeshDescription, &SourceModel->BuildSettings); #endif //WITH_EDITORONLY_DATA return MeshDescription; } int32 FGeometryCollectionEngineConversion::AppendMaterials(const TArray& Materials, UGeometryCollection* GeometryCollectionObject, bool bAddInteriorCopy) { // for each material, add a reference in our GeometryCollectionObject const int32 MaterialStart = GeometryCollectionObject->Materials.Num(); const int32 NumMeshMaterials = Materials.Num(); GeometryCollectionObject->Materials.Reserve(MaterialStart + NumMeshMaterials); for (int32 Index = 0; Index < NumMeshMaterials; ++Index) { UMaterialInterface* CurrMaterial = Materials[Index]; // Possible we have a null entry - replace with default if (CurrMaterial == nullptr) { CurrMaterial = UMaterial::GetDefaultMaterial(MD_Surface); } // We add the material twice, once for interior and again for exterior. GeometryCollectionObject->Materials.Add(CurrMaterial); if (bAddInteriorCopy) { GeometryCollectionObject->Materials.Add(CurrMaterial); } } return MaterialStart; } void FGeometryCollectionEngineConversion::AppendAutoInstanceMeshIndices(UGeometryCollection* GeometryCollectionObject, int32 FromTransformIndex, const UStaticMesh* StaticMesh, const TArray& Materials) { TSharedPtr GeometryCollectionPtr = GeometryCollectionObject->GetGeometryCollection(); if (GeometryCollectionPtr) { using namespace GeometryCollection::Facades; FCollectionInstancedMeshFacade InstancedMeshFacade(*GeometryCollectionPtr); const int32 NewNumOfTransforms = GeometryCollectionPtr->NumElements(FGeometryCollection::TransformGroup); if (NewNumOfTransforms > FromTransformIndex) { // create the schema if necessary InstancedMeshFacade.DefineSchema(); const int32 AutoInstanceMeshIndex = GeometryCollectionObject->FindOrAddAutoInstanceMesh(StaticMesh, Materials); for (int32 TransformIndex = FromTransformIndex; TransformIndex < NewNumOfTransforms; TransformIndex++) { InstancedMeshFacade.SetIndex(TransformIndex, AutoInstanceMeshIndex); } } } } bool FGeometryCollectionEngineConversion::AppendStaticMesh(const UStaticMesh* StaticMesh, const TArray& Materials, const FTransform& StaticMeshTransform, UGeometryCollection* GeometryCollectionObject, bool bReindexMaterials, bool bAddInternalMaterials, bool bSplitComponents, bool bSetInternalFromMaterialIndex) { #if WITH_EDITORONLY_DATA int32 StartMaterialIndex = GeometryCollectionObject->Materials.Num(); check(GeometryCollectionObject); TSharedPtr GeometryCollectionPtr = GeometryCollectionObject->GetGeometryCollection(); FGeometryCollection* GeometryCollection = GeometryCollectionPtr.Get(); check(GeometryCollection); const int32 OriginalNumOfTransforms = GeometryCollection->NumElements(FGeometryCollection::TransformGroup); if (AppendStaticMesh(StaticMesh, StartMaterialIndex, StaticMeshTransform, GeometryCollection, bReindexMaterials, bAddInternalMaterials, bSplitComponents, bSetInternalFromMaterialIndex)) { AppendMaterials(Materials, GeometryCollectionObject, bAddInternalMaterials); AppendAutoInstanceMeshIndices(GeometryCollectionObject, OriginalNumOfTransforms, StaticMesh, Materials); return true; } #endif //WITH_EDITORONLY_DATA return false; } bool FGeometryCollectionEngineConversion::AppendStaticMesh(const UStaticMesh* StaticMesh, int32 StartMaterialIndex, const FTransform& StaticMeshTransform, FGeometryCollection* GeometryCollection, bool bReindexMaterials, bool bAddInternalMaterials, bool bSplitComponents, bool bSetInternalFromMaterialIndex) { #if WITH_EDITORONLY_DATA FScopedSlowTask AppendStaticMeshTask(bSplitComponents ? 3 : 2, LOCTEXT("AppendStaticMeshTask", "Appending Static Mesh")); if (StaticMesh) { AppendStaticMeshTask.EnterProgressFrame(1); FMeshDescription* MeshDescription = GetMaxResMeshDescriptionWithNormalsAndTangents(StaticMesh); check(GeometryCollection); if (MeshDescription) { const FVector MeshBuildScale3D = GetMeshBuildScale3D(*StaticMesh); const FTransform MeshTransform( StaticMeshTransform.GetRotation(), StaticMeshTransform.GetTranslation(), StaticMeshTransform.GetScale3D() * MeshBuildScale3D ); if (bSplitComponents) { AppendStaticMeshTask.EnterProgressFrame(1); int32 MaxVID = MeshDescription->Vertices().Num(); UE::Geometry::FVertexConnectedComponents Components(MaxVID); for (const FTriangleID TriangleID : MeshDescription->Triangles().GetElementIDs()) { TArrayView TriangleIDs = MeshDescription->GetTriangleVertices(TriangleID); Components.ConnectVertices(TriangleIDs[0].GetValue(), TriangleIDs[1].GetValue()); Components.ConnectVertices(TriangleIDs[1].GetValue(), TriangleIDs[2].GetValue()); } if (Components.HasMultipleComponents(MaxVID, 2)) { // look up vertex positions TVertexAttributesConstRef VertexPositions = MeshDescription->GetVertexPositions(); // vertex instance attributes FStaticMeshConstAttributes Attributes(*MeshDescription); TVertexInstanceAttributesConstRef InstanceUVs = Attributes.GetVertexInstanceUVs(); TVertexInstanceAttributesConstRef InstanceNormals = Attributes.GetVertexInstanceNormals(); TVertexInstanceAttributesConstRef InstanceTangents = Attributes.GetVertexInstanceTangents(); TVertexInstanceAttributesConstRef InstanceBiTangentSign = Attributes.GetVertexInstanceBinormalSigns(); TVertexInstanceAttributesConstRef InstanceColors = Attributes.GetVertexInstanceColors(); const int NumUVLayers = InstanceUVs.GetNumChannels(); TMap Map = Components.MakeComponentMap(MaxVID, 2); int32 NumIslands = Map.Num(); TArray Descriptions; Descriptions.SetNum(NumIslands); TArray Builders; Builders.SetNum(NumIslands); for (int32 MeshIdx = 0; MeshIdx < NumIslands; ++MeshIdx) { FStaticMeshAttributes MeshAttributes(Descriptions[MeshIdx]); MeshAttributes.Register(); Builders[MeshIdx].SetMeshDescription(&Descriptions[MeshIdx]); Builders[MeshIdx].SuspendMeshDescriptionIndexing(); Builders[MeshIdx].SetNumUVLayers(NumUVLayers); } for (TPair IDToIdx : Map) { int32 ID = IDToIdx.Key; int32 Idx = IDToIdx.Value; int32 NumVertices = Components.GetComponentSize(ID); Builders[Idx].ReserveNewVertices(NumVertices); } TArray VertexIDMap; VertexIDMap.Init(INDEX_NONE, MeshDescription->Vertices().Num()); for (const FVertexID VertexID : MeshDescription->Vertices().GetElementIDs()) { int32 MeshID = Components.GetComponent(VertexID.GetValue()); int32* MeshIdx = Map.Find(MeshID); if (MeshIdx) { FVector Position = (FVector)VertexPositions.Get(VertexID); VertexIDMap[VertexID.GetValue()] = Builders[*MeshIdx].AppendVertex(Position); } } for (const FTriangleID TriangleID : MeshDescription->Triangles().GetElementIDs()) { TArrayView TriangleVerts = MeshDescription->GetTriangleVertices(TriangleID); TArrayView SourceInstanceTri = MeshDescription->GetTriangleVertexInstances(TriangleID); int32 MeshID = Components.GetComponent(TriangleVerts[0].GetValue()); int32 MeshIdx = Map[MeshID]; FMeshDescriptionBuilder& Builder = Builders[MeshIdx]; // create new vtx instances for each triangle FVertexInstanceID DestInstanceTri[3]; for (int32 j = 0; j < 3; ++j) { const FVertexID TriVertex = VertexIDMap[TriangleVerts[j].GetValue()]; DestInstanceTri[j] = Builder.AppendInstance(TriVertex); } // add the triangle to MeshDescription FPolygonGroupID MaterialID = MeshDescription->GetTrianglePolygonGroup(TriangleID); FTriangleID NewTriangleID = Builder.AppendTriangle(DestInstanceTri[0], DestInstanceTri[1], DestInstanceTri[2], MaterialID); // transfer UVs. Note the Builder sets both the shared and per-instance UVs from this for (int32 UVLayer = 0; UVLayer < NumUVLayers; ++UVLayer) { FUVID UVIDs[3] = { FUVID(-1), FUVID(-1), FUVID(-1) }; for (int32 j = 0; j < 3; ++j) { FVector2D UV = (FVector2D)InstanceUVs.Get(SourceInstanceTri[j], UVLayer); UVIDs[j] = Builder.AppendUV(UV, UVLayer); } // append the UV triangle - builder takes care of the rest Builder.AppendUVTriangle(NewTriangleID, UVIDs[0], UVIDs[1], UVIDs[2], UVLayer); } // Set instance attributes: normal/tangent/bitangent frame and color for (int32 j = 0; j < 3; ++j) { const FVertexInstanceID SourceInstanceID = SourceInstanceTri[j]; const FVertexInstanceID DestInstanceID = DestInstanceTri[j]; FVector TriVertNormal = (FVector)InstanceNormals.Get(SourceInstanceID); FVector TriVertTangent = (FVector)InstanceTangents.Get(SourceInstanceID); float BiTangentSign = (float)InstanceBiTangentSign.Get(SourceInstanceID); Builder.SetInstanceTangentSpace(DestInstanceID, TriVertNormal, TriVertTangent, BiTangentSign); FVector4f InstColor = InstanceColors.Get(SourceInstanceID); Builder.SetInstanceColor(DestInstanceID, InstColor); } } for (int32 MeshIdx = 0; MeshIdx < NumIslands; ++MeshIdx) { Builders[MeshIdx].ResumeMeshDescriptionIndexing(); } for (FMeshDescription& MD : Descriptions) { AppendMeshDescription(&MD, StaticMesh->GetName(), StartMaterialIndex, MeshTransform, GeometryCollection, StaticMesh->GetBodySetup(), false, bAddInternalMaterials, bSetInternalFromMaterialIndex); } if (bReindexMaterials) { GeometryCollection->ReindexMaterials(); } return true; } // else only one component -- fall back to just using the original mesh description } AppendStaticMeshTask.EnterProgressFrame(1); AppendMeshDescription(MeshDescription, StaticMesh->GetName(), StartMaterialIndex, MeshTransform, GeometryCollection, StaticMesh->GetBodySetup(), bReindexMaterials, bAddInternalMaterials, bSetInternalFromMaterialIndex); return true; } } #endif //WITH_EDITORONLY_DATA return false; } bool FGeometryCollectionEngineConversion::AppendGeometryCollection(const FGeometryCollection* SourceGeometryCollectionPtr, int32 AssetMaterialStart, const FTransform& GeometryCollectionTransform, FGeometryCollection* TargetGeometryCollection, bool bReindexMaterials) { if (SourceGeometryCollectionPtr == nullptr) { return false; } // Assemble offsets and add elements const int32 VertexCount = SourceGeometryCollectionPtr->Vertex.Num(); const int32 FaceCount = SourceGeometryCollectionPtr->Indices.Num(); const int32 TransformCount = SourceGeometryCollectionPtr->Transform.Num(); const int32 GeometryCount = SourceGeometryCollectionPtr->TransformIndex.Num(); const int32 SectionCount = SourceGeometryCollectionPtr->Sections.Num(); FVector3f Scale = FVector3f(GeometryCollectionTransform.GetScale3D()); FTransform3f AppliedTransform = FTransform3f(GeometryCollectionTransform); AppliedTransform.RemoveScaling(); const int32 VertexStart = TargetGeometryCollection->AddElements(VertexCount, FGeometryCollection::VerticesGroup); const int32 FaceStart = TargetGeometryCollection->AddElements(FaceCount, FGeometryCollection::FacesGroup); const int32 TransformStart = TargetGeometryCollection->AddElements(TransformCount, FGeometryCollection::TransformGroup); const int32 GeometryStart = TargetGeometryCollection->AddElements(GeometryCount, FGeometryCollection::GeometryGroup); const int32 SectionStart = TargetGeometryCollection->AddElements(SectionCount, FGeometryCollection::MaterialGroup); // source vertex information const TManagedArray& SourceVertex = SourceGeometryCollectionPtr->Vertex; const TManagedArray& SourceTangentU = SourceGeometryCollectionPtr->TangentU; const TManagedArray& SourceTangentV = SourceGeometryCollectionPtr->TangentV; const TManagedArray& SourceNormal = SourceGeometryCollectionPtr->Normal; const TManagedArray& SourceColor = SourceGeometryCollectionPtr->Color; const TManagedArray& SourceBoneMap = SourceGeometryCollectionPtr->BoneMap; // target vertex information TManagedArray& TargetVertex = TargetGeometryCollection->Vertex; TManagedArray& TargetTangentU = TargetGeometryCollection->TangentU; TManagedArray& TargetTangentV = TargetGeometryCollection->TangentV; TManagedArray& TargetNormal = TargetGeometryCollection->Normal; TManagedArray& TargetColor = TargetGeometryCollection->Color; TManagedArray& TargetBoneMap = TargetGeometryCollection->BoneMap; TargetGeometryCollection->SetNumUVLayers(FMath::Max(TargetGeometryCollection->NumUVLayers(), SourceGeometryCollectionPtr->NumUVLayers())); GeometryCollection::UV::FUVLayers TargetUVLayers = GeometryCollection::UV::FindActiveUVLayers(*TargetGeometryCollection); GeometryCollection::UV::FConstUVLayers SourceUVLayers = GeometryCollection::UV::FindActiveUVLayers(*SourceGeometryCollectionPtr); // append vertices for (int32 VertexIndex = 0; VertexIndex < VertexCount; VertexIndex++) { const int32 VertexOffset = VertexStart + VertexIndex; TargetVertex[VertexOffset] = SourceVertex[VertexIndex] * (FVector3f)Scale; TargetTangentU[VertexOffset] = SourceTangentU[VertexIndex]; TargetTangentV[VertexOffset] = SourceTangentV[VertexIndex]; TargetNormal[VertexOffset] = SourceNormal[VertexIndex]; for (int32 UVLayer = 0; UVLayer < SourceUVLayers.Num(); ++UVLayer) { TargetUVLayers[UVLayer][VertexOffset] = SourceUVLayers[UVLayer][VertexIndex]; } TargetColor[VertexOffset] = SourceColor[VertexIndex]; TargetBoneMap[VertexOffset] = SourceBoneMap[VertexIndex] + TransformStart; } // source face information const TManagedArray& SourceIndices = SourceGeometryCollectionPtr->Indices; const TManagedArray& SourceVisible = SourceGeometryCollectionPtr->Visible; const TManagedArray& SourceMaterialID = SourceGeometryCollectionPtr->MaterialID; const TManagedArray& SourceMaterialIndex = SourceGeometryCollectionPtr->MaterialIndex; const TManagedArray& SourceInternal = SourceGeometryCollectionPtr->Internal; // target face information TManagedArray& TargetIndices = TargetGeometryCollection->Indices; TManagedArray& TargetVisible = TargetGeometryCollection->Visible; TManagedArray& TargetMaterialID = TargetGeometryCollection->MaterialID; TManagedArray& TargetMaterialIndex = TargetGeometryCollection->MaterialIndex; TManagedArray& TargetInternal = TargetGeometryCollection->Internal; // append faces for (int32 FaceIndex = 0; FaceIndex < FaceCount; ++FaceIndex) { const FIntVector& SourceFace = SourceIndices[FaceIndex]; const int32 FaceOffset = FaceStart + FaceIndex; TargetIndices[FaceOffset] = FIntVector( SourceFace[0] + VertexStart, SourceFace[1] + VertexStart, SourceFace[2] + VertexStart); TargetVisible[FaceOffset] = SourceVisible[FaceIndex]; TargetMaterialID[FaceOffset] = AssetMaterialStart + SourceMaterialID[FaceIndex]; TargetMaterialIndex[FaceOffset] = FaceOffset; TargetInternal[FaceOffset] = SourceInternal[FaceIndex]; } // source transform information const TManagedArray& SourceTransform = SourceGeometryCollectionPtr->Transform; const TManagedArray& SourceBoneName = SourceGeometryCollectionPtr->BoneName; const TManagedArray& SourceBoneColor = SourceGeometryCollectionPtr->BoneColor; const TManagedArray& SourceParent = SourceGeometryCollectionPtr->Parent; const TManagedArray>& SourceChildren = SourceGeometryCollectionPtr->Children; const TManagedArray& SourceTransformToGeometryIndex = SourceGeometryCollectionPtr->TransformToGeometryIndex; const TManagedArray& SourceSimulationType = SourceGeometryCollectionPtr->SimulationType; const TManagedArray& SourceStatusFlags = SourceGeometryCollectionPtr->StatusFlags; const TManagedArray& SourceInitialDynamicState = SourceGeometryCollectionPtr->InitialDynamicState; const TManagedArray* SourceExternalCollisions = SourceGeometryCollectionPtr->FindAttribute(FGeometryCollection::ExternalCollisionsAttribute, FGeometryCollection::TransformGroup); // target transform information TManagedArray& TargetTransform = TargetGeometryCollection->Transform; TManagedArray& TargetBoneName = TargetGeometryCollection->BoneName; TManagedArray& TargetBoneColor = TargetGeometryCollection->BoneColor; TManagedArray& TargetParent = TargetGeometryCollection->Parent; TManagedArray>& TargetChildren = TargetGeometryCollection->Children; TManagedArray& TargetTransformToGeometryIndex = TargetGeometryCollection->TransformToGeometryIndex; TManagedArray& TargetSimulationType = TargetGeometryCollection->SimulationType; TManagedArray& TargetStatusFlags = TargetGeometryCollection->StatusFlags; TManagedArray& TargetInitialDynamicState = TargetGeometryCollection->InitialDynamicState; TManagedArray& TargetExternalCollisions = TargetGeometryCollection->AddAttribute(FGeometryCollection::ExternalCollisionsAttribute, FGeometryCollection::TransformGroup); // append transform hierarchy for (int32 TransformIndex = 0; TransformIndex < TransformCount; ++TransformIndex) { const int32 TransformOffset = TransformStart + TransformIndex; // Only apply the transform to the parent node. Child nodes only need scaling applied to translation offsets. if (SourceParent[TransformIndex] == INDEX_NONE) { TargetTransform[TransformOffset] = SourceTransform[TransformIndex] * AppliedTransform; } else { FTransform3f ScaledTranslation = SourceTransform[TransformIndex]; ScaledTranslation.ScaleTranslation(Scale); TargetTransform[TransformOffset] = ScaledTranslation; } // #todo Get this Bone name to be unique TargetBoneName[TransformOffset] = SourceBoneName[TransformIndex]; const FColor RandBoneColor(FMath::Rand() % 100 + 5, FMath::Rand() % 100 + 5, FMath::Rand() % 100 + 5, 255); TargetBoneColor[TransformOffset] = FLinearColor(RandBoneColor); TargetParent[TransformOffset] = (SourceParent[TransformIndex] == INDEX_NONE) ? INDEX_NONE : SourceParent[TransformIndex] + TransformStart; const TSet& SourceChildrenSet = SourceChildren[TransformIndex]; for (int32 ChildIndex : SourceChildrenSet) { TargetChildren[TransformOffset].Add(ChildIndex + TransformStart); } TargetTransformToGeometryIndex[TransformOffset] = SourceTransformToGeometryIndex[TransformIndex] + GeometryStart; TargetSimulationType[TransformOffset] = SourceSimulationType[TransformIndex]; TargetStatusFlags[TransformOffset] = SourceStatusFlags[TransformIndex]; TargetInitialDynamicState[TransformOffset] = SourceInitialDynamicState[TransformIndex]; TargetExternalCollisions[TransformOffset] = nullptr; if (SourceExternalCollisions) { TargetExternalCollisions[TransformOffset] = (*SourceExternalCollisions)[TransformIndex]; } } // source geometry information const TManagedArray& SourceTransformIndex = SourceGeometryCollectionPtr->TransformIndex; const TManagedArray& SourceVertexStart = SourceGeometryCollectionPtr->VertexStart; const TManagedArray& SourceVertexCount = SourceGeometryCollectionPtr->VertexCount; const TManagedArray& SourceFaceStart = SourceGeometryCollectionPtr->FaceStart; const TManagedArray& SourceFaceCount = SourceGeometryCollectionPtr->FaceCount; // target geometry information TManagedArray& TargetTransformIndex = TargetGeometryCollection->TransformIndex; TManagedArray& TargetBoundingBox = TargetGeometryCollection->BoundingBox; TManagedArray& TargetInnerRadius = TargetGeometryCollection->InnerRadius; TManagedArray& TargetOuterRadius = TargetGeometryCollection->OuterRadius; TManagedArray& TargetVertexStart = TargetGeometryCollection->VertexStart; TManagedArray& TargetVertexCount = TargetGeometryCollection->VertexCount; TManagedArray& TargetFaceStart = TargetGeometryCollection->FaceStart; TManagedArray& TargetFaceCount = TargetGeometryCollection->FaceCount; // append geometry for (int32 GeometryIndex = 0; GeometryIndex < GeometryCount; ++GeometryIndex) { const int32 GeometryOffset = GeometryStart + GeometryIndex; TargetTransformIndex[GeometryOffset] = SourceTransformIndex[GeometryIndex] + TransformStart; TargetVertexStart[GeometryOffset] = SourceVertexStart[GeometryIndex] + VertexStart; TargetVertexCount[GeometryOffset] = SourceVertexCount[GeometryIndex]; TargetFaceStart[GeometryOffset] = SourceFaceStart[GeometryIndex] + FaceStart; TargetFaceCount[GeometryOffset] = SourceFaceCount[GeometryIndex]; // Find centroid of geometry for inner/outer radius calculations FVector Center(0); for (int32 VertexIndex = TargetVertexStart[GeometryOffset]; VertexIndex < TargetVertexStart[GeometryOffset] + TargetVertexCount[GeometryOffset]; ++VertexIndex) { Center += (FVector)TargetVertex[VertexIndex]; } if (TargetVertexCount[GeometryOffset]) Center /= TargetVertexCount[GeometryOffset]; TargetBoundingBox[GeometryOffset] = FBox(ForceInitToZero); TargetInnerRadius[GeometryOffset] = FLT_MAX; TargetOuterRadius[GeometryOffset] = -FLT_MAX; for (int32 VertexIndex = TargetVertexStart[GeometryOffset]; VertexIndex < TargetVertexStart[GeometryOffset] + TargetVertexCount[GeometryOffset]; ++VertexIndex) { TargetBoundingBox[GeometryOffset] += (FVector)TargetVertex[VertexIndex]; float Delta = (Center - (FVector)TargetVertex[VertexIndex]).Size(); TargetInnerRadius[GeometryOffset] = FMath::Min(TargetInnerRadius[GeometryOffset], Delta); TargetOuterRadius[GeometryOffset] = FMath::Max(TargetOuterRadius[GeometryOffset], Delta); } } // source material information const TManagedArray& SourceSections = SourceGeometryCollectionPtr->Sections; // target material information TManagedArray& TargetSections = TargetGeometryCollection->Sections; // append sections for (int32 SectionIndex = 0; SectionIndex < SectionCount; ++SectionIndex) { int32 SectionOffset = SectionStart + SectionIndex; TargetSections[SectionOffset].MaterialID = AssetMaterialStart + SourceSections[SectionIndex].MaterialID; TargetSections[SectionOffset].FirstIndex = SourceSections[SectionIndex].FirstIndex + FaceStart * 3; TargetSections[SectionOffset].MinVertexIndex = VertexStart + SourceSections[SectionIndex].MinVertexIndex; TargetSections[SectionOffset].NumTriangles = SourceSections[SectionIndex].NumTriangles; TargetSections[SectionOffset].MaxVertexIndex = VertexStart + SourceSections[SectionIndex].MaxVertexIndex; } if (bReindexMaterials) { TargetGeometryCollection->ReindexMaterials(); } return true; } void FGeometryCollectionEngineConversion::AppendGeometryCollection(const UGeometryCollection* SourceGeometryCollection, const TArray& Materials, const FTransform& GeometryCollectionTransform, UGeometryCollection* TargetGeometryCollectionObject, bool bReindexMaterials) { if (SourceGeometryCollection == nullptr) { return; } const TSharedPtr SourceGeometryCollectionPtr = SourceGeometryCollection->GetGeometryCollection(); check(TargetGeometryCollectionObject); TSharedPtr GeometryCollectionPtr = TargetGeometryCollectionObject->GetGeometryCollection(); FGeometryCollection* GeometryCollection = GeometryCollectionPtr.Get(); check(GeometryCollection); int32 MaterialStart = AppendMaterials(Materials, TargetGeometryCollectionObject, false); const int32 TargetTransformStart = GeometryCollectionPtr->NumElements(FGeometryCollection::TransformGroup); if (AppendGeometryCollection(SourceGeometryCollectionPtr.Get(), MaterialStart, GeometryCollectionTransform, GeometryCollection, bReindexMaterials)) { AppendGeometryCollectionInstancedMeshes(SourceGeometryCollection, TargetGeometryCollectionObject, TargetTransformStart); } } void FGeometryCollectionEngineConversion::AppendStaticMesh(const UStaticMesh* StaticMesh, const UStaticMeshComponent* StaticMeshComponent, const FTransform& StaticMeshTransform, UGeometryCollection* GeometryCollectionObject, bool ReindexMaterials, bool bAddInternalMaterials, bool bSplitComponents, bool bSetInternalFromMaterialIndex) { if (StaticMesh == nullptr) { return; } TArray Materials; Materials.Reserve(StaticMesh->GetStaticMaterials().Num()); for (int32 Index = 0; Index < StaticMesh->GetStaticMaterials().Num(); ++Index) { UMaterialInterface* CurrMaterial = StaticMeshComponent ? StaticMeshComponent->GetMaterial(Index) : StaticMesh->GetMaterial(Index); Materials.Add(CurrMaterial); } // Geometry collections usually carry the selection material, which we'll delete before appending UMaterialInterface* BoneSelectedMaterial = LoadObject(nullptr, UGeometryCollection::GetSelectedMaterialPath(), nullptr, LOAD_None, nullptr); GeometryCollectionObject->Materials.Remove(BoneSelectedMaterial); Materials.Remove(BoneSelectedMaterial); AppendStaticMesh(StaticMesh, Materials, StaticMeshTransform, GeometryCollectionObject, ReindexMaterials, bAddInternalMaterials, bSplitComponents, bSetInternalFromMaterialIndex); } int32 FGeometryCollectionEngineConversion::AppendGeometryCollectionMaterials(const UGeometryCollection* SourceGeometryCollection, const UGeometryCollectionComponent* GeometryCollectionComponent, UGeometryCollection* TargetGeometryCollectionObject) { check(SourceGeometryCollection); check(GeometryCollectionComponent); check(TargetGeometryCollectionObject); TArray Materials; Materials.Reserve(SourceGeometryCollection->Materials.Num()); for (int32 Index = 0; Index < SourceGeometryCollection->Materials.Num(); ++Index) { UMaterialInterface* CurrMaterial = GeometryCollectionComponent ? GeometryCollectionComponent->GetMaterial(Index) : SourceGeometryCollection->Materials[Index].Get(); Materials.Add(CurrMaterial); } // Geometry collections usually carry the selection material, which we'll delete before appending UMaterialInterface* BoneSelectedMaterial = LoadObject(nullptr, UGeometryCollection::GetSelectedMaterialPath(), nullptr, LOAD_None, nullptr); TargetGeometryCollectionObject->Materials.Remove(BoneSelectedMaterial); Materials.Remove(BoneSelectedMaterial); return AppendMaterials(Materials, TargetGeometryCollectionObject, false); } void FGeometryCollectionEngineConversion::AppendGeometryCollectionInstancedMeshes(const UGeometryCollection* SourceGeometryCollectionObject, UGeometryCollection* TargetGeometryCollectionObject, int32 TargetTransformStartIndex) { TSharedPtr SourceGeometryCollectionPtr = SourceGeometryCollectionObject->GetGeometryCollection(); TSharedPtr TargetGeometryCollectionPtr = TargetGeometryCollectionObject->GetGeometryCollection(); using namespace GeometryCollection::Facades; if (SourceGeometryCollectionPtr && TargetGeometryCollectionPtr) { const FCollectionInstancedMeshFacade SourceInstancedMeshFacade(*SourceGeometryCollectionPtr); FCollectionInstancedMeshFacade TargetInstancedMeshFacade(*TargetGeometryCollectionPtr); if (SourceInstancedMeshFacade.IsValid()) { TargetInstancedMeshFacade.DefineSchema(); const int32 NumSourceIndices = SourceInstancedMeshFacade.GetNumIndices(); for (int32 SourceTransformIndex = 0; SourceTransformIndex < NumSourceIndices; SourceTransformIndex++) { int32 TargetInstancedMeshIndex = INDEX_NONE; const int32 SourceAutoInstanceIndex = SourceInstancedMeshFacade.GetIndex(SourceTransformIndex); if (SourceAutoInstanceIndex != INDEX_NONE) { const FGeometryCollectionAutoInstanceMesh& SourceAutoInstanceMesh = SourceGeometryCollectionObject->GetAutoInstanceMesh(SourceAutoInstanceIndex); TargetInstancedMeshIndex = TargetGeometryCollectionObject->FindOrAddAutoInstanceMesh(SourceAutoInstanceMesh); } const int32 TargetTransformIndex = TargetTransformStartIndex + SourceTransformIndex; TargetInstancedMeshFacade.SetIndex(TargetTransformIndex, TargetInstancedMeshIndex); } } } } void FGeometryCollectionEngineConversion::AppendGeometryCollection(const UGeometryCollection* SourceGeometryCollection, const UGeometryCollectionComponent* GeometryCollectionComponent, const FTransform& GeometryCollectionTransform, UGeometryCollection* TargetGeometryCollectionObject, bool bReindexMaterials) { if (SourceGeometryCollection == nullptr) { return; } int32 MaterialStartIndex = AppendGeometryCollectionMaterials(SourceGeometryCollection, GeometryCollectionComponent, TargetGeometryCollectionObject); const TSharedPtr SourceGeometryCollectionPtr = SourceGeometryCollection->GetGeometryCollection(); check(TargetGeometryCollectionObject); TSharedPtr GeometryCollectionPtr = TargetGeometryCollectionObject->GetGeometryCollection(); FGeometryCollection* GeometryCollection = GeometryCollectionPtr.Get(); check(GeometryCollection); const int32 TargetTransformStart = GeometryCollectionPtr->NumElements(FGeometryCollection::TransformGroup); if (AppendGeometryCollection(SourceGeometryCollectionPtr.Get(), MaterialStartIndex, GeometryCollectionTransform, GeometryCollection, bReindexMaterials)) { AppendGeometryCollectionInstancedMeshes(SourceGeometryCollection, TargetGeometryCollectionObject, TargetTransformStart); } } bool FGeometryCollectionEngineConversion::AppendSkeletalMesh(const USkeletalMesh* InSkeletalMesh, int32 MaterialStartIndex, const FTransform& SkeletalMeshTransform, FManagedArrayCollection* InManagedArrayCollection, bool bReindexMaterials) { //UE_LOG(UGeometryCollectionConversionLogging, Log, TEXT("FGeometryCollectionEngineConversion::AppendSkeletalMesh()")); #if WITH_EDITOR int LODIndex = 0; if (!InManagedArrayCollection) { return false; } FMeshDescription MeshDescription; if (!InSkeletalMesh->CloneMeshDescription(LODIndex, MeshDescription)) { return false; } FGeometryCollection GeometryCollection; // Transform Attributes TManagedArray& LocalSpaceTransform = GeometryCollection.ModifyAttribute(FTransformCollection::TransformAttribute, FTransformCollection::TransformGroup); TManagedArray& Parent = GeometryCollection.ModifyAttribute(FTransformCollection::ParentAttribute, FTransformCollection::TransformGroup); TManagedArray& BoneColor = GeometryCollection.ModifyAttribute("BoneColor", FTransformCollection::TransformGroup); TManagedArray& BoneName = GeometryCollection.ModifyAttribute("BoneName", FTransformCollection::TransformGroup); TManagedArray& SimulationType = GeometryCollection.ModifyAttribute("SimulationType", FTransformCollection::TransformGroup); // Vertices Attributes TManagedArray& Vertex = GeometryCollection.ModifyAttribute("Vertex", FGeometryCollection::VerticesGroup); TManagedArray& Normal = GeometryCollection.ModifyAttribute("Normal", FGeometryCollection::VerticesGroup); TManagedArray& Color = GeometryCollection.ModifyAttribute("Color", FGeometryCollection::VerticesGroup); TManagedArray& TangentU = GeometryCollection.ModifyAttribute("TangentU", FGeometryCollection::VerticesGroup); TManagedArray& TangentV = GeometryCollection.ModifyAttribute("TangentV", FGeometryCollection::VerticesGroup); TManagedArray& BoneMap = GeometryCollection.ModifyAttribute("BoneMap", FGeometryCollection::VerticesGroup); // Index Attributes TManagedArray& Indices = GeometryCollection.ModifyAttribute("Indices", FGeometryCollection::FacesGroup); TManagedArray& Visible = GeometryCollection.ModifyAttribute("Visible", FGeometryCollection::FacesGroup); TManagedArray& MaterialIndex = GeometryCollection.ModifyAttribute("MaterialIndex", FGeometryCollection::FacesGroup); TManagedArray& MaterialID = GeometryCollection.ModifyAttribute("MaterialID", FGeometryCollection::FacesGroup); FMeshDescriptionToDynamicMesh Converter; UE::Geometry::FDynamicMesh3 DynamicMesh; Converter.Convert(&MeshDescription, DynamicMesh); // // Convert the transform hierarchy // int32 RootIndex = INDEX_NONE; int32 TransformBaseIndex = INDEX_NONE; const USkeleton* Skeleton = InSkeletalMesh->GetSkeleton(); const TArray& RestArray = Skeleton->GetRefLocalPoses(); const FReferenceSkeleton& ReferenceSkeletion = Skeleton->GetReferenceSkeleton(); if (ReferenceSkeletion.GetNum()) { TransformBaseIndex = GeometryCollection.AddElements(ReferenceSkeletion.GetNum(), FGeometryCollection::TransformGroup); RootIndex = TransformBaseIndex; for (int32 BoneIndex = 0; BoneIndex < ReferenceSkeletion.GetNum(); BoneIndex++) { // For validation against the component space position use // FTransform ComponentSpaceTransform =FAnimationRuntime::GetComponentSpaceTransformRefPose(ReferenceSkeletion, SkeletalBoneMap[BoneIndex]); LocalSpaceTransform[TransformBaseIndex + BoneIndex] = FTransform3f(RestArray[BoneIndex]); BoneName[TransformBaseIndex + BoneIndex] = ReferenceSkeletion.GetRefBoneInfo()[BoneIndex].Name.ToString(); Parent[TransformBaseIndex + BoneIndex] = ReferenceSkeletion.GetRefBoneInfo()[BoneIndex].ParentIndex; SimulationType[TransformBaseIndex + BoneIndex] = FGeometryCollection::ESimulationTypes::FST_None; BoneColor[TransformBaseIndex + BoneIndex] = FLinearColor::MakeRandomColor(); if (Parent[TransformBaseIndex + BoneIndex] == INDEX_NONE) { RootIndex = TransformBaseIndex + BoneIndex; } } } // // Identify disconnected geoemtry // int32 VertexCount = 0, TriangleCount = 0; TArray SourceVertexToComponentMap; // Map from mesh vertex index to target vertex index TArray> ComponentsSourceIndices; // Mesh triangle indices of each component. TArray> SourceToTargetTriangleMap; // Mesh triangle index of each triangle in the component. GeometryCollectionEngineUtility::GenerateConnectedComponents(InSkeletalMesh, ComponentsSourceIndices, SourceToTargetTriangleMap, SourceVertexToComponentMap,TriangleCount, VertexCount); // // Add the Triangles to the Geometry Collection //.. ensure all component vertices are contigious in the array // int NumVertices = GeometryCollection.NumElements(FGeometryCollection::VerticesGroup); int IndicesBaseIndex = GeometryCollection.AddElements(TriangleCount, FGeometryCollection::FacesGroup); int CurrentIndex = IndicesBaseIndex; for (int ComponentIndex = 0; ComponentIndex < ComponentsSourceIndices.Num(); ComponentIndex++) { for(int32 TriangleIndex = 0; TriangleIndex< ComponentsSourceIndices[ComponentIndex].Num(); TriangleIndex++) { SourceToTargetTriangleMap[ComponentIndex][TriangleIndex][1] = CurrentIndex; FIntVector& Triangle = ComponentsSourceIndices[ComponentIndex][TriangleIndex]; for (int k = 0; k < 3; k++) { Indices[CurrentIndex][k] = SourceVertexToComponentMap[Triangle[k]] + IndicesBaseIndex; } Visible[CurrentIndex] = true; MaterialID[CurrentIndex] = 0; MaterialIndex[CurrentIndex] = CurrentIndex; CurrentIndex++; } } // // Vertex Attributes // int VertexBaseIndex = GeometryCollection.AddElements(VertexCount, FGeometryCollection::VerticesGroup); int NumTargetVertices = GeometryCollection.NumElements(FGeometryCollection::VerticesGroup); // // Transform Attributes // // add transforms for the separated geometry components. TArray ComponentToTransformGroupIndex; ComponentToTransformGroupIndex.Init(INDEX_NONE, ComponentsSourceIndices.Num()); int32 ComponentTransformBaseIndex = GeometryCollection.AddElements(ComponentsSourceIndices.Num(), FTransformCollection::TransformGroup); for (int ComponentIndex = 0; ComponentIndex < ComponentsSourceIndices.Num(); ComponentIndex++) { int32 ComponentTransformIndex = ComponentTransformBaseIndex + ComponentIndex; Parent[ComponentTransformIndex] = RootIndex; BoneName[ComponentTransformIndex] = FString::Printf(TEXT("%s_Mesh%d"), *BoneName[RootIndex], ComponentTransformIndex); LocalSpaceTransform[ComponentTransformIndex] = FTransform3f::Identity; SimulationType[ComponentTransformIndex] = FGeometryCollection::ESimulationTypes::FST_None; BoneColor[ComponentTransformIndex] = FLinearColor::MakeRandomColor(); ComponentToTransformGroupIndex[ComponentIndex] = ComponentTransformIndex; } TArray ComponentTransform; GeometryCollectionAlgo::GlobalMatrices(LocalSpaceTransform, Parent, ComponentTransform); FLinearColor DefaultColor = FLinearColor::White; TVertexInstanceAttributesConstRef InstanceColors; UE::Geometry::FDynamicMeshColorOverlay* ColorOverlay = nullptr; if (UE::Geometry::FDynamicMeshAttributeSet* Attributes = DynamicMesh.Attributes()) { FSkeletalMeshConstAttributes SkeletalMeshConstAttributes(MeshDescription); InstanceColors = SkeletalMeshConstAttributes.GetVertexInstanceColors(); if (InstanceColors.IsValid()) { ColorOverlay = Attributes->PrimaryColors(); DefaultColor = InstanceColors.GetDefaultValue(); } } // @todo(GeometryCollectionConversion) : Add support for UV's, Normals //const int32 NumUVLayers = VertexBuffers.StaticMeshVertexBuffer.GetNumTexCoords(); //GeometryCollection::UV::SetNumUVLayers(*GeometryCollection, NumUVLayers); //GeometryCollection::UV::FUVLayers UVLayers = GeometryCollection::UV::FindActiveUVLayers(*GeometryCollection); TArray TargetVertexVisited; TargetVertexVisited.Init(false, NumTargetVertices); for (int ComponentIndex = 0;ComponentIndex 2 // SourceTriangleIndex is a TriangleID from the DynamicMesh // TargetVertexIndex is the index for the vertex in the GeometryCollection int32 SourceInstanceID = ColorOverlay->GetTriangle(SourceTriangleIndex)[k]; if (ColorOverlay->IsElement(SourceInstanceID)) { FVector4f InstColor = ColorOverlay->GetElement(SourceInstanceID); if (Converter.bTransformVertexColorsLinearToSRGB) { UE::Geometry::LinearColors::SRGBToLinear(InstColor); } Color[TargetVertexIndex] = FLinearColor(InstColor); } } // @todo(GeometryCollectionConversion) : Add support for UV's, Normals //TangentU[VertexOffset] = VertexBuffers.StaticMeshVertexBuffer.VertexTangentX(VertexIndex); //TangentV[VertexOffset] = VertexBuffers.StaticMeshVertexBuffer.VertexTangentY(VertexIndex); //Normal[VertexOffset] = VertexBuffers.StaticMeshVertexBuffer.VertexTangentZ(VertexIndex); //for (int32 UVLayerIdx = 0; UVLayerIdx < NumUVLayers; ++UVLayerIdx) //{ // UVLayers[UVLayerIdx][VertexOffset] = VertexBuffers.StaticMeshVertexBuffer.GetVertexUV(VertexIndex, UVLayerIdx); //} } } } } } } // Geometry Group TArray GeometryIndices; FGeometryCollection::DefineGeometrySchema(GeometryCollection); GeometryCollectionAlgo::ContiguousArray(GeometryIndices, GeometryCollection.NumElements(FGeometryCollection::GeometryGroup)); GeometryCollection.RemoveDependencyFor(FGeometryCollection::GeometryGroup); GeometryCollection.RemoveElements(FGeometryCollection::GeometryGroup, GeometryIndices); ::GeometryCollection::AddGeometryProperties(&GeometryCollection); GeometryCollection.CopyTo(InManagedArrayCollection); return true; #else return false; #endif } void FGeometryCollectionEngineConversion::AppendSkeleton(const USkeleton* InSkeleton, const FTransform& SkeletalMeshTransform, FManagedArrayCollection* InCollection) { //UE_LOG(UGeometryCollectionConversionLogging, Log, TEXT("FGeometryCollectionEngineConversion::AppendSkeletalMesh()")); if (!InCollection || !InSkeleton) { return; } FGeometryCollection::DefineTransformSchema(*InCollection); GeometryCollection::Facades::FTransformSource TransformSourceFacade(*InCollection); TManagedArray& Transform = InCollection->ModifyAttribute(FTransformCollection::TransformAttribute, FTransformCollection::TransformGroup); TManagedArray& BoneColor = InCollection->ModifyAttribute("BoneColor", FTransformCollection::TransformGroup); TManagedArray& BoneName = InCollection->ModifyAttribute("BoneName", FTransformCollection::TransformGroup); TManagedArray& Parent = InCollection->ModifyAttribute(FTransformCollection::ParentAttribute, FTransformCollection::TransformGroup); TManagedArray< TSet >& Child = InCollection->ModifyAttribute< TSet >(FTransformCollection::ChildrenAttribute, FTransformCollection::TransformGroup); const FReferenceSkeleton& Skeleton = InSkeleton->GetReferenceSkeleton(); int32 NumBones = Skeleton.GetNum(); if (NumBones) { const TArray& RestTransform = Skeleton.GetRefBonePose(); const TArray& BoneInfo = Skeleton.GetRefBoneInfo(); TSet Roots; int32 TransformBaseIndex = InCollection->AddElements(NumBones, FGeometryCollection::TransformGroup); for (int i = 0, Idx = TransformBaseIndex; i < NumBones; i++, Idx++) { Transform[Idx] = FTransform3f(RestTransform[i]); BoneColor[Idx] = FLinearColor(FColor(FMath::Rand() % 100 + 5, FMath::Rand() % 100 + 5, FMath::Rand() % 100 + 5, 255)); BoneName[Idx] = BoneInfo[i].Name.ToString(); Parent[Idx] = BoneInfo[i].ParentIndex; if (Parent[Idx] != INDEX_NONE) { Child[Parent[Idx]].Add(Idx); } else { Roots.Add(Idx); } } ensure(Roots.Num()); TransformSourceFacade.AddTransformSource(InSkeleton->GetName(), InSkeleton->GetGuid().ToString(), Roots); } } const FSkeletalMeshLODRenderData* FGeometryCollectionEngineConversion::GetSkeletalMeshLOD(const USkeletalMesh* SkeletalMesh, int32 LOD) { if (const USkeleton* Skeleton = SkeletalMesh->GetSkeleton()) { if (const FSkeletalMeshRenderData* SkelMeshRenderData = SkeletalMesh->GetResourceForRendering()) { if (SkelMeshRenderData->LODRenderData.IsValidIndex(LOD)) { return &SkelMeshRenderData->LODRenderData[LOD]; } } } return nullptr; } void FGeometryCollectionEngineConversion::AppendSkeletalMesh(const USkeletalMesh* SkeletalMesh, const USkeletalMeshComponent* SkeletalMeshComponent, const FTransform& SkeletalMeshTransform, UGeometryCollection* GeometryCollectionObject, bool bReindexMaterials) { //UE_LOG(UGeometryCollectionConversionLogging, Log, TEXT("FGeometryCollectionEngineConversion::AppendSkeletalMesh()")); check(SkeletalMesh); if (GeometryCollectionObject) { TSharedPtr GeometryCollectionPtr = GeometryCollectionObject->GetGeometryCollection(); if (FGeometryCollection* GeometryCollection = GeometryCollectionPtr.Get()) { int32 MaterialStart = GeometryCollectionObject->Materials.Num(); if (AppendSkeletalMesh(SkeletalMesh, MaterialStart, SkeletalMeshTransform, GeometryCollection, bReindexMaterials)) { AppendSkeletalMeshMaterials(SkeletalMesh, SkeletalMeshComponent, GeometryCollectionObject); } } } } int32 FGeometryCollectionEngineConversion::AppendSkeletalMeshMaterials(const USkeletalMesh* SkeletalMesh, const USkeletalMeshComponent* SkeletalMeshComponent, UGeometryCollection* GeometryCollectionObject) { check(SkeletalMesh); check(SkeletalMeshComponent); check(GeometryCollectionObject); const TArray& SkeletalMeshMaterials = SkeletalMesh->GetMaterials(); int32 CurrIdx = 0; UMaterialInterface* CurrMaterial = SkeletalMeshComponent ? SkeletalMeshComponent->GetMaterial(CurrIdx) : ToRawPtr(SkeletalMeshMaterials[CurrIdx].MaterialInterface); int MaterialStart = GeometryCollectionObject->Materials.Num(); while (CurrMaterial) { GeometryCollectionObject->Materials.Add(CurrMaterial); CurrMaterial = SkeletalMeshComponent ? SkeletalMeshComponent->GetMaterial(++CurrIdx) : ToRawPtr(SkeletalMeshMaterials[++CurrIdx].MaterialInterface); } return MaterialStart; } void FGeometryCollectionEngineConversion::AppendGeometryCollectionSource(const FGeometryCollectionSource& GeometryCollectionSource, FGeometryCollection& GeometryCollectionInOut, TArray& MaterialsInOut, bool ReindexMaterials) { if (const UObject* SourceObject = GeometryCollectionSource.SourceGeometryObject.TryLoad()) { const int32 StartMaterialIndex = MaterialsInOut.Num(); MaterialsInOut.Append(GeometryCollectionSource.SourceMaterial); if (const UStaticMesh* SourceStaticMesh = Cast(SourceObject)) { bool bLegacyAddInternal = GeometryCollectionSource.bAddInternalMaterials; AppendStaticMesh( SourceStaticMesh, StartMaterialIndex, GeometryCollectionSource.LocalTransform, &GeometryCollectionInOut, ReindexMaterials, bLegacyAddInternal, GeometryCollectionSource.bSplitComponents, GeometryCollectionSource.bSetInternalFromMaterialIndex ); } else if (const USkeletalMesh* SourceSkeletalMesh = Cast(SourceObject)) { AppendSkeletalMesh( SourceSkeletalMesh, StartMaterialIndex, GeometryCollectionSource.LocalTransform, &GeometryCollectionInOut, ReindexMaterials ); } else if (const UGeometryCollection* SourceGeometryCollection = Cast(SourceObject)) { AppendGeometryCollection( SourceGeometryCollection->GetGeometryCollection().Get(), StartMaterialIndex, GeometryCollectionSource.LocalTransform, &GeometryCollectionInOut, ReindexMaterials ); } } } void FGeometryCollectionEngineConversion::ConvertStaticMeshToGeometryCollection(const TObjectPtr StaticMesh, FManagedArrayCollection& OutCollection, TArray>& OutMaterials, TArray& OutInstancedMeshes, bool bSetInternalFromMaterialIndex, bool bSplitComponents) { #if WITH_EDITORONLY_DATA if (UGeometryCollection* NewGeometryCollection = NewObject()) { // If any of the static meshes have Nanite enabled, also enable on the new geometry collection asset for convenience. NewGeometryCollection->EnableNanite |= StaticMesh->IsNaniteEnabled(); FTransform ComponentTransform = FTransform::Identity; // Record the contributing source on the asset. FSoftObjectPath SourceSoftObjectPath(StaticMesh); // Materials TArray> MatArr; for (auto& StaticMaterial : StaticMesh->GetStaticMaterials()) { MatArr.Emplace(StaticMaterial.MaterialInterface); } TArray> SourceMaterials(MatArr); // InstanceMeshes FGeometryCollectionAutoInstanceMesh NewInstanceMesh; NewInstanceMesh.Mesh = StaticMesh; NewInstanceMesh.Materials = SourceMaterials; OutInstancedMeshes.Emplace(NewInstanceMesh); bool bAddInternalMaterials = false; NewGeometryCollection->GeometrySource.Emplace(SourceSoftObjectPath, ComponentTransform, SourceMaterials, bSplitComponents, bSetInternalFromMaterialIndex); FGeometryCollectionEngineConversion::AppendStaticMesh(StaticMesh, SourceMaterials, ComponentTransform, NewGeometryCollection, false, bAddInternalMaterials, bSplitComponents, bSetInternalFromMaterialIndex); NewGeometryCollection->InitializeMaterials(); // Materials for (auto& Material : NewGeometryCollection->Materials) { OutMaterials.Emplace(Material->GetMaterial()); } TSharedPtr OutCollectionPtr = NewGeometryCollection->GetGeometryCollection(); OutCollectionPtr->CopyTo(&OutCollection); } #endif //WITH_EDITORONLY_DATA } void FGeometryCollectionEngineConversion::ConvertGeometryCollectionToGeometryCollection(const TObjectPtr InGeometryCollectionAssetPtr, FManagedArrayCollection& OutCollection, TArray>& OutMaterials, TArray& OutInstancedMeshes) { if (InGeometryCollectionAssetPtr) { const TSharedPtr NewGeometryCollectionPtr = InGeometryCollectionAssetPtr->GetGeometryCollection(); // Materials for (auto& Material : InGeometryCollectionAssetPtr->Materials) { OutMaterials.Emplace(Material->GetMaterial()); } // InstanceMeshes OutInstancedMeshes = InGeometryCollectionAssetPtr->AutoInstanceMeshes; if (NewGeometryCollectionPtr) { NewGeometryCollectionPtr->CopyTo(&OutCollection); } } } void FGeometryCollectionEngineConversion::ConvertActorToGeometryCollection(const AActor* Actor, FManagedArrayCollection& OutCollection, TArray>& OutMaterials, TArray& OutInstancedMeshes, bool bSplitComponents) { #if WITH_EDITORONLY_DATA const FTransform ActorTransform(Actor->GetTransform()); if (UGeometryCollection* NewGeometryCollection = NewObject()) { TInlineComponentArray StaticMeshComponents(Actor); for (UStaticMeshComponent* StaticMeshComponent : StaticMeshComponents) { if (StaticMeshComponent) { if (const UStaticMesh* ComponentStaticMesh = StaticMeshComponent->GetStaticMesh()) { NewGeometryCollection->EnableNanite |= ComponentStaticMesh->IsNaniteEnabled(); FTransform ComponentTransform(StaticMeshComponent->GetComponentTransform()); ComponentTransform.SetTranslation((ComponentTransform.GetTranslation() - ActorTransform.GetTranslation())); // Record the contributing source on the asset. FSoftObjectPath SourceSoftObjectPath(ComponentStaticMesh); TArray> SourceMaterials(StaticMeshComponent->GetMaterials()); NewGeometryCollection->GeometrySource.Emplace(SourceSoftObjectPath, ComponentTransform, SourceMaterials, bSplitComponents, true/*bSetInternalFromMaterialIndex*/); FGeometryCollectionEngineConversion::AppendStaticMesh(ComponentStaticMesh, SourceMaterials, ComponentTransform, NewGeometryCollection, false/*bReindexMaterials*/, false/*bAddInternalMaterials*/, bSplitComponents, true/*bSetInternalFromMaterialIndex*/); } } } TInlineComponentArray GeometryCollectionComponents(Actor); for (UGeometryCollectionComponent* GeometryCollectionComponent : GeometryCollectionComponents) { if (GeometryCollectionComponent) { if (const UGeometryCollection* RestCollection = GeometryCollectionComponent->GetRestCollection()) { NewGeometryCollection->EnableNanite |= RestCollection->EnableNanite; FTransform ComponentTransform(GeometryCollectionComponent->GetComponentTransform()); ComponentTransform.SetTranslation((ComponentTransform.GetTranslation() - ActorTransform.GetTranslation())); // Record the contributing source on the asset. FSoftObjectPath SourceSoftObjectPath(RestCollection); int32 NumMaterials = GeometryCollectionComponent->GetNumMaterials(); TArray> SourceMaterials; SourceMaterials.SetNum(NumMaterials); for (int32 MaterialIndex = 0; MaterialIndex < NumMaterials; ++MaterialIndex) { SourceMaterials[MaterialIndex] = GeometryCollectionComponent->GetMaterial(MaterialIndex); } NewGeometryCollection->GeometrySource.Emplace(SourceSoftObjectPath, ComponentTransform, SourceMaterials, bSplitComponents, true/*bSetInternalFromMaterialIndex*/); FGeometryCollectionEngineConversion::AppendGeometryCollection(RestCollection, GeometryCollectionComponent, ComponentTransform, NewGeometryCollection, false /*bReindexMaterials*/); } } } NewGeometryCollection->InitializeMaterials(); // InstanceMeshes OutInstancedMeshes.Append(NewGeometryCollection->AutoInstanceMeshes); // Materials for (auto& Material : NewGeometryCollection->Materials) { OutMaterials.Emplace(Material->GetMaterial()); } TSharedPtr OutCollectionPtr = NewGeometryCollection->GetGeometryCollection(); OutCollectionPtr->CopyTo(&OutCollection); } #endif //WITH_EDITORONLY_DATA } #undef LOCTEXT_NAMESPACE