/*********************************************************************************************************************** * * Copyright (c) 2010 - 2025 by Tech Soft 3D, Inc. * The information contained herein is confidential and proprietary to Tech Soft 3D, Inc., and considered a trade secret * as defined under civil and criminal statutes. Tech Soft 3D shall pursue its civil and criminal remedies in the event * of unauthorized use or misappropriation of its trade secrets. Use of this information by anyone other than authorized * employees of Tech Soft 3D, Inc. is granted only under a written non-disclosure agreement, expressly prescribing the * scope and manner of such use. * ***********************************************************************************************************************/ /** \file MultiplePrc.cpp This file demonstrates how to programmatically create and read multiple PRC files from a CAD assembly using HOOPS Exchange. The input is the CAD assembly file path and a directory. The program generates multiple PRC files from this CAD assembly. This mode is supported for the following format: V4, V5, ProE, UG, SLDW. With other formats, only one PRC file is generated. ***********************************************************************************************************************/ #define _CRT_SECURE_NO_DEPRECATE 1 #include #define INITIALIZE_A3D_API #include #include #include "../common.hpp" //###################################################################################################################### bool MakePath(const std::string& strPath); std::string ExtractPath(const A3DUTF8Char* pcIn); std::string ExtractFileName(const A3DUTF8Char* pcIn); A3DStatus TestOutputFile(const std::string& strFileName); static MY_CHAR acSrcFileName[_MAX_PATH * 2]; static MY_CHAR acDstPathName[_MAX_PATH * 2]; static MY_CHAR acLogFileName[_MAX_PATH * 2]; //###################################################################################################################### // Main function #ifdef _MSC_VER int wmain(A3DInt32 iArgc, A3DUniChar** ppcArgv) #else int main(A3DInt32 iArgc, A3DUTF8Char** ppcArgv) #endif { // // ### INITIALIZE HOOPS EXCHANGE // A3DSDKHOOPSExchangeLoader sHoopsExchangeLoader(_T(HOOPS_BINARY_DIRECTORY), HOOPS_LICENSE); CHECK_RET(sHoopsExchangeLoader.m_eSDKStatus); // Initialize callbacks CHECK_RET(A3DDllSetCallbacksMemory(CheckMalloc, CheckFree)); CHECK_RET(A3DDllSetCallbacksReport(PrintLogMessage, PrintLogWarning, PrintLogError)); // // ### COMMAND LINE ARGUMENTS // if (iArgc > 4) { MY_PRINTF2("Usage:\n %s [input CAD file] [output PRC path] [output LOG file]\n", ppcArgv[0]); MY_PRINTF(" Default output PRC path is from [input CAD file]\n"); MY_PRINTF(" Default output LOG file is [input CAD file]_Log.txt\n\n"); return A3D_ERROR; } A3DUTF8Char sSrcFileNameUTF8[_MAX_PATH]; if (iArgc > 1) MY_STRCPY(acSrcFileName, ppcArgv[1]); else MY_STRCPY(acSrcFileName, DEFAULT_INPUT_CAD); #ifdef _MSC_VER A3DMiscUTF16ToUTF8(acSrcFileName, sSrcFileNameUTF8); #else MY_STRCPY(sSrcFileNameUTF8, acSrcFileName); #endif A3DUTF8Char sDstFileNameUTF8[_MAX_PATH]; if (iArgc > 2) { MY_STRCPY(acDstPathName, ppcArgv[2]); #ifdef _MSC_VER A3DMiscUTF16ToUTF8(acDstPathName, sDstFileNameUTF8); #else MY_STRCPY(sDstFileNameUTF8, acDstPathName); #endif } else { std::string strPath = ExtractPath(sSrcFileNameUTF8); strcpy(sDstFileNameUTF8, strPath.c_str()); } if (iArgc > 3) MY_STRCPY(acLogFileName, ppcArgv[3]); else MY_SPRINTF(acLogFileName, "%s_Log.txt", acSrcFileName); GetLogFile(acLogFileName); // Initialize log file // // ### PROCESS SAMPLE CODE // std::string strMainFileName = ExtractFileName(sSrcFileNameUTF8); std::string strOutDir = sDstFileNameUTF8; // remove " from path (if found). for (size_t iFound = strOutDir.find('"'); iFound != std::string::npos; iFound = strOutDir.find('"')) strOutDir.erase(iFound, 1); //////////////////////////////////////////////////////////////////////////// // Check input file and output directory. //////////////////////////////////////////////////////////////////////////// if (FILE* pFile = fopen(sSrcFileNameUTF8, "rb")) { fclose(pFile); } else { fprintf(stderr, "cannot open input file %s\n", sSrcFileNameUTF8); return A3D_LOAD_CANNOT_ACCESS_CADFILE; } //////////////////////////////////////////////////////////////////////////// // 2- Read input file in mode "tree only". //////////////////////////////////////////////////////////////////////////// A3DImport sImport(acSrcFileName); // see A3DSDKInternalConvert.hxx for import and export detailed parameters sImport.m_sLoadData.m_sIncremental.m_bLoadStructureOnly = true; A3DStatus iRet = sHoopsExchangeLoader.Import(sImport); if (iRet != A3D_SUCCESS && iRet != A3D_LOAD_MISSING_COMPONENTS) CHECK_RET(iRet); //////////////////////////////////////////////////////////////////////////// // 3- Extract file names. //////////////////////////////////////////////////////////////////////////// A3DUns32 nbFiles = 0, nbAssemblyFiles = 0, nbMissingFiles = 0; A3DUTF8Char** ppPaths = NULL, ** ppAssemblyPaths = NULL, ** ppMissingPaths = NULL; CHECK_RET(A3DAsmGetFilesPathFromModelFile(sHoopsExchangeLoader.m_psModelFile, &nbFiles, &ppPaths, &nbAssemblyFiles, &ppAssemblyPaths, &nbMissingFiles, &ppMissingPaths)); //////////////////////////////////////////////////////////////////////////// // Check consistency for this sample : if the input file doesn't need this pipeline, export it as a prc and avoid // the "tree only" export and the reassembly. //////////////////////////////////////////////////////////////////////////// const std::string strOutBaseFileName = strOutDir #ifdef WIN32 + "\\__" #else + "/__" #endif + strMainFileName; if (nbFiles == 1 && strcmp(ppPaths[0], sSrcFileNameUTF8) == 0) //The file found is a part or an assembly file. In order to not generate 3 output files (tree only, part // file and re-assembled file) for one input file, we test this case and avoid the useless work. { // test output file writeability const std::string strOutFileName = strOutBaseFileName + ".prc"; const A3DStatus eFileOK = TestOutputFile(strOutFileName); if (eFileOK != A3D_SUCCESS) return eFileOK; CHECK_RET(A3DAsmModelFileDelete(sHoopsExchangeLoader.m_psModelFile)); sHoopsExchangeLoader.m_psModelFile = NULL; sImport.m_sLoadData.m_sIncremental.m_bLoadStructureOnly = false; iRet = sHoopsExchangeLoader.Import(sImport); if (iRet != A3D_SUCCESS && iRet != A3D_LOAD_MISSING_COMPONENTS) CHECK_RET(iRet); A3DExport sExport(strOutFileName.c_str()); // see A3DSDKInternalConvert.hxx for import and export detailed parameters CHECK_RET(sHoopsExchangeLoader.Export(sExport)); // // ### TERMINATE HOOPS EXCHANGE // // Check memory allocations return (int)ListLeaks(); } ////////////////////////////////////////////////////////////////////////// // 4- make output path ////////////////////////////////////////////////////////////////////////// if (MakePath(strOutDir) == false) { fprintf(stderr, "Cannot create output base folder: '%s'.\n", strOutDir.c_str()); } ////////////////////////////////////////////////////////////////////////// // 5- export main file as an prc assembly ////////////////////////////////////////////////////////////////////////// // test output file writability const std::string strOutMainFileName = strOutBaseFileName + "Structure.prc"; const A3DStatus eFileOK = TestOutputFile(strOutMainFileName); if (eFileOK != A3D_SUCCESS) return eFileOK; A3DExport sExport(strOutMainFileName.c_str()); // see A3DSDKInternalConvert.hxx for import and export detailed parameters CHECK_RET(sHoopsExchangeLoader.Export(sExport)); CHECK_RET(A3DAsmModelFileDelete(sHoopsExchangeLoader.m_psModelFile)); sHoopsExchangeLoader.m_psModelFile = NULL; ////////////////////////////////////////////////////////////////////////// // 6- create mapped file names for PRC part files // export part as PRC // incremental load for part with multiple configurations ////////////////////////////////////////////////////////////////////////// sImport.m_sLoadData.m_sIncremental.m_bLoadStructureOnly = false; sImport.m_sLoadData.m_sMultiEntries.m_bLoadDefault = false; A3DUTF8Char** ppMappedPartPaths = (A3DUTF8Char**)CheckMalloc(size_t(nbFiles) * sizeof(A3DUTF8Char*)); struct Cleaner { size_t uNbFiles_; A3DUTF8Char** ptr_; Cleaner(size_t uNbFiles, A3DUTF8Char** ptr) : uNbFiles_(uNbFiles), ptr_(ptr) {} ~Cleaner() { Release(); } void Release() { for (unsigned ui = 0; ui < uNbFiles_; ++ui) CheckFree(ptr_[ui]); CheckFree(ptr_); uNbFiles_ = 0; ptr_ = nullptr; } } sCleaner(nbFiles, ppMappedPartPaths); for (A3DUns32 i = 0; i < nbFiles; ++i) { A3DImport sSubImport(ppPaths[i]); // see A3DSDKInternalConvert.hxx for import and export detailed parameters iRet = sHoopsExchangeLoader.Import(sSubImport); A3DAsmModelFile* pCurrentModelFile = sHoopsExchangeLoader.m_psModelFile; sHoopsExchangeLoader.m_psModelFile = NULL; if (iRet == A3D_LOAD_CANNOT_ACCESS_CADFILE || iRet == A3D_LOAD_INVALID_FILE_FORMAT) { ppMappedPartPaths[i] = (A3DUTF8Char*)CheckMalloc( 10* sizeof(A3DUTF8Char)); strcpy(ppMappedPartPaths[i], "INVALID"); continue; } if (iRet == A3D_LOAD_MULTI_MODELS_CADFILE) //multi config!! { A3DAsmModelFileData sModelFileData; A3D_INITIALIZE_DATA(A3DAsmModelFileData, sModelFileData); A3DAsmModelFileGet(pCurrentModelFile, &sModelFileData); A3DAsmProductOccurrence* pPO = sModelFileData.m_ppPOccurrences[0]; A3DAsmProductOccurrenceData sData; A3D_INITIALIZE_DATA(A3DAsmProductOccurrenceData, sData); A3DAsmProductOccurrenceGet(pPO, &sData); for (A3DUns32 uiSonIndex = 0; uiSonIndex < sData.m_uiPOccurrencesSize; uiSonIndex++) { A3DImport sImportLocal(ppPaths[i]); // see A3DSDKInternalConvert.hxx for import and export detailed parameters //connect the new read product occurrence with the current product occurrence sImportLocal.m_sLoadData.m_sIncremental.m_uiProductOccurrencesSize = 1; sImportLocal.m_sLoadData.m_sIncremental.m_ppProductOccurrences = &sData.m_ppPOccurrences[uiSonIndex]; sImportLocal.m_sLoadData.m_sIncremental.m_pRootProductOccurrence = pPO; iRet = sHoopsExchangeLoader.Import(sImportLocal); TEST_RET(iRet); } } const std::string strCurrentPRCName = strOutDir #ifdef WIN32 + "\\" #else + "/" #endif + ExtractFileName(ppPaths[i]) + ".prc"; ppMappedPartPaths[i] = (A3DUTF8Char*)CheckMalloc((strCurrentPRCName.length() + 1) * sizeof(A3DUTF8Char)); strcpy(ppMappedPartPaths[i], strCurrentPRCName.c_str()); A3DExport sSubExport(ppMappedPartPaths[i]); // see A3DSDKInternalConvert.hxx for import and export detailed parameters sHoopsExchangeLoader.m_psModelFile = pCurrentModelFile; CHECK_RET(sHoopsExchangeLoader.Export(sSubExport)); CHECK_RET(A3DAsmModelFileDelete(sHoopsExchangeLoader.m_psModelFile)); sHoopsExchangeLoader.m_psModelFile = NULL; } ////////////////////////////////////////////////////////////////////////// // 7 - Rebuild part ////////////////////////////////////////////////////////////////////////// A3DAsmModelFile* pOutModelFile = NULL; A3DRWParamsLoadData sRPartParam; A3D_INITIALIZE_DATA(A3DRWParamsLoadData, sRPartParam); sRPartParam.m_sGeneral.m_bReadSolids = true; sRPartParam.m_sGeneral.m_bReadSurfaces = true; sRPartParam.m_sGeneral.m_eReadGeomTessMode = kA3DReadGeomAndTess; sRPartParam.m_sMultiEntries.m_bLoadDefault = true; // sRPartParam.m_sMultiEntries.m_bLoadDefault = false; // A3DUTF8Char* pcName = "Full";//"Chassis and Fuel Tank" // sRPartParam.m_sMultiEntries.m_ppcEntries = &pcName; // sRPartParam.m_sMultiEntries.m_uiEntriesSize = 1; CHECK_RET(A3DAsmModelFileLoadFromMultiplePrcFile(strOutMainFileName.c_str(), nbFiles, (const A3DUTF8Char**)ppPaths, (const A3DUTF8Char**)ppMappedPartPaths, &sRPartParam, &pOutModelFile)); //////////////////////////////////////////////////////////////////////////// // 8- Export final file //////////////////////////////////////////////////////////////////////////// A3DExport sFinalExport((strOutBaseFileName + "All.prc").c_str()); sHoopsExchangeLoader.m_psModelFile = pOutModelFile; CHECK_RET(sHoopsExchangeLoader.Export(sFinalExport)); CHECK_RET(A3DAsmGetFilesPathFromModelFile(NULL, &nbFiles, &ppPaths, &nbAssemblyFiles, &ppAssemblyPaths, &nbMissingFiles, &ppMissingPaths)); sCleaner.Release(); // // ### TERMINATE HOOPS EXCHANGE // // Check memory allocations return (int)ListLeaks(); } //###################################################################################################################### // Recursively creates the folder path if it does not exist // Returns true if the operation succeeds or if the folder already exists. False otherwise. #ifdef _MSC_VER bool MakePath(const std::string& strPath) { if (CreateDirectoryA(strPath.c_str(), 0) == 0) { return GetLastError() == ERROR_ALREADY_EXISTS; } return true; } #else #include #include bool MakePath(const std::string& strPath) { if (mkdir(strPath.c_str(), ACCESSPERMS) == -1) { if (errno == EEXIST) { return true; } return false; } return true; } #endif //###################################################################################################################### std::string ExtractPath(const A3DUTF8Char* pcIn) { const std::string strIn(pcIn); size_t lastSlash = strIn.rfind("/"); if (lastSlash == std::string::npos) lastSlash = strIn.rfind("\\"); return strIn.substr(0, lastSlash + 1); } //###################################################################################################################### std::string ExtractFileName(const A3DUTF8Char* pcIn) { const std::string strIn(pcIn); size_t lastSlash = strIn.rfind("/"); if (lastSlash == std::string::npos) lastSlash = strIn.rfind("\\"); return strIn.substr(lastSlash + 1); } //###################################################################################################################### A3DStatus TestOutputFile(const std::string& strFileName) { if (FILE* pFile = fopen(strFileName.c_str(), "wb")) { fclose(pFile); return A3D_SUCCESS; } else { fprintf(stderr, "cannot write output file %s\n", strFileName.c_str()); return A3D_WRITE_INVALID_FILE_NAME; } }