677 lines
25 KiB
C++
677 lines
25 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
#include "CADMeshDescriptionHelper.h"
|
|
|
|
#include "Algo/AnyOf.h"
|
|
#include "CADData.h"
|
|
#include "CADOptions.h"
|
|
#include "DatasmithMaterialElements.h"
|
|
#include "DatasmithSceneFactory.h"
|
|
#include "DatasmithUtils.h"
|
|
#include "Math/UnrealMathUtility.h"
|
|
#include "MeshDescription.h"
|
|
#include "MeshOperator.h"
|
|
#include "Misc/FileHelper.h"
|
|
#include "StaticMeshAttributes.h"
|
|
#include "StaticMeshOperations.h"
|
|
|
|
typedef uint32 TriangleIndex[3];
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogCADLibrary, Log, All);
|
|
|
|
namespace CADLibrary
|
|
{
|
|
|
|
template<typename VectorType>
|
|
struct TGeometricData
|
|
{
|
|
float W;
|
|
int32 Index;
|
|
VectorType Coordinates;
|
|
bool bIsMerged;
|
|
|
|
/** Default constructor. */
|
|
TGeometricData() {}
|
|
|
|
/** Initialization constructor. */
|
|
TGeometricData(int32 InIndex, const VectorType& V, const VectorType& OneVector)
|
|
{
|
|
W = V | OneVector; // V.X + V.Y or V.X + V.Y + V.Z according to the vector dimension
|
|
Index = InIndex;
|
|
Coordinates = V;
|
|
bIsMerged = false;
|
|
}
|
|
};
|
|
|
|
// Verify the 3 input indices are not defining a degenerated triangle and fill up the corresponding FVertexIDs
|
|
bool IsTriangleDegenerated(const int32_t* Indices, const TArray<FVertexID>& RemapVertexPosition, FVertexID VertexIDs[3])
|
|
{
|
|
if (Indices[0] == Indices[1] || Indices[0] == Indices[2] || Indices[1] == Indices[2])
|
|
{
|
|
return true;
|
|
}
|
|
|
|
for (int32 Corner = 0; Corner < 3; ++Corner)
|
|
{
|
|
VertexIDs[Corner] = RemapVertexPosition[Indices[Corner]];
|
|
}
|
|
|
|
return (VertexIDs[0] == VertexIDs[1] || VertexIDs[0] == VertexIDs[2] || VertexIDs[1] == VertexIDs[2]);
|
|
}
|
|
|
|
template<typename VectorType>
|
|
void MergeCoincidents(const TArray<VectorType>& DataArray, const VectorType& OneVector, const double CoincidenceTolerance, TArray<int32>& InOutIndices)
|
|
{
|
|
// Create a list of Data W/index pairs
|
|
TArray<TGeometricData<VectorType>> GeometicDataArray;
|
|
GeometicDataArray.Reserve(DataArray.Num());
|
|
|
|
const VectorType* Data = DataArray.GetData();
|
|
const int32* DataId = InOutIndices.GetData();
|
|
for (int32 Index = 0; Index < DataArray.Num(); ++Index, ++Data, ++DataId)
|
|
{
|
|
GeometicDataArray.Emplace(*DataId, *Data, OneVector);
|
|
}
|
|
|
|
// Sort the vertices by z value
|
|
GeometicDataArray.Sort([](TGeometricData<VectorType> const& A, TGeometricData<VectorType> const& B) { return A.W < B.W; });
|
|
|
|
// Search for duplicates
|
|
for (int32 Index = 0; Index < GeometicDataArray.Num(); Index++)
|
|
{
|
|
if (GeometicDataArray[Index].bIsMerged)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
GeometicDataArray[Index].bIsMerged = true;
|
|
int32 NewIndex = GeometicDataArray[Index].Index;
|
|
|
|
const VectorType& PositionA = GeometicDataArray[Index].Coordinates;
|
|
|
|
// only need to search forward, since we add pairs both ways
|
|
for (int32 Bndex = Index + 1; Bndex < GeometicDataArray.Num(); Bndex++)
|
|
{
|
|
if ((GeometicDataArray[Bndex].W - GeometicDataArray[Index].W) > CoincidenceTolerance)
|
|
{
|
|
break; // can't be any more duplicated
|
|
}
|
|
|
|
const VectorType& PositionB = GeometicDataArray[Bndex].Coordinates;
|
|
if (PositionA.Equals(PositionB, CoincidenceTolerance))
|
|
{
|
|
GeometicDataArray[Bndex].bIsMerged = true;
|
|
InOutIndices[GeometicDataArray[Bndex].Index] = NewIndex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FillVertexPosition(const FMeshConversionContext& MeshConversionContext, FBodyMesh& Body, FMeshDescription& MeshDescription)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(CADLibrary::FillVertexPosition);
|
|
|
|
int32 TriangleCount = Body.TriangleCount;
|
|
TArray<FTessellationData>& FaceTessellationSet = Body.Faces;
|
|
|
|
TVertexAttributesRef<FVector3f> VertexPositions = MeshDescription.GetVertexPositions();
|
|
|
|
TArray<FVector3f>& VertexArray = Body.VertexArray;
|
|
TArray<int32>& VertexIdSet = Body.VertexIds;
|
|
int32 VertexCount = VertexArray.Num();
|
|
VertexIdSet.SetNumZeroed(VertexCount);
|
|
|
|
// Make MeshDescription.VertexPositions and VertexID
|
|
MeshDescription.ReserveNewVertices(MeshConversionContext.MeshParameters.bIsSymmetric ? VertexCount * 2 : VertexCount);
|
|
|
|
int32 VertexIndex = -1;
|
|
for (const FVector3f& Vertex : VertexArray)
|
|
{
|
|
VertexIndex++;
|
|
|
|
// Vertex is outside bbox
|
|
if (VertexIdSet[VertexIndex] == INDEX_NONE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FVertexID VertexID = MeshDescription.CreateVertex();
|
|
VertexPositions[VertexID] = (FVector3f)FDatasmithUtils::ConvertVector((FDatasmithUtils::EModelCoordSystem)MeshConversionContext.ImportParameters.GetModelCoordSys(), Vertex);
|
|
VertexIdSet[VertexIndex] = VertexID;
|
|
}
|
|
|
|
MergeCoincidents(VertexArray, FVector3f::OneVector, (float)MeshConversionContext.GeometricTolerance, VertexIdSet);
|
|
|
|
// if Symmetric mesh, the symmetric side of the mesh have to be generated
|
|
if (MeshConversionContext.MeshParameters.bIsSymmetric)
|
|
{
|
|
FMatrix44f SymmetricMatrix = (FMatrix44f) FDatasmithUtils::GetSymmetricMatrix(MeshConversionContext.MeshParameters.SymmetricOrigin, MeshConversionContext.MeshParameters.SymmetricNormal);
|
|
|
|
TArray<int32>& SymmetricVertexIds = Body.SymmetricVertexIds;
|
|
SymmetricVertexIds.SetNum(VertexArray.Num());
|
|
|
|
VertexIndex = 0;
|
|
for (const FVector3f& Vertex : VertexArray)
|
|
{
|
|
if (VertexIdSet[VertexIndex] == INDEX_NONE)
|
|
{
|
|
SymmetricVertexIds[VertexIndex++] = INDEX_NONE;
|
|
continue;
|
|
}
|
|
|
|
FVertexID VertexID = MeshDescription.CreateVertex();
|
|
const FVector3f VertexPosition = FDatasmithUtils::ConvertVector((FDatasmithUtils::EModelCoordSystem)MeshConversionContext.ImportParameters.GetModelCoordSys(), Vertex);
|
|
const FVector4f SymmetricPosition = FVector4f(SymmetricMatrix.TransformPosition(VertexPosition));
|
|
VertexPositions[VertexID] = FVector3f(SymmetricPosition);
|
|
SymmetricVertexIds[VertexIndex++] = VertexID;
|
|
}
|
|
}
|
|
}
|
|
|
|
// PolygonAttributes name used into modeling tools (ExtendedMeshAttribute::PolyTriGroups)
|
|
const FName PolyTriGroups("PolyTriGroups");
|
|
|
|
// Copy of FMeshDescriptionBuilder::EnablePolyGroups()
|
|
TPolygonAttributesRef<int32> EnableCADPatchGroups(FMeshDescription& OutMeshDescription)
|
|
{
|
|
TPolygonAttributesRef<int32> PatchGroups = OutMeshDescription.PolygonAttributes().GetAttributesRef<int32>(PolyTriGroups);
|
|
if (PatchGroups.IsValid() == false)
|
|
{
|
|
OutMeshDescription.PolygonAttributes().RegisterAttribute<int32>(PolyTriGroups, 1, 0, EMeshAttributeFlags::AutoGenerated);
|
|
PatchGroups = OutMeshDescription.PolygonAttributes().GetAttributesRef<int32>(PolyTriGroups);
|
|
check(PatchGroups.IsValid());
|
|
}
|
|
return PatchGroups;
|
|
}
|
|
|
|
void GetExistingPatches(FMeshDescription& MeshSource, TSet<int32>& OutExistingPatchIds)
|
|
{
|
|
TPolygonAttributesRef<int32> ElementToGroups = MeshSource.PolygonAttributes().GetAttributesRef<int32>(PolyTriGroups);
|
|
int32 LastPatchId = -1;
|
|
for (const FPolygonID TriangleID : MeshSource.Polygons().GetElementIDs())
|
|
{
|
|
int32 PatchId = ElementToGroups[TriangleID];
|
|
if (PatchId != LastPatchId)
|
|
{
|
|
OutExistingPatchIds.Add(PatchId);
|
|
LastPatchId = PatchId;
|
|
}
|
|
}
|
|
}
|
|
|
|
FMeshDescriptionDataCache::FMeshDescriptionDataCache(FMeshDescription& MeshSource)
|
|
{
|
|
FStaticMeshAttributes MeshSourceAttributes(MeshSource);
|
|
|
|
TPolygonAttributesRef<FPolygonGroupID> PolygoneToPolygoneGroupId = MeshSourceAttributes.GetPolygonPolygonGroupIndices();
|
|
TPolygonAttributesRef<int32> PatchGroup = EnableCADPatchGroups(MeshSource);
|
|
|
|
int32 LastPatchGroupId = -1;
|
|
for (int32 PolygoneID = 0; PolygoneID < PolygoneToPolygoneGroupId.GetNumElements(); ++PolygoneID)
|
|
{
|
|
const FPolygonGroupID GroupID = PolygoneToPolygoneGroupId[PolygoneID];
|
|
int32 PatchGroupID = PatchGroup[PolygoneID];
|
|
|
|
if (PatchGroupID != LastPatchGroupId)
|
|
{
|
|
PatchGroupToPolygonGroup.Add(PatchGroupID, GroupID);
|
|
LastPatchGroupId = PatchGroupID;
|
|
}
|
|
}
|
|
|
|
TPolygonGroupAttributesRef<FName> MeshSourcePolygonGroupImportedMaterialSlotNames = MeshSourceAttributes.GetPolygonGroupMaterialSlotNames();
|
|
MaterialSlotNames = MeshSourcePolygonGroupImportedMaterialSlotNames.GetRawArray(0);
|
|
}
|
|
|
|
void FMeshDescriptionDataCache::RestoreMaterialSlotNames(FMeshDescription& Mesh) const
|
|
{
|
|
TPolygonAttributesRef<int32> PatchGroup = EnableCADPatchGroups(Mesh);
|
|
FStaticMeshAttributes MeshAttributes(Mesh);
|
|
TPolygonAttributesRef<FPolygonGroupID> PolygoneToPolygoneGroupId = MeshAttributes.GetPolygonPolygonGroupIndices();
|
|
TPolygonGroupAttributesRef<FName> PolygonGroupMaterialSlotNames = MeshAttributes.GetPolygonGroupMaterialSlotNames();
|
|
|
|
TSet<FPolygonGroupID> GroupIdDone;
|
|
int32 LastPatchGroupId = -1;
|
|
for (int32 PolygoneID = 0; PolygoneID < PolygoneToPolygoneGroupId.GetNumElements(); ++PolygoneID)
|
|
{
|
|
const int32 PatchGroupID = PatchGroup[PolygoneID];
|
|
if (PatchGroupID != LastPatchGroupId)
|
|
{
|
|
const FPolygonGroupID GroupID = PolygoneToPolygoneGroupId[PolygoneID];
|
|
if (!GroupIdDone.Contains(GroupID))
|
|
{
|
|
GroupIdDone.Add(GroupID);
|
|
|
|
const FPolygonGroupID* GroupIDMeshSource = Find(PatchGroupID);
|
|
if (GroupIDMeshSource)
|
|
{
|
|
const FName& SlotName = GetSlotName(*GroupIDMeshSource);
|
|
ensure(GroupID < PolygonGroupMaterialSlotNames.GetNumElements());
|
|
PolygonGroupMaterialSlotNames[GroupID] = SlotName;
|
|
}
|
|
// else PatchGroupeID doesn't exist in MeshSource
|
|
}
|
|
|
|
LastPatchGroupId = PatchGroupID;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FillMesh(const FMeshConversionContext& MeshConversionContext, FBodyMesh& BodyTessellation, FMeshDescription& MeshDescription)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(CADLibrary::FillMesh);
|
|
|
|
const int32 UVChannel = 0;
|
|
const int32 VertexCountPerFace = 3;
|
|
const TriangleIndex Clockwise = { 0, 1, 2 };
|
|
const TriangleIndex CounterClockwise = { 0, 2, 1 };
|
|
|
|
TArray<FVertexInstanceID> TriangleVertexInstanceIDs;
|
|
TriangleVertexInstanceIDs.SetNum(VertexCountPerFace);
|
|
|
|
TArray<FVertexInstanceID> MeshVertexInstanceIDs;
|
|
|
|
// Gather all array data
|
|
FStaticMeshAttributes Attributes(MeshDescription);
|
|
TVertexInstanceAttributesRef<FVector3f> VertexInstanceNormals = Attributes.GetVertexInstanceNormals();
|
|
TVertexInstanceAttributesRef<FVector3f> VertexInstanceTangents = Attributes.GetVertexInstanceTangents();
|
|
TVertexInstanceAttributesRef<float> VertexInstanceBinormalSigns = Attributes.GetVertexInstanceBinormalSigns();
|
|
TVertexInstanceAttributesRef<FVector4f> VertexInstanceColors = Attributes.GetVertexInstanceColors();
|
|
TVertexInstanceAttributesRef<FVector2f> VertexInstanceUVs = Attributes.GetVertexInstanceUVs();
|
|
TPolygonGroupAttributesRef<FName> PolygonGroupImportedMaterialSlotNames = Attributes.GetPolygonGroupMaterialSlotNames();
|
|
|
|
if (!VertexInstanceNormals.IsValid() || !VertexInstanceTangents.IsValid() || !VertexInstanceBinormalSigns.IsValid() || !VertexInstanceColors.IsValid() || !VertexInstanceUVs.IsValid() || !PolygonGroupImportedMaterialSlotNames.IsValid())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// To avoid duplicated triangle
|
|
TSet<TTuple<FVertexInstanceID, FVertexInstanceID, FVertexInstanceID>> VertexIdsToTriangle;
|
|
|
|
const TSet<int32>& ExistingPatches = MeshConversionContext.PatchesToMesh;
|
|
bool bImportOnlyAlreadyPresent = (bool)ExistingPatches.Num();
|
|
|
|
// Find all the materials used
|
|
TMap<uint32, FPolygonGroupID> MaterialToPolygonGroupMapping;
|
|
for (const FTessellationData& FaceTessellation : BodyTessellation.Faces)
|
|
{
|
|
// don't add material of empty face
|
|
if (FaceTessellation.VertexIndices.Num() == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (bImportOnlyAlreadyPresent && !ExistingPatches.Contains(FaceTessellation.PatchId))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// material is preferred over color
|
|
MaterialToPolygonGroupMapping.Add(FaceTessellation.MaterialUId ? FaceTessellation.MaterialUId : FaceTessellation.ColorUId, INDEX_NONE);
|
|
}
|
|
|
|
// Add to the mesh, a polygon groups per material
|
|
int32 PolyGroupIndex = 0;
|
|
FPolygonGroupID PolyGroupID = 0;
|
|
for (auto& Material : MaterialToPolygonGroupMapping)
|
|
{
|
|
if (PolyGroupIndex < FImportParameters::GMaxMaterialCountPerMesh)
|
|
{
|
|
uint32 MaterialHash = Material.Key;
|
|
FName ImportedSlotName = *LexToString(MaterialHash);
|
|
PolyGroupID = MeshDescription.CreatePolygonGroup();
|
|
PolygonGroupImportedMaterialSlotNames[PolyGroupID] = ImportedSlotName;
|
|
}
|
|
else if (PolyGroupIndex == FImportParameters::GMaxMaterialCountPerMesh)
|
|
{
|
|
UE_LOG(LogCADLibrary, Warning, TEXT("The main UE5 rendering systems do not support more than 256 materials per mesh and the limit has been defined to %d materials. Only the first %d materials are kept. The others are replaced by the last one"), FImportParameters::GMaxMaterialCountPerMesh, FImportParameters::GMaxMaterialCountPerMesh);
|
|
}
|
|
Material.Value = PolyGroupID;
|
|
PolyGroupIndex++;
|
|
}
|
|
|
|
if (Algo::AnyOf(BodyTessellation.Faces, [](const FTessellationData& FaceTessellation) { return !FaceTessellation.TexCoordArray.IsEmpty(); }))
|
|
{
|
|
VertexInstanceUVs.SetNumChannels(1);
|
|
}
|
|
|
|
int32 NbStep = 1;
|
|
if (MeshConversionContext.MeshParameters.bIsSymmetric)
|
|
{
|
|
NbStep = 2;
|
|
}
|
|
|
|
TPolygonAttributesRef<int32> PatchGroups = EnableCADPatchGroups(MeshDescription);
|
|
int32 PatchIndex = 0;
|
|
for (int32 Step = 0; Step < NbStep; ++Step)
|
|
{
|
|
// Swap mesh if needed
|
|
const TriangleIndex& Orientation = (!MeshConversionContext.MeshParameters.bNeedSwapOrientation == (bool)Step) ? CounterClockwise : Clockwise;
|
|
TArray<int32>& VertexIdSet = (Step == 0) ? BodyTessellation.VertexIds : BodyTessellation.SymmetricVertexIds;
|
|
|
|
for (FTessellationData& Tessellation : BodyTessellation.Faces)
|
|
{
|
|
if (bImportOnlyAlreadyPresent && !ExistingPatches.Contains(Tessellation.PatchId))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Get the polygonGroup (material is preferred over color)
|
|
FMaterialUId GraphicUId = Tessellation.MaterialUId ? Tessellation.MaterialUId : Tessellation.ColorUId;
|
|
const FPolygonGroupID* PolygonGroupID = MaterialToPolygonGroupMapping.Find(GraphicUId);
|
|
if (PolygonGroupID == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!Step)
|
|
{
|
|
FDatasmithUtils::ConvertVectorArray(MeshConversionContext.ImportParameters.GetModelCoordSys(), Tessellation.NormalArray);
|
|
for (FVector3f& Normal : Tessellation.NormalArray)
|
|
{
|
|
Normal = Normal.GetSafeNormal();
|
|
}
|
|
}
|
|
|
|
TArray<int32> UVIndices;
|
|
TArray<int32> NormalIndices;
|
|
TMap<TTuple<FVertexID, int32, int32>, FVertexInstanceID> VertexIDToInstanceIDForMesh;
|
|
TFunction<FVertexInstanceID(FVertexID, int32)> FindOrAddVertexInstanceIDForMesh = [&](FVertexID VertexID, int32 VertexIndex) ->FVertexInstanceID
|
|
{
|
|
int32 NormalIndex = NormalIndices[VertexIndex];
|
|
int32 UVIndex = UVIndices[VertexIndex];
|
|
|
|
FVertexInstanceID& VertexInstanceID = VertexIDToInstanceIDForMesh.FindOrAdd(TTuple<FVertexID, int32, int32>(VertexID, NormalIndices[VertexIndex], UVIndices[VertexIndex]));
|
|
if (VertexInstanceID == -1)
|
|
{
|
|
VertexInstanceID = MeshDescription.CreateVertexInstance(VertexID);
|
|
VertexInstanceColors[VertexInstanceID] = FLinearColor::White;
|
|
VertexInstanceTangents[VertexInstanceID] = FVector3f(ForceInitToZero);
|
|
VertexInstanceBinormalSigns[VertexInstanceID] = 0.0f;
|
|
|
|
MeshVertexInstanceIDs.Add(VertexInstanceID);
|
|
VertexInstanceNormals[VertexInstanceID] = (FVector3f)Tessellation.NormalArray[NormalIndex];
|
|
if (!Tessellation.TexCoordArray.IsEmpty())
|
|
{
|
|
VertexInstanceUVs.Set(VertexInstanceID, UVChannel, FVector2f(Tessellation.TexCoordArray[UVIndex]));
|
|
}
|
|
}
|
|
return VertexInstanceID;
|
|
};
|
|
|
|
TMap<FVertexID, FVertexInstanceID> VertexIDToInstanceIDForCad;
|
|
TFunction<FVertexInstanceID(FVertexID, int32)> FindOrAddVertexInstanceIDForCad = [&](FVertexID VertexID, int32 VertexIndex) ->FVertexInstanceID
|
|
{
|
|
FVertexInstanceID& VertexInstanceID = VertexIDToInstanceIDForCad.FindOrAdd(VertexID);
|
|
if (VertexInstanceID == -1)
|
|
{
|
|
VertexInstanceID = MeshDescription.CreateVertexInstance(VertexID);
|
|
VertexInstanceColors[VertexInstanceID] = FLinearColor::White;
|
|
VertexInstanceTangents[VertexInstanceID] = FVector3f(ForceInitToZero);
|
|
VertexInstanceBinormalSigns[VertexInstanceID] = 0.0f;
|
|
|
|
MeshVertexInstanceIDs.Add(VertexInstanceID);
|
|
VertexInstanceNormals[VertexInstanceID] = (FVector3f)Tessellation.NormalArray[VertexIndex];
|
|
if (!Tessellation.TexCoordArray.IsEmpty())
|
|
{
|
|
VertexInstanceUVs.Set(VertexInstanceID, UVChannel, FVector2f(Tessellation.TexCoordArray[VertexIndex]));
|
|
}
|
|
}
|
|
return VertexInstanceID;
|
|
};
|
|
|
|
TFunction<FVertexInstanceID(FVertexID, int32)> FindOrAddVertexInstanceID = BodyTessellation.bIsFromCad ? FindOrAddVertexInstanceIDForCad : FindOrAddVertexInstanceIDForMesh;
|
|
|
|
|
|
if (BodyTessellation.bIsFromCad)
|
|
{
|
|
VertexIDToInstanceIDForMesh.Reserve(Tessellation.VertexIndices.Num());
|
|
}
|
|
else
|
|
{
|
|
VertexIDToInstanceIDForCad.Reserve(Tessellation.VertexIndices.Num());
|
|
|
|
if (Tessellation.TexCoordArray.IsEmpty())
|
|
{
|
|
UVIndices.Init(0, Tessellation.VertexIndices.Num());
|
|
}
|
|
else
|
|
{
|
|
UVIndices.SetNum(Tessellation.VertexIndices.Num());
|
|
for (int32 Index = 0; Index < Tessellation.VertexIndices.Num(); ++Index)
|
|
{
|
|
UVIndices[Index] = Index;
|
|
}
|
|
const FVector2f OneVector(1.f, 1.f);
|
|
MergeCoincidents(Tessellation.TexCoordArray, OneVector, KINDA_SMALL_NUMBER, UVIndices);
|
|
}
|
|
|
|
if (Tessellation.NormalArray.Num() == 1)
|
|
{
|
|
NormalIndices.Init(0, Tessellation.VertexIndices.Num());
|
|
}
|
|
else
|
|
{
|
|
NormalIndices.SetNum(Tessellation.VertexIndices.Num());
|
|
for (int32 Index = 0; Index < Tessellation.VertexIndices.Num(); ++Index)
|
|
{
|
|
NormalIndices[Index] = Index;
|
|
}
|
|
MergeCoincidents(Tessellation.NormalArray, FVector3f::OneVector, KINDA_SMALL_NUMBER, NormalIndices);
|
|
}
|
|
}
|
|
|
|
int32 FaceVertexIDs[3];
|
|
int32 FaceVertexIndices[3];
|
|
int32 FaceVertexPositionIndices[3];
|
|
FVector Temp3D = { 0, 0, 0 };
|
|
FVector2D TexCoord2D = { 0, 0 };
|
|
|
|
MeshVertexInstanceIDs.Empty(Tessellation.VertexIndices.Num());
|
|
|
|
PatchIndex++;
|
|
|
|
// build each valid face i.e. 3 different indexes
|
|
for (int32 FaceIndex = 0; FaceIndex < Tessellation.VertexIndices.Num(); FaceIndex += VertexCountPerFace)
|
|
{
|
|
FaceVertexIndices[0] = Tessellation.VertexIndices[FaceIndex + Orientation[0]];
|
|
FaceVertexIndices[1] = Tessellation.VertexIndices[FaceIndex + Orientation[1]];
|
|
FaceVertexIndices[2] = Tessellation.VertexIndices[FaceIndex + Orientation[2]];
|
|
|
|
FaceVertexPositionIndices[0] = Tessellation.PositionIndices[FaceVertexIndices[0]];
|
|
FaceVertexPositionIndices[1] = Tessellation.PositionIndices[FaceVertexIndices[1]];
|
|
FaceVertexPositionIndices[2] = Tessellation.PositionIndices[FaceVertexIndices[2]];
|
|
|
|
if (FaceVertexPositionIndices[0] == INDEX_NONE || FaceVertexPositionIndices[1] == INDEX_NONE || FaceVertexPositionIndices[2] == INDEX_NONE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FaceVertexIDs[0] = (FVertexID) VertexIdSet[FaceVertexPositionIndices[0]];
|
|
FaceVertexIDs[1] = (FVertexID) VertexIdSet[FaceVertexPositionIndices[1]];
|
|
FaceVertexIDs[2] = (FVertexID) VertexIdSet[FaceVertexPositionIndices[2]];
|
|
|
|
// Verify the 3 input indices are not defining a degenerated triangle
|
|
if (FaceVertexIDs[0] == FaceVertexIDs[1] || FaceVertexIDs[0] == FaceVertexIDs[2] || FaceVertexIDs[1] == FaceVertexIDs[2])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
TriangleVertexInstanceIDs[0] = FindOrAddVertexInstanceID(FaceVertexIDs[0], FaceVertexIndices[0]);
|
|
TriangleVertexInstanceIDs[1] = FindOrAddVertexInstanceID(FaceVertexIDs[1], FaceVertexIndices[1]);
|
|
TriangleVertexInstanceIDs[2] = FindOrAddVertexInstanceID(FaceVertexIDs[2], FaceVertexIndices[2]);
|
|
|
|
if(FImportParameters::bGRemoveDuplicatedTriangle)
|
|
{
|
|
Algo::Sort(FaceVertexIDs);
|
|
bool bIsAlreadyInSet;
|
|
VertexIdsToTriangle.Emplace(TTuple<FVertexInstanceID, FVertexInstanceID, FVertexInstanceID>(FaceVertexIDs[0], FaceVertexIDs[1], FaceVertexIDs[2]), &bIsAlreadyInSet);
|
|
if (bIsAlreadyInSet)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Add the triangle as a polygon to the mesh description
|
|
const FPolygonID PolygonID = MeshDescription.CreatePolygon(*PolygonGroupID, TriangleVertexInstanceIDs);
|
|
|
|
// Set patch id attribute
|
|
PatchGroups[PolygonID] = Tessellation.PatchId;
|
|
}
|
|
|
|
if (Step)
|
|
{
|
|
// compute normals of Symmetric vertex
|
|
FMatrix44f SymmetricMatrix = FDatasmithUtils::GetSymmetricMatrix(MeshConversionContext.MeshParameters.SymmetricOrigin, MeshConversionContext.MeshParameters.SymmetricNormal);
|
|
for (const FVertexInstanceID& VertexInstanceID : MeshVertexInstanceIDs) //-V1078
|
|
{
|
|
VertexInstanceNormals[VertexInstanceID] = SymmetricMatrix.TransformVector(VertexInstanceNormals[VertexInstanceID]);
|
|
}
|
|
}
|
|
|
|
if (MeshConversionContext.MeshParameters.bNeedSwapOrientation)
|
|
{
|
|
for (int32 Index = 0; Index < MeshVertexInstanceIDs.Num(); Index++)
|
|
{
|
|
VertexInstanceNormals[MeshVertexInstanceIDs[Index]] = VertexInstanceNormals[MeshVertexInstanceIDs[Index]] * -1.f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ConvertBodyMeshToMeshDescription(const FMeshConversionContext& MeshConversionContext, FBodyMesh& Body, FMeshDescription& MeshDescription)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(CADLibrary::ConvertBodyMeshToMeshDescription);
|
|
|
|
// in a closed big mesh VertexCount ~ TriangleCount / 2, EdgeCount ~ 1.5* TriangleCount
|
|
MeshDescription.ReserveNewVertexInstances(Body.VertexArray.Num());
|
|
MeshDescription.ReserveNewPolygons(Body.TriangleCount);
|
|
MeshDescription.ReserveNewEdges(Body.TriangleCount * 3);
|
|
|
|
FillVertexPosition(MeshConversionContext, Body, MeshDescription);
|
|
|
|
if (!FillMesh(MeshConversionContext, Body, MeshDescription))
|
|
{
|
|
return false;
|
|
}
|
|
if (!Body.bIsFromCad)
|
|
{
|
|
MeshOperator::FixNonManifoldMesh(MeshDescription);
|
|
}
|
|
|
|
// Workaround SDHE-19725: Compute any null normals.
|
|
MeshOperator::RecomputeNullNormal(MeshDescription);
|
|
|
|
// Orient mesh
|
|
MeshOperator::OrientMesh(MeshDescription);
|
|
|
|
// Sew mesh
|
|
if(FImportParameters::bGSewMeshIfNeeded)
|
|
{
|
|
double Tolerance = FImportParameters::GStitchingTolerance;
|
|
MeshOperator::ResolveTJunctions(MeshDescription, Tolerance);
|
|
}
|
|
|
|
// Build edge meta data
|
|
FStaticMeshOperations::DetermineEdgeHardnessesFromVertexInstanceNormals(MeshDescription);
|
|
|
|
return MeshDescription.Polygons().Num() > 0;
|
|
}
|
|
|
|
TSharedPtr<IDatasmithUEPbrMaterialElement> CreateDefaultUEPbrMaterial()
|
|
{
|
|
// Take the Material diffuse color and connect it to the BaseColor of a UEPbrMaterial
|
|
TSharedRef<IDatasmithUEPbrMaterialElement> MaterialElement = FDatasmithSceneFactory::CreateUEPbrMaterial(TEXT("0"));
|
|
MaterialElement->SetLabel(TEXT("DefaultCADImportMaterial"));
|
|
|
|
FLinearColor LinearColor = FLinearColor::FromPow22Color(FColor(200, 200, 200, 255));
|
|
IDatasmithMaterialExpressionColor* ColorExpression = MaterialElement->AddMaterialExpression<IDatasmithMaterialExpressionColor>();
|
|
ColorExpression->SetName(TEXT("Base Color"));
|
|
ColorExpression->GetColor() = LinearColor;
|
|
MaterialElement->GetBaseColor().SetExpression(ColorExpression);
|
|
MaterialElement->SetParentLabel(TEXT("M_DatasmithCAD"));
|
|
|
|
return MaterialElement;
|
|
}
|
|
|
|
TSharedPtr<IDatasmithUEPbrMaterialElement> CreateUEPbrMaterialFromColor(const FColor& InColor)
|
|
{
|
|
FString Name = FString::FromInt(BuildColorUId(InColor));
|
|
FString Label = FString::Printf(TEXT("color_%02x%02x%02x%02x"), InColor.R, InColor.G, InColor.B, InColor.A);
|
|
|
|
// Take the Material diffuse color and connect it to the BaseColor of a UEPbrMaterial
|
|
TSharedRef<IDatasmithUEPbrMaterialElement> MaterialElement = FDatasmithSceneFactory::CreateUEPbrMaterial(*Name);
|
|
MaterialElement->SetLabel(*Label);
|
|
|
|
FLinearColor LinearColor = FLinearColor::FromSRGBColor(InColor);
|
|
|
|
IDatasmithMaterialExpressionColor* ColorExpression = MaterialElement->AddMaterialExpression<IDatasmithMaterialExpressionColor>();
|
|
ColorExpression->SetName(TEXT("Base Color"));
|
|
ColorExpression->GetColor() = LinearColor;
|
|
|
|
MaterialElement->GetBaseColor().SetExpression(ColorExpression);
|
|
|
|
if (LinearColor.A < 1.0f)
|
|
{
|
|
MaterialElement->SetBlendMode(/*EBlendMode::BLEND_Translucent*/2);
|
|
|
|
IDatasmithMaterialExpressionScalar* Scalar = MaterialElement->AddMaterialExpression<IDatasmithMaterialExpressionScalar>();
|
|
Scalar->GetScalar() = LinearColor.A;
|
|
Scalar->SetName(TEXT("Opacity Level"));
|
|
|
|
MaterialElement->GetOpacity().SetExpression(Scalar);
|
|
MaterialElement->SetParentLabel(TEXT("M_DatasmithCADTransparent"));
|
|
}
|
|
else
|
|
{
|
|
MaterialElement->SetParentLabel(TEXT("M_DatasmithCAD"));
|
|
}
|
|
|
|
return MaterialElement;
|
|
}
|
|
|
|
TSharedPtr<IDatasmithUEPbrMaterialElement> CreateUEPbrMaterialFromMaterial(FCADMaterial& InMaterial, TSharedRef<IDatasmithScene> Scene)
|
|
{
|
|
FString Name = FString::FromInt(BuildMaterialUId(InMaterial));
|
|
|
|
// Take the Material diffuse color and connect it to the BaseColor of a UEPbrMaterial
|
|
TSharedRef<IDatasmithUEPbrMaterialElement> MaterialElement = FDatasmithSceneFactory::CreateUEPbrMaterial(*Name);
|
|
FString MaterialLabel(InMaterial.MaterialName);
|
|
if (MaterialLabel.IsEmpty())
|
|
{
|
|
MaterialLabel = TEXT("Material");
|
|
}
|
|
MaterialElement->SetLabel(*MaterialLabel);
|
|
|
|
// Set a diffuse color if there's nothing in the BaseColor
|
|
if (MaterialElement->GetBaseColor().GetExpression() == nullptr)
|
|
{
|
|
FLinearColor LinearColor = FLinearColor::FromSRGBColor(InMaterial.Diffuse);
|
|
|
|
IDatasmithMaterialExpressionColor* ColorExpression = MaterialElement->AddMaterialExpression<IDatasmithMaterialExpressionColor>();
|
|
ColorExpression->SetName(TEXT("Base Color"));
|
|
ColorExpression->GetColor() = LinearColor;
|
|
|
|
MaterialElement->GetBaseColor().SetExpression(ColorExpression);
|
|
}
|
|
|
|
if (InMaterial.Transparency > 0.0f)
|
|
{
|
|
MaterialElement->SetBlendMode(/*EBlendMode::BLEND_Translucent*/2);
|
|
IDatasmithMaterialExpressionScalar* Scalar = MaterialElement->AddMaterialExpression<IDatasmithMaterialExpressionScalar>();
|
|
Scalar->GetScalar() = InMaterial.Transparency;
|
|
Scalar->SetName(TEXT("Opacity Level"));
|
|
MaterialElement->GetOpacity().SetExpression(Scalar);
|
|
MaterialElement->SetParentLabel(TEXT("M_DatasmithCADTransparent"));
|
|
}
|
|
else
|
|
{
|
|
MaterialElement->SetParentLabel(TEXT("M_DatasmithCAD"));
|
|
}
|
|
|
|
return MaterialElement;
|
|
}
|
|
|
|
}
|