///////////////////////////////////////////////////////////////////////////////
// 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.
///////////////////////////////////////////////////////////////////////////////
/*
OdVisualizeAssemblyConsoleApp
Converts files inside input folder into .VSFX and creates assembly from them.
*/
#include "OdaCommon.h"
#include "Tv.h"
#include "TvFactory.h"
#include "TvDatabaseReceiver.h"
#include "OdVisualizeAssemblyCommon.h"
#include "RxInit.h"
#include "ExPrintConsole.h"
#include "OdPerfTimer.h"
#include "OdModuleNames.h"
#include "RxDynamicModule.h"
#include "TvModuleNames.h"
#include "TvCoreModulesNames.h"
// Ifc
#ifdef IFC_MODULES_ENABLED
#include "Common/daiModuleNames.h"
#include "Common/ModuleNames.h"
#endif
#ifndef _TOOLKIT_IN_DLL_
// Visualize API
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdTvModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdRxCommonDataAccessModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdTvDbCoreModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdTvDbIOModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( TvISMModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( TvSCENEOEModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdTvTfModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdTvDbPartialViewingModuleImpl );
//Visualize device
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( VisualizeDeviceModule );
//Obj2Visualize
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdTvVisualizeObjFilerModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdObjToolkitModuleImpl );
//Raster images
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdRasterProcessingServicesImpl );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( ExRasterModule );
//Device
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdTrGL2RenderModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( GLES2Module );
//DWG
#ifdef DWG_MODULES_ENABLED
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdTvVisualizeDwgFilerModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdTvVisualize2DwgFilerModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdDbIOModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( ISMModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( SCENEOEModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( ModelerModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdDbEntitiesModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdRecomputeDimBlockModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( TD_DynBlocksModule );
#endif // DWG_MODULES_ENABLED
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdRxThreadPoolImpl );
// IFC
#ifdef IFC_MODULES_ENABLED
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdTvVisualizeIfcFilerModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdSDAIModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdIfcCoreModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdIfc2x3Module );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdIfc4Module );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdIfcGeomModuleImpl );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdIfcFacetModelerModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdIfcBrepBuilderModule );
#endif // IFC_MODULES_ENABLED
//TvCore
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdTvCoreDatabaseModule );
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdTvCoreDatabaseIOModule );
//Vsf2Visualize
ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT( OdTvVisualizeVsfFilerModule );
ODRX_BEGIN_STATIC_MODULE_MAP()
ODRX_DEFINE_STATIC_APPMODULE( OdTvModuleName, OdTvModule )
ODRX_DEFINE_STATIC_APPMODULE( RxCommonDataAccessModuleName, OdRxCommonDataAccessModule )
ODRX_DEFINE_STATIC_APPMODULE( OdTvDbCoreModuleName, OdTvDbCoreModule )
ODRX_DEFINE_STATIC_APPMODULE( L"TV_SCENEOE", TvSCENEOEModule )
ODRX_DEFINE_STATIC_APPMODULE( OdTvDbIOAppName, OdTvDbIOModule )
ODRX_DEFINE_STATIC_APPMODULE( L"TV_ISM", TvISMModule )
ODRX_DEFINE_STATIC_APPMODULE( OdTvTfModuleName, OdTvTfModule )
ODRX_DEFINE_STATIC_APPMODULE( OdTvPartialViewingModuleName, OdTvDbPartialViewingModuleImpl )
ODRX_DEFINE_STATIC_APPMODULE( OdTvVisualizeDeviceModuleName, VisualizeDeviceModule )
ODRX_DEFINE_STATIC_APPMODULE( OdTvObj2VisualizeModuleName, OdTvVisualizeObjFilerModule )
ODRX_DEFINE_STATIC_APPMODULE( OdObjToolkitModuleName, OdObjToolkitModuleImpl )
ODRX_DEFINE_STATIC_APPMODULE( OdRasterProcessorModuleName, OdRasterProcessingServicesImpl )
ODRX_DEFINE_STATIC_APPMODULE( RX_RASTER_SERVICES_APPNAME, ExRasterModule )
ODRX_DEFINE_STATIC_APPMODULE( OdTrGL2ModuleName, OdTrGL2RenderModule )
ODRX_DEFINE_STATIC_APPMODULE( OdWinGLES2ModuleName, GLES2Module )
//DWG
#ifdef DWG_MODULES_ENABLED
ODRX_DEFINE_STATIC_APPMODULE( OdTvDwg2VisualizeModuleName, OdTvVisualizeDwgFilerModule )
ODRX_DEFINE_STATIC_APPMODULE( OdTvVisualize2DwgModuleName, OdTvVisualize2DwgFilerModule )
ODRX_DEFINE_STATIC_APPMODULE( OdDbIOAppName, OdDbIOModule )
ODRX_DEFINE_STATIC_APPMODULE( L"ISM", ISMModule )
ODRX_DEFINE_STATIC_APPMODULE( L"SCENEOE", SCENEOEModule )
ODRX_DEFINE_STATIC_APPLICATION( OdModelerGeometryModuleName, ModelerModule )
ODRX_DEFINE_STATIC_APPMODULE( OdDbEntitiesAppName, OdDbEntitiesModule )
ODRX_DEFINE_STATIC_APPLICATION( OdRecomputeDimBlockModuleName, OdRecomputeDimBlockModule )
ODRX_DEFINE_STATIC_APPMODULE( OdDynBlocksModuleName, TD_DynBlocksModule )
#endif // DWG_MODULES_ENABLED
// IFC
#ifdef IFC_MODULES_ENABLED
ODRX_DEFINE_STATIC_APPMODULE( OdTvIfc2VisualizeModuleName, OdTvVisualizeIfcFilerModule )
ODRX_DEFINE_STATIC_APPMODULE( OdSDAIModuleName, OdSDAIModule )
ODRX_DEFINE_STATIC_APPMODULE( OdIfcCoreModuleName, OdIfcCoreModule )
ODRX_DEFINE_STATIC_APPMODULE( OdIfc2x3ModuleName, OdIfc2x3Module )
ODRX_DEFINE_STATIC_APPMODULE( OdIfc4ModuleName, OdIfc4Module )
ODRX_DEFINE_STATIC_APPMODULE( OdIfcGeomModuleName, OdIfcGeomModuleImpl )
ODRX_DEFINE_STATIC_APPMODULE( OdIfcFacetModelerModuleName, OdIfcFacetModelerModule )
ODRX_DEFINE_STATIC_APPMODULE( OdIfcBrepBuilderModuleName, OdIfcBrepBuilderModule )
#endif
//TvCore
ODRX_DEFINE_STATIC_APPMODULE( OdTvCoreDatabaseModuleName, OdTvCoreDatabaseModule )
ODRX_DEFINE_STATIC_APPMODULE( OdTvCoreDatabaseIOModuleName, OdTvCoreDatabaseIOModule )
//Vsf2Visualize
ODRX_DEFINE_STATIC_APPMODULE( OdTvVsf2VisualizeModuleName, OdTvVisualizeVsfFilerModule )
ODRX_DEFINE_STATIC_APPMODULE( OdThreadPoolModuleName, OdRxThreadPoolImpl ) // uncomment for multicore/multicpu support in statically linked version
ODRX_END_STATIC_MODULE_MAP()
#endif
void ODASdkActivate()
{
static const char* ActInfo[] = {
#ifdef TEIGHA_TRIAL
"", ""
#else
//"UserInfo", "UserSignature"
// Before compiling, a ODA SDK activation file should be placed in a location that a compiler can access,
// otherwise you get a compiler error such as "Kernel/Extensions/ExServices/ExSystemServices.h:43:10: fatal error: 'OdActivationInfo' file not found".
// To learn about ODA SDK activation, see the activation guide at https://docs.opendesign.com/tkernel/oda_activation.html
#include "OdActivationInfo"
#endif
};
odActivate(ActInfo[0], ActInfo[1]);
}
void ODASdkDeactivate()
{
odCleanUpStaticData();
}
int main( int argc, char* argv[] )
{
#ifndef _TOOLKIT_IN_DLL_
ODRX_INIT_STATIC_MODULE_MAP();
#endif
// Verify the argument count and display an error message as required
if( argc < 2 )
{
odPrintConsoleString(
L"usage: OdVisualizeAssemblyConsoleApp -id [-od ] [-r path, if output is not specified.\n" );
return 1;
}
//Parse arguments list
enum class SupportedFormat
{
kVSFX = 1 << 0,
kDWG = 1 << 1,
kIFC = 1 << 2,
kNWD = 1 << 3,
kRVT = 1 << 4
};
int nArg = 1;
OdString inputPath, outputPath;
bool bRecursive = false;
OdUInt8 formats = 0;
bool bExtPI = false;
OdString tmpPath = OdString::kEmpty;
while( nArg < argc )
{
OdString arg = argv[ nArg ];
if( arg == OD_T( "-id" ) )
{
inputPath = argv[ ++nArg ];
if( outputPath.isEmpty() )
outputPath = inputPath;
}
else if( arg == OD_T( "-od" ) )
{
outputPath = argv[ ++nArg ];
}
else if( arg == OD_T( "-r" ) )
{
bRecursive = true;
}
else if( arg == OD_T( "-VSFX" ) )
{
SETBIT_1( formats, (OdUInt8)( SupportedFormat::kVSFX ) );
}
else if( arg == OD_T( "-DWG" ) )
{
SETBIT_1( formats, (OdUInt8)( SupportedFormat::kDWG ) );
}
else if( arg == OD_T( "-IFC" ) )
{
SETBIT_1( formats, (OdUInt8)( SupportedFormat::kIFC ) );
}
else if( arg == OD_T( "-NWD" ) )
{
SETBIT_1( formats, (OdUInt8)( SupportedFormat::kNWD ) );
}
else if( arg == OD_T( "-RVT" ) )
{
SETBIT_1( formats, (OdUInt8)( SupportedFormat::kRVT ) );
}
else if( arg == OD_T( "-piext" ) )
{
bExtPI = true;
}
else if( arg == OD_T( "-stream" ) )
{
tmpPath = argv[ ++nArg ];
}
else
{
ODA_FAIL();
odPrintConsoleString( OD_T( "Invalid input parameter ") + arg + OD_T( "\n" ) );
return 1;
}
nArg++;
}
// Activate ODA SDK
ODASdkActivate();
// Initialize factory
odTvInitialize();
OdTvFileServices coll;
if( outputPath[ outputPath.getLength() - 1 ] != coll.folderDelimeter() )
{
outputPath += coll.folderDelimeter();
}
//Setup files collector
if( formats == 0 )
{
coll.addSupportedExt( OD_T( "VSFX" ) );
coll.addSupportedExt( OD_T( "RVT" ) );
coll.addSupportedExt( OD_T( "IFC" ) );
coll.addSupportedExt( OD_T( "NWD" ) );
coll.addSupportedExt( OD_T( "DWG" ) );
}
else
{
if( GETBIT( formats, (OdUInt8)( SupportedFormat::kVSFX ) ) ) coll.addSupportedExt( OD_T( "VSFX" ) );
if( GETBIT( formats, (OdUInt8)( SupportedFormat::kDWG ) ) ) coll.addSupportedExt( OD_T( "DWG" ) );
if( GETBIT( formats, (OdUInt8)( SupportedFormat::kIFC ) ) ) coll.addSupportedExt( OD_T( "IFC" ) );
if( GETBIT( formats, (OdUInt8)( SupportedFormat::kNWD ) ) ) coll.addSupportedExt( OD_T( "NWD" ) );
if( GETBIT( formats, (OdUInt8)( SupportedFormat::kRVT ) ) ) coll.addSupportedExt( OD_T( "RVT" ) );
}
OdTvFactoryId factId = odTvGetFactory();
odPrintConsoleString( OD_T( "OdVisualizeAssemblyConsoleApp developed by ODA\n" ) );
//Collect input files
OdStringArray files = coll.collectFiles( inputPath, bRecursive );
if( files.isEmpty() )
{
odPrintConsoleString( OD_T( "No files found\n" ) );
odTvUninitialize();
ODASdkDeactivate();
return 0;
}
if( !OdTvFileServices::createDirectoryIfNotExist( outputPath ) )
{
odPrintConsoleString( OD_T( "Error: can not create output directory\n" ) );
odTvUninitialize();
ODASdkDeactivate();
return 1;
}
//Convert all non-VFSX files into VSFX
odPrintConsoleString( OD_T( "\nConversion phase\n" ) );
OdTvFileConverter converter;
OdString outFileName = OdString::kEmpty;
for( unsigned int i = 0; i < files.size(); ++i )
{
if( OdTvFileServices::getFileExt( files[ i ] ) != OD_T( "VSFX" ) )
{
OdString fileName = coll.getFileName( files[ i ] );
if( !tmpPath.isEmpty() )
{
outFileName = outputPath + fileName + OD_T( ".vsfx" );
fileName = tmpPath;
}
else
{
outFileName = OdString::kEmpty;
fileName = outputPath + fileName + OD_T( ".vsfx" );
}
odPrintConsoleString( OD_T( "Converting " ) + files[ i ] + " -> " + fileName + OD_T( "... " ) );
OdTvFileConverter::Statistic stat;
if( converter.convertFile( files[ i ], fileName, &stat, bExtPI ) == tvOk )
{
if( !outFileName.isEmpty() )
{
odPrintConsoleString( OD_T( "DONE\nMaking streaming compatible " ) + fileName + " -> " + outFileName + OD_T( "... " ) );
OdTvDatabaseReceiver::makeStreamingCompatible( fileName, outFileName );
fileName = outFileName;
}
files[ i ] = fileName;
odPrintConsoleString( OD_T( "DONE" ) );
OdString statStr;
statStr.format( OD_T( "( %.3f secs, %.3f MB )\n" ), (float)( stat.conversionTime ) / 1000.f, (float)( stat.fileSize ) / ( 1024.f * 1024.f ) );
odPrintConsoleString( statStr );
}
else
{
files[ i ] = OdString::kEmpty;
odPrintConsoleString( OD_T( "FAILED\n" ) );
}
}
}
//Create assembly
odPrintConsoleString( OD_T( "\nReferencing phase\n" ) );
OdPerfTimerWrapper refTimer;
refTimer.getTimer()->start();
//Create database, device and view
OdTvDatabaseId dbId = odTvGetFactory().createDatabase();
OdTvDatabasePtr pDb = dbId.openObject( OdTv::kForWrite );
OdTvGsDeviceId devId = pDb->createDevice( OD_T( "AssemblyDevice" ) );
OdTvGsViewId viewId = devId.openObject( OdTv::kForWrite )->createView( OD_T( "AssemblyView" ) );
OdTvGsViewPtr pView = viewId.openObject( OdTv::kForWrite );
devId.openObject( OdTv::kForWrite )->addView( viewId );
bool bCopyViewParams = true;
for( unsigned int i = 0; i < files.size(); ++i )
{
if( files[ i ].isEmpty() ) continue;
//Prevent self-referencing
if( coll.getFileName( files[ i ] ) == OD_T( "assembly.vsfx" ) )
{
odPrintConsoleString( OD_T( "Skipped " ) + files[ i ] + OD_T( "\n" ) );
continue;
}
OdString modelName = OdString::kEmpty;
odPrintConsoleString( OD_T( "Referencing " ) + files[ i ] + OD_T( "... " ) );
//Find model to be referenced - first model in the database
OdTvDatabaseId refDbId = odTvGetFactory().readVSFX( files[ i ], false, true );
if( refDbId.isNull() )
{
odPrintConsoleString( OD_T( "FAILED, can not load database\n" ) );
continue;
}
{
OdTvDatabasePtr pRefDb = refDbId.openObject();
OdTvModelId modelId = pRefDb->getModelsIterator()->getModel();
if( modelId.isNull() )
{
odPrintConsoleString( OD_T( "FAILED, can not find model\n" ) );
continue;
}
modelName = modelId.openObject()->getName();
//Copy view parameters if need
if( bCopyViewParams )
{
OdTvDevicesIteratorPtr pDevs = pRefDb->getDevicesIterator();
for( ; !pDevs->done(); pDevs->step() )
{
OdTvGsDeviceId dId = pDevs->getDevice();
OdTvGsDevicePtr pD = dId.openObject();
if( pD->getActive() )
{
OdTvGsViewId vId = pD->getActiveView();
if( !vId.isNull() )
{
OdTvGsViewPtr pV = vId.openObject();
//Copy view params
pView->setView( pV->position(), pV->target(), pV->upVector(), pV->fieldWidth(), pV->fieldHeight(), pV->isPerspective() ? OdTvGsView::kPerspective : OdTvGsView::kParallel );
//Copy visual style
OdTvVisualStyleId stId = pV->getVisualStyle();
if( !stId.isNull() )
{
OdTvVisualStyleId styleId = pDb->createVisualStyle( OD_T( "AssemblyVisualStyle" ) );
styleId.openObject( OdTv::kForWrite )->copyFrom( stId );
pView->setVisualStyle( styleId );
}
bCopyViewParams = false;
}
}
}
}
}
odTvGetFactory().removeDatabase( refDbId );
//Create model reference
OdTvModelId modelrefId = pDb->createModelReference( coll.getFileName( files[ i ] ), /*files[i]*/coll.getFileName( files[ i ] ), modelName, OdTvModel::kMain, true);
if( modelrefId.isNull() )
{
odPrintConsoleString( OD_T( "FAILED, can not reffer model\n" ) );
continue;
}
else
{
//Add model to the view
pView->addModel( modelrefId );
odPrintConsoleString( OD_T( "DONE\n" ) );
}
}
//Save assembly file
OdTvVSFExportOptions opts;
opts.compression = OdTvVSFExportOptions::kInt16;
opts.normalsCompression = OdTvVSFExportOptions::kTwoFloats;
opts.m_IgnoreFaceNormals = true;
if( tmpPath.isEmpty() )
{
pDb->addPartialViewIndexes( true, bExtPI );
pDb->writeVSFX( outputPath + OD_T( "assembly.vsfx" ), &opts );
}
else
{
pDb->addPartialViewIndexes( true, bExtPI );
pDb->writeVSFX( tmpPath, &opts );
odPrintConsoleString( OD_T( "Making streaming-compatible assembly...\n " ) );
OdTvDatabaseReceiver::makeStreamingCompatible( tmpPath, outputPath + OD_T( "assembly.vsfx" ) );
}
refTimer.getTimer()->stop();
OdString statStr;
statStr.format( OD_T( "Assembly created in %.3f secs\n" ), (float)( refTimer.getTimer()->countedSec() ) );
odPrintConsoleString( statStr );
odPrintConsoleString( OD_T( "Assembly saved as " ) + outputPath + OD_T( "assembly.vsfx\n" ) );
//Release pointers, remove database
pView.release();
pDb.release();
odTvGetFactory().removeDatabase( dbId );
// Uninitialize factory
odTvUninitialize();
// Deactivate ODA SDK
ODASdkDeactivate();
return 0;
}