From a3b32f306cbf73a9879c6dad58b6d418bc47119b Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Fri, 10 Apr 2009 21:59:22 +0000 Subject: [PATCH] Adding a file system filter layer to correct invalid paths automatically. git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@381 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/BaseImporter.cpp | 75 +++-------- code/BaseImporter.h | 11 -- code/FileSystemFilter.h | 220 +++++++++++++++++++++++++++++++ code/IRRLoader.cpp | 2 +- code/Importer.cpp | 3 +- code/LWOAnimation.cpp | 7 +- code/LWSLoader.cpp | 17 ++- include/aiVector3D.h | 8 +- include/assimp.hpp | 4 +- mkutil/mkrev.bat | 2 +- workspaces/vc9/assimp.vcproj | 246 ++++++++++++++++++----------------- 11 files changed, 388 insertions(+), 207 deletions(-) create mode 100644 code/FileSystemFilter.h diff --git a/code/BaseImporter.cpp b/code/BaseImporter.cpp index bd1b6b48c..9daa0922b 100644 --- a/code/BaseImporter.cpp +++ b/code/BaseImporter.cpp @@ -45,10 +45,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AssimpPCH.h" #include "BaseImporter.h" +#include "FileSystemFilter.h" using namespace Assimp; - // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer BaseImporter::BaseImporter() @@ -67,13 +67,16 @@ BaseImporter::~BaseImporter() // Imports the given file and returns the imported data. aiScene* BaseImporter::ReadFile( const std::string& pFile, IOSystem* pIOHandler) { + // Construct a file system filter to improve our success ratio reading external files + FileSystemFilter filter(pFile,pIOHandler); + // create a scene object to hold the data aiScene* scene = new aiScene(); // dispatch importing try { - InternReadFile( pFile, scene, pIOHandler); + InternReadFile( pFile, scene, &filter); } catch( ImportErrorException* exception) { // extract error description @@ -291,71 +294,38 @@ BatchLoader::BatchLoader(IOSystem* pIO) data = new BatchData(); data->pIOSystem = pIO; + data->pImporter = new Importer(); + data->pImporter->SetIOHandler(data->pIOSystem); } // ------------------------------------------------------------------------------------------------ BatchLoader::~BatchLoader() { // delete all scenes wthat have not been polled by the user - for (std::list::iterator it = data->requests.begin(); - it != data->requests.end(); ++it) - { + for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) { + delete (*it).scene; } + data->pImporter->SetIOHandler(NULL); /* get pointer back into our posession */ delete data->pImporter; delete data; } -// ------------------------------------------------------------------------------------------------ -void BatchLoader::SetBasePath (const std::string& pBase) -{ - data->pathBase = pBase; - - // file name? we just need the directory - std::string::size_type ss,ss2; - if (std::string::npos != (ss = data->pathBase.find_first_of('.'))) - { - if (std::string::npos != (ss2 = data->pathBase.find_last_of("\\/"))) - { - if (ss > ss2) - data->pathBase.erase(ss2,data->pathBase.length()-ss2); - } - else { - data->pathBase = ""; - return; - } - } - - // make sure the directory is terminated properly - char s; - if ((s = *(data->pathBase.end()-1)) != '\\' && s != '/') - data->pathBase.append("\\"); -} // ------------------------------------------------------------------------------------------------ unsigned int BatchLoader::AddLoadRequest (const std::string& file, unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/) { ai_assert(!file.empty()); - - // no threaded implementation for the moment - std::string real; - - // build a full path if this is a relative path and - // we have a new base directory given - if (file.length() > 2 && file[1] != ':' && data->pathBase.length()) { - real = data->pathBase + file; - } - else real = file; // check whether we have this loading request already std::list::iterator it; - for (it = data->requests.begin();it != data->requests.end(); ++it) - { + for (it = data->requests.begin();it != data->requests.end(); ++it) { + // Call IOSystem's path comparison function here - if (data->pIOSystem->ComparePaths((*it).file,real)) - { + if (data->pIOSystem->ComparePaths((*it).file,file)) { + if (map) { if (!((*it).map == *map)) continue; @@ -369,20 +339,19 @@ unsigned int BatchLoader::AddLoadRequest (const std::string& file, } // no, we don't have it. So add it to the queue ... - data->requests.push_back(LoadRequest(real,steps,map,data->next_id)); + data->requests.push_back(LoadRequest(file,steps,map,data->next_id)); return data->next_id++; } // ------------------------------------------------------------------------------------------------ aiScene* BatchLoader::GetImport (unsigned int which) { - for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) - { - if ((*it).id == which && (*it).loaded) - { + for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) { + + if ((*it).id == which && (*it).loaded) { + aiScene* sc = (*it).scene; - if (!(--(*it).refCnt)) - { + if (!(--(*it).refCnt)) { data->requests.erase(it); } return sc; @@ -395,9 +364,7 @@ aiScene* BatchLoader::GetImport (unsigned int which) void BatchLoader::LoadAll() { // no threaded implementation for the moment - for (std::list::iterator it = data->requests.begin(); - it != data->requests.end(); ++it) - { + for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) { // force validation in debug builds unsigned int pp = (*it).flags; #ifdef _DEBUG diff --git a/code/BaseImporter.h b/code/BaseImporter.h index f8359615e..419b1426a 100644 --- a/code/BaseImporter.h +++ b/code/BaseImporter.h @@ -356,17 +356,6 @@ public: ~BatchLoader(); - /** Sets the base path to be used for all subsequent load - * calls. This is the working directory of Assimp. - * - * Every (inplicit) occurence of '.\' will be replaced with it. - * - * @param pBase Base path. This *may* also be the path to - * a file (the directory of the file is taken then, of course) - */ - void SetBasePath (const std::string& pBase); - - /** Add a new file to the list of files to be loaded. * * @param file File to be loaded diff --git a/code/FileSystemFilter.h b/code/FileSystemFilter.h new file mode 100644 index 000000000..5be14d118 --- /dev/null +++ b/code/FileSystemFilter.h @@ -0,0 +1,220 @@ +/* +Open Asset Import Library (ASSIMP) +---------------------------------------------------------------------- + +Copyright (c) 2006-2008, ASSIMP Development Team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the ASSIMP team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the ASSIMP Development Team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file FileSystemFilter.h + * Implements a filter system to filter calls to Exists() and Open() + * in order to improve the sucess rate of file opening ... + */ +#ifndef AI_FILESYSTEMFILTER_H_INC +#define AI_FILESYSTEMFILTER_H_INC + +#include "../include/IOSystem.h" +#include "fast_atof.h" +#include "ParsingUtils.h" +namespace Assimp { + +// --------------------------------------------------------------------------- +/** File system filter + */ +class FileSystemFilter : public IOSystem +{ +public: + /** Constructor. */ + FileSystemFilter(const std::string& file, IOSystem* old) + : wrapped (old) + , src_file (file) + { + ai_assert(NULL != wrapped); + + // Determine base directory + base = src_file; + std::string::size_type ss2; + if (std::string::npos != (ss2 = base.find_last_of("\\/"))) { + base.erase(ss2,base.length()-ss2); + } + else { + base = ""; + return; + } + + // make sure the directory is terminated properly + char s; + + if (base.length() == 0) { + base = ".\\"; + } + else if ((s = *(base.end()-1)) != '\\' && s != '/') + base.append("\\"); + + DefaultLogger::get()->info("Import root directory is \'" + base + "\'"); + } + + /** Destructor. */ + ~FileSystemFilter() + { + // haha + } + + // ------------------------------------------------------------------- + /** Tests for the existence of a file at the given path. */ + bool Exists( const char* pFile) const + { + std::string tmp = pFile; + + // Currently this IOSystem is also used to open THE ONE FILE. + if (tmp != src_file) { + BuildPath(tmp); + Cleanup(tmp); + } + + return wrapped->Exists(tmp); + } + + // ------------------------------------------------------------------- + /** Returns the directory separator. */ + char getOsSeparator() const + { + return wrapped->getOsSeparator(); + } + + // ------------------------------------------------------------------- + /** Open a new file with a given path. */ + IOStream* Open( const char* pFile, const char* pMode = "rb") + { + std::string tmp = pFile; + + // Currently this IOSystem is also used to open THE ONE FILE. + if (tmp != src_file) { + BuildPath(tmp); + Cleanup(tmp); + } + + return wrapped->Open(tmp,pMode); + } + + // ------------------------------------------------------------------- + /** Closes the given file and releases all resources associated with it. */ + void Close( IOStream* pFile) + { + return wrapped->Close(pFile); + } + + // ------------------------------------------------------------------- + /** Compare two paths */ + bool ComparePaths (const char* one, const char* second) const + { + return wrapped->ComparePaths (one,second); + } + +private: + IOSystem* wrapped; + std::string src_file, base; + + // ------------------------------------------------------------------- + /** Build a valid path from a given relative or absolute path. + */ + void BuildPath (std::string& in) const + { + // if we can already access the file, great. + if (in.length() < 3 || wrapped->Exists(in.c_str())) { + return; + } + + // Determine whether this is a relative path. + if (in[1] != ':') { + + // append base path and try + in = base + in; + if (wrapped->Exists(in.c_str())) { + return; + } + } + + // hopefully the underyling file system has another few tricks to access this file ... + } + + // ------------------------------------------------------------------- + /** Cleanup the given path + */ + void Cleanup (std::string& in) const + { + char last = 0; + + // Remove a very common issue when we're parsing file names: spaces at the + // beginning of the path. + std::string::iterator it = in.begin(); + while (IsSpaceOrNewLine( *it ))++it; + if (it != in.begin()) + in.erase(in.begin(),it+1); + + const char sep = getOsSeparator(); + for (it = in.begin(); it != in.end(); ++it) { + + // Both Windows and Linux accept both separators, but to avoid conflicts + // or mixed seperators in a single path we're cleaning up. + if (*it == '/' || (*it) == '\\') { + *it = sep; + + // And we're removing double delimiters, frequent issue with + // incorrectly composited paths ... + if (last == *it) { + it = in.erase(it); + --it; + } + } + else if (*it == '%' && in.end() - it > 2) { + + // Hex sequence, common _artifact_ in URIs + uint32_t tmp; + if( 0xffffffff != (tmp = HexOctetToDecimal(&*it))) { + *it = (char)tmp; + it = in.erase(it+1,it+2); + --it; + } + } + + last = *it; + } + } +}; + +} //!ns Assimp + +#endif //AI_DEFAULTIOSYSTEM_H_INC diff --git a/code/IRRLoader.cpp b/code/IRRLoader.cpp index f0fa8a87a..7d5c85a23 100644 --- a/code/IRRLoader.cpp +++ b/code/IRRLoader.cpp @@ -906,7 +906,7 @@ void IRRImporter::InternReadFile( const std::string& pFile, // Batch loader used to load external models BatchLoader batch(pIOHandler); - batch.SetBasePath(pFile); +// batch.SetBasePath(pFile); cameras.reserve(5); lights.reserve(5); diff --git a/code/Importer.cpp b/code/Importer.cpp index 14549aa09..924c547e0 100644 --- a/code/Importer.cpp +++ b/code/Importer.cpp @@ -559,7 +559,8 @@ void Importer::SetIOHandler( IOSystem* pIOHandler) // If the new handler is zero, allocate a default IO implementation. if (!pIOHandler) { - delete mIOHandler; + // Release pointer in the possession of the caller + // delete mIOHandler; mIOHandler = new DefaultIOSystem(); mIsDefaultHandler = true; } diff --git a/code/LWOAnimation.cpp b/code/LWOAnimation.cpp index f582edd56..f25a92e8c 100644 --- a/code/LWOAnimation.cpp +++ b/code/LWOAnimation.cpp @@ -318,9 +318,9 @@ void AnimResolver::DoInterpolation2(std::vector::const_iterator beg, void AnimResolver::SubsampleAnimTrack(std::vector& out, double time,double sample_delta) { - ai_assert(!out.empty() && sample_delta); + ai_assert(out.empty() && sample_delta); - const double time_start = out.back().mTime; + //const double time_start = out.back().mTime; // for () } @@ -512,8 +512,7 @@ void AnimResolver::ExtractAnimChannel(aiNodeAnim** out, unsigned int flags /*= 0 return; } - // We won't spawn an animation channel if we don't have at least one - // envelope with more than one keyframe defined. + // We won't spawn an animation channel if we don't have at least one envelope with more than one keyframe defined. const bool trans = (trans_x && trans_x->keys.size() > 1 || trans_y && trans_y->keys.size() > 1 || trans_z && trans_z->keys.size() > 1); const bool rotat = (rotat_x && rotat_x->keys.size() > 1 || rotat_y && rotat_y->keys.size() > 1 || rotat_z && rotat_z->keys.size() > 1); const bool scale = (scale_x && scale_x->keys.size() > 1 || scale_y && scale_y->keys.size() > 1 || scale_z && scale_z->keys.size() > 1); diff --git a/code/LWSLoader.cpp b/code/LWSLoader.cpp index f01197531..45b953b1e 100644 --- a/code/LWSLoader.cpp +++ b/code/LWSLoader.cpp @@ -312,10 +312,10 @@ void LWSImporter::BuildGraph(aiNode* nd, LWS::NodeDesc& src, std::vectorerror("LWS: Failed to read external file " + src.path); } @@ -370,7 +370,7 @@ void LWSImporter::BuildGraph(aiNode* nd, LWS::NodeDesc& src, std::vectormName.Set("$Pivot"); + pivot->mName.length = sprintf( pivot->mName.data, "$Pivot_%s",nd->mName.data); pivot->mTransformation = tmp; pivot->mChildren = new aiNode*[pivot->mNumChildren = 1]; @@ -406,11 +405,11 @@ void LWSImporter::BuildGraph(aiNode* nd, LWS::NodeDesc& src, std::vectormParent = nd->mParent; nd->mParent = pivot; - // swap children ad hope the parents wont see a huge difference + // swap children and hope the parents wont see a huge difference pivot->mParent->mChildren[pivot->mParent->mNumChildren-1] = pivot; } else { - nd->mTransformation = tmp * nd->mTransformation; + nd->mTransformation = tmp*nd->mTransformation; } } @@ -484,7 +483,7 @@ void LWSImporter::InternReadFile( const std::string& pFile, aiScene* pScene, // Construct a Batchimporter to read more files recursively BatchLoader batch(pIOHandler); - batch.SetBasePath(pFile); +// batch.SetBasePath(pFile); // Construct an array to receive the flat output graph std::list nodes; diff --git a/include/aiVector3D.h b/include/aiVector3D.h index 751f56e2a..2b04926a2 100644 --- a/include/aiVector3D.h +++ b/include/aiVector3D.h @@ -78,12 +78,12 @@ public: aiVector3D& operator *= (const aiMatrix4x4& mat); // access a single element - inline float operator[](unsigned int i) const; - inline float& operator[](unsigned int i); + float operator[](unsigned int i) const; + float& operator[](unsigned int i); // comparison - inline bool operator== (const aiVector3D& other) const; - inline bool operator!= (const aiVector3D& other) const; + bool operator== (const aiVector3D& other) const; + bool operator!= (const aiVector3D& other) const; public: diff --git a/include/assimp.hpp b/include/assimp.hpp index f737334e3..0bc803fc9 100644 --- a/include/assimp.hpp +++ b/include/assimp.hpp @@ -273,9 +273,11 @@ public: * * The Importer takes ownership of the object and will destroy it * afterwards. The previously assigned handler will be deleted. + * Pass NULL to take again ownership of your IOSystem and reset Assimp + * to use its default implementation. * * @param pIOHandler The IO handler to be used in all file accesses - * of the Importer. NULL resets it to the default handler. + * of the Importer. */ void SetIOHandler( IOSystem* pIOHandler); diff --git a/mkutil/mkrev.bat b/mkutil/mkrev.bat index f65b1cfdd..38ae87a51 100644 --- a/mkutil/mkrev.bat +++ b/mkutil/mkrev.bat @@ -7,7 +7,7 @@ rem It is included by assimp.rc. rem ----------------------------------------------------- rem This is not very elegant, but it works. -rem ./bin shouldn't have any alocal modifications +rem ./bin shouldn't have any local modifications cd ..\bin svnversion > tmpfile.txt diff --git a/workspaces/vc9/assimp.vcproj b/workspaces/vc9/assimp.vcproj index d27ae8ea1..6d3943142 100644 --- a/workspaces/vc9/assimp.vcproj +++ b/workspaces/vc9/assimp.vcproj @@ -1300,126 +1300,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2363,14 +2243,42 @@ RelativePath="..\..\code\DefaultIOSystem.h" > + + + + + + + + + + + + + + @@ -2387,6 +2295,22 @@ RelativePath="..\..\code\Hash.h" > + + + + + + + + @@ -2395,10 +2319,90 @@ RelativePath="..\..\code\qnan.h" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +