Files
UnrealEngineUWP/Engine/Source/Runtime/Experimental/GeometryCollectionEngine/Private/GeometryCollection/GeometryCollectionEngineConversion.cpp

1379 lines
62 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/CollectionTransformSourceFacade.h"
#include "GeometryCollection/GeometryCollection.h"
#include "GeometryCollection/GeometryCollectionActor.h"
#include "GeometryCollection/GeometryCollectionAlgo.h"
#include "GeometryCollection/GeometryCollectionComponent.h"
#include "GeometryCollection/GeometryCollectionClusteringUtility.h"
#include "GeometryCollectionProxyData.h"
#include "GeometryCollection/GeometryCollectionUtility.h"
#include "Logging/LogMacros.h"
#include "MaterialDomain.h"
#include "Materials/Material.h"
#include "Rendering/SkeletalMeshRenderData.h"
#include "MeshDescription.h"
#include "StaticMeshAttributes.h"
#include "StaticMeshOperations.h"
#include "Physics/Experimental/ChaosInterfaceUtils.h"
#include "PhysicsEngine/BodySetup.h"
#include "MeshDescriptionBuilder.h"
#include "VertexConnectedComponents.h"
#include "GeometryCollection/Facades/CollectionInstancedMeshFacade.h"
DEFINE_LOG_CATEGORY_STATIC(UGeometryCollectionConversionLogging, Log, All);
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(Chaos::EImplicitObjectType Type)
{
const Chaos::EImplicitObjectType InnerType = Type & (~(Chaos::ImplicitObjectType::IsScaled | Chaos::ImplicitObjectType::IsInstanced));
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();
}
void FGeometryCollectionEngineConversion::AppendMeshDescription(
const FMeshDescription* MeshDescription, const FString& Name, int32 MaterialStartIndex, const FTransform& StaticMeshTransform,
FGeometryCollection* GeometryCollection, UBodySetup* BodySetup, bool ReindexMaterials, bool bAddInternalMaterials)
{
#if WITH_EDITORONLY_DATA
if (!MeshDescription)
{
return;
}
check(GeometryCollection);
// 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());
for (const FVertexID VertexIndex : MeshDescription->Vertices().GetElementIDs())
{
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;
}
}
// target triangle indices
TManagedArray<FIntVector>& TargetIndices = GeometryCollection->Indices;
TManagedArray<bool>& TargetVisible = GeometryCollection->Visible;
TManagedArray<int32>& TargetMaterialID = GeometryCollection->MaterialID;
TManagedArray<int32>& TargetMaterialIndex = GeometryCollection->MaterialIndex;
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;
// If adding internal materials, then materials are ganged in pairs and we want the id to associate with the first of each pair.
int32 MaterialIndexScale = 1 + int32(bAddInternalMaterials);
TargetMaterialID[TargetIndex] = MaterialStartIndex + (MeshDescription->GetTrianglePolygonGroup(TriangleIndex) * MaterialIndexScale);
// Is this right?
TargetMaterialIndex[TargetIndex] = TargetIndex;
++TargetIndex;
}
// Geometry transform
TManagedArray<FTransform>& Transform = GeometryCollection->Transform;
int32 TransformIndex1 = GeometryCollection->AddElements(1, FGeometryCollection::TransformGroup);
Transform[TransformIndex1] = StaticMeshTransform;
Transform[TransformIndex1].SetScale3D(FVector(1.f, 1.f, 1.f));
// collisions
if (BodySetup)
{
TArray<TUniquePtr<Chaos::FImplicitObject>> 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.ChaosTriMeshes = MakeArrayView(BodySetup->ChaosTriMeshes);
// 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);
using FCollisionType = FGeometryDynamicCollection::FSharedImplicit;
TManagedArray<FCollisionType>& ExternaCollisions = GeometryCollection->AddAttribute<FCollisionType>("ExternalCollisions", FGeometryCollection::TransformGroup);
ExternaCollisions[TransformIndex1] = nullptr;
for (int32 GeomIndex = 0; GeomIndex < Geoms.Num();)
{
// make sure we only import box, sphere, capsule or convex
if (IsImportableImplicitObjectType(Geoms[GeomIndex]->GetType()))
{
GeomIndex++;
}
else
{
Geoms.RemoveAtSwap(GeomIndex);
}
}
if (Geoms.Num() > 0)
{
ExternaCollisions[TransformIndex1] = MakeShared<Chaos::FImplicitObjectUnion>(MoveTemp(Geoms));
}
}
// 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;
// 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);
}
// 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);
}
// 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);
}
}
if (ReindexMaterials) {
GeometryCollection->ReindexMaterials();
}
#endif //WITH_EDITORONLY_DATA
}
namespace
{
// Note: This is the same as the corresponding ModelingComponents AssetUtils function, in the MeshModelingToolset plugin,
// but we cannot use a plugin function from here, so have our own copy
void InitializeAutoGeneratedAttributes(FMeshDescription& Mesh, const FMeshBuildSettings* BuildSettings)
{
check(BuildSettings);
// if the build settings don't indicate calculated normals/tangents, we will assume they are good
if (BuildSettings->bRecomputeNormals || BuildSettings->bRecomputeTangents)
{
// check if any are invalid
bool bHasInvalidNormals, bHasInvalidTangents;
FStaticMeshOperations::AreNormalsAndTangentsValid(Mesh, bHasInvalidNormals, bHasInvalidTangents);
// if neither are invalid we are not going to recompute
if (bHasInvalidNormals || bHasInvalidTangents)
{
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 |= BuildSettings->bRecomputeNormals ? EComputeNTBsFlags::Normals : EComputeNTBsFlags::None;
ComputeNTBsOptions |= BuildSettings->bRecomputeTangents ? 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);
}
InitializeAutoGeneratedAttributes(*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)
{
#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))
{
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)
{
#if WITH_EDITORONLY_DATA
if (StaticMesh)
{
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)
{
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, StaticMesh->GetBodySetup(), false, bAddInternalMaterials);
}
if (bReindexMaterials)
{
GeometryCollection->ReindexMaterials();
}
return true;
}
// else only one component -- fall back to just using the original mesh description
}
AppendMeshDescription(MeshDescription, StaticMesh->GetName(), StartMaterialIndex, MeshTransform, GeometryCollection, StaticMesh->GetBodySetup(), bReindexMaterials, bAddInternalMaterials);
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();
FVector Scale = GeometryCollectionTransform.GetScale3D();
FTransform AppliedTransform = 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;
// target face information
TManagedArray<FIntVector>& TargetIndices = TargetGeometryCollection->Indices;
TManagedArray<bool>& TargetVisible = TargetGeometryCollection->Visible;
TManagedArray<int32>& TargetMaterialID = TargetGeometryCollection->MaterialID;
TManagedArray<int32>& TargetMaterialIndex = TargetGeometryCollection->MaterialIndex;
// 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;
}
using FCollisionType = FGeometryDynamicCollection::FSharedImplicit;
// source transform information
const TManagedArray<FTransform>& 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<FCollisionType>* SourceExternalCollisions = SourceGeometryCollectionPtr->FindAttribute<FCollisionType>("ExternalCollisions", FGeometryCollection::TransformGroup);
// target transform information
TManagedArray<FTransform>& 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<FCollisionType>& TargetExternalCollisions = TargetGeometryCollection->AddAttribute<FCollisionType>("ExternalCollisions", 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
{
FTransform 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)
{
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);
}
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 (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<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* SkeletalMesh, int32 MaterialStartIndex, const FTransform& SkeletalMeshTransform, FManagedArrayCollection* InCollection, bool bReindexMaterials)
{
//UE_LOG(UGeometryCollectionConversionLogging, Log, TEXT("FGeometryCollectionEngineConversion::AppendSkeletalMesh()"));
if (!InCollection)
{
return false;
}
FGeometryCollection::DefineGeometrySchema(*InCollection);
const FSkeletalMeshLODRenderData* MeshLODData = GetSkeletalMeshLOD(SkeletalMesh, 0);
if (!MeshLODData)
{
return false;
}
const FSkeletalMeshLODRenderData& SkeletalMeshLODRenderData = *MeshLODData;
const FSkinWeightVertexBuffer& SkinWeightVertexBuffer = *SkeletalMeshLODRenderData.GetSkinWeightVertexBuffer();
const FStaticMeshVertexBuffers& VertexBuffers = SkeletalMeshLODRenderData.StaticVertexBuffers;
const FPositionVertexBuffer& PositionVertexBuffer = VertexBuffers.PositionVertexBuffer;
const int32 VertexCount = PositionVertexBuffer.GetNumVertices();
// Check that all vertex weightings are rigid.
for (int32 VertexIndex = 0; VertexIndex < VertexCount; VertexIndex++)
{
int32 SkeletalBoneIndex = -1;
if (!SkinWeightVertexBuffer.GetRigidWeightBone(VertexIndex, SkeletalBoneIndex))
{
UE_LOG(UGeometryCollectionConversionLogging, Error, TEXT("Non-rigid weighting found on vertex %d: Cannot convert to GeometryCollection."), VertexIndex);
return false;
}
}
const FSkelMeshRenderSection& RenderSection = SkeletalMeshLODRenderData.RenderSections[0];
const TArray<FBoneIndexType>& SkeletalBoneMap = RenderSection.BoneMap;
//
// The Component transform for each Mesh will become the FTransform that drives
// its associated VerticesGroup. The Skeleton will contain a nested transform hierarchy
// that is evaluated using the GetComponentSpaceTransformRefPose. The resulting
// Transforms array stored in the GeometryCollection will be the same size as
// the SkeletalBoneMap. Note the @todo: the SkeletalBoneMap is pulled from only
// the first render section, this will need to be expanded to include all render
// sections.
//
const USkeleton* Skeleton = SkeletalMesh->GetSkeleton();
TManagedArray<FTransform>& Transform = InCollection->ModifyAttribute<FTransform>(FTransformCollection::TransformAttribute, FTransformCollection::TransformGroup);
int32 TransformBaseIndex = InCollection->AddElements(SkeletalBoneMap.Num(), FGeometryCollection::TransformGroup);
const FReferenceSkeleton & ReferenceSkeletion = Skeleton->GetReferenceSkeleton();
const TArray<FTransform> & RestArray = Skeleton->GetRefLocalPoses();
for (int32 BoneIndex = 0; BoneIndex < SkeletalBoneMap.Num(); BoneIndex++)
{
FTransform BoneTransform = FAnimationRuntime::GetComponentSpaceTransformRefPose(ReferenceSkeletion, SkeletalBoneMap[BoneIndex]);
Transform[TransformBaseIndex + BoneIndex] = BoneTransform;
}
//
// The Triangle Indices
//
TManagedArray<FIntVector>& Indices = InCollection->ModifyAttribute<FIntVector>("Indices", FGeometryCollection::FacesGroup);
TManagedArray<bool>& Visible = InCollection->ModifyAttribute<bool>("Visible", FGeometryCollection::FacesGroup);
TManagedArray<int32>& MaterialIndex = InCollection->ModifyAttribute<int32>("MaterialIndex", FGeometryCollection::FacesGroup);
TManagedArray<int32>& MaterialID = InCollection->ModifyAttribute<int32>("MaterialID", FGeometryCollection::FacesGroup);
TArray<uint32> IndexBuffer;
SkeletalMeshLODRenderData.MultiSizeIndexContainer.GetIndexBuffer(IndexBuffer);
const int32 IndicesCount = IndexBuffer.Num() / 3;
int NumVertices = InCollection->NumElements(FGeometryCollection::VerticesGroup);
int InitialNumIndices = InCollection->NumElements(FGeometryCollection::FacesGroup);
int IndicesBaseIndex = InCollection->AddElements(IndicesCount, FGeometryCollection::FacesGroup);
for (int32 IndicesIndex = 0, StaticIndex = 0; IndicesIndex < IndicesCount; IndicesIndex++, StaticIndex += 3)
{
int32 IndicesOffset = IndicesBaseIndex + IndicesIndex;
Indices[IndicesOffset] = FIntVector(
IndexBuffer[StaticIndex] + NumVertices,
IndexBuffer[StaticIndex + 1] + NumVertices,
IndexBuffer[StaticIndex + 2] + NumVertices);
Visible[IndicesOffset] = true;
MaterialID[IndicesOffset] = 0;
MaterialIndex[IndicesOffset] = IndicesOffset;
}
//
// Vertex Attributes
//
TManagedArray<FVector3f>& Vertex = InCollection->ModifyAttribute<FVector3f>("Vertex", FGeometryCollection::VerticesGroup);
TManagedArray<FVector3f>& Normal = InCollection->ModifyAttribute<FVector3f>("Normal", FGeometryCollection::VerticesGroup);
TManagedArray<FLinearColor>& Color = InCollection->ModifyAttribute<FLinearColor>("Color", FGeometryCollection::VerticesGroup);
TManagedArray<FVector3f>& TangentU = InCollection->ModifyAttribute<FVector3f>("TangentU", FGeometryCollection::VerticesGroup);
TManagedArray<FVector3f>& TangentV = InCollection->ModifyAttribute<FVector3f>("TangentV", FGeometryCollection::VerticesGroup);
TManagedArray<int32>& BoneMap = InCollection->ModifyAttribute<int32>("BoneMap", FGeometryCollection::VerticesGroup);
TManagedArray<FLinearColor>& BoneColor = InCollection->ModifyAttribute<FLinearColor>("BoneColor", FTransformCollection::TransformGroup);
TManagedArray<FString>& BoneName = InCollection->ModifyAttribute<FString>("BoneName", FTransformCollection::TransformGroup);
//
// Transform Attributes
//
TManagedArray<int32>& Parent = InCollection->ModifyAttribute<int32>(FTransformCollection::ParentAttribute, FTransformCollection::TransformGroup);
TManagedArray<int32>& SimulationType = InCollection->ModifyAttribute<int32>("SimulationType", FTransformCollection::TransformGroup);
int InitialNumVertices = InCollection->NumElements(FGeometryCollection::VerticesGroup);
int VertexBaseIndex = InCollection->AddElements(VertexCount, FGeometryCollection::VerticesGroup);
const int32 NumUVLayers = VertexBuffers.StaticMeshVertexBuffer.GetNumTexCoords();
GeometryCollection::UV::SetNumUVLayers(*InCollection, NumUVLayers);
GeometryCollection::UV::FUVLayers UVLayers = GeometryCollection::UV::FindActiveUVLayers(*InCollection);
for (int32 VertexIndex = 0; VertexIndex < VertexCount; VertexIndex++)
{
int VertexOffset = VertexBaseIndex + VertexIndex;
BoneMap[VertexOffset] = -1;
int32 SkeletalBoneIndex = -1;
SkinWeightVertexBuffer.GetRigidWeightBone(VertexIndex, SkeletalBoneIndex);
if (SkeletalBoneIndex > -1)
{
BoneMap[VertexOffset] = SkeletalBoneIndex + TransformBaseIndex;
Vertex[VertexOffset] = (FVector4f)Transform[BoneMap[VertexOffset]].ToInverseMatrixWithScale().TransformPosition((FVector)PositionVertexBuffer.VertexPosition(VertexIndex));
}
check(BoneMap[VertexOffset] != -1);
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);
}
if (VertexBuffers.ColorVertexBuffer.GetNumVertices() == VertexCount)
Color[VertexOffset] = VertexBuffers.ColorVertexBuffer.VertexColor(VertexIndex);
else
Color[VertexOffset] = FLinearColor::White;
}
int32 InitialIndex = -1;
int32 LastParentIndex = -1;
int32 CurrentLevel = 0;
for (int32 BoneIndex = 0; BoneIndex < SkeletalBoneMap.Num(); BoneIndex++)
{
// transform based on position of the actor.
Transform[TransformBaseIndex + BoneIndex] = SkeletalMeshTransform * Transform[TransformBaseIndex + BoneIndex];
// bone attributes
BoneName[TransformBaseIndex + BoneIndex] = ReferenceSkeletion.GetBoneName(SkeletalBoneMap[BoneIndex]).ToString();
const FColor RandBoneColor(FMath::Rand() % 100 + 5, FMath::Rand() % 100 + 5, FMath::Rand() % 100 + 5, 255);
BoneColor[TransformBaseIndex + BoneIndex] = FLinearColor(RandBoneColor);
// Bone Hierarchy - Added at root with no common parent
int32 ParentIndex = ReferenceSkeletion.GetParentIndex(SkeletalBoneMap[BoneIndex]);
int32 UseParentIndex = ParentIndex + InitialIndex;
if (LastParentIndex != UseParentIndex)
{
LastParentIndex = UseParentIndex;
}
Parent[TransformBaseIndex + BoneIndex] = UseParentIndex;
SimulationType[TransformBaseIndex + BoneIndex] = FGeometryCollection::ESimulationTypes::FST_Rigid;
}
// Geometry Group
TArray<int32> GeometryIndices;
GeometryCollectionAlgo::ContiguousArray(GeometryIndices, InCollection->NumElements(FGeometryCollection::GeometryGroup));
InCollection->RemoveDependencyFor(FGeometryCollection::GeometryGroup);
InCollection->RemoveElements(FGeometryCollection::GeometryGroup, GeometryIndices);
::GeometryCollection::AddGeometryProperties(InCollection);
const TArray<FSkelMeshRenderSection> &StaticMeshSections = SkeletalMesh->GetResourceForRendering()->LODRenderData[0].RenderSections;
TManagedArray<FGeometryCollectionSection> & Sections = InCollection->ModifyAttribute<FGeometryCollectionSection>("Sections", FGeometryCollection::MaterialGroup);
for (const FSkelMeshRenderSection &CurrSection : StaticMeshSections)
{
// create new section
int32 SectionIndex = InCollection->AddElements(1, FGeometryCollection::MaterialGroup);
Sections[SectionIndex].MaterialID = MaterialStartIndex + CurrSection.MaterialIndex;
Sections[SectionIndex].FirstIndex = IndicesBaseIndex * 3 + CurrSection.BaseIndex;
Sections[SectionIndex].MinVertexIndex = VertexBaseIndex + CurrSection.BaseVertexIndex;
Sections[SectionIndex].NumTriangles = CurrSection.NumTriangles;
// #todo(dmp): what should we set this to? SkeletalMesh sections are different
// but we are resetting this when the re indexing happens
Sections[SectionIndex].MaxVertexIndex = VertexBaseIndex + CurrSection.NumVertices;
// set the materialid for all of the faces
for (int32 i = Sections[SectionIndex].FirstIndex / 3; i < Sections[SectionIndex].FirstIndex / 3 + Sections[SectionIndex].NumTriangles; ++i)
{
MaterialID[i] = SectionIndex;
}
}
if (bReindexMaterials)
{
FGeometryCollection::ReindexMaterials(*InCollection);
}
return true;
}
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<FTransform>& Transform = InCollection->ModifyAttribute<FTransform>(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] = 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<FGeometryCollection, ESPMode::ThreadSafe> 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<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)
{
if (const UObject* SourceObject = GeometryCollectionSource.SourceGeometryObject.TryLoad())
{
const int32 StartMaterialIndex = MaterialsInOut.Num();
MaterialsInOut.Append(GeometryCollectionSource.SourceMaterial);
if (const UStaticMesh* SourceStaticMesh = Cast<UStaticMesh>(SourceObject))
{
AppendStaticMesh(
SourceStaticMesh,
StartMaterialIndex,
GeometryCollectionSource.LocalTransform,
&GeometryCollectionInOut,
ReindexMaterials,
GeometryCollectionSource.bAddInternalMaterials,
GeometryCollectionSource.bSplitComponents
);
}
else if (const USkeletalMesh* SourceSkeletalMesh = Cast<USkeletalMesh>(SourceObject))
{
AppendSkeletalMesh(
SourceSkeletalMesh,
StartMaterialIndex,
GeometryCollectionSource.LocalTransform,
&GeometryCollectionInOut,
ReindexMaterials
);
}
else if (const UGeometryCollection* SourceGeometryCollection = Cast<UGeometryCollection>(SourceObject))
{
AppendGeometryCollection(
SourceGeometryCollection->GetGeometryCollection().Get(),
StartMaterialIndex,
GeometryCollectionSource.LocalTransform,
&GeometryCollectionInOut,
ReindexMaterials
);
}
}
}