26#define GLM_FORCE_RADIANS
31#include <wx/datetime.h>
34#include <wx/stdpaths.h>
36#include <boost/version.hpp>
38#if BOOST_VERSION >= 106800
39#include <boost/uuid/detail/sha1.hpp>
41#include <boost/uuid/sha1.hpp>
61#define MASK_3D_CACHE "3D_CACHE"
66static bool isSHA1Same(
const unsigned char* shaA,
const unsigned char* shaB )
noexcept
68 for(
int i = 0; i < 20; ++i )
70 if( shaA[i] != shaB[i] )
78static bool checkTag(
const char* aTag,
void* aPluginMgrPtr )
80 if(
nullptr == aTag ||
nullptr == aPluginMgrPtr )
96 for(
int i = 0; i < 20; ++i )
119 return wxString::FromUTF8Unchecked( sha1 );
129 void SetSHA1(
const unsigned char* aSHA1Sum );
166 if(
nullptr == aSHA1Sum )
168 wxLogTrace(
MASK_3D_CACHE, wxT(
"%s:%s:%d\n * [BUG] NULL passed for aSHA1Sum" ),
169 __FILE__, __FUNCTION__, __LINE__ );
174 memcpy(
sha1sum, aSHA1Sum, 20 );
208 *aCachePtr =
nullptr;
212 if( full3Dpath.empty() )
215 wxLogTrace(
MASK_3D_CACHE, wxT(
"%s:%s:%d\n * [3D model] could not find model '%s'\n" ),
216 __FILE__, __FUNCTION__, __LINE__, aModelFile );
223 std::map< wxString, S3D_CACHE_ENTRY*, rsort_wxString >::iterator mi;
228 wxFileName fname( full3Dpath );
230 if( fname.FileExists() )
233 wxDateTime fmdate = fname.GetModificationTime();
235 if( fmdate != mi->second->modTime )
237 unsigned char hashSum[20];
238 getSHA1( full3Dpath, hashSum );
239 mi->second->modTime = fmdate;
241 if( !
isSHA1Same( hashSum, mi->second->sha1sum ) )
243 mi->second->SetSHA1( hashSum );
250 if(
nullptr != mi->second->sceneData )
253 mi->second->sceneData =
nullptr;
256 if(
nullptr != mi->second->renderData )
260 mi->second->pluginInfo );
264 if(
nullptr != aCachePtr )
265 *aCachePtr = mi->second;
277 return load( aModelFile, aBasePath );
284 *aCachePtr =
nullptr;
286 unsigned char sha1sum[20];
289 wxFileName fname( aFileName );
290 ep->
modTime = fname.GetModificationTime();
298 if(
m_CacheMap.emplace( aFileName, ep ).second ==
false )
301 wxT(
"%s:%s:%d\n * [BUG] duplicate entry in map file; key = '%s'" ),
302 __FILE__, __FUNCTION__, __LINE__, aFileName );
316 if(
m_CacheMap.emplace( aFileName, ep ).second ==
false )
319 wxT(
"%s:%s:%d\n * [BUG] duplicate entry in map file; key = '%s'" ),
320 __FILE__, __FUNCTION__, __LINE__, aFileName );
333 wxString cachename =
m_CacheDir + bname + wxT(
".3dc" );
350 if( aFileName.empty() )
352 wxLogTrace(
MASK_3D_CACHE, wxT(
"%s:%s:%d\n * [BUG] empty filename" ),
353 __FILE__, __FUNCTION__, __LINE__ );
358 if(
nullptr == aSHA1Sum )
360 wxLogTrace(
MASK_3D_CACHE, wxT(
"%s\n * [BUG] NULL pointer passed for aMD5Sum" ),
361 __FILE__, __FUNCTION__, __LINE__ );
367 FILE* fp = _wfopen( aFileName.wc_str(), L
"rb" );
369 FILE* fp = fopen( aFileName.ToUTF8(),
"rb" );
375 boost::uuids::detail::sha1 dblock;
376 unsigned char block[4096];
379 while( ( bsize = fread( &block, 1, 4096, fp ) ) > 0 )
380 dblock.process_bytes( block, bsize );
383 unsigned int digest[5];
384 dblock.get_digest( digest );
387 for(
int i = 0; i < 5; ++i )
390 unsigned int tmp = digest[i];
391 aSHA1Sum[idx+3] = tmp & 0xff;
393 aSHA1Sum[idx+2] = tmp & 0xff;
395 aSHA1Sum[idx+1] = tmp & 0xff;
397 aSHA1Sum[idx] = tmp & 0xff;
411 wxT(
" * [3D model] cannot load cached model; no file hash available" ) );
419 wxT(
" * [3D model] cannot load cached model; config directory unknown" ) );
424 wxString fname =
m_CacheDir + bname + wxT(
".3dc" );
426 if( !wxFileName::FileExists( fname ) )
428 wxLogTrace(
MASK_3D_CACHE, wxT(
" * [3D model] cannot open file '%s'" ), fname.GetData() );
446 if(
nullptr == aCacheItem )
448 wxLogTrace(
MASK_3D_CACHE, wxT(
"%s:%s:%d\n * NULL passed for aCacheItem" ),
449 __FILE__, __FUNCTION__, __LINE__ );
456 wxLogTrace(
MASK_3D_CACHE, wxT(
"%s:%s:%d\n * aCacheItem has no valid scene data" ),
457 __FILE__, __FUNCTION__, __LINE__ );
467 wxT(
" * [3D model] cannot load cached model; no file hash available" ) );
475 wxT(
" * [3D model] cannot load cached model; config directory unknown" ) );
480 wxString fname =
m_CacheDir + bname + wxT(
".3dc" );
482 if( wxFileName::Exists( fname ) )
484 if( !wxFileName::FileExists( fname ) )
487 wxT(
" * [3D model] path exists but is not a regular file '%s'" ), fname );
507 if( !cfgdir.DirExists() )
509 cfgdir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
511 if( !cfgdir.DirExists() )
514 wxT(
"%s:%s:%d\n * failed to create 3D configuration directory '%s'" ),
515 __FILE__, __FUNCTION__, __LINE__, cfgdir.GetPath() );
527 wxT(
"%s:%s:%d\n * could not set 3D Config Directory on filename resolver\n"
528 " * config directory: '%s'" ),
541 cacheDir.AppendDir( wxT(
"3d" ) );
543 if( !cacheDir.DirExists() )
545 cacheDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
547 if( !cacheDir.DirExists() )
550 wxT(
"%s:%s:%d\n * failed to create 3D cache directory '%s'" ),
551 __FILE__, __FUNCTION__, __LINE__, cacheDir.GetPath() );
566 bool hasChanged =
false;
572 std::list< S3D_CACHE_ENTRY* >::iterator sL =
m_CacheList.begin();
573 std::list< S3D_CACHE_ENTRY* >::iterator eL =
m_CacheList.end();
610 std::list< S3D_CACHE_ENTRY* >::iterator sCL =
m_CacheList.begin();
611 std::list< S3D_CACHE_ENTRY* >::iterator eCL =
m_CacheList.end();
645 wxT(
"%s:%s:%d\n * [BUG] model loaded with no associated S3D_CACHE_ENTRY" ),
646 __FILE__, __FUNCTION__, __LINE__ );
663 wxString fileSpec = wxT(
"*.3dc" );
664 wxArrayString fileList;
665 size_t numFilesFound = 0;
668 wxDateTime lastAccess, thresholdDate;
669 wxDateSpan durationInDays;
672 durationInDays.SetDays( aNumDaysOld );
673 thresholdDate = wxDateTime::Now() - durationInDays;
681 numFilesFound = dir.GetAllFiles(
m_CacheDir, &fileList, fileSpec );
683 for(
unsigned int i = 0; i < numFilesFound; i++ )
686 thisFile.SetFullName( fileList[i] );
689 if( thisFile.GetTimes( &lastAccess,
nullptr,
nullptr ) )
691 if( lastAccess.IsEarlierThan( thresholdDate ) )
694 wxRemoveFile( thisFile.GetFullPath() );
static bool isSHA1Same(const unsigned char *shaA, const unsigned char *shaB) noexcept
static std::mutex mutex3D_cache
static const wxString sha1ToWXString(const unsigned char *aSHA1Sum)
static bool checkTag(const char *aTag, void *aPluginMgrPtr)
defines the basic data associated with a single 3D model.
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
Provide an extensible class to resolve 3D model paths.
bool Set3DConfigDir(const wxString &aConfigDir)
Set the user's configuration directory for 3D models.
bool SetProject(PROJECT *aProject, bool *flgChanged=nullptr)
Set the current KiCad project directory as the first entry in the model path list.
void SetProgramBase(PGM_BASE *aBase)
Set a pointer to the application's PGM_BASE instance used to extract the local env vars.
wxString ResolvePath(const wxString &aFileName, const wxString &aWorkingPath)
Determines the full path of the given file name.
static wxString GetUserCachePath()
Gets the stock (install) 3d viewer plugins path.
Container for data for KiCad programs.
Container for project specific data.
void SetSHA1(const unsigned char *aSHA1Sum)
S3D_CACHE_ENTRY & operator=(const S3D_CACHE_ENTRY &source)
S3D_CACHE_ENTRY(const S3D_CACHE_ENTRY &source)
const wxString GetCacheBaseName()
unsigned char sha1sum[20]
bool getSHA1(const wxString &aFileName, unsigned char *aSHA1Sum)
Calculate the SHA1 hash of the given file.
void SetProgramBase(PGM_BASE *aBase)
Set the filename resolver's pointer to the application's PGM_BASE instance.
SCENEGRAPH * load(const wxString &aModelFile, const wxString &aBasePath, S3D_CACHE_ENTRY **aCachePtr=nullptr)
bool loadCacheData(S3D_CACHE_ENTRY *aCacheItem)
void FlushCache(bool closePlugins=true)
Free all data in the cache and by default closes all plugins.
bool Set3DConfigDir(const wxString &aConfigDir)
Sets the configuration directory to be used by the model manager for storing 3D model manager configu...
S3DMODEL * GetModel(const wxString &aModelFileName, const wxString &aBasePath)
Attempt to load the scene data for a model and to translate it into an S3D_MODEL structure for displa...
SCENEGRAPH * checkCache(const wxString &aFileName, S3D_CACHE_ENTRY **aCachePtr=nullptr)
Find or create cache entry for file name.
S3D_PLUGIN_MANAGER * m_Plugins
bool saveCacheData(S3D_CACHE_ENTRY *aCacheItem)
std::list< wxString > const * GetFileFilters() const
Return the list of file filters retrieved from the plugins.
FILENAME_RESOLVER * GetResolver() noexcept
std::list< S3D_CACHE_ENTRY * > m_CacheList
cache entries
bool SetProject(PROJECT *aProject)
Set the current project's working directory; this affects the model search path.
void CleanCacheDir(int aNumDaysOld)
Delete up old cache files in cache directory.
std::map< wxString, S3D_CACHE_ENTRY *, rsort_wxString > m_CacheMap
mapping of file names to cache names and data
SCENEGRAPH * Load(const wxString &aModelFile, const wxString &aBasePath)
Attempt to load the scene data for a model.
void ClosePlugins()
Unload plugins to free memory.
FILENAME_RESOLVER * m_FNResolver
void ClosePlugins(void)
Iterate through all discovered plugins and closes them to reclaim memory.
SCENEGRAPH * Load3DModel(const wxString &aFileName, std::string &aPluginInfo)
std::list< wxString > const * GetFileFilters(void) const noexcept
Return the list of file filters; this will contain at least the default "All Files (*....
bool CheckTag(const char *aTag)
Check the given tag and returns true if the plugin named in the tag is not loaded or the plugin is lo...
Define the basic data set required to represent a 3D model.
The base class of all Scene Graph nodes.
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
bool m_Skip3DModelMemoryCache
Skip reading/writing 3d model memory caches This ensures 3d models are always reloaded from disk even...
defines the API calls for the manipulation of SG* classes
SGLIB_API SGNODE * ReadCache(const char *aFileName, void *aPluginMgr, bool(*aTagCheck)(const char *, void *))
Function ReadCache reads a binary cache file and creates an SGNODE tree.
SGLIB_API bool WriteCache(const char *aFileName, bool overwrite, SGNODE *aNode, const char *aPluginInfo)
Function WriteCache writes the SGNODE tree to a binary cache file.
SGLIB_API void DestroyNode(SGNODE *aNode) noexcept
Function DestroyNode deletes the given SG* class node.
SGLIB_API S3DMODEL * GetModel(SCENEGRAPH *aNode)
Function GetModel creates an S3DMODEL representation of aNode (raw data, no transforms)
SGLIB_API void Destroy3DModel(S3DMODEL **aModel)
Function Destroy3DModel frees memory used by an S3DMODEL structure and sets the pointer to the struct...
Store the a model based on meshes and materials.
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().