// Copyright Epic Games, Inc. All Rights Reserved. #include "AliasModelToTechSoftConverter.h" #ifdef USE_OPENMODEL #include "OpenModelUtils.h" #include "HAL/PlatformMemory.h" #include "Math/Color.h" #include "CADMeshDescriptionHelper.h" #include "TechSoftInterface.h" #include "TechSoftUtils.h" #include "TUniqueTechSoftObj.h" #if PLATFORM_WINDOWS #include "Windows/AllowWindowsPlatformTypes.h" #endif // Alias API wrappes object in AlObjects. This is an abstract base class which holds a reference to an anonymous data structure. // The only way to compare two AlObjects is to compare their data structure. That is the reason why private fields are made public. #define private public #include "AlCurve.h" #include "AlDagNode.h" #include "AlShell.h" #include "AlShellNode.h" #include "AlSurface.h" #include "AlSurfaceNode.h" #include "AlTrimBoundary.h" #include "AlTrimCurve.h" #include "AlTrimRegion.h" #include "AlTM.h" #if PLATFORM_WINDOWS #include "Windows/HideWindowsPlatformTypes.h" #endif #define LOCTEXT_NAMESPACE "WireInterface" namespace UE_DATASMITHWIRETRANSLATOR_NAMESPACE { #ifdef USE_TECHSOFT_SDK namespace AliasToTechSoftUtils { enum EAxis { U, V }; template A3DSurfBase* AddNURBSSurface(const Surface_T& AliasSurface, EAliasObjectReference InObjectReference, const AlMatrix4x4& InAlMatrix) { CADLibrary::TUniqueTSObj NurbsSurfaceData; NurbsSurfaceData->m_eKnotType = A3DEKnotType::kA3DKnotTypeUnspecified; NurbsSurfaceData->m_eSurfaceForm = A3DEBSplineSurfaceForm::kA3DBSplineSurfaceFormUnspecified; NurbsSurfaceData->m_uiUDegree = AliasSurface.uDegree(); NurbsSurfaceData->m_uiVDegree = AliasSurface.vDegree(); NurbsSurfaceData->m_uiUCtrlSize = AliasSurface.uNumberOfCVsInclMultiples(); NurbsSurfaceData->m_uiVCtrlSize = AliasSurface.vNumberOfCVsInclMultiples(); NurbsSurfaceData->m_uiUKnotSize = AliasSurface.realuNumberOfKnots() + 2; NurbsSurfaceData->m_uiVKnotSize = AliasSurface.realvNumberOfKnots() + 2; TArray UNodalVector; TArray VNodalVector; TFunction&)> FillNodalVector = [&](EAxis Axis, TArray& OutNodalVector) { TArray NodalVector; if(Axis == EAxis::U) { OutNodalVector.Reserve(NurbsSurfaceData->m_uiUKnotSize); NodalVector.SetNumUninitialized(NurbsSurfaceData->m_uiUKnotSize - 2); AliasSurface.realuKnotVector(NodalVector.GetData()); } else { OutNodalVector.Reserve(NurbsSurfaceData->m_uiVKnotSize); NodalVector.SetNumUninitialized(NurbsSurfaceData->m_uiVKnotSize - 2); AliasSurface.realvKnotVector(NodalVector.GetData()); } OutNodalVector.Add(NodalVector[0]); for(double Value : NodalVector) { OutNodalVector.Add(Value); } OutNodalVector.Add(NodalVector.Last()); if (Axis == EAxis::U) { NurbsSurfaceData->m_pdUKnots = OutNodalVector.GetData(); } else { NurbsSurfaceData->m_pdVKnots = OutNodalVector.GetData(); } }; FillNodalVector(EAxis::U, UNodalVector); FillNodalVector(EAxis::V, VNodalVector); const int32 CoordinateCount = NurbsSurfaceData->m_uiUCtrlSize * NurbsSurfaceData->m_uiVCtrlSize * 4; TArray HomogeneousPoles; HomogeneousPoles.SetNumUninitialized(CoordinateCount); if (InObjectReference == EAliasObjectReference::WorldReference) { AliasSurface.CVsWorldPositionInclMultiples(HomogeneousPoles.GetData()); } else if (InObjectReference == EAliasObjectReference::ParentReference) { AlTM TranformMatrix(InAlMatrix); AliasSurface.CVsAffectedPositionInclMultiples(TranformMatrix, HomogeneousPoles.GetData()); } else // EAliasObjectReference::LocalReference { AliasSurface.CVsUnaffectedPositionInclMultiples(HomogeneousPoles.GetData()); } int32 PoleCount = NurbsSurfaceData->m_uiUCtrlSize * NurbsSurfaceData->m_uiVCtrlSize; TArray Weights; TArray ControlPoints; ControlPoints.SetNum(PoleCount); Weights.SetNum(PoleCount); A3DVector3dData* ControlPointPtr = ControlPoints.GetData(); A3DDouble* WeightPtr = Weights.GetData(); NurbsSurfaceData->m_pdWeights = WeightPtr; NurbsSurfaceData->m_pCtrlPts = ControlPointPtr; TFunction SetA3DPole = [](const double* HomogeneousPole, A3DVector3dData & OutTSPoint, A3DDouble & OutWeight) { OutTSPoint.m_dX = UE_TO_CADKERNEL(HomogeneousPole[0]); // cm (Alias MetricUnit) to mm OutTSPoint.m_dY = UE_TO_CADKERNEL(HomogeneousPole[1]); OutTSPoint.m_dZ = UE_TO_CADKERNEL(HomogeneousPole[2]); OutWeight = HomogeneousPole[3]; }; double* AliasPoles = HomogeneousPoles.GetData(); for (int32 Index = 0; Index < PoleCount; ++Index, AliasPoles += 4, ++ControlPointPtr, ++WeightPtr) { SetA3DPole(AliasPoles, *ControlPointPtr, *WeightPtr); } return CADLibrary::TechSoftInterface::CreateSurfaceNurbs(*NurbsSurfaceData); } } // ns AliasToTechsoftUtils A3DCrvBase* FAliasModelToTechSoftConverter::CreateCurve(const AlTrimCurve& AliasTrimCurve) { CADLibrary::TUniqueTSObj NurbsCurveData; NurbsCurveData->m_bIs2D = true; NurbsCurveData->m_bRational = true; NurbsCurveData->m_uiDegree = AliasTrimCurve.degree(); int ControlPointCount = AliasTrimCurve.numberOfCVs(); int32 KnotCount = AliasTrimCurve.realNumberOfKnots(); using AlPoint = TStaticArray; TArray AliasPoles; TArray AliasNodalVector; AliasNodalVector.SetNum(ControlPointCount); AliasPoles.SetNumUninitialized(ControlPointCount*4); // Notice that each CV has three coordinates - the three coordinates describe 2D parameter space, with a homogeneous coordinate. // Each control point is u, v and w, where u and v are parameter space and w is the homogeneous coordinate. AliasTrimCurve.CVsUVPosition(AliasNodalVector.GetData(), (double(*)[3])AliasPoles.GetData()); AliasNodalVector.SetNum(KnotCount); AliasTrimCurve.realKnotVector(AliasNodalVector.GetData()); TArray NodalVector; NodalVector.Reserve(KnotCount+2); NodalVector.Add(AliasNodalVector[0]); for (double Value : AliasNodalVector) { NodalVector.Add(Value); } NodalVector.Add(AliasNodalVector.Last()); TArray Weights; Weights.SetNumUninitialized(ControlPointCount); AliasNodalVector.SetNumUninitialized(KnotCount); TArray ControlPointArray; TArray WeightArray; ControlPointArray.SetNumUninitialized(ControlPointCount); WeightArray.SetNumUninitialized(ControlPointCount); A3DVector3dData* ControlPointPtr = ControlPointArray.GetData(); A3DDouble* WeightPtr = WeightArray.GetData(); AlPoint* AliasPolePtr = AliasPoles.GetData(); TFunction SetA3DPole = [](const AlPoint& AliasPole, A3DVector3dData& OutTSPoint, A3DDouble& OutWeight) { OutTSPoint.m_dX = AliasPole[0]; OutTSPoint.m_dY = AliasPole[1]; OutTSPoint.m_dZ = 0; OutWeight = AliasPole[2]; }; for (int32 Index = 0; Index < ControlPointCount; ++Index, ++AliasPolePtr, ++ControlPointPtr, ++WeightPtr) { SetA3DPole(*AliasPolePtr, *ControlPointPtr, *WeightPtr); } NurbsCurveData->m_eKnotType = kA3DKnotTypeUnspecified; NurbsCurveData->m_eCurveForm = kA3DBSplineCurveFormUnspecified; NurbsCurveData->m_pCtrlPts = ControlPointArray.GetData(); NurbsCurveData->m_uiCtrlSize = ControlPointArray.Num(); NurbsCurveData->m_pdWeights = WeightArray.GetData(); NurbsCurveData->m_uiWeightSize = WeightArray.Num(); NurbsCurveData->m_pdKnots = NodalVector.GetData(); NurbsCurveData->m_uiKnotSize = NodalVector.Num(); return CADLibrary::TechSoftInterface::CreateCurveNurbs(*NurbsCurveData); } A3DTopoCoEdge* FAliasModelToTechSoftConverter::CreateEdge(const AlTrimCurve& TrimCurve) { A3DCrvBase* NurbsCurvePtr = CreateCurve(TrimCurve); if (NurbsCurvePtr == nullptr) { return nullptr; } A3DTopoEdge* EdgePtr = CADLibrary::TechSoftUtils::CreateTopoEdge(); if (EdgePtr == nullptr) { return nullptr; } CADLibrary::TUniqueTSObj CoEdgeData; CoEdgeData->m_pUVCurve = NurbsCurvePtr; CoEdgeData->m_pEdge = EdgePtr; CoEdgeData->m_ucOrientationWithLoop = TrimCurve.isReversed(); CoEdgeData->m_ucOrientationUVWithLoop = 1; A3DTopoCoEdge* CoEdgePtr = CADLibrary::TechSoftInterface::CreateTopoCoEdge(*CoEdgeData); // Only TrimCurve with twin need to be in the map used in LinkEdgesLoop TAlObjectPtr TwinCurve(TrimCurve.getTwinCurve()); if (TwinCurve.IsValid()) { AlEdgeToTSCoEdge.Add(TrimCurve.fSpline, CoEdgePtr); } return CoEdgePtr; } A3DTopoLoop* FAliasModelToTechSoftConverter::CreateTopoLoop(const AlTrimBoundary& TrimBoundary) { TArray Edges; Edges.Reserve(20); TAlObjectPtr TrimCurve(TrimBoundary.firstCurve()); statusCode Status = TrimCurve ? sSuccess : sObjectNotFound; while(Status == sSuccess) { A3DTopoCoEdge* Edge = CreateEdge(*TrimCurve); if (Edge != nullptr) { Edges.Add(Edge); } Status = TrimCurve->nextCurveD(); } if (Edges.Num() == 0) { return nullptr; } CADLibrary::TUniqueTSObj LoopData; LoopData->m_ppCoEdges = Edges.GetData(); LoopData->m_uiCoEdgeSize = Edges.Num(); LoopData->m_ucOrientationWithSurface = 1; return CADLibrary::TechSoftInterface::CreateTopoLoop(*LoopData); } void FAliasModelToTechSoftConverter::LinkEdgesLoop(const AlTrimBoundary& TrimBoundary) { TAlObjectPtr TrimCurve(TrimBoundary.firstCurve()); statusCode Status = TrimCurve ? sSuccess : sObjectNotFound; while (Status == sSuccess) { if (A3DTopoCoEdge** Edge = AlEdgeToTSCoEdge.Find(TrimCurve->fSpline)) { TAlObjectPtr TwinCurve(TrimCurve->getTwinCurve()); if (TwinCurve.IsValid()) { if (A3DTopoCoEdge** TwinEdge = AlEdgeToTSCoEdge.Find(TwinCurve->fSpline)) { CADLibrary::TechSoftInterface::LinkCoEdges(*Edge, *TwinEdge); break; } } } Status = TrimCurve->nextCurveD(); } } A3DTopoFace* FAliasModelToTechSoftConverter::AddTrimRegion(const AlTrimRegion& InTrimRegion, const FColor& Color, EAliasObjectReference InObjectReference, const AlMatrix4x4& InAlMatrix) { A3DSurfBase* CarrierSurface = AliasToTechSoftUtils::AddNURBSSurface(InTrimRegion, InObjectReference, InAlMatrix); if (CarrierSurface == nullptr) { return nullptr; } TArray Loops; Loops.Reserve(5); TAlObjectPtr TrimBoundary(InTrimRegion.firstBoundary()); statusCode Status = TrimBoundary ? sSuccess : sObjectNotFound; while(Status == sSuccess) { A3DTopoLoop* Loop = CreateTopoLoop(*TrimBoundary); if (Loop != nullptr) { Loops.Add(Loop); LinkEdgesLoop(*TrimBoundary); } Status = TrimBoundary->nextBoundaryD(); } if (Loops.Num() == 0) { return nullptr; } CADLibrary::TUniqueTSObj Face; Face->m_pSurface = CarrierSurface; Face->m_bHasTrimDomain = false; Face->m_ppLoops = Loops.GetData(); Face->m_uiLoopSize = Loops.Num(); Face->m_uiOuterLoopIndex = 0; Face->m_dTolerance = 0.01; //mm A3DTopoFace* FacePtr = CADLibrary::TechSoftInterface::CreateTopoFace(*Face); CADLibrary::TechSoftUtils::SetEntityGraphicsColor(FacePtr, Color); return FacePtr; } #endif bool FAliasModelToTechSoftConverter::AddBRep(const FAlDagNodePtr& DagNode, uint32 SlotID, EAliasObjectReference InObjectReference) { FColor Color(SlotID); return AddBRep(DagNode, Color, InObjectReference); } bool FAliasModelToTechSoftConverter::AddBRep(const FAlDagNodePtr& DagNode, const FColor& Color, EAliasObjectReference InObjectReference) { #ifdef USE_TECHSOFT_SDK AlEdgeToTSCoEdge.Empty(); AlMatrix4x4 AlMatrix; if (InObjectReference == EAliasObjectReference::ParentReference) { DagNode->localTransformationMatrix(AlMatrix); } boolean bAlOrientation; DagNode->getSurfaceOrientation(bAlOrientation); bool bOrientation = !(bool)bAlOrientation; TArray TSFaces; TSFaces.Reserve(100); TAlObjectPtr Shell; if (DagNode.GetShell(Shell)) { TAlObjectPtr TrimRegion(Shell->firstTrimRegion()); statusCode Status = TrimRegion ? sSuccess : sObjectNotFound; while(Status == sSuccess) { A3DTopoFace* TSFace = AddTrimRegion(*TrimRegion, Color, InObjectReference, AlMatrix); if (TSFace != nullptr) { TSFaces.Add(TSFace); } Status = TrimRegion->nextRegionD(); } } else { TAlObjectPtr Surface; if (DagNode.GetSurface(Surface)) { TAlObjectPtr TrimRegion(Surface->firstTrimRegion()); statusCode Status = TrimRegion ? sSuccess : sObjectNotFound; if (Status == sSuccess) { while (Status == sSuccess) { A3DTopoFace* TSFace = AddTrimRegion(*TrimRegion, Color, InObjectReference, AlMatrix); if (TSFace != nullptr) { TSFaces.Add(TSFace); } Status = TrimRegion->nextRegionD(); } } else { A3DSurfBase* TSSurface = AliasToTechSoftUtils::AddNURBSSurface(*Surface, InObjectReference, AlMatrix); if (TSSurface != nullptr) { A3DTopoFace* TSFace = CADLibrary::TechSoftUtils::CreateTopoFaceWithNaturalLoop(TSSurface); CADLibrary::TechSoftUtils::SetEntityGraphicsColor(TSFace, Color); if (TSFace != nullptr) { TSFaces.Add(TSFace); } } } } } if (TSFaces.IsEmpty()) { return false; } A3DTopoShell* TopoShellPtr = nullptr; { TArray FaceOrientations; FaceOrientations.Init(bOrientation, TSFaces.Num()); CADLibrary::TUniqueTSObj TopoShellData; TopoShellData->m_bClosed = false; TopoShellData->m_ppFaces = TSFaces.GetData(); TopoShellData->m_uiFaceSize = TSFaces.Num(); TopoShellData->m_pucOrientationWithShell = FaceOrientations.GetData(); TopoShellPtr = CADLibrary::TechSoftInterface::CreateTopoShell(*TopoShellData); if (TopoShellPtr == nullptr) { return false; } } A3DRiRepresentationItem* RiRepresentationItem = CADLibrary::TechSoftUtils::CreateRIBRep(TopoShellPtr); if (RiRepresentationItem != nullptr) { RiRepresentationItems.Add(RiRepresentationItem); return true; } #endif return false; } bool FAliasModelToTechSoftConverter::AddGeometry(const CADLibrary::FCADModelGeometry& Geometry) { if (Geometry.Type == (int32)ECADModelGeometryType::DagNode) { const FDagNodeGeometry& DagNodeGeometry = static_cast(Geometry); return AddBRep(DagNodeGeometry.DagNode, 0, DagNodeGeometry.Reference); } else if (Geometry.Type == (int32)ECADModelGeometryType::BodyNode) { const FBodyNodeGeometry& BodyNodeGeometry = static_cast(Geometry); bool bBodyAdded = false; BodyNodeGeometry.BodyNode->IterateOnDagNodes([&](const FAlDagNodePtr& DagNode) { // #wire_import: TODO: Inform user one DagNode was not imported const bool bBRepAdded = AddBRep(DagNode, BodyNodeGeometry.BodyNode->GetSlotIndex(DagNode), BodyNodeGeometry.Reference); if (!bBRepAdded) { UE_LOG(LogWireInterface, Warning, TEXT("Failed to add DagNode %s to StaticMesh."), *DagNode.GetName()); } bBodyAdded |= bBRepAdded; }); return bBodyAdded; } return false; } } #endif #undef LOCTEXT_NAMESPACE // "WireInterface"