/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// #include "OdaCommon.h" #include "MemoryStream.h" #include "DynamicLinker.h" #include "RxDynamicModule.h" #include "diagnostics.h" #include "RxObjectImpl.h" #include "BrepBuilderFillerModule.h" #include "BaseMaterialAndColorHelper.h" #include "Br/BrBrep.h" #include "Br/BrEdge.h" #include "Br/BrBrepFaceTraverser.h" #include "Br/BrFaceLoopTraverser.h" #include "Br/BrLoopEdgeTraverser.h" #include "Br/BrBrepComplexTraverser.h" #include "DbBlockTableRecord.h" #include "DbBody.h" #include "ExHostAppServices.h" #include "ExSystemServices.h" #include "DgModelerGeometry.h" #include "DgDatabase.h" #include "DgBRepEntityPE.h" #include "ExDgnServices.h" #include "ExDgnHostAppServices.h" #include "ModelerGeometry/ModelerModule.h" #include const OdString kErrorIncorrectArguments(OdString().format(L"" \ L"Usage: OdAcis2DgnEx \n" \ L" - .dwg file\n" \ L" - .dgn file\n")); /************************************************************************/ /* Define a Custom Services class. */ /* */ /* Combines the platform dependent functionality of */ /* ExSystemServices and OdExDgnHostAppServices */ /************************************************************************/ class MyServicesDgn : public ExSystemServices, public OdExDgnHostAppServices { protected: ODRX_USING_HEAP_OPERATORS(ExSystemServices); }; /************************************************************************/ /* Define a Custom Services class. */ /* */ /* Combines the platform dependent functionality of */ /* ExSystemServices and ExHostAppServices */ /************************************************************************/ class MyServicesDwg : public ExSystemServices, public ExHostAppServices { protected: ODRX_USING_HEAP_OPERATORS(ExSystemServices); }; /************************************************************************/ /* Define a Custom Helper class. */ /* */ /* Used to convert color from DWG database into DGN database */ /************************************************************************/ class Acis2DgnMaterialAndColorHelper : public OdBaseMaterialAndColorHelper { public: explicit Acis2DgnMaterialAndColorHelper(OdDbStub* pDefaultMaterial = NULL) : OdBaseMaterialAndColorHelper(pDefaultMaterial) { } OdResult convertColor(const OdCmEntityColor& srcColor, OdCmEntityColor& destColor) ODRX_OVERRIDE { destColor = srcColor; return eOk; } }; /************************************************************************/ /* Custom assertion method */ /************************************************************************/ static void CustomAssert(const char* expression, const char* fileName, int nLineNo) { OdString message; message.format(L"\n!!! Assertion failed: \"%ls\"\n file: %ls, line %d\n", OdString(expression).c_str(), OdString(fileName).c_str(), nLineNo); odPrintConsoleString(message); } /************************************************************************/ /* Method to count the number of complexes in brep */ /************************************************************************/ int getNumComplexesInBrep(OdBrBrep& brep) { int numComplexes = 0; OdBrErrorStatus err = odbrOK; OdBrBrepComplexTraverser bct; if (bct.setBrep(brep) == odbrOK) { while (!bct.done() && (err == odbrOK)) { ++numComplexes; err = bct.next(); } } return numComplexes; } /************************************************************************/ /* Method to count the number of faces in brep */ /************************************************************************/ int getNumFacesInBrep(OdBrBrep& brep) { int numFaces = 0; OdBrErrorStatus err = odbrOK; OdBrBrepFaceTraverser bft; if (bft.setBrep(brep) == odbrOK) { while (!bft.done() && (err == odbrOK)) { OdBrFace f = bft.getFace(); std::unique_ptr pSurf; try { pSurf.reset(f.getSurface()); } catch (...) { pSurf.reset(); } if (pSurf.get() == NULL) { OdGeNurbSurface srf; OdBrErrorStatus st = f.getSurfaceAsNurb(srf); if (st == odbrOK) { ++numFaces; } } else { ++numFaces; } err = bft.next(); } } return numFaces; } /************************************************************************/ /* Method to check if brep is empty (has no faces) */ /************************************************************************/ inline bool isBrepEmpty(OdBrBrep& brep) { int numComplexes = getNumComplexesInBrep(brep); if (numComplexes == 0) { return true; } int numFaces = getNumFacesInBrep(brep); return numFaces == 0; } /***************************************************************************************/ /* Method to check if brep is solid (has no boundary edges with single adjacent face) */ /***************************************************************************************/ bool isBrepSolid(OdBrBrep& brep) { std::map edgeCnt; OdBrErrorStatus err = odbrOK; OdBrBrepFaceTraverser bft; if (bft.setBrep(brep) != odbrOK) { return false; } while (!bft.done() && (err == odbrOK)) { OdBrFaceLoopTraverser faLoTrav; OdBrFace face = bft.getFace(); for (faLoTrav.setFace(face); !faLoTrav.done(); faLoTrav.next()) { OdBrLoopEdgeTraverser loEdTrav; OdBrLoop loop = faLoTrav.getLoop(); err = loEdTrav.setLoop(loop); for (; !loEdTrav.done(); loEdTrav.next()) { OdBrEdge edge = loEdTrav.getEdge(); ++edgeCnt[edge.getUniqueId()]; } } bft.next(); } for (std::map::const_iterator eit = edgeCnt.begin(); eit != edgeCnt.end(); eit++) { if (eit->second < 2) { return false; } } return true; } inline OdString getFileNameWithoutExt(const OdString& filename) { OdString sFileName = filename, sRes; const int nExtPos = sFileName.reverseFind(L'.'); if (-1 != nExtPos) { sRes = sFileName.left(nExtPos); } else { sRes = sFileName; } return sRes; } inline OdString getFileNameWithoutPath(const OdString& filename) { OdString sFileName = filename, sRes; #if defined(ODA_WINDOWS) sFileName.replace(L'/', L'\\'); const int nPathPos = sFileName.reverseFind(L'\\'); #else sFileName.replace(L'\\', L'/'); const int nPathPos = sFileName.reverseFind(L'/'); #endif if (-1 != nPathPos) { sRes = sFileName.right(sFileName.getLength() - nPathPos - 1); } else { sRes = sFileName; } return sRes; } inline OdString getFileExtension(const OdString& sFileName) { OdString sBuf = sFileName; OdString sExt; const int iDotPos = sBuf.makeLower().reverseFind(L'.'); if (-1 != iDotPos) sExt = sBuf.right(sBuf.getLength() - iDotPos - 1); return sExt; } enum ConversionResult { crOk, crNullInput, crNoBrep, crInvalidBrep, crEmptyBrep, crBadBrepData, crBrepBuilderCreationFailed, crBrepBuilderInitializationFailed, crBrepBuilderFailed, crBrepTransformationFailed, crSerializationFailed, crAppendFailed }; ConversionResult convertEntity(OdDbEntityPtr pInputEntity, OdDbDatabasePtr pDwgDb, OdDgDatabasePtr pDgnDb, OdDgModelPtr pDgnModel, OdDgHostAppServices* pDgnServices, OdBaseMaterialAndColorHelper* pMaterialHelper) { const bool CONVERT_TO_PARASOLID = true; const OdDgModelerGeometry::OdDgModelerVersion MODELER_VERSION = CONVERT_TO_PARASOLID ? OdDgModelerGeometry::kXB_Bare | OdDgModelerGeometry::kPS_12_6 : OdDgModelerGeometry::kSab | OdDgModelerGeometry::kAS_7_0; OdResult err = eOk; if (pInputEntity.isNull()) { return crNullInput; } // try to obtain brep and brep type for current entity OdBrBrep brep; bool isBrepOpen = true; if (pInputEntity->isKindOf(OdDbBody::desc())) { OdDbBodyPtr pBody = pInputEntity; pBody->brep(brep); } else if (pInputEntity->isKindOf(OdDbSurface::desc())) { OdDbSurfacePtr pSurface = pInputEntity; pSurface->brep(brep); } else if (pInputEntity->isA() == OdDbRegion::desc()) { OdDbRegionPtr pRegion = pInputEntity; pRegion->brep(brep); } else if (pInputEntity->isA() == OdDb3dSolid::desc()) { OdDb3dSolidPtr pSolid = pInputEntity; OdModelerGeometry* pModel = static_cast(pSolid->body()); if (pModel && pModel->bodyType() == OdModelerGeometry::kSolid) { isBrepOpen = false; } pSolid->brep(brep); } else { return crNoBrep; } // entity skipped if it has no brep OdDbHandle entityHandle = pInputEntity->handle(); odPrintConsoleString(OD_T("Converting %ls entity:"), entityHandle.ascii().c_str()); // try to init additional brep data OdGeMatrix3d brepTransformation; bool isBrepTransformed = false; try { if (!brep.isValid()) { odPrintConsoleString(L" invalid brep -> skip\n\n."); return crInvalidBrep; } if (isBrepEmpty(brep)) { odPrintConsoleString(L" empty brep -> skip\n\n."); return crEmptyBrep; } bool isSolid = isBrepSolid(brep); if (isSolid != !isBrepOpen) { isBrepOpen = !isSolid; odPrintConsoleString(L" change brep type (%s -> %s).", isBrepOpen ? L"sheet" : L"solid", isSolid ? L"solid" : L"sheet"); } isBrepTransformed = brep.getTransformation(brepTransformation); } catch (const OdError& e) { odPrintConsoleString(L" failed (%s) -> skip\n\n.", e.description().c_str()); return crBadBrepData; } catch (...) { odPrintConsoleString(L" failed (unknown error) -> skip\n\n."); return crBadBrepData; } // try to init converter OdBrepBuilder builder; OdBrepBuilderFiller filler; BrepType brepType = isBrepOpen ? kOpenShell : kSolid; try { err = pDgnServices->brepBuilder(builder, brepType, !CONVERT_TO_PARASOLID); if (err != eOk) { odPrintConsoleString(L" failed to create BrepBuilder -> skip\n\n."); return crBrepBuilderCreationFailed; } builder.enableValidator(false); filler.params().setupFor(OdBrepBuilderFillerParams::kBrepAcisDwg, pDwgDb, CONVERT_TO_PARASOLID ? OdBrepBuilderFillerParams::kBrepPS : OdBrepBuilderFillerParams::kBrepAcisDgn, pDgnDb); try { err = filler.initFrom(builder, brep, pMaterialHelper); } catch (const OdError& e) { odPrintConsoleString(L" failed (%s) -> skip\n\n.", e.description().c_str()); return crBrepBuilderInitializationFailed; } catch (...) { odPrintConsoleString(L" failed (unknown error) -> skip\n\n."); return crBrepBuilderInitializationFailed; } if (err != eOk) { odPrintConsoleString(L" failed to init BrepBuilder -> skip\n\n."); return crBrepBuilderInitializationFailed; } } catch (const OdError& e) { odPrintConsoleString(L" failed (%s) -> skip\n\n.", e.description().c_str()); return crBrepBuilderInitializationFailed; } catch (...) { odPrintConsoleString(L" failed (unknown error) -> skip\n\n."); return crBrepBuilderInitializationFailed; } if (err != eOk) { odPrintConsoleString(L" failed to init BrepBuilder -> skip\n\n."); return crBrepBuilderInitializationFailed; } // convert entity brep OdDgModelerGeometryPtr pConversionResult; try { pConversionResult = builder.finish(); if (pConversionResult.isNull()) { odPrintConsoleString(L" failed to convert (NULL result) -> skip\n\n."); return crBrepBuilderFailed; } if (isBrepTransformed) { err = pConversionResult->transformBy(brepTransformation); } if (err != eOk) { odPrintConsoleString(L" failed to apply transformation to converted result -> skip\n\n."); return crBrepTransformationFailed; } } catch (const OdError& e) { odPrintConsoleString(L" failed to convert (%s) -> skip\n\n.", e.description().c_str()); return crBrepBuilderFailed; } catch (...) { odPrintConsoleString(L" failed to convert (unknown error) -> skip\n\n."); return crBrepBuilderFailed; } // add converted entity to database try { OdStreamBufPtr pMemStream = OdMemoryStream::createNew(); err = pConversionResult->out(pMemStream, MODELER_VERSION); if (err != eOk) { odPrintConsoleString(L" failed to output entity to stream -> skip\n\n."); return crSerializationFailed; } pMemStream->rewind(); // create entity OdDgCellHeader3dPtr pCellHeader = OdDgCellHeader3d::createObject(); pCellHeader->setLevelEntryId(0x40); // add entity to db pDgnModel->addElement(pCellHeader); // fill entity with data OdDgBRepEntityPEPtr(pCellHeader)->fillSmartSolid(*pCellHeader, *pMemStream.get(), !CONVERT_TO_PARASOLID, 0.0, isBrepOpen); } catch (const OdError& e) { odPrintConsoleString(L" failed to add entity to database (%s) -> skip\n\n.", e.description().c_str()); return crAppendFailed; } catch (...) { odPrintConsoleString(L" failed to add entity to database (unknown error) -> skip\n\n."); return crAppendFailed; } return crOk; } void convertDatabase(const OdString& inputPath, const OdString& outputPath, const OdString& name, OdDbHostAppServices* pDwgServices, OdDgHostAppServices* pDgnServices) { // read DWG database OdDbDatabasePtr pDwgDb = pDwgServices->readFile(inputPath); ODA_ASSERT(!pDwgDb.isNull()); odPrintConsoleString(L"Opened source database:%s\nProcessing entities...\n", inputPath.c_str()); // create DGN database OdDgDatabasePtr pDgnDb = pDgnServices->createDatabase(OdDgHostAppServices::kDbDefaultModelIs3d); ODA_ASSERT(!pDgnDb.isNull()); // prepare DGN database pDgnDb->startUndoRecord(); pDgnDb->startTransaction(); OdDgModelTablePtr pDgnModelTable = pDgnDb->getModelTable(OdDg::kForWrite); OdDgModelPtr pDgnModel = pDgnDb->getActiveModelId().openObject(OdDg::kForWrite); pDgnModel->setName(name); pDgnModel->setWorkingUnit(OdDgModel::kWuWorldUnit); // convert entities int numConvertedEntities = 0; Acis2DgnMaterialAndColorHelper materialHelper; // iterate over database entities OdDbBlockTableRecordPtr pModelSpace = pDwgDb->getModelSpaceId().safeOpenObject(OdDb::kForWrite); OdDbObjectIteratorPtr pObjectIterator = pModelSpace->newIterator(); for (int entityIdx = 0; !pObjectIterator->done(); pObjectIterator->step(), ++entityIdx) { OdDbEntityPtr pInputEntity = pObjectIterator->entity(); ConversionResult res = convertEntity(pInputEntity, pDwgDb, pDgnDb, pDgnModel, pDgnServices, &materialHelper); if (res == crOk) { ++numConvertedEntities; } } if (numConvertedEntities > 0) { // finish DGN database OdDgViewGroupTablePtr pViewGroup = pDgnDb->getViewGroupTable(OdDg::kForWrite); OdDgElementIteratorPtr pVGIter = pViewGroup->createIterator(); for (; !pVGIter->done(); pVGIter->step()) { OdDgViewGroupPtr pViewGroup = pVGIter->item().openObject(OdDg::kForWrite); if (pViewGroup->getModelId() == pDgnModel->elementId()) { pViewGroup->setName(name); OdDgElementIteratorPtr pViewIter = pViewGroup->createIterator(); bool bFirstView = true; for (; !pViewIter->done(); pViewIter->step()) { OdDgViewPtr pView = pViewIter->item().openObject(OdDg::kForWrite); if (bFirstView) { pView->setVisibleFlag(true); pView->setShowFillsFlag(true); bFirstView = false; } else { pView->setVisibleFlag(false); } } } } pDgnModel->fitToView(); pDgnDb->setActiveModelId(pDgnModel->elementId()); pDgnDb->endTransaction(); // save database to file pDgnDb->writeFile(outputPath); odPrintConsoleString(L"\nSave result to: %s\n", outputPath.c_str()); } else { odPrintConsoleString(L"\nEmpty conversion result. Database not saved.\n"); } } #ifndef _TOOLKIT_IN_DLL_ ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(OdDgnModule); ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(DgModelerModule); ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(ModelerModule); ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(ExRasterModule); ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(OdRecomputeDimBlockModule); ODRX_BEGIN_STATIC_MODULE_MAP() ODRX_DEFINE_STATIC_APPMODULE(OdDgnModelerGeometryModuleName, DgModelerModule) ODRX_DEFINE_STATIC_APPLICATION(OdDgnDbModuleName, OdDgnModule) ODRX_DEFINE_STATIC_APPLICATION(OdModelerGeometryModuleName, ModelerModule) ODRX_DEFINE_STATIC_APPLICATION(RX_RASTER_SERVICES_APPNAME, ExRasterModule) ODRX_DEFINE_STATIC_APPLICATION(OdRecomputeDimBlockModuleName, OdRecomputeDimBlockModule) ODRX_END_STATIC_MODULE_MAP() #endif /************************************************************************/ /* Main */ /************************************************************************/ #if defined(ODA_WINDOWS) int wmain(int argc, wchar_t* argv[]) #else int main(int argc, char* argv[]) #endif { #if defined(TARGET_OS_MAC) && !defined(__MACH__) argc = ccommand(&argv); #endif if( argc < 2 ) { oddgPrintConsoleString(kErrorIncorrectArguments); return 1; } OdString sFileToExport(argv[1]); // for UNICODE support OdString ext = getFileExtension(sFileToExport); if (ext.iCompare(L"dwg") != 0) { oddgPrintConsoleString(kErrorIncorrectArguments); return 1; } OdString sFileToImport(argv[2]); ext = getFileExtension(sFileToImport); if (ext.iCompare(L"dgn") != 0) { oddgPrintConsoleString(kErrorIncorrectArguments); return 1; } #ifndef _TOOLKIT_IN_DLL_ ODRX_INIT_STATIC_MODULE_MAP(); #endif // create some service OdStaticRxObject< MyServicesDgn > svcDgn; OdStaticRxObject< MyServicesDwg > svcDwg; // set custom assertion method odSetAssertFunc(CustomAssert); /**********************************************************************/ /* Initialize Runtime Extension environment */ /**********************************************************************/ odInitialize( &svcDwg); oddgPrintConsoleString(L"Developed using %ls ver %ls\n\n", svcDgn.product().c_str(), svcDgn.versionString().c_str()); bool bSuccess = false; try { ::odrxDynamicLinker()->loadModule(OdModelerGeometryModuleName, false); ::odrxDynamicLinker()->loadModule(OdDgnDbModuleName, false); ::odrxDynamicLinker()->loadModule(OdDgnModelerGeometryModuleName, false); OdString name = getFileNameWithoutExt(getFileNameWithoutPath(sFileToImport)); convertDatabase(sFileToExport, sFileToImport, name, &svcDwg, &svcDgn); } catch (OdError& e) { oddgPrintConsoleString(L"\nError: %ls\n", e.description().c_str()); } catch (...) { oddgPrintConsoleString(L"\nUnknown Error.\n"); } /**********************************************************************/ /* Uninitialize Runtime Extension environment */ /**********************************************************************/ odUninitialize(); return 0; }