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

415 lines
15 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CADKernelTools.h"
#include "CADData.h"
#include "CADOptions.h"
#include "MeshDescription.h"
#include "CADMeshDescriptionHelper.h"
#include "StaticMeshAttributes.h"
#include "StaticMeshOperations.h"
#include "Core/CADEntity.h"
#include "Core/MetadataDictionary.h"
#include "Core/Session.h"
#include "Core/Types.h"
#include "Mesh/Criteria/Criterion.h"
#include "Mesh/Meshers/Mesher.h"
#include "Mesh/Structure/FaceMesh.h"
#include "Mesh/Structure/ModelMesh.h"
#include "Topo/Model.h"
#include "Topo/Body.h"
#include "Topo/Shell.h"
#include "Topo/TopologicalEdge.h"
#include "Topo/TopologicalShapeEntity.h"
#include "Topo/TopologicalFace.h"
#include "Topo/TopologicalVertex.h"
typedef uint32 TriangleIndex[3];
namespace CADLibrary
{
static void FillVertexPosition(FMeshConversionContext& Context, UE::CADKernel::FModelMesh& ModelMesh, FMeshDescription& MeshDescription)
{
TArray<FVector3f> VertexArray;
ModelMesh.GetNodeCoordinates(VertexArray);
for (FVector3f& Vertex : VertexArray)
{
Vertex *= 0.1f; // mm (CADKernel unit) to cm (UE unit)
}
int32 VertexCount = VertexArray.Num();
TVertexAttributesRef<FVector3f> VertexPositions = MeshDescription.GetVertexPositions();
MeshDescription.ReserveNewVertices(Context.MeshParameters.bIsSymmetric ? VertexCount * 2 : VertexCount);
Context.VertexIds.SetNumZeroed(VertexCount);
// Make MeshDescription.VertexPositions and VertexID
int32 VertexIndex = -1;
for (const FVector3f& Vertex : VertexArray)
{
VertexIndex++;
FVertexID VertexID = MeshDescription.CreateVertex();
VertexPositions[VertexID] = FDatasmithUtils::ConvertVector((FDatasmithUtils::EModelCoordSystem)Context.ImportParameters.GetModelCoordSys(), Vertex);
Context.VertexIds[VertexIndex] = VertexID;
}
// if Symmetric mesh, the symmetric side of the mesh have to be generated
if (Context.MeshParameters.bIsSymmetric)
{
FMatrix44f SymmetricMatrix = FDatasmithUtils::GetSymmetricMatrix(Context.MeshParameters.SymmetricOrigin, Context.MeshParameters.SymmetricNormal);
Context.SymmetricVertexIds.SetNum(VertexArray.Num());
VertexIndex = 0;
for (const FVector3f& Vertex : VertexArray)
{
FVertexID VertexID = MeshDescription.CreateVertex();
VertexPositions[VertexID] = FDatasmithUtils::ConvertVector((FDatasmithUtils::EModelCoordSystem)Context.ImportParameters.GetModelCoordSys(), Vertex);
VertexPositions[VertexID] = SymmetricMatrix.TransformPosition(VertexPositions[VertexID]);
Context.SymmetricVertexIds[VertexIndex++] = VertexID;
}
}
}
bool FillMesh(FMeshConversionContext& Context, UE::CADKernel::FModelMesh& ModelMesh, FMeshDescription& MeshDescription)
{
using namespace UE::CADKernel;
const int32 UVChannel = 0;
const int32 TriangleCount = 3;
const TriangleIndex Clockwise = { 0, 1, 2 };
const TriangleIndex CounterClockwise = { 0, 2, 1 };
TArray<FVertexInstanceID> TriangleVertexInstanceIDs;
TriangleVertexInstanceIDs.SetNum(TriangleCount);
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;
}
// Find all the materials used
TMap<uint32, FPolygonGroupID> MaterialToPolygonGroupMapping;
for (const FFaceMesh* FaceMesh : ModelMesh.GetFaceMeshes())
{
const FTopologicalFace& Face = (const FTopologicalFace&) FaceMesh->GetGeometricEntity();
uint32 MaterialId = Face.GetMaterialId() ? Face.GetMaterialId() : Face.GetColorId();
MaterialToPolygonGroupMapping.Add(MaterialId, INDEX_NONE);
}
// Add to the mesh, a polygon groups per material
for (auto& Material : MaterialToPolygonGroupMapping)
{
uint32 MaterialHash = Material.Key;
FName ImportedSlotName = *LexToString(MaterialHash);
FPolygonGroupID PolyGroupID = MeshDescription.CreatePolygonGroup();
PolygonGroupImportedMaterialSlotNames[PolyGroupID] = ImportedSlotName;
Material.Value = PolyGroupID;
}
VertexInstanceUVs.SetNumChannels(1);
int32 NbStep = 1;
if (Context.MeshParameters.bIsSymmetric)
{
NbStep = 2;
}
int32 FaceIndex = 0;
const TSet<int32>& PatchIdSet = Context.PatchesToMesh;
bool bImportOnlyAlreadyPresent = (bool)PatchIdSet.Num();
TPolygonAttributesRef<int32> PatchGroups = EnableCADPatchGroups(MeshDescription);
int32 PatchIndex = 0;
for (int32 Step = 0; Step < NbStep; ++Step)
{
// Swap mesh if needed
const TriangleIndex& Orientation = (!Context.MeshParameters.bNeedSwapOrientation == (bool)Step) ? CounterClockwise : Clockwise;
TArray<int32>& VertexIdSet = (Step == 0) ? Context.VertexIds : Context.SymmetricVertexIds;
// Loop through the FaceMeshes and collect all tessellation data
for (FFaceMesh* FaceMesh : ModelMesh.GetFaceMeshes())
{
const FTopologicalFace& Face = (const FTopologicalFace&)FaceMesh->GetGeometricEntity();
if (bImportOnlyAlreadyPresent && !PatchIdSet.Contains(Face.GetPatchId()))
{
continue;
}
// Get the polygonGroup
const FPolygonGroupID* PolygonGroupID = MaterialToPolygonGroupMapping.Find(Face.GetColorId());
if (PolygonGroupID == nullptr)
{
continue;
}
int32 VertexIDs[3];
//FVector Temp3D = { 0, 0, 0 };
//FVector2D TexCoord2D = { 0, 0 };
const TArray<int32>& TriangleVertexIndices = FaceMesh->TrianglesVerticesIndex;
const TArray<int32>& VerticesGlobalIndex = FaceMesh->VerticesGlobalIndex;
MeshVertexInstanceIDs.Empty(TriangleVertexIndices.Num());
PatchIndex++;
// build each valid face i.e. 3 different indexes
for (int32 Index = 0; Index < TriangleVertexIndices.Num(); Index += 3)
{
VertexIDs[0] = VertexIdSet[VerticesGlobalIndex[TriangleVertexIndices[Index + Orientation[0]]]];
VertexIDs[1] = VertexIdSet[VerticesGlobalIndex[TriangleVertexIndices[Index + Orientation[1]]]];
VertexIDs[2] = VertexIdSet[VerticesGlobalIndex[TriangleVertexIndices[Index + Orientation[2]]]];
MeshVertexInstanceIDs.Add(TriangleVertexInstanceIDs[0] = MeshDescription.CreateVertexInstance((FVertexID)VertexIDs[0]));
MeshVertexInstanceIDs.Add(TriangleVertexInstanceIDs[1] = MeshDescription.CreateVertexInstance((FVertexID)VertexIDs[1]));
MeshVertexInstanceIDs.Add(TriangleVertexInstanceIDs[2] = MeshDescription.CreateVertexInstance((FVertexID)VertexIDs[2]));
// Add the triangle as a polygon to the mesh description
const FPolygonID PolygonID = MeshDescription.CreatePolygon(*PolygonGroupID, TriangleVertexInstanceIDs);
// Set patch id attribute
PatchGroups[PolygonID] = Face.GetPatchId();
}
for (int32 IndexFace = 0; IndexFace < MeshVertexInstanceIDs.Num(); IndexFace += 3)
{
for (int32 Index = 0; Index < TriangleCount; Index++)
{
const FVertexInstanceID VertexInstanceID = MeshVertexInstanceIDs[IndexFace + Index];
// TODO
// The UV scaling should be done in CADKernel
const double ScaleUV = 0.001; // mm to m
VertexInstanceUVs.Set(VertexInstanceID, UVChannel, FVector2f(FaceMesh->UVMap[TriangleVertexIndices[IndexFace + Orientation[Index]]] * ScaleUV));
VertexInstanceColors[VertexInstanceID] = FLinearColor::White;
VertexInstanceTangents[VertexInstanceID] = FVector3f(ForceInitToZero);
VertexInstanceBinormalSigns[VertexInstanceID] = 0.0f;
}
}
if (!Step)
{
FDatasmithUtils::ConvertVectorArray(Context.ImportParameters.GetModelCoordSys(), FaceMesh->Normals);
for (FVector3f& Normal : FaceMesh->Normals)
{
Normal = Normal.GetSafeNormal();
}
}
for (int32 IndexFace = 0; IndexFace < MeshVertexInstanceIDs.Num(); IndexFace += 3)
{
for (int32 Index = 0; Index < 3; Index++)
{
const FVertexInstanceID VertexInstanceID = MeshVertexInstanceIDs[IndexFace + Index];
VertexInstanceNormals[VertexInstanceID] = (FVector3f)FaceMesh->Normals[TriangleVertexIndices[IndexFace + Orientation[Index]]];
}
}
// compute normals
if (Step)
{
// compute normals of Symmetric vertex
FMatrix44f SymmetricMatrix = FDatasmithUtils::GetSymmetricMatrix(Context.MeshParameters.SymmetricOrigin, Context.MeshParameters.SymmetricNormal);
for (const FVertexInstanceID& VertexInstanceID : MeshVertexInstanceIDs)
{
VertexInstanceNormals[VertexInstanceID] = SymmetricMatrix.TransformVector(VertexInstanceNormals[VertexInstanceID]);
}
}
}
}
return true;
}
static bool ConvertModelMeshToMeshDescription(FMeshConversionContext& Context, UE::CADKernel::FModelMesh& InModelMesh, FMeshDescription& MeshDescription)
{
int32 VertexCount = InModelMesh.GetVertexCount();
int32 TriangleCount = InModelMesh.GetTriangleCount();
MeshDescription.ReserveNewVertexInstances(VertexCount);
MeshDescription.ReserveNewPolygons(TriangleCount);
MeshDescription.ReserveNewEdges(TriangleCount);
FillVertexPosition(Context, InModelMesh, MeshDescription);
if (!FillMesh(Context, InModelMesh, MeshDescription))
{
return false;
}
// Build edge meta data
FStaticMeshOperations::DetermineEdgeHardnessesFromVertexInstanceNormals(MeshDescription);
return MeshDescription.Polygons().Num() > 0;
}
bool FCADKernelTools::Tessellate(UE::CADKernel::FTopologicalShapeEntity& CADTopologicalEntity, FMeshConversionContext& TessellationContext, FMeshDescription& OutMeshDescription)
{
using namespace UE::CADKernel;
// Tessellate the model
TSharedRef<FModelMesh> CADKernelModelMesh = FEntity::MakeShared<FModelMesh>();
const bool bActivateThinZoneMeshing = FImportParameters::bGActivateThinZoneMeshing;
FMesher Mesher(*CADKernelModelMesh, TessellationContext.GeometricTolerance, bActivateThinZoneMeshing);
DefineMeshCriteria(*CADKernelModelMesh, TessellationContext.ImportParameters, TessellationContext.GeometricTolerance);
Mesher.MeshEntity(CADTopologicalEntity);
return ConvertModelMeshToMeshDescription(TessellationContext, *CADKernelModelMesh, OutMeshDescription);
}
uint32 FCADKernelTools::GetFaceTessellation(UE::CADKernel::FFaceMesh& FaceMesh, FBodyMesh& OutBodyMesh, FObjectDisplayDataId FaceMaterial)
{
// Something wrong happened, either an error or no data to collect
if (FaceMesh.TrianglesVerticesIndex.Num() == 0)
{
return 0;
}
FTessellationData& Tessellation = OutBodyMesh.Faces.Emplace_GetRef();
const UE::CADKernel::FTopologicalFace& TopologicalFace = (const UE::CADKernel::FTopologicalFace&)FaceMesh.GetGeometricEntity();
Tessellation.PatchId = TopologicalFace.GetPatchId();
Tessellation.PositionIndices = MoveTemp(FaceMesh.VerticesGlobalIndex);
Tessellation.VertexIndices = MoveTemp(FaceMesh.TrianglesVerticesIndex);
Tessellation.NormalArray = MoveTemp(FaceMesh.Normals);
Tessellation.TexCoordArray = MoveTemp(FaceMesh.UVMap);
if (FaceMaterial.ColorUId)
{
Tessellation.ColorUId = FaceMaterial.ColorUId;
OutBodyMesh.ColorSet.Add(FaceMaterial.ColorUId);
}
if (FaceMaterial.MaterialUId)
{
Tessellation.MaterialUId = FaceMaterial.MaterialUId;
OutBodyMesh.MaterialSet.Add(FaceMaterial.MaterialUId);
}
return Tessellation.VertexIndices.Num() / 3;
}
void GetDisplayDataIds(const UE::CADKernel::FTopologicalShapeEntity& ShapeEntity, FObjectDisplayDataId& DisplayDataId)
{
if (ShapeEntity.GetColorId() != 0)
{
DisplayDataId.ColorUId = ShapeEntity.GetColorId();
}
if (ShapeEntity.GetMaterialId() != 0)
{
DisplayDataId.MaterialUId = ShapeEntity.GetMaterialId();
}
}
void FCADKernelTools::GetBodyTessellation(const UE::CADKernel::FModelMesh& ModelMesh, const UE::CADKernel::FBody& Body, FBodyMesh& OutBodyMesh)
{
ModelMesh.GetNodeCoordinates(OutBodyMesh.VertexArray);
for (FVector3f& Vertex : OutBodyMesh.VertexArray)
{
Vertex *= 0.1f;
}
uint32 FaceSize = Body.FaceCount();
// Allocate memory space for tessellation data
OutBodyMesh.Faces.Reserve(FaceSize);
OutBodyMesh.ColorSet.Reserve(FaceSize);
OutBodyMesh.MaterialSet.Reserve(FaceSize);
FObjectDisplayDataId BodyMaterial;
GetDisplayDataIds(Body, BodyMaterial);
BodyMaterial.DefaultMaterialUId = BodyMaterial.MaterialUId != 0 ? BodyMaterial.MaterialUId : BodyMaterial.ColorUId;
// Loop through the face of bodies and collect all tessellation data
for (const TSharedPtr<UE::CADKernel::FShell>& Shell : Body.GetShells())
{
if (!Shell.IsValid())
{
continue;
}
FObjectDisplayDataId ShellMaterial = BodyMaterial;
GetDisplayDataIds(*Shell, ShellMaterial);
for (const UE::CADKernel::FOrientedFace& Face : Shell->GetFaces())
{
if (!Face.Entity.IsValid())
{
continue;
}
if (!Face.Entity->HasTesselation())
{
continue;
}
FObjectDisplayDataId FaceMaterial = ShellMaterial;
GetDisplayDataIds(*Face.Entity, FaceMaterial);
UE::CADKernel::FFaceMesh* FaceMesh = Face.Entity->GetMesh();
uint32 TriangleNum = GetFaceTessellation(*FaceMesh, OutBodyMesh, FaceMaterial);
OutBodyMesh.TriangleCount += TriangleNum;
}
}
}
void FCADKernelTools::DefineMeshCriteria(UE::CADKernel::FModelMesh& MeshModel, const FImportParameters& ImportParameters, double GeometricTolerance)
{
{
TSharedPtr<UE::CADKernel::FCriterion> CurvatureCriterion = UE::CADKernel::FCriterion::CreateCriterion(UE::CADKernel::ECriterion::CADCurvature);
MeshModel.AddCriterion(CurvatureCriterion);
TSharedPtr<UE::CADKernel::FCriterion> MinSizeCriterion = UE::CADKernel::FCriterion::CreateCriterion(UE::CADKernel::ECriterion::MinSize, 2. * GeometricTolerance);
MeshModel.AddCriterion(MinSizeCriterion);
}
if (ImportParameters.GetMaxEdgeLength() > SMALL_NUMBER)
{
const double MaxSize = ImportParameters.GetMaxEdgeLength() * 10.; // cm to mm
TSharedPtr<UE::CADKernel::FCriterion> MaxSizeCriterion = UE::CADKernel::FCriterion::CreateCriterion(UE::CADKernel::ECriterion::MaxSize, MaxSize);
MeshModel.AddCriterion(MaxSizeCriterion);
}
if (ImportParameters.GetChordTolerance() > SMALL_NUMBER)
{
double MaxChord = ImportParameters.GetChordTolerance() * 10.; // cm to mm
TSharedPtr<UE::CADKernel::FCriterion> ChordCriterion = UE::CADKernel::FCriterion::CreateCriterion(UE::CADKernel::ECriterion::Sag, MaxChord);
MeshModel.AddCriterion(ChordCriterion);
}
if (ImportParameters.GetMaxNormalAngle() > SMALL_NUMBER)
{
TSharedPtr<UE::CADKernel::FCriterion> MaxNormalAngleCriterion = UE::CADKernel::FCriterion::CreateCriterion(UE::CADKernel::ECriterion::Angle, ImportParameters.GetMaxNormalAngle());
MeshModel.AddCriterion(MaxNormalAngleCriterion);
}
}
}