// 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 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& 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 void MergeCoincidents(const TArray& DataArray, const VectorType& OneVector, const double CoincidenceTolerance, TArray& InOutIndices) { // Create a list of Data W/index pairs TArray> 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 const& A, TGeometricData 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& FaceTessellationSet = Body.Faces; TVertexAttributesRef VertexPositions = MeshDescription.GetVertexPositions(); TArray& VertexArray = Body.VertexArray; TArray& 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& 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 EnableCADPatchGroups(FMeshDescription& OutMeshDescription) { TPolygonAttributesRef PatchGroups = OutMeshDescription.PolygonAttributes().GetAttributesRef(PolyTriGroups); if (PatchGroups.IsValid() == false) { OutMeshDescription.PolygonAttributes().RegisterAttribute(PolyTriGroups, 1, 0, EMeshAttributeFlags::AutoGenerated); PatchGroups = OutMeshDescription.PolygonAttributes().GetAttributesRef(PolyTriGroups); check(PatchGroups.IsValid()); } return PatchGroups; } void GetExistingPatches(FMeshDescription& MeshSource, TSet& OutExistingPatchIds) { TPolygonAttributesRef ElementToGroups = MeshSource.PolygonAttributes().GetAttributesRef(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 PolygoneToPolygoneGroupId = MeshSourceAttributes.GetPolygonPolygonGroupIndices(); TPolygonAttributesRef 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 MeshSourcePolygonGroupImportedMaterialSlotNames = MeshSourceAttributes.GetPolygonGroupMaterialSlotNames(); MaterialSlotNames = MeshSourcePolygonGroupImportedMaterialSlotNames.GetRawArray(0); } void FMeshDescriptionDataCache::RestoreMaterialSlotNames(FMeshDescription& Mesh) const { TPolygonAttributesRef PatchGroup = EnableCADPatchGroups(Mesh); FStaticMeshAttributes MeshAttributes(Mesh); TPolygonAttributesRef PolygoneToPolygoneGroupId = MeshAttributes.GetPolygonPolygonGroupIndices(); TPolygonGroupAttributesRef PolygonGroupMaterialSlotNames = MeshAttributes.GetPolygonGroupMaterialSlotNames(); TSet 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 TriangleVertexInstanceIDs; TriangleVertexInstanceIDs.SetNum(VertexCountPerFace); TArray MeshVertexInstanceIDs; // Gather all array data FStaticMeshAttributes Attributes(MeshDescription); TVertexInstanceAttributesRef VertexInstanceNormals = Attributes.GetVertexInstanceNormals(); TVertexInstanceAttributesRef VertexInstanceTangents = Attributes.GetVertexInstanceTangents(); TVertexInstanceAttributesRef VertexInstanceBinormalSigns = Attributes.GetVertexInstanceBinormalSigns(); TVertexInstanceAttributesRef VertexInstanceColors = Attributes.GetVertexInstanceColors(); TVertexInstanceAttributesRef VertexInstanceUVs = Attributes.GetVertexInstanceUVs(); TPolygonGroupAttributesRef PolygonGroupImportedMaterialSlotNames = Attributes.GetPolygonGroupMaterialSlotNames(); if (!VertexInstanceNormals.IsValid() || !VertexInstanceTangents.IsValid() || !VertexInstanceBinormalSigns.IsValid() || !VertexInstanceColors.IsValid() || !VertexInstanceUVs.IsValid() || !PolygonGroupImportedMaterialSlotNames.IsValid()) { return false; } // To avoid duplicated triangle TSet> VertexIdsToTriangle; const TSet& ExistingPatches = MeshConversionContext.PatchesToMesh; bool bImportOnlyAlreadyPresent = (bool)ExistingPatches.Num(); // Find all the materials used TMap 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 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& 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 UVIndices; TArray NormalIndices; TMap, FVertexInstanceID> VertexIDToInstanceIDForMesh; TFunction FindOrAddVertexInstanceIDForMesh = [&](FVertexID VertexID, int32 VertexIndex) ->FVertexInstanceID { int32 NormalIndex = NormalIndices[VertexIndex]; int32 UVIndex = UVIndices[VertexIndex]; FVertexInstanceID& VertexInstanceID = VertexIDToInstanceIDForMesh.FindOrAdd(TTuple(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 VertexIDToInstanceIDForCad; TFunction 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 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(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 CreateDefaultUEPbrMaterial() { // Take the Material diffuse color and connect it to the BaseColor of a UEPbrMaterial TSharedRef MaterialElement = FDatasmithSceneFactory::CreateUEPbrMaterial(TEXT("0")); MaterialElement->SetLabel(TEXT("DefaultCADImportMaterial")); FLinearColor LinearColor = FLinearColor::FromPow22Color(FColor(200, 200, 200, 255)); IDatasmithMaterialExpressionColor* ColorExpression = MaterialElement->AddMaterialExpression(); ColorExpression->SetName(TEXT("Base Color")); ColorExpression->GetColor() = LinearColor; MaterialElement->GetBaseColor().SetExpression(ColorExpression); MaterialElement->SetParentLabel(TEXT("M_DatasmithCAD")); return MaterialElement; } TSharedPtr 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 MaterialElement = FDatasmithSceneFactory::CreateUEPbrMaterial(*Name); MaterialElement->SetLabel(*Label); FLinearColor LinearColor = FLinearColor::FromSRGBColor(InColor); IDatasmithMaterialExpressionColor* ColorExpression = MaterialElement->AddMaterialExpression(); 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(); 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 CreateUEPbrMaterialFromMaterial(FCADMaterial& InMaterial, TSharedRef Scene) { FString Name = FString::FromInt(BuildMaterialUId(InMaterial)); // Take the Material diffuse color and connect it to the BaseColor of a UEPbrMaterial TSharedRef 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(); 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(); 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; } }