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"
67static bool isSHA1Same(
const unsigned char* shaA,
const unsigned char* shaB )
noexcept
69 for(
int i = 0; i < 20; ++i )
71 if( shaA[i] != shaB[i] )
79static bool checkTag(
const char* aTag,
void* aPluginMgrPtr )
81 if(
nullptr == aTag ||
nullptr == aPluginMgrPtr )
97 for(
int i = 0; i < 20; ++i )
120 return wxString::FromUTF8Unchecked( sha1 );
130 void SetSHA1(
const unsigned char* aSHA1Sum );
167 if(
nullptr == aSHA1Sum )
169 wxLogTrace(
MASK_3D_CACHE, wxT(
"%s:%s:%d\n * [BUG] NULL passed for aSHA1Sum" ),
170 __FILE__, __FUNCTION__, __LINE__ );
175 memcpy(
sha1sum, aSHA1Sum, 20 );
209 *aCachePtr =
nullptr;
213 if( full3Dpath.empty() )
216 wxLogTrace(
MASK_3D_CACHE, wxT(
"%s:%s:%d\n * [3D model] could not find model '%s'\n" ),
217 __FILE__, __FUNCTION__, __LINE__, aModelFile );
224 std::map< wxString, S3D_CACHE_ENTRY*, rsort_wxString >::iterator mi;
229 wxFileName fname( full3Dpath );
231 if( fname.FileExists() )
234 wxDateTime fmdate = fname.GetModificationTime();
236 if( fmdate != mi->second->modTime )
238 unsigned char hashSum[20];
239 getSHA1( full3Dpath, hashSum );
240 mi->second->modTime = fmdate;
242 if( !
isSHA1Same( hashSum, mi->second->sha1sum ) )
244 mi->second->SetSHA1( hashSum );
251 if(
nullptr != mi->second->sceneData )
254 mi->second->sceneData =
nullptr;
257 if(
nullptr != mi->second->renderData )
261 mi->second->pluginInfo );
265 if(
nullptr != aCachePtr )
266 *aCachePtr = mi->second;
278 return load( aModelFile, aBasePath );
285 *aCachePtr =
nullptr;
287 unsigned char sha1sum[20];
290 wxFileName fname( aFileName );
291 ep->
modTime = fname.GetModificationTime();
299 if(
m_CacheMap.emplace( aFileName, ep ).second ==
false )
302 wxT(
"%s:%s:%d\n * [BUG] duplicate entry in map file; key = '%s'" ),
303 __FILE__, __FUNCTION__, __LINE__, aFileName );
317 if(
m_CacheMap.emplace( aFileName, ep ).second ==
false )
320 wxT(
"%s:%s:%d\n * [BUG] duplicate entry in map file; key = '%s'" ),
321 __FILE__, __FUNCTION__, __LINE__, aFileName );
334 wxString cachename =
m_CacheDir + bname + wxT(
".3dc" );
351 if( aFileName.empty() )
353 wxLogTrace(
MASK_3D_CACHE, wxT(
"%s:%s:%d\n * [BUG] empty filename" ),
354 __FILE__, __FUNCTION__, __LINE__ );
359 if(
nullptr == aSHA1Sum )
361 wxLogTrace(
MASK_3D_CACHE, wxT(
"%s\n * [BUG] NULL pointer passed for aMD5Sum" ),
362 __FILE__, __FUNCTION__, __LINE__ );
368 FILE* fp = _wfopen( aFileName.wc_str(), L
"rb" );
370 FILE* fp = fopen( aFileName.ToUTF8(),
"rb" );
376 boost::uuids::detail::sha1 dblock;
377 unsigned char block[4096];
380 while( ( bsize = fread( &block, 1, 4096, fp ) ) > 0 )
381 dblock.process_bytes( block, bsize );
384 unsigned int digest[5];
385 dblock.get_digest( digest );
388 for(
int i = 0; i < 5; ++i )
391 unsigned int tmp = digest[i];
392 aSHA1Sum[idx+3] = tmp & 0xff;
394 aSHA1Sum[idx+2] = tmp & 0xff;
396 aSHA1Sum[idx+1] = tmp & 0xff;
398 aSHA1Sum[idx] = tmp & 0xff;
412 wxT(
" * [3D model] cannot load cached model; no file hash available" ) );
420 wxT(
" * [3D model] cannot load cached model; config directory unknown" ) );
425 wxString fname =
m_CacheDir + bname + wxT(
".3dc" );
427 if( !wxFileName::FileExists( fname ) )
429 wxLogTrace(
MASK_3D_CACHE, wxT(
" * [3D model] cannot open file '%s'" ), fname.GetData() );
447 if(
nullptr == aCacheItem )
449 wxLogTrace(
MASK_3D_CACHE, wxT(
"%s:%s:%d\n * NULL passed for aCacheItem" ),
450 __FILE__, __FUNCTION__, __LINE__ );
457 wxLogTrace(
MASK_3D_CACHE, wxT(
"%s:%s:%d\n * aCacheItem has no valid scene data" ),
458 __FILE__, __FUNCTION__, __LINE__ );
468 wxT(
" * [3D model] cannot load cached model; no file hash available" ) );
476 wxT(
" * [3D model] cannot load cached model; config directory unknown" ) );
481 wxString fname =
m_CacheDir + bname + wxT(
".3dc" );
483 if( wxFileName::Exists( fname ) )
485 if( !wxFileName::FileExists( fname ) )
488 wxT(
" * [3D model] path exists but is not a regular file '%s'" ), fname );
508 if( !cfgdir.DirExists() )
510 cfgdir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
512 if( !cfgdir.DirExists() )
515 wxT(
"%s:%s:%d\n * failed to create 3D configuration directory '%s'" ),
516 __FILE__, __FUNCTION__, __LINE__, cfgdir.GetPath() );
528 wxT(
"%s:%s:%d\n * could not set 3D Config Directory on filename resolver\n"
529 " * config directory: '%s'" ),
542 cacheDir.AppendDir( wxT(
"3d" ) );
544 if( !cacheDir.DirExists() )
546 cacheDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
548 if( !cacheDir.DirExists() )
551 wxT(
"%s:%s:%d\n * failed to create 3D cache directory '%s'" ),
552 __FILE__, __FUNCTION__, __LINE__, cacheDir.GetPath() );
567 bool hasChanged =
false;
573 std::list< S3D_CACHE_ENTRY* >::iterator sL =
m_CacheList.begin();
574 std::list< S3D_CACHE_ENTRY* >::iterator eL =
m_CacheList.end();
611 std::list< S3D_CACHE_ENTRY* >::iterator sCL =
m_CacheList.begin();
612 std::list< S3D_CACHE_ENTRY* >::iterator eCL =
m_CacheList.end();
646 wxT(
"%s:%s:%d\n * [BUG] model loaded with no associated S3D_CACHE_ENTRY" ),
647 __FILE__, __FUNCTION__, __LINE__ );
664 wxString fileSpec = wxT(
"*.3dc" );
665 wxArrayString fileList;
666 size_t numFilesFound = 0;
669 wxDateTime lastAccess, thresholdDate;
670 wxDateSpan durationInDays;
673 durationInDays.SetDays( aNumDaysOld );
674 thresholdDate = wxDateTime::Now() - durationInDays;
682 numFilesFound = dir.GetAllFiles(
m_CacheDir, &fileList, fileSpec );
684 for(
unsigned int i = 0; i < numFilesFound; i++ )
687 thisFile.SetFullName( fileList[i] );
690 if( thisFile.GetTimes( &lastAccess,
nullptr,
nullptr ) )
692 if( lastAccess.IsEarlierThan( thresholdDate ) )
695 wxRemoveFile( thisFile.GetFullPath() );
703void PROJECT::Cleanup3DCache()
713 int clearCacheInterval = 0;
715 if(
Pgm().GetCommonSettings() )
716 clearCacheInterval =
Pgm().GetCommonSettings()->m_System.clear_3d_cache_interval;
720 if( clearCacheInterval > 0 )
726S3D_CACHE* PROJECT::Get3DCacheManager(
bool aUpdateProjDir )
740 cfgpath.AppendDir( wxT(
"3d" ) );
746 aUpdateProjDir =
true;
758 return Get3DCacheManager()->GetResolver();
static bool isSHA1Same(const unsigned char *shaA, const unsigned char *shaB) noexcept
static std::mutex mutex3D_cacheManager
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.
virtual _ELEM * GetElem(ELEM_T aIndex)
Get and set the elements for this project.
virtual void SetElem(ELEM_T aIndex, _ELEM *aElem)
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]
Cache for storing the 3D shapes.
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.
static wxString GetUserSettingsPath()
Return the user configuration path used to store KiCad's configuration files.
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...
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Store the a model based on meshes and materials.
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().