Add relative to footprint 3d model path resolution

Fixes https://gitlab.com/kicad/code/kicad/-/issues/2073
This commit is contained in:
Marek Roszko 2022-09-25 22:54:08 -04:00
parent 05f01ab6c6
commit c50b4fb04f
11 changed files with 79 additions and 18 deletions

View File

@ -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 ) if( aCachePtr )
*aCachePtr = nullptr; *aCachePtr = nullptr;
wxString full3Dpath = m_FNResolver->ResolvePath( aModelFile ); wxString full3Dpath = m_FNResolver->ResolvePath( aModelFile, aBasePath );
if( full3Dpath.empty() ) 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; S3D_CACHE_ENTRY* cp = nullptr;
SCENEGRAPH* sp = load( aModelFileName, &cp ); SCENEGRAPH* sp = load( aModelFileName, aBasePath,&cp );
if( !sp ) if( !sp )
return nullptr; return nullptr;

View File

@ -92,9 +92,10 @@ public:
* *
* @param aModelFile is the partial or full path to the model to be loaded. * @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. * @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; FILENAME_RESOLVER* GetResolver() noexcept;
@ -124,7 +125,7 @@ public:
* @param aModelFileName is the full path to the model to be loaded. * @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. * @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. * Delete up old cache files in cache directory.
@ -164,7 +165,7 @@ private:
bool saveCacheData( S3D_CACHE_ENTRY* aCacheItem ); bool saveCacheData( S3D_CACHE_ENTRY* aCacheItem );
// the real load function (can supply a cache entry pointer to member functions) // 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 /// cache entries
std::list< S3D_CACHE_ENTRY* > m_CacheList; std::list< S3D_CACHE_ENTRY* > m_CacheList;

View File

@ -156,7 +156,7 @@ void EDA_3D_MODEL_VIEWER::Set3DModel( const wxString& aModelPathName)
if( m_cacheManager ) if( m_cacheManager )
{ {
const S3DMODEL* model = m_cacheManager->GetModel( aModelPathName ); const S3DMODEL* model = m_cacheManager->GetModel( aModelPathName, wxEmptyString );
if( model ) if( model )
Set3DModel( (const S3DMODEL &)*model ); Set3DModel( (const S3DMODEL &)*model );

View File

@ -30,6 +30,7 @@
#include <trigo.h> #include <trigo.h>
#include <project.h> #include <project.h>
#include <profile.h> // To use GetRunningMicroSecs or another profiling utility #include <profile.h> // To use GetRunningMicroSecs or another profiling utility
#include <fp_lib_table.h>
#include <eda_3d_canvas.h> #include <eda_3d_canvas.h>
#include <eda_3d_viewer_frame.h> #include <eda_3d_viewer_frame.h>
@ -922,6 +923,19 @@ void RENDER_3D_OPENGL::load3dModels( REPORTER* aStatusReporter )
// Go for all footprints // Go for all footprints
for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->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() ) for( const FP_3DMODEL& fp_model : footprint->Models() )
{ {
if( fp_model.m_Show && !fp_model.m_Filename.empty() ) 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 // It is not present, try get it from cache
const S3DMODEL* modelPtr = 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 // only add it if the return is not NULL
if( modelPtr ) if( modelPtr )

View File

@ -39,6 +39,8 @@
#include <board.h> #include <board.h>
#include <footprint.h> #include <footprint.h>
#include <fp_lib_table.h>
#include <eda_3d_viewer_frame.h>
#include <base_units.h> #include <base_units.h>
#include <profile.h> // To use GetRunningMicroSecs or another profiling utility #include <profile.h> // 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 sM = fp->Models().begin();
auto eM = fp->Models().end(); 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 ) while( sM != eM )
{ {
if( ( static_cast<float>( sM->m_Opacity ) > FLT_EPSILON ) if( ( static_cast<float>( sM->m_Opacity ) > FLT_EPSILON )
&& ( sM->m_Show && !sM->m_Filename.empty() ) ) && ( sM->m_Show && !sM->m_Filename.empty() ) )
{ {
// get it from cache // 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. // only add it if the return is not NULL.
if( modelPtr ) if( modelPtr )

View File

@ -54,6 +54,7 @@ PANEL_PREVIEW_3D_MODEL::PANEL_PREVIEW_3D_MODEL( wxWindow* aParent, PCB_BASE_FRAM
m_userUnits = aFrame->GetUserUnits(); m_userUnits = aFrame->GetUserUnits();
m_dummyBoard = new BOARD(); m_dummyBoard = new BOARD();
m_dummyBoard->SetProject( &aFrame->Prj() );
// This board will only be used to hold a footprint for viewing // This board will only be used to hold a footprint for viewing
m_dummyBoard->SetBoardUse( BOARD_USE::FPHOLDER ); m_dummyBoard->SetBoardUse( BOARD_USE::FPHOLDER );

View File

@ -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<std::mutex> lock( mutex_resolver ); std::lock_guard<std::mutex> 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) // check the partial path relative to ${KICAD6_3DMODEL_DIR} (legacy behavior)
if( !tname.StartsWith( ":" ) ) if( !tname.StartsWith( ":" ) )
{ {

View File

@ -101,8 +101,11 @@ public:
* In the future remote files may be supported, in which case it is best to require a full * 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 * 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. * 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 * Produce a relative path based on the existing search directories or returns the same path

View File

@ -32,6 +32,7 @@
#include <widgets/grid_text_button_helpers.h> #include <widgets/grid_text_button_helpers.h>
#include <widgets/wx_grid.h> #include <widgets/wx_grid.h>
#include <footprint.h> #include <footprint.h>
#include <fp_lib_table.h>
#include <footprint_edit_frame.h> #include <footprint_edit_frame.h>
#include <footprint_editor_settings.h> #include <footprint_editor_settings.h>
#include <dialog_footprint_properties_fp_editor.h> #include <dialog_footprint_properties_fp_editor.h>
@ -427,7 +428,15 @@ MODEL_VALIDATE_ERRORS PANEL_FP_PROPERTIES_3D_MODEL::validateModelExists( const w
if( !resolv->ValidateFileName( aFilename, hasAlias ) ) if( !resolv->ValidateFileName( aFilename, hasAlias ) )
return MODEL_VALIDATE_ERRORS::ILLEGAL_FILENAME; 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() ) if( fullPath.IsEmpty() )
return MODEL_VALIDATE_ERRORS::RESOLVE_FAIL; return MODEL_VALIDATE_ERRORS::RESOLVE_FAIL;

View File

@ -411,7 +411,7 @@ static void idf_export_footprint( BOARD* aPcb, FOOTPRINT* aFootprint, IDF3_BOARD
continue; continue;
} }
idfFile.Assign( resolver->ResolvePath( sM->m_Filename ) ); idfFile.Assign( resolver->ResolvePath( sM->m_Filename, wxEmptyString ) );
idfExt = idfFile.GetExt(); idfExt = idfFile.GetExt();
if( idfExt.Cmp( wxT( "idf" ) ) && idfExt.Cmp( wxT( "IDF" ) ) ) if( idfExt.Cmp( wxT( "idf" ) ) && idfExt.Cmp( wxT( "IDF" ) ) )

View File

@ -1013,7 +1013,7 @@ void EXPORTER_PCB_VRML::ExportVrmlFootprint( FOOTPRINT* aFootprint, std::ostream
continue; continue;
} }
SGNODE* mod3d = (SGNODE*) m_Cache3Dmodels->Load( sM->m_Filename ); SGNODE* mod3d = (SGNODE*) m_Cache3Dmodels->Load( sM->m_Filename, wxEmptyString );
if( nullptr == mod3d ) if( nullptr == mod3d )
{ {
@ -1078,7 +1078,8 @@ void EXPORTER_PCB_VRML::ExportVrmlFootprint( FOOTPRINT* aFootprint, std::ostream
int old_precision = aOutputFile->precision(); int old_precision = aOutputFile->precision();
aOutputFile->precision( m_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; wxFileName dstFile;
dstFile.SetPath( m_Subdir3DFpModels ); dstFile.SetPath( m_Subdir3DFpModels );
dstFile.SetName( srcFile.GetName() ); dstFile.SetName( srcFile.GetName() );