Files
2025-05-18 13:04:45 +08:00

1875 lines
85 KiB
C++

// 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 "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 "Misc/ScopedSlowTask.h"
#include "Physics/Experimental/ChaosInterfaceUtils.h"
#include "PhysicsEngine/BodySetup.h"
#include "Rendering/SkeletalMeshRenderData.h"
#include "ReferenceSkeleton.h"
#include "SkeletalMeshAttributes.h"
#include "StaticMeshAttributes.h"
#include "StaticMeshOperations.h"
#include "VertexConnectedComponents.h"
#include "Util/ColorConstants.h"
#include "GeometryCollection/Facades/CollectionVertexBoneWeightsFacade.h"
DEFINE_LOG_CATEGORY_STATIC(UGeometryCollectionConversionLogging, Log, All);
#define LOCTEXT_NAMESPACE "GeometryCollectionConversion"
struct FUniqueVertex
{
FVector3f Normal;
FVector3f Tangent;
TArray<FVector2f> 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<const Chaos::FImplicitObjectTransformed&>(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<FStaticMeshSourceModel>& SourceModels = StaticMesh.GetSourceModels();
if (SourceModels.Num() > 0)
{
return SourceModels[0].BuildSettings.BuildScale3D;
}
#endif
return FVector::One();
}
static void SetExternalCollisions(FVector3d MeshScale, UBodySetup* BodySetup, FGeometryCollection* GeometryCollection, int32 TransformIndex)
{
if (BodySetup)
{
TArray<Chaos::FImplicitObjectPtr> 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 = MeshScale;
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<Chaos::FImplicitObjectPtr>& ExternalCollisions = GeometryCollection->AddAttribute<Chaos::FImplicitObjectPtr>(FGeometryCollection::ExternalCollisionsAttribute, FGeometryCollection::TransformGroup);
ExternalCollisions[TransformIndex] = 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)
{
ExternalCollisions[TransformIndex] = MakeImplicitObjectPtr<Chaos::FImplicitObjectUnion>(MoveTemp(Geoms));
}
}
}
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<const FVector3f> SourcePosition = Attributes.GetVertexPositions().GetRawArray();
TArrayView<const FVector3f> SourceTangent = Attributes.GetVertexInstanceTangents().GetRawArray();
TArrayView<const float> SourceBinormalSign = Attributes.GetVertexInstanceBinormalSigns().GetRawArray();
TArrayView<const FVector3f> SourceNormal = Attributes.GetVertexInstanceNormals().GetRawArray();
TArrayView<const FVector4f> SourceColor = Attributes.GetVertexInstanceColors().GetRawArray();
TVertexInstanceAttributesConstRef<FVector2f> InstanceUVs = Attributes.GetVertexInstanceUVs();
const int32 NumUVLayers = InstanceUVs.GetNumChannels();
TArray<TArrayView<const FVector2f>> SourceUVArrays;
SourceUVArrays.SetNum(NumUVLayers);
for (int32 UVLayerIdx = 0; UVLayerIdx < NumUVLayers; ++UVLayerIdx)
{
SourceUVArrays[UVLayerIdx] = InstanceUVs.GetRawArray(UVLayerIdx);
}
// target vertex information
TManagedArray<FVector3f>& TargetVertex = GeometryCollection->Vertex;
TManagedArray<FVector3f>& TargetTangentU = GeometryCollection->TangentU;
TManagedArray<FVector3f>& TargetTangentV = GeometryCollection->TangentV;
TManagedArray<FVector3f>& TargetNormal = GeometryCollection->Normal;
TManagedArray<FLinearColor>& TargetColor = GeometryCollection->Color;
TManagedArray<int32>& TargetBoneMap = GeometryCollection->BoneMap;
TManagedArray<FLinearColor>& TargetBoneColor = GeometryCollection->BoneColor;
TManagedArray<FString>& 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<FVertexInstanceID, int32> 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<const FVertexInstanceID> ReferencingVertexInstances = MeshDescription->GetVertexVertexInstanceIDs(VertexIndex);
// Generate per instance hash of splittable attributes.
TMap<FUniqueVertex, TArray<FVertexInstanceID>> SplitVertices;
for (const FVertexInstanceID& InstanceID : ReferencingVertexInstances)
{
TArray<FVector2f> SourceUVs;
SourceUVs.SetNum(NumUVLayers);
for (int32 UVLayerIdx = 0; UVLayerIdx < NumUVLayers; ++UVLayerIdx)
{
SourceUVs[UVLayerIdx] = SourceUVArrays[UVLayerIdx][InstanceID];
}
FUniqueVertex UniqueVertex{ SourceNormal[InstanceID], SourceTangent[InstanceID], SourceUVs };
TArray<FVertexInstanceID>& 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<FUniqueVertex,TArray<FVertexInstanceID>>& SplitVertex : SplitVertices)
{
const TArray<FVertexInstanceID>& 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<FIntVector>& TargetIndices = GeometryCollection->Indices;
TManagedArray<bool>& TargetVisible = GeometryCollection->Visible;
TManagedArray<int32>& TargetMaterialID = GeometryCollection->MaterialID;
TManagedArray<int32>& TargetMaterialIndex = GeometryCollection->MaterialIndex;
TManagedArray<bool>& 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<const FVertexInstanceID> 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<FTransform3f>& Transform = GeometryCollection->Transform;
int32 TransformIndex1 = GeometryCollection->AddElements(1, FGeometryCollection::TransformGroup);
Transform[TransformIndex1] = FTransform3f(StaticMeshTransform);
Transform[TransformIndex1].SetScale3D(FVector3f(1.f, 1.f, 1.f));
SetExternalCollisions(Scale, BodySetup, GeometryCollection, TransformIndex1);
// Bone Hierarchy - Added at root with no common parent
TManagedArray<int32>& Parent = GeometryCollection->Parent;
TManagedArray<int32>& 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<int32>& TransformIndex = GeometryCollection->TransformIndex;
TManagedArray<FBox>& BoundingBox = GeometryCollection->BoundingBox;
TManagedArray<float>& InnerRadius = GeometryCollection->InnerRadius;
TManagedArray<float>& OuterRadius = GeometryCollection->OuterRadius;
TManagedArray<int32>& VertexStartArray = GeometryCollection->VertexStart;
TManagedArray<int32>& VertexCountArray = GeometryCollection->VertexCount;
TManagedArray<int32>& FaceStartArray = GeometryCollection->FaceStart;
TManagedArray<int32>& FaceCountArray = GeometryCollection->FaceCount;
TransformIndex[GeometryIndex] = TargetBoneMap[VertexStart];
VertexStartArray[GeometryIndex] = VertexStart;
VertexCountArray[GeometryIndex] = VertexCount;
FaceStartArray[GeometryIndex] = InitialNumIndices;
FaceCountArray[GeometryIndex] = IndicesCount;
// TransformGroup
TManagedArray<int32>& 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<const FVector3f> VertexInstanceNormals = Attributes.GetVertexInstanceNormals().GetRawArray();
TArrayView<const FVector3f> 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<UMaterialInterface*>& 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<UMaterialInterface*>& Materials)
{
TSharedPtr<FGeometryCollection, ESPMode::ThreadSafe> 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<UMaterialInterface*>& 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<FGeometryCollection, ESPMode::ThreadSafe> 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<const FVertexID> 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<FVector3f> VertexPositions = MeshDescription->GetVertexPositions();
// vertex instance attributes
FStaticMeshConstAttributes Attributes(*MeshDescription);
TVertexInstanceAttributesConstRef<FVector2f> InstanceUVs = Attributes.GetVertexInstanceUVs();
TVertexInstanceAttributesConstRef<FVector3f> InstanceNormals = Attributes.GetVertexInstanceNormals();
TVertexInstanceAttributesConstRef<FVector3f> InstanceTangents = Attributes.GetVertexInstanceTangents();
TVertexInstanceAttributesConstRef<float> InstanceBiTangentSign = Attributes.GetVertexInstanceBinormalSigns();
TVertexInstanceAttributesConstRef<FVector4f> InstanceColors = Attributes.GetVertexInstanceColors();
const int NumUVLayers = InstanceUVs.GetNumChannels();
TMap<int32, int32> Map = Components.MakeComponentMap(MaxVID, 2);
int32 NumIslands = Map.Num();
TArray<FMeshDescription> Descriptions;
Descriptions.SetNum(NumIslands);
TArray<FMeshDescriptionBuilder> 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<int32, int32> IDToIdx : Map)
{
int32 ID = IDToIdx.Key;
int32 Idx = IDToIdx.Value;
int32 NumVertices = Components.GetComponentSize(ID);
Builders[Idx].ReserveNewVertices(NumVertices);
}
TArray<int32> 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<const FVertexID> TriangleVerts = MeshDescription->GetTriangleVertices(TriangleID);
TArrayView<const FVertexInstanceID> 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, nullptr /*Body Setup to be set later, on root*/,
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<FVector3f>& SourceVertex = SourceGeometryCollectionPtr->Vertex;
const TManagedArray<FVector3f>& SourceTangentU = SourceGeometryCollectionPtr->TangentU;
const TManagedArray<FVector3f>& SourceTangentV = SourceGeometryCollectionPtr->TangentV;
const TManagedArray<FVector3f>& SourceNormal = SourceGeometryCollectionPtr->Normal;
const TManagedArray<FLinearColor>& SourceColor = SourceGeometryCollectionPtr->Color;
const TManagedArray<int32>& SourceBoneMap = SourceGeometryCollectionPtr->BoneMap;
// target vertex information
TManagedArray<FVector3f>& TargetVertex = TargetGeometryCollection->Vertex;
TManagedArray<FVector3f>& TargetTangentU = TargetGeometryCollection->TangentU;
TManagedArray<FVector3f>& TargetTangentV = TargetGeometryCollection->TangentV;
TManagedArray<FVector3f>& TargetNormal = TargetGeometryCollection->Normal;
TManagedArray<FLinearColor>& TargetColor = TargetGeometryCollection->Color;
TManagedArray<int32>& 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<FIntVector>& SourceIndices = SourceGeometryCollectionPtr->Indices;
const TManagedArray<bool>& SourceVisible = SourceGeometryCollectionPtr->Visible;
const TManagedArray<int32>& SourceMaterialID = SourceGeometryCollectionPtr->MaterialID;
const TManagedArray<int32>& SourceMaterialIndex = SourceGeometryCollectionPtr->MaterialIndex;
const TManagedArray<bool>& SourceInternal = SourceGeometryCollectionPtr->Internal;
// target face information
TManagedArray<FIntVector>& TargetIndices = TargetGeometryCollection->Indices;
TManagedArray<bool>& TargetVisible = TargetGeometryCollection->Visible;
TManagedArray<int32>& TargetMaterialID = TargetGeometryCollection->MaterialID;
TManagedArray<int32>& TargetMaterialIndex = TargetGeometryCollection->MaterialIndex;
TManagedArray<bool>& 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<FTransform3f>& SourceTransform = SourceGeometryCollectionPtr->Transform;
const TManagedArray<FString>& SourceBoneName = SourceGeometryCollectionPtr->BoneName;
const TManagedArray<FLinearColor>& SourceBoneColor = SourceGeometryCollectionPtr->BoneColor;
const TManagedArray<int32>& SourceParent = SourceGeometryCollectionPtr->Parent;
const TManagedArray<TSet<int32>>& SourceChildren = SourceGeometryCollectionPtr->Children;
const TManagedArray<int32>& SourceTransformToGeometryIndex = SourceGeometryCollectionPtr->TransformToGeometryIndex;
const TManagedArray<int32>& SourceSimulationType = SourceGeometryCollectionPtr->SimulationType;
const TManagedArray<int32>& SourceStatusFlags = SourceGeometryCollectionPtr->StatusFlags;
const TManagedArray<int32>& SourceInitialDynamicState = SourceGeometryCollectionPtr->InitialDynamicState;
const TManagedArray<Chaos::FImplicitObjectPtr>* SourceExternalCollisions = SourceGeometryCollectionPtr->FindAttribute<Chaos::FImplicitObjectPtr>(FGeometryCollection::ExternalCollisionsAttribute, FGeometryCollection::TransformGroup);
// target transform information
TManagedArray<FTransform3f>& TargetTransform = TargetGeometryCollection->Transform;
TManagedArray<FString>& TargetBoneName = TargetGeometryCollection->BoneName;
TManagedArray<FLinearColor>& TargetBoneColor = TargetGeometryCollection->BoneColor;
TManagedArray<int32>& TargetParent = TargetGeometryCollection->Parent;
TManagedArray<TSet<int32>>& TargetChildren = TargetGeometryCollection->Children;
TManagedArray<int32>& TargetTransformToGeometryIndex = TargetGeometryCollection->TransformToGeometryIndex;
TManagedArray<int32>& TargetSimulationType = TargetGeometryCollection->SimulationType;
TManagedArray<int32>& TargetStatusFlags = TargetGeometryCollection->StatusFlags;
TManagedArray<int32>& TargetInitialDynamicState = TargetGeometryCollection->InitialDynamicState;
TManagedArray<Chaos::FImplicitObjectPtr>& TargetExternalCollisions = TargetGeometryCollection->AddAttribute<Chaos::FImplicitObjectPtr>(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<int32>& 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<int32>& SourceTransformIndex = SourceGeometryCollectionPtr->TransformIndex;
const TManagedArray<int32>& SourceVertexStart = SourceGeometryCollectionPtr->VertexStart;
const TManagedArray<int32>& SourceVertexCount = SourceGeometryCollectionPtr->VertexCount;
const TManagedArray<int32>& SourceFaceStart = SourceGeometryCollectionPtr->FaceStart;
const TManagedArray<int32>& SourceFaceCount = SourceGeometryCollectionPtr->FaceCount;
// target geometry information
TManagedArray<int32>& TargetTransformIndex = TargetGeometryCollection->TransformIndex;
TManagedArray<FBox>& TargetBoundingBox = TargetGeometryCollection->BoundingBox;
TManagedArray<float>& TargetInnerRadius = TargetGeometryCollection->InnerRadius;
TManagedArray<float>& TargetOuterRadius = TargetGeometryCollection->OuterRadius;
TManagedArray<int32>& TargetVertexStart = TargetGeometryCollection->VertexStart;
TManagedArray<int32>& TargetVertexCount = TargetGeometryCollection->VertexCount;
TManagedArray<int32>& TargetFaceStart = TargetGeometryCollection->FaceStart;
TManagedArray<int32>& 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<FGeometryCollectionSection>& SourceSections = SourceGeometryCollectionPtr->Sections;
// target material information
TManagedArray<FGeometryCollectionSection>& 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<UMaterialInterface*>& Materials, const FTransform& GeometryCollectionTransform, UGeometryCollection* TargetGeometryCollectionObject, bool bReindexMaterials)
{
if (SourceGeometryCollection == nullptr)
{
return;
}
const TSharedPtr<FGeometryCollection, ESPMode::ThreadSafe> SourceGeometryCollectionPtr = SourceGeometryCollection->GetGeometryCollection();
check(TargetGeometryCollectionObject);
TSharedPtr<FGeometryCollection, ESPMode::ThreadSafe> 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<UMaterialInterface*> 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<UMaterialInterface>(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<UMaterialInterface*> 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<UMaterialInterface>(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<const FGeometryCollection, ESPMode::ThreadSafe> SourceGeometryCollectionPtr = SourceGeometryCollectionObject->GetGeometryCollection();
TSharedPtr<FGeometryCollection, ESPMode::ThreadSafe> 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 (SourceGeometryCollectionObject->AutoInstanceMeshes.IsValidIndex(SourceAutoInstanceIndex))
{
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<FGeometryCollection, ESPMode::ThreadSafe> SourceGeometryCollectionPtr = SourceGeometryCollection->GetGeometryCollection();
check(TargetGeometryCollectionObject);
TSharedPtr<FGeometryCollection, ESPMode::ThreadSafe> 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, bool bImportTransformOnly)
{
//UE_LOG(UGeometryCollectionConversionLogging, Log, TEXT("FGeometryCollectionEngineConversion::AppendSkeletalMesh()"));
#if WITH_EDITOR
int LODIndex = 0;
if (!InManagedArrayCollection || !InSkeletalMesh)
{
return false;
}
FGeometryCollection GeometryCollection;
// Transform Attributes
TManagedArray<FTransform3f>& LocalSpaceTransform = GeometryCollection.ModifyAttribute<FTransform3f>(FTransformCollection::TransformAttribute, FTransformCollection::TransformGroup);
TManagedArray<int32>& Parent = GeometryCollection.ModifyAttribute<int32>(FTransformCollection::ParentAttribute, FTransformCollection::TransformGroup);
TManagedArray<TSet<int32>>& Children = GeometryCollection.ModifyAttribute<TSet<int32>>(FTransformCollection::ChildrenAttribute, FTransformCollection::TransformGroup);
TManagedArray<FLinearColor>& BoneColor = GeometryCollection.ModifyAttribute<FLinearColor>("BoneColor", FTransformCollection::TransformGroup);
TManagedArray<FString>& BoneName = GeometryCollection.ModifyAttribute<FString>("BoneName", FTransformCollection::TransformGroup);
TManagedArray<int32>& SimulationType = GeometryCollection.ModifyAttribute<int32>("SimulationType", FTransformCollection::TransformGroup);
//
// Convert the transform hierarchy
//
int32 RootIndex = INDEX_NONE;
int32 TransformBaseIndex = INDEX_NONE;
const USkeleton* Skeleton = InSkeletalMesh->GetSkeleton();
const FReferenceSkeleton& ReferenceSkeleton = InSkeletalMesh->GetRefSkeleton();
if (Skeleton && ReferenceSkeleton.GetNum())
{
const TArray<FTransform>& RestArray = ReferenceSkeleton.GetRefBonePose();
TransformBaseIndex = GeometryCollection.AddElements(ReferenceSkeleton.GetNum(), FGeometryCollection::TransformGroup);
RootIndex = TransformBaseIndex;
for (int32 BoneIndex = 0; BoneIndex < ReferenceSkeleton.GetNum(); BoneIndex++)
{
// For validation against the component space position use
// FTransform ComponentSpaceTransform =FAnimationRuntime::GetComponentSpaceTransformRefPose(ReferenceSkeleton, SkeletalBoneMap[BoneIndex]);
LocalSpaceTransform[TransformBaseIndex + BoneIndex] = FTransform3f(RestArray[BoneIndex]);
BoneName[TransformBaseIndex + BoneIndex] = ReferenceSkeleton.GetRefBoneInfo()[BoneIndex].Name.ToString();
Parent[TransformBaseIndex + BoneIndex] = ReferenceSkeleton.GetRefBoneInfo()[BoneIndex].ParentIndex;
TArray<int32> ChildrenArr;
if (ReferenceSkeleton.GetDirectChildBones(BoneIndex, ChildrenArr))
{
Children[TransformBaseIndex + BoneIndex].Append(ChildrenArr);
}
SimulationType[TransformBaseIndex + BoneIndex] = FGeometryCollection::ESimulationTypes::FST_None;
BoneColor[TransformBaseIndex + BoneIndex] = FLinearColor::MakeRandomColor();
if (Parent[TransformBaseIndex + BoneIndex] == INDEX_NONE)
{
RootIndex = TransformBaseIndex + BoneIndex;
}
}
GeometryCollection::Facades::FTransformSource TransformSourceFacade(*InManagedArrayCollection);
TSet<int32> Roots;
Roots.Add(RootIndex);
TransformSourceFacade.AddTransformSource(Skeleton->GetName(), Skeleton->GetGuid().ToString(), Roots, InSkeletalMesh->GetName());
}
if (bImportTransformOnly)
{
GeometryCollection.CopyTo(InManagedArrayCollection);
return true;
}
FMeshDescription MeshDescription;
if (!InSkeletalMesh->CloneMeshDescription(LODIndex, MeshDescription))
{
return false;
}
// Vertices Attributes
TManagedArray<FVector3f>& Vertex = GeometryCollection.ModifyAttribute<FVector3f>("Vertex", FGeometryCollection::VerticesGroup);
TManagedArray<FVector3f>& Normal = GeometryCollection.ModifyAttribute<FVector3f>("Normal", FGeometryCollection::VerticesGroup);
TManagedArray<FLinearColor>& Color = GeometryCollection.ModifyAttribute<FLinearColor>("Color", FGeometryCollection::VerticesGroup);
TManagedArray<FVector3f>& TangentU = GeometryCollection.ModifyAttribute<FVector3f>("TangentU", FGeometryCollection::VerticesGroup);
TManagedArray<FVector3f>& TangentV = GeometryCollection.ModifyAttribute<FVector3f>("TangentV", FGeometryCollection::VerticesGroup);
TManagedArray<int32>& BoneMap = GeometryCollection.ModifyAttribute<int32>("BoneMap", FGeometryCollection::VerticesGroup);
// Index Attributes
TManagedArray<FIntVector>& Indices = GeometryCollection.ModifyAttribute<FIntVector>("Indices", FGeometryCollection::FacesGroup);
TManagedArray<bool>& Visible = GeometryCollection.ModifyAttribute<bool>("Visible", FGeometryCollection::FacesGroup);
TManagedArray<int32>& MaterialIndex = GeometryCollection.ModifyAttribute<int32>("MaterialIndex", FGeometryCollection::FacesGroup);
TManagedArray<int32>& MaterialID = GeometryCollection.ModifyAttribute<int32>("MaterialID", FGeometryCollection::FacesGroup);
//
// Identify disconnected geoemtry
//
int32 VertexCount = 0, TriangleCount = 0;
TArray<int32> SourceVertexToComponentMap; // Map from mesh vertex index to target vertex index
TArray<TArray<FIntVector>> ComponentsSourceIndices; // Mesh triangle indices of each component.
TArray<TArray<FIntVector2>> SourceToTargetTriangleMap; // Mesh triangle index of each triangle in the component.
GeometryCollectionEngineUtility::GenerateConnectedComponents(InSkeletalMesh, ComponentsSourceIndices,
SourceToTargetTriangleMap, SourceVertexToComponentMap, TriangleCount, VertexCount);
//
// Vertex Attributes
//
int VertexBaseIndex = GeometryCollection.AddElements(VertexCount, FGeometryCollection::VerticesGroup);
int NumTargetVertices = GeometryCollection.NumElements(FGeometryCollection::VerticesGroup);
//
// Transform Attributes
//
auto MakeUnique = [&BoneName](FString& NewName, int32& CurrentIndex)
{
FString TestName = FString::Printf(TEXT("%s%d"), *NewName, CurrentIndex);
while (BoneName.Contains(TestName))
{
TestName = FString::Printf(TEXT("%s%d"), *NewName, CurrentIndex);
CurrentIndex++;
}
return TestName;
};
// add transforms for the separated geometry components.
int32 SplitMeshIndex = 1;
TArray<int32> ComponentToTransformGroupIndex;
ComponentToTransformGroupIndex.Init(INDEX_NONE, ComponentsSourceIndices.Num());
int32 ComponentTransformBaseIndex = GeometryCollection.AddElements(ComponentsSourceIndices.Num(), FTransformCollection::TransformGroup);
for (int ComponentIndex = 0; ComponentIndex < ComponentsSourceIndices.Num(); ComponentIndex++)
{
FString BaseName("SplitMesh");
int32 ComponentTransformIndex = ComponentTransformBaseIndex + ComponentIndex;
Parent[ComponentTransformIndex] = RootIndex;
if (RootIndex != INDEX_NONE)
{
Children[RootIndex].Add(ComponentTransformIndex);
BaseName = FString::Printf(TEXT("%s_SplitMesh"), *InSkeletalMesh->GetName());
}
BoneName[ComponentTransformIndex] = MakeUnique(BaseName, SplitMeshIndex);
LocalSpaceTransform[ComponentTransformIndex] = FTransform3f::Identity;
SimulationType[ComponentTransformIndex] = FGeometryCollection::ESimulationTypes::FST_None;
BoneColor[ComponentTransformIndex] = FLinearColor::MakeRandomColor();
ComponentToTransformGroupIndex[ComponentIndex] = ComponentTransformIndex;
SplitMeshIndex++;
}
TArray<FTransform> ComponentTransform;
GeometryCollectionAlgo::GlobalMatrices(LocalSpaceTransform, Parent, ComponentTransform);
FSkeletalMeshConstAttributes SkeletalMeshConstAttributes(MeshDescription);
// normal detection
const bool bHasNormals = MeshDescription.VertexInstanceAttributes().HasAttribute(MeshAttribute::VertexInstance::Normal);
// color attribute detection
bool bHasVertexColors = false;
FLinearColor DefaultColor = FLinearColor::White;
TVertexInstanceAttributesConstRef<FVector4f> InstanceColors = SkeletalMeshConstAttributes.GetVertexInstanceColors();
if (InstanceColors.IsValid())
{
bHasVertexColors = true;
DefaultColor = InstanceColors.GetDefaultValue();
}
// vertex weight attribute detection
FSkinWeightsVertexAttributesConstRef VertexSkinWeights = SkeletalMeshConstAttributes.GetVertexSkinWeights();
const bool bHasVertexSkinWeights = VertexSkinWeights.IsValid();
GeometryCollection::Facades::FVertexBoneWeightsFacade VertexBoneWeightsFacade(GeometryCollection);
TArray<int32> VertexBoneIndex;
TArray<float> VertexBoneWeight;
// @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<bool> TargetVertexVisited; TargetVertexVisited.Init(false, NumTargetVertices);
for (int32 ComponentIndex = 0;ComponentIndex<ComponentsSourceIndices.Num(); ComponentIndex++)
{
for (int32 TriangleIndex = 0; TriangleIndex < ComponentsSourceIndices[ComponentIndex].Num(); TriangleIndex++)
{
const int32 SourceTriangleIndex = SourceToTargetTriangleMap[ComponentIndex][TriangleIndex][0];
const FIntVector& Triangle = ComponentsSourceIndices[ComponentIndex][TriangleIndex];
for (int TriVtxIdx = 0; TriVtxIdx < 3; TriVtxIdx++)
{
int SourceVertexIndex = Triangle[TriVtxIdx];
if (ensure(0 <= SourceVertexIndex && SourceVertexIndex < SourceVertexToComponentMap.Num()))
{
int TargetVertexIndex = SourceVertexToComponentMap[SourceVertexIndex] + VertexBaseIndex;
if (ensure(VertexBaseIndex <= TargetVertexIndex && TargetVertexIndex < TargetVertexVisited.Num()))
{
if (!TargetVertexVisited[TargetVertexIndex])
{
TargetVertexVisited[TargetVertexIndex] = true;
// Vertex positions
BoneMap[TargetVertexIndex] = ComponentToTransformGroupIndex[ComponentIndex];
FVector SourceVertex = (FVector)MeshDescription.GetVertexPosition(SourceVertexIndex);
FMatrix M = ComponentTransform[BoneMap[TargetVertexIndex]].ToInverseMatrixWithScale();
Vertex[TargetVertexIndex] = (FVector4f)M.TransformPosition(SourceVertex);
// Get Avg Vertex Normals
// TODO(chaos) - once we support UV and split vertices accordingly, we can use the actual normal instead of averaging it
if (bHasNormals)
{
Normal[TargetVertexIndex] = FVector3f::ZeroVector;
const TArrayView<const FVertexInstanceID> VertexInstances = MeshDescription.GetVertexVertexInstanceIDs(SourceVertexIndex);
for (const FVertexInstanceID VertexInstanceID : VertexInstances)
{
const FVector3f VertexInstanceNormal = MeshDescription.VertexInstanceAttributes().GetAttribute<FVector3f>(VertexInstanceID, MeshAttribute::VertexInstance::Normal);
Normal[TargetVertexIndex] += VertexInstanceNormal / VertexInstances.Num();
}
Normal[TargetVertexIndex].Normalize();
}
// Vertex Colors
Color[TargetVertexIndex] = DefaultColor;
if (bHasVertexColors)
{
TArrayView<const FVertexInstanceID> SourceInstanceTri = MeshDescription.GetTriangleVertexInstances(SourceTriangleIndex);
FVector4f InstColor = InstanceColors[SourceInstanceTri[TriVtxIdx]];
UE::Geometry::LinearColors::SRGBToLinear(InstColor);
Color[TargetVertexIndex] = FLinearColor(InstColor);
}
// Vertex skin weights
if (bHasVertexSkinWeights)
{
const FVertexBoneWeightsConst BoneWeights = VertexSkinWeights.Get(FVertexID(SourceVertexIndex));
const int32 InfluenceCount = BoneWeights.Num();
VertexBoneIndex.SetNum(InfluenceCount);
VertexBoneWeight.SetNum(InfluenceCount);
for (int32 InfluenceIndex = 0; InfluenceIndex < InfluenceCount; ++InfluenceIndex)
{
VertexBoneIndex[InfluenceIndex] = BoneWeights[InfluenceIndex].GetBoneIndex();
VertexBoneWeight[InfluenceIndex] = BoneWeights[InfluenceIndex].GetWeight();
}
VertexBoneWeightsFacade.ModifyBoneWeight(TargetVertexIndex, VertexBoneIndex, VertexBoneWeight);
}
// @todo(GeometryCollectionConversion) : Add support for UV's
//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);
//}
}
}
}
}
}
}
//
// Add the Triangles to the Geometry Collection
//.. ensure all component vertices are contigious in the array
//
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 TriVtxIdx = 0; TriVtxIdx < 3; TriVtxIdx++)
{
Indices[CurrentIndex][TriVtxIdx] = SourceVertexToComponentMap[Triangle[TriVtxIdx]] + VertexBaseIndex;
}
Visible[CurrentIndex] = true;
MaterialID[CurrentIndex] = 0;
MaterialIndex[CurrentIndex] = CurrentIndex;
CurrentIndex++;
}
}
// Geometry Group
TArray<int32> GeometryIndices;
FGeometryCollection::DefineGeometrySchema(GeometryCollection);
GeometryCollectionAlgo::ContiguousArray(GeometryIndices, GeometryCollection.NumElements(FGeometryCollection::GeometryGroup));
GeometryCollection.RemoveDependencyFor(FGeometryCollection::GeometryGroup);
GeometryCollection.RemoveElements(FGeometryCollection::GeometryGroup, GeometryIndices);
::GeometryCollection::AddGeometryProperties(&GeometryCollection);
// copy to the target collection
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<FTransform3f>& Transform = InCollection->ModifyAttribute<FTransform3f>(FTransformCollection::TransformAttribute, FTransformCollection::TransformGroup);
TManagedArray<FLinearColor>& BoneColor = InCollection->ModifyAttribute<FLinearColor>("BoneColor", FTransformCollection::TransformGroup);
TManagedArray<FString>& BoneName = InCollection->ModifyAttribute<FString>("BoneName", FTransformCollection::TransformGroup);
TManagedArray<int32>& Parent = InCollection->ModifyAttribute<int32>(FTransformCollection::ParentAttribute, FTransformCollection::TransformGroup);
TManagedArray< TSet<int32> >& Child = InCollection->ModifyAttribute< TSet<int32> >(FTransformCollection::ChildrenAttribute, FTransformCollection::TransformGroup);
const FReferenceSkeleton& Skeleton = InSkeleton->GetReferenceSkeleton();
int32 NumBones = Skeleton.GetNum();
if (NumBones)
{
const TArray<FTransform>& RestTransform = Skeleton.GetRefBonePose();
const TArray<FMeshBoneInfo>& BoneInfo = Skeleton.GetRefBoneInfo();
TSet<int32> 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, FString() /*SKMName*/);
}
}
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<FGeometryCollection, ESPMode::ThreadSafe> GeometryCollectionPtr = GeometryCollectionObject->GetGeometryCollection();
if (FGeometryCollection* GeometryCollection = GeometryCollectionPtr.Get())
{
int32 MaterialStart = GeometryCollectionObject->Materials.Num();
constexpr bool bImportTransformOnly = false;
if (AppendSkeletalMesh(SkeletalMesh, MaterialStart, SkeletalMeshTransform, GeometryCollection, bReindexMaterials, bImportTransformOnly))
{
AppendSkeletalMeshMaterials(SkeletalMesh, SkeletalMeshComponent, GeometryCollectionObject);
}
}
}
}
int32 FGeometryCollectionEngineConversion::AppendSkeletalMeshMaterials(const USkeletalMesh* SkeletalMesh, const USkeletalMeshComponent* SkeletalMeshComponent, UGeometryCollection* GeometryCollectionObject)
{
check(SkeletalMesh);
check(SkeletalMeshComponent);
check(GeometryCollectionObject);
const TArray<FSkeletalMaterial>& 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<UMaterial*>& MaterialsInOut, bool ReindexMaterials)
{
const int32 StartMaterialIndex = MaterialsInOut.Num();
if (AppendGeometryCollectionSourceNoMaterial(GeometryCollectionSource, GeometryCollectionInOut, StartMaterialIndex, ReindexMaterials))
{
MaterialsInOut.Append(GeometryCollectionSource.SourceMaterial);
}
}
void FGeometryCollectionEngineConversion::AppendGeometryCollectionSource(const FGeometryCollectionSource& GeometryCollectionSource, FGeometryCollection& GeometryCollectionInOut, TArray<UMaterialInterface*>& MaterialInstancesInOut, bool ReindexMaterials)
{
const int32 StartMaterialIndex = MaterialInstancesInOut.Num();
if (AppendGeometryCollectionSourceNoMaterial(GeometryCollectionSource, GeometryCollectionInOut, StartMaterialIndex, ReindexMaterials))
{
MaterialInstancesInOut.Append(GeometryCollectionSource.SourceMaterial);
}
}
bool FGeometryCollectionEngineConversion::AppendGeometryCollectionSourceNoMaterial(const FGeometryCollectionSource& GeometryCollectionSource, FGeometryCollection& GeometryCollectionInOut, int32 StartMaterialIndex, bool ReindexMaterials)
{
if (const UObject* SourceObject = GeometryCollectionSource.SourceGeometryObject.TryLoad())
{
if (const UStaticMesh* SourceStaticMesh = Cast<UStaticMesh>(SourceObject))
{
bool bLegacyAddInternal = GeometryCollectionSource.bAddInternalMaterials;
AppendStaticMesh(
SourceStaticMesh,
StartMaterialIndex,
GeometryCollectionSource.LocalTransform,
&GeometryCollectionInOut,
ReindexMaterials,
bLegacyAddInternal,
GeometryCollectionSource.bSplitComponents,
GeometryCollectionSource.bSetInternalFromMaterialIndex
);
return true;
}
else if (const USkeletalMesh* SourceSkeletalMesh = Cast<USkeletalMesh>(SourceObject))
{
AppendSkeletalMesh(
SourceSkeletalMesh,
StartMaterialIndex,
GeometryCollectionSource.LocalTransform,
&GeometryCollectionInOut,
ReindexMaterials,
false
);
return true;
}
else if (const UGeometryCollection* SourceGeometryCollection = Cast<UGeometryCollection>(SourceObject))
{
AppendGeometryCollection(
SourceGeometryCollection->GetGeometryCollection().Get(),
StartMaterialIndex,
GeometryCollectionSource.LocalTransform,
&GeometryCollectionInOut,
ReindexMaterials
);
return true;
}
}
return false;
}
void FGeometryCollectionEngineConversion::ConvertStaticMeshToGeometryCollection(const TObjectPtr<UStaticMesh> StaticMesh, FManagedArrayCollection& OutCollection, TArray<TObjectPtr<UMaterial>>& OutMaterials, TArray<FGeometryCollectionAutoInstanceMesh>& OutInstancedMeshes, bool bSetInternalFromMaterialIndex, bool bSplitComponents)
{
TArray<TObjectPtr<UMaterialInterface>> OutMaterialInstances;
ConvertStaticMeshToGeometryCollection(StaticMesh, OutCollection, OutMaterialInstances, OutInstancedMeshes, bSetInternalFromMaterialIndex, bSplitComponents);
GetMaterialsFromInstances(OutMaterialInstances, OutMaterials);
}
void FGeometryCollectionEngineConversion::ConvertStaticMeshToGeometryCollection(const TObjectPtr<UStaticMesh> StaticMesh, FManagedArrayCollection& OutCollection, TArray<TObjectPtr<UMaterialInterface>>& OutMaterialInstances, TArray<FGeometryCollectionAutoInstanceMesh>& OutInstancedMeshes, bool bSetInternalFromMaterialIndex, bool bSplitComponents)
{
ConvertStaticMeshToGeometryCollection(StaticMesh, FTransform::Identity, OutCollection, OutMaterialInstances, OutInstancedMeshes, bSetInternalFromMaterialIndex, bSplitComponents);
}
void FGeometryCollectionEngineConversion::ConvertStaticMeshToGeometryCollection(const TObjectPtr<UStaticMesh> StaticMesh, const FTransform& MeshTransform, FManagedArrayCollection& OutCollection, TArray<TObjectPtr<UMaterialInterface>>& OutMaterialInstances, TArray<FGeometryCollectionAutoInstanceMesh>& OutInstancedMeshes, bool bSetInternalFromMaterialIndex, bool bSplitComponents)
{
#if WITH_EDITORONLY_DATA
if (UGeometryCollection* NewGeometryCollection = NewObject<UGeometryCollection>())
{
// If any of the static meshes have Nanite enabled, also enable on the new geometry collection asset for convenience.
NewGeometryCollection->EnableNanite |= StaticMesh->IsNaniteEnabled();
// Record the contributing source on the asset.
FSoftObjectPath SourceSoftObjectPath(StaticMesh);
// Materials
TArray<TObjectPtr<UMaterialInterface>> MatArr;
for (auto& StaticMaterial : StaticMesh->GetStaticMaterials())
{
MatArr.Emplace(StaticMaterial.MaterialInterface);
}
TArray<TObjectPtr<UMaterialInterface>> SourceMaterials(MatArr);
// InstanceMeshes
FGeometryCollectionAutoInstanceMesh NewInstanceMesh;
NewInstanceMesh.Mesh = StaticMesh;
NewInstanceMesh.Materials = SourceMaterials;
OutInstancedMeshes.Emplace(NewInstanceMesh);
bool bAddInternalMaterials = false;
NewGeometryCollection->GeometrySource.Emplace(SourceSoftObjectPath, MeshTransform, SourceMaterials, bSplitComponents, bSetInternalFromMaterialIndex);
FGeometryCollectionEngineConversion::AppendStaticMesh(StaticMesh, SourceMaterials, MeshTransform, NewGeometryCollection, false, bAddInternalMaterials, bSplitComponents, bSetInternalFromMaterialIndex);
// make sure we have only one root if we split components
TSharedPtr<FGeometryCollection> OutCollectionPtr = NewGeometryCollection->GetGeometryCollection();
if (bSplitComponents && FGeometryCollectionClusteringUtility::ContainsMultipleRootBones(OutCollectionPtr.Get()))
{
FGeometryCollectionClusteringUtility::ClusterAllBonesUnderNewRoot(OutCollectionPtr.Get());
// If we split components, set external collision (if any) on the new root
TArray<int32> RootBones;
FGeometryCollectionClusteringUtility::GetRootBones(OutCollectionPtr.Get(), RootBones);
if (ensure(RootBones.Num() == 1))
{
SetExternalCollisions(GetMeshBuildScale3D(*StaticMesh) * MeshTransform.GetScale3D(), StaticMesh->GetBodySetup(), OutCollectionPtr.Get(), RootBones[0]);
}
}
NewGeometryCollection->InitializeMaterials();
// Materials
OutMaterialInstances.Append(NewGeometryCollection->Materials);
OutCollectionPtr->CopyTo(&OutCollection);
}
#endif //WITH_EDITORONLY_DATA
}
void FGeometryCollectionEngineConversion::ConvertGeometryCollectionToGeometryCollection(const TObjectPtr<UGeometryCollection> InGeometryCollectionAssetPtr, FManagedArrayCollection& OutCollection, TArray<TObjectPtr<UMaterial>>& OutMaterials, TArray<FGeometryCollectionAutoInstanceMesh>& OutInstancedMeshes)
{
TArray<TObjectPtr<UMaterialInterface>> OutMaterialInstances;
ConvertGeometryCollectionToGeometryCollection(InGeometryCollectionAssetPtr, OutCollection, OutMaterialInstances, OutInstancedMeshes);
GetMaterialsFromInstances(OutMaterialInstances, OutMaterials);
}
void FGeometryCollectionEngineConversion::ConvertGeometryCollectionToGeometryCollection(const TObjectPtr<UGeometryCollection> InGeometryCollectionAssetPtr, FManagedArrayCollection& OutCollection, TArray<TObjectPtr<UMaterialInterface>>& OutMaterialInstances, TArray<FGeometryCollectionAutoInstanceMesh>& OutInstancedMeshes)
{
if (InGeometryCollectionAssetPtr)
{
const TSharedPtr<FGeometryCollection, ESPMode::ThreadSafe> NewGeometryCollectionPtr = InGeometryCollectionAssetPtr->GetGeometryCollection();
// Materials
OutMaterialInstances = InGeometryCollectionAssetPtr->Materials;
// InstanceMeshes
OutInstancedMeshes = InGeometryCollectionAssetPtr->AutoInstanceMeshes;
if (NewGeometryCollectionPtr)
{
NewGeometryCollectionPtr->CopyTo(&OutCollection);
}
}
}
void FGeometryCollectionEngineConversion::ConvertActorToGeometryCollection(const AActor* Actor, FManagedArrayCollection& OutCollection, TArray<TObjectPtr<UMaterial>>& OutMaterials, TArray<FGeometryCollectionAutoInstanceMesh>& OutInstancedMeshes, const FSkeletalMeshToCollectionConversionParameters& ConversionParameters, bool bSplitComponents)
{
TArray<TObjectPtr<UMaterialInterface>> OutMaterialInstances;
ConvertActorToGeometryCollection(Actor, OutCollection, OutMaterialInstances, OutInstancedMeshes, ConversionParameters, bSplitComponents);
GetMaterialsFromInstances(OutMaterialInstances, OutMaterials);
}
void FGeometryCollectionEngineConversion::ConvertActorToGeometryCollection(const AActor* Actor, FManagedArrayCollection& OutCollection, TArray<TObjectPtr<UMaterialInterface>>& OutMaterialInstances, TArray<FGeometryCollectionAutoInstanceMesh>& OutInstancedMeshes, const FSkeletalMeshToCollectionConversionParameters& ConversionParameters, bool bSplitComponents)
{
#if WITH_EDITORONLY_DATA
const FTransform ActorTransform(Actor->GetTransform());
if (UGeometryCollection* NewGeometryCollection = NewObject<UGeometryCollection>())
{
TInlineComponentArray<UStaticMeshComponent*> 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<TObjectPtr<UMaterialInterface>> 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<UGeometryCollectionComponent*> 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<TObjectPtr<UMaterialInterface>> 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();
if (ConversionParameters.bParentAllBonesUnderNewRoot && FGeometryCollectionClusteringUtility::ContainsMultipleRootBones(NewGeometryCollection->GetGeometryCollection().Get()))
{
UE_LOG(UGeometryCollectionConversionLogging, Log, TEXT("FGeometryCollectionEngineConversion::ConvertActorToGeometryCollection() - All bones were parented under new root."));
FGeometryCollectionClusteringUtility::ClusterAllBonesUnderNewRoot(NewGeometryCollection->GetGeometryCollection().Get(), FName("root"), false);
}
// InstanceMeshes
OutInstancedMeshes.Append(NewGeometryCollection->AutoInstanceMeshes);
// Materials
OutMaterialInstances = NewGeometryCollection->Materials;
TSharedPtr<FGeometryCollection> OutCollectionPtr = NewGeometryCollection->GetGeometryCollection();
OutCollectionPtr->CopyTo(&OutCollection);
}
#endif //WITH_EDITORONLY_DATA
}
void FGeometryCollectionEngineConversion::ConvertCollectionToSkeleton(const FManagedArrayCollection& InCollection, USkeleton* OutSkeleton, TArray<int32>& OutIndexRemap)
{
FManagedArrayCollection LocalCollection = InCollection;
GeometryCollection::Facades::FCollectionTransformFacade Transforms(LocalCollection);
if (Transforms.IsValid() && Transforms.HasBoneNameAttribute() && OutSkeleton)
{
Transforms.EnforceSingleRoot("root");
OutIndexRemap.Init(INDEX_NONE, Transforms.Num());
auto AddMapping = [&OutIndexRemap](int32 A, int32 B)
{
OutIndexRemap[A] = B;
};
auto AddChildren = [&Transforms, &OutSkeleton, &AddMapping](const TArray<int32>& CollectionChildren)
{
TQueue<int32> Children;
auto Enqueue = [&Children](const TArray<int32>& List)
{
for (int32 Elem : List)
Children.Enqueue(Elem);
};
int CurrentIndex = INDEX_NONE;
Enqueue(CollectionChildren);
while (!Children.IsEmpty())
{
Children.Dequeue(CurrentIndex);
int32 CollectionParentIndex = (*Transforms.GetParents())[CurrentIndex];
int32 SkeletionParentIndex = OutSkeleton->GetReferenceSkeleton().FindBoneIndex(FName((*Transforms.FindBoneNames())[CollectionParentIndex]));
FName BoneName = FName((*Transforms.FindBoneNames())[CurrentIndex]);
FTransform Transform = FTransform((*Transforms.FindTransforms())[CurrentIndex]);
FMeshBoneInfo Info(BoneName, BoneName.ToString(), SkeletionParentIndex);
{
FReferenceSkeletonModifier Edit(OutSkeleton);
if (Edit.FindBoneIndex(BoneName) == INDEX_NONE) //Bone does not exist
{
Edit.Add(Info, Transform, true /*bAllowMultipleRoots*/);
}
}
AddMapping(CurrentIndex, OutSkeleton->GetReferenceSkeleton().GetRawBoneNum());
Enqueue((*Transforms.FindChildren())[CurrentIndex].Array());
}
};
// must insert in decending order from parent to child.
TArray<FString> BoneNameStrings = Transforms.FindBoneNames()->GetConstArray();
for (int i = 0; i < Transforms.GetParents()->Num(); i++)
{
if ((*Transforms.GetParents())[i] == INDEX_NONE) // No parent
{
FName BoneName = FName((*Transforms.FindBoneNames())[i]);
FTransform Transform = FTransform((*Transforms.FindTransforms())[i]);
FMeshBoneInfo Info(BoneName, BoneName.ToString(), INDEX_NONE);
{
FReferenceSkeletonModifier Edit(OutSkeleton);
Edit.Add(Info, Transform, true /*bAllowMultipleRoots*/);
}
AddMapping(i,OutSkeleton->GetReferenceSkeleton().GetNum());
if ((*Transforms.FindChildren())[i].Num())
{
AddChildren((*Transforms.FindChildren())[i].Array());
}
}
}
Transforms.EnforceSingleRoot("root");
UE_LOG(UGeometryCollectionConversionLogging, Log, TEXT("FGeometryCollectionEngineConversion::ConvertCollectionToSkeleton(NumTransforms:%d)"), OutSkeleton->GetReferenceSkeleton().GetRawBoneNum());
}
}
void FGeometryCollectionEngineConversion::GetMaterialsFromInstances(const TArray<TObjectPtr<UMaterialInterface>>& MaterialInstances, TArray<TObjectPtr<UMaterial>>& OutMaterials)
{
// somehow append does not like appending to a TArray<ObjectPtr<>>
// so we need to store in a array of raw pointers and then transfer over
TArray<UMaterial*> MaterialArray;
MaterialArray.Append(MaterialInstances);
OutMaterials.Reserve(MaterialInstances.Num());
for (UMaterial* Material : MaterialArray)
{
OutMaterials.Add(Material);
}
}
#undef LOCTEXT_NAMESPACE