From c50b4fb04f12a484668f99f6482d3e230d0f4950 Mon Sep 17 00:00:00 2001 From: Marek Roszko Date: Sun, 25 Sep 2022 22:54:08 -0400 Subject: [PATCH] Add relative to footprint 3d model path resolution Fixes https://gitlab.com/kicad/code/kicad/-/issues/2073 --- 3d-viewer/3d_cache/3d_cache.cpp | 13 +++++++------ 3d-viewer/3d_cache/3d_cache.h | 7 ++++--- .../3d_model_viewer/eda_3d_model_viewer.cpp | 2 +- 3d-viewer/3d_rendering/opengl/create_scene.cpp | 16 +++++++++++++++- .../3d_rendering/raytracing/create_scene.cpp | 18 +++++++++++++++++- 3d-viewer/dialogs/panel_preview_3d_model.cpp | 1 + common/filename_resolver.cpp | 17 ++++++++++++++++- include/filename_resolver.h | 5 ++++- .../dialogs/panel_fp_properties_3d_model.cpp | 11 ++++++++++- pcbnew/exporters/export_idf.cpp | 2 +- pcbnew/exporters/exporter_vrml.cpp | 5 +++-- 11 files changed, 79 insertions(+), 18 deletions(-) diff --git a/3d-viewer/3d_cache/3d_cache.cpp b/3d-viewer/3d_cache/3d_cache.cpp index 18b6ad98de..a2d22d6036 100644 --- a/3d-viewer/3d_cache/3d_cache.cpp +++ b/3d-viewer/3d_cache/3d_cache.cpp @@ -212,12 +212,13 @@ S3D_CACHE::~S3D_CACHE() } -SCENEGRAPH* S3D_CACHE::load( const wxString& aModelFile, S3D_CACHE_ENTRY** aCachePtr ) +SCENEGRAPH* S3D_CACHE::load( const wxString& aModelFile, const wxString& aBasePath, + S3D_CACHE_ENTRY** aCachePtr ) { if( aCachePtr ) *aCachePtr = nullptr; - wxString full3Dpath = m_FNResolver->ResolvePath( aModelFile ); + wxString full3Dpath = m_FNResolver->ResolvePath( aModelFile, aBasePath ); if( full3Dpath.empty() ) { @@ -282,9 +283,9 @@ SCENEGRAPH* S3D_CACHE::load( const wxString& aModelFile, S3D_CACHE_ENTRY** aCach } -SCENEGRAPH* S3D_CACHE::Load( const wxString& aModelFile ) +SCENEGRAPH* S3D_CACHE::Load( const wxString& aModelFile, const wxString& aBasePath ) { - return load( aModelFile ); + return load( aModelFile, aBasePath ); } @@ -641,10 +642,10 @@ void S3D_CACHE::ClosePlugins() } -S3DMODEL* S3D_CACHE::GetModel( const wxString& aModelFileName ) +S3DMODEL* S3D_CACHE::GetModel( const wxString& aModelFileName, const wxString& aBasePath ) { S3D_CACHE_ENTRY* cp = nullptr; - SCENEGRAPH* sp = load( aModelFileName, &cp ); + SCENEGRAPH* sp = load( aModelFileName, aBasePath,&cp ); if( !sp ) return nullptr; diff --git a/3d-viewer/3d_cache/3d_cache.h b/3d-viewer/3d_cache/3d_cache.h index fe79162e03..4abc62e78b 100644 --- a/3d-viewer/3d_cache/3d_cache.h +++ b/3d-viewer/3d_cache/3d_cache.h @@ -92,9 +92,10 @@ public: * * @param aModelFile is the partial or full path to the model to be loaded. + * @param aBasePath is the path to search for any relative files * @return true if the model was successfully loaded, otherwise false. */ - SCENEGRAPH* Load( const wxString& aModelFile ); + SCENEGRAPH* Load( const wxString& aModelFile, const wxString& aBasePath ); FILENAME_RESOLVER* GetResolver() noexcept; @@ -124,7 +125,7 @@ public: * @param aModelFileName is the full path to the model to be loaded. * @return is a pointer to the render data or NULL if not available. */ - S3DMODEL* GetModel( const wxString& aModelFileName ); + S3DMODEL* GetModel( const wxString& aModelFileName, const wxString& aBasePath ); /** * Delete up old cache files in cache directory. @@ -164,7 +165,7 @@ private: bool saveCacheData( S3D_CACHE_ENTRY* aCacheItem ); // the real load function (can supply a cache entry pointer to member functions) - SCENEGRAPH* load( const wxString& aModelFile, S3D_CACHE_ENTRY** aCachePtr = nullptr ); + SCENEGRAPH* load( const wxString& aModelFile, const wxString& aBasePath, S3D_CACHE_ENTRY** aCachePtr = nullptr ); /// cache entries std::list< S3D_CACHE_ENTRY* > m_CacheList; diff --git a/3d-viewer/3d_model_viewer/eda_3d_model_viewer.cpp b/3d-viewer/3d_model_viewer/eda_3d_model_viewer.cpp index 65766cf366..f52241042f 100644 --- a/3d-viewer/3d_model_viewer/eda_3d_model_viewer.cpp +++ b/3d-viewer/3d_model_viewer/eda_3d_model_viewer.cpp @@ -156,7 +156,7 @@ void EDA_3D_MODEL_VIEWER::Set3DModel( const wxString& aModelPathName) if( m_cacheManager ) { - const S3DMODEL* model = m_cacheManager->GetModel( aModelPathName ); + const S3DMODEL* model = m_cacheManager->GetModel( aModelPathName, wxEmptyString ); if( model ) Set3DModel( (const S3DMODEL &)*model ); diff --git a/3d-viewer/3d_rendering/opengl/create_scene.cpp b/3d-viewer/3d_rendering/opengl/create_scene.cpp index 8d6a28860d..e3741379c6 100644 --- a/3d-viewer/3d_rendering/opengl/create_scene.cpp +++ b/3d-viewer/3d_rendering/opengl/create_scene.cpp @@ -30,6 +30,7 @@ #include #include #include // To use GetRunningMicroSecs or another profiling utility +#include #include #include @@ -922,6 +923,19 @@ void RENDER_3D_OPENGL::load3dModels( REPORTER* aStatusReporter ) // Go for all footprints for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() ) { + wxString libraryName = footprint->GetFPID().GetLibNickname(); + wxString footprintBasePath = wxEmptyString; + + if( m_boardAdapter.GetBoard()->GetProject() ) + { + const FP_LIB_TABLE_ROW* fpRow = + m_boardAdapter.GetBoard()->GetProject()->PcbFootprintLibs()->FindRow( + libraryName, false ); + + if( fpRow ) + footprintBasePath = fpRow->GetFullURI( true ); + } + for( const FP_3DMODEL& fp_model : footprint->Models() ) { if( fp_model.m_Show && !fp_model.m_Filename.empty() ) @@ -941,7 +955,7 @@ void RENDER_3D_OPENGL::load3dModels( REPORTER* aStatusReporter ) { // It is not present, try get it from cache const S3DMODEL* modelPtr = - m_boardAdapter.Get3dCacheManager()->GetModel( fp_model.m_Filename ); + m_boardAdapter.Get3dCacheManager()->GetModel( fp_model.m_Filename, footprintBasePath ); // only add it if the return is not NULL if( modelPtr ) diff --git a/3d-viewer/3d_rendering/raytracing/create_scene.cpp b/3d-viewer/3d_rendering/raytracing/create_scene.cpp index ad4bb212dc..e06b78f0b7 100644 --- a/3d-viewer/3d_rendering/raytracing/create_scene.cpp +++ b/3d-viewer/3d_rendering/raytracing/create_scene.cpp @@ -39,6 +39,8 @@ #include #include +#include +#include #include #include // To use GetRunningMicroSecs or another profiling utility @@ -1251,13 +1253,27 @@ void RENDER_3D_RAYTRACE::load3DModels( CONTAINER_3D& aDstContainer, bool aSkipMa auto sM = fp->Models().begin(); auto eM = fp->Models().end(); + wxString libraryName = fp->GetFPID().GetLibNickname(); + + wxString footprintBasePath = wxEmptyString; + if( m_boardAdapter.GetBoard()->GetProject() ) + { + const FP_LIB_TABLE_ROW* fpRow = + m_boardAdapter.GetBoard()->GetProject()->PcbFootprintLibs()->FindRow( + libraryName, false ); + + if( fpRow ) + footprintBasePath = fpRow->GetFullURI( true ); + } + while( sM != eM ) { if( ( static_cast( sM->m_Opacity ) > FLT_EPSILON ) && ( sM->m_Show && !sM->m_Filename.empty() ) ) { // get it from cache - const S3DMODEL* modelPtr = cacheMgr->GetModel( sM->m_Filename ); + const S3DMODEL* modelPtr = + cacheMgr->GetModel( sM->m_Filename, footprintBasePath ); // only add it if the return is not NULL. if( modelPtr ) diff --git a/3d-viewer/dialogs/panel_preview_3d_model.cpp b/3d-viewer/dialogs/panel_preview_3d_model.cpp index 0a11485a94..f8f7f1fc7f 100644 --- a/3d-viewer/dialogs/panel_preview_3d_model.cpp +++ b/3d-viewer/dialogs/panel_preview_3d_model.cpp @@ -54,6 +54,7 @@ PANEL_PREVIEW_3D_MODEL::PANEL_PREVIEW_3D_MODEL( wxWindow* aParent, PCB_BASE_FRAM m_userUnits = aFrame->GetUserUnits(); m_dummyBoard = new BOARD(); + m_dummyBoard->SetProject( &aFrame->Prj() ); // This board will only be used to hold a footprint for viewing m_dummyBoard->SetBoardUse( BOARD_USE::FPHOLDER ); diff --git a/common/filename_resolver.cpp b/common/filename_resolver.cpp index 10ec4b6196..530659d58e 100644 --- a/common/filename_resolver.cpp +++ b/common/filename_resolver.cpp @@ -240,7 +240,7 @@ bool FILENAME_RESOLVER::UpdatePathList( const std::vector< SEARCH_PATH >& aPathL } -wxString FILENAME_RESOLVER::ResolvePath( const wxString& aFileName ) +wxString FILENAME_RESOLVER::ResolvePath( const wxString& aFileName, const wxString& aWorkingPath ) { std::lock_guard lock( mutex_resolver ); @@ -324,6 +324,21 @@ wxString FILENAME_RESOLVER::ResolvePath( const wxString& aFileName ) } + // check path relative to search path + if( !aWorkingPath.IsEmpty() && !tname.StartsWith( ":" ) ) + { + wxString tmp = aWorkingPath; + tmp.Append( tmpFN.GetPathSeparator() ); + tmp.Append( tname ); + tmpFN.Assign( tmp ); + + if( tmpFN.MakeAbsolute() && tmpFN.FileExists() ) + { + tname = tmpFN.GetFullPath(); + return tname; + } + } + // check the partial path relative to ${KICAD6_3DMODEL_DIR} (legacy behavior) if( !tname.StartsWith( ":" ) ) { diff --git a/include/filename_resolver.h b/include/filename_resolver.h index 8e68e42280..7d8febdcfc 100644 --- a/include/filename_resolver.h +++ b/include/filename_resolver.h @@ -101,8 +101,11 @@ public: * In the future remote files may be supported, in which case it is best to require a full * URI in which case ResolvePath should check that the URI conforms to RFC-2396 and related * documents and copies \a aFileName into aResolvedName if the URI is valid. + * + * @param aFileName The configured file path to resolve + * @param aWorkingPath The current working path for relative path resolutions */ - wxString ResolvePath( const wxString& aFileName ); + wxString ResolvePath( const wxString& aFileName, const wxString& aWorkingPath ); /** * Produce a relative path based on the existing search directories or returns the same path diff --git a/pcbnew/dialogs/panel_fp_properties_3d_model.cpp b/pcbnew/dialogs/panel_fp_properties_3d_model.cpp index 6697d5828e..31cffd14b5 100644 --- a/pcbnew/dialogs/panel_fp_properties_3d_model.cpp +++ b/pcbnew/dialogs/panel_fp_properties_3d_model.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -427,7 +428,15 @@ MODEL_VALIDATE_ERRORS PANEL_FP_PROPERTIES_3D_MODEL::validateModelExists( const w if( !resolv->ValidateFileName( aFilename, hasAlias ) ) return MODEL_VALIDATE_ERRORS::ILLEGAL_FILENAME; - wxString fullPath = resolv->ResolvePath( aFilename ); + wxString libraryName = m_footprint->GetFPID().GetLibNickname(); + const FP_LIB_TABLE_ROW* fpRow = + m_frame->Prj().PcbFootprintLibs()->FindRow( libraryName, false ); + + wxString footprintBasePath = wxEmptyString; + if( fpRow ) + footprintBasePath = fpRow->GetFullURI( true ); + + wxString fullPath = resolv->ResolvePath( aFilename, footprintBasePath ); if( fullPath.IsEmpty() ) return MODEL_VALIDATE_ERRORS::RESOLVE_FAIL; diff --git a/pcbnew/exporters/export_idf.cpp b/pcbnew/exporters/export_idf.cpp index c8f4282d5a..7c99b2bb56 100644 --- a/pcbnew/exporters/export_idf.cpp +++ b/pcbnew/exporters/export_idf.cpp @@ -411,7 +411,7 @@ static void idf_export_footprint( BOARD* aPcb, FOOTPRINT* aFootprint, IDF3_BOARD continue; } - idfFile.Assign( resolver->ResolvePath( sM->m_Filename ) ); + idfFile.Assign( resolver->ResolvePath( sM->m_Filename, wxEmptyString ) ); idfExt = idfFile.GetExt(); if( idfExt.Cmp( wxT( "idf" ) ) && idfExt.Cmp( wxT( "IDF" ) ) ) diff --git a/pcbnew/exporters/exporter_vrml.cpp b/pcbnew/exporters/exporter_vrml.cpp index 0c9a2131b2..a2326a5afa 100644 --- a/pcbnew/exporters/exporter_vrml.cpp +++ b/pcbnew/exporters/exporter_vrml.cpp @@ -1013,7 +1013,7 @@ void EXPORTER_PCB_VRML::ExportVrmlFootprint( FOOTPRINT* aFootprint, std::ostream continue; } - SGNODE* mod3d = (SGNODE*) m_Cache3Dmodels->Load( sM->m_Filename ); + SGNODE* mod3d = (SGNODE*) m_Cache3Dmodels->Load( sM->m_Filename, wxEmptyString ); if( nullptr == mod3d ) { @@ -1078,7 +1078,8 @@ void EXPORTER_PCB_VRML::ExportVrmlFootprint( FOOTPRINT* aFootprint, std::ostream int old_precision = aOutputFile->precision(); aOutputFile->precision( m_precision ); - wxFileName srcFile = m_Cache3Dmodels->GetResolver()->ResolvePath( sM->m_Filename ); + wxFileName srcFile = + m_Cache3Dmodels->GetResolver()->ResolvePath( sM->m_Filename, wxEmptyString ); wxFileName dstFile; dstFile.SetPath( m_Subdir3DFpModels ); dstFile.SetName( srcFile.GetName() );