Przeglądaj źródła

Obj-Importer: fix https://github.com/assimp/assimp/issues/641

Kim Kulling 10 lat temu
rodzic
commit
e4510c26ba

+ 15 - 9
code/ObjFileImporter.cpp

@@ -133,15 +133,16 @@ void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene,
     TextFileToBuffer(file.get(),m_Buffer);
 
     // Get the model name
-    std::string  strModelName;
+    std::string  modelName, folderName;
     std::string::size_type pos = pFile.find_last_of( "\\/" );
-    if ( pos != std::string::npos )
-    {
-        strModelName = pFile.substr(pos+1, pFile.size() - pos - 1);
-    }
-    else
-    {
-        strModelName = pFile;
+    if ( pos != std::string::npos ) {
+        modelName = pFile.substr(pos+1, pFile.size() - pos - 1);
+        folderName = pFile.substr( 0, pos );
+        if ( folderName.empty() ) {
+            pIOHandler->PushDirectory( folderName );
+        }
+    } else {
+        modelName = pFile;
     }
 
     // process all '\'
@@ -161,13 +162,18 @@ void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene,
     }
 
     // parse the file into a temporary representation
-    ObjFileParser parser(m_Buffer, strModelName, pIOHandler);
+    ObjFileParser parser(m_Buffer, modelName, pIOHandler);
 
     // And create the proper return structures out of it
     CreateDataFromImport(parser.GetModel(), pScene);
 
     // Clean up allocated storage for the next import
     m_Buffer.clear();
+
+    // Pop directory stack
+    if ( pIOHandler->StackSize() > 0 ) {
+        pIOHandler->PopDirectory();
+    }
 }
 
 // ------------------------------------------------------------------------------------------------

+ 33 - 27
code/ObjFileParser.cpp

@@ -61,21 +61,21 @@ const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME;
 
 // -------------------------------------------------------------------
 //  Constructor with loaded data and directories.
-ObjFileParser::ObjFileParser(std::vector<char> &Data,const std::string &strModelName, IOSystem *io ) :
-    m_DataIt(Data.begin()),
-    m_DataItEnd(Data.end()),
+ObjFileParser::ObjFileParser(std::vector<char> &data,const std::string &modelName, IOSystem *io ) :
+    m_DataIt(data.begin()),
+    m_DataItEnd(data.end()),
     m_pModel(NULL),
     m_uiLine(0),
     m_pIO( io )
 {
-    std::fill_n(m_buffer,BUFFERSIZE,0);
+    std::fill_n(m_buffer,Buffersize,0);
 
     // Create the model instance to store all the data
     m_pModel = new ObjFile::Model();
-    m_pModel->m_ModelName = strModelName;
+    m_pModel->m_ModelName = modelName;
 
     // create default material and store it
-    m_pModel->m_pDefaultMaterial = new ObjFile::Material();
+    m_pModel->m_pDefaultMaterial = new ObjFile::Material;
     m_pModel->m_pDefaultMaterial->MaterialName.Set( DEFAULT_MATERIAL );
     m_pModel->m_MaterialLib.push_back( DEFAULT_MATERIAL );
     m_pModel->m_MaterialMap[ DEFAULT_MATERIAL ] = m_pModel->m_pDefaultMaterial;
@@ -248,20 +248,20 @@ void ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
     }
     float x, y, z;
     if( 2 == numComponents ) {
-        copyNextWord( m_buffer, BUFFERSIZE );
+        copyNextWord( m_buffer, Buffersize );
         x = ( float ) fast_atof( m_buffer );
 
-        copyNextWord( m_buffer, BUFFERSIZE );
+        copyNextWord( m_buffer, Buffersize );
         y = ( float ) fast_atof( m_buffer );
         z = 0.0;
     } else if( 3 == numComponents ) {
-        copyNextWord( m_buffer, BUFFERSIZE );
+        copyNextWord( m_buffer, Buffersize );
         x = ( float ) fast_atof( m_buffer );
 
-        copyNextWord( m_buffer, BUFFERSIZE );
+        copyNextWord( m_buffer, Buffersize );
         y = ( float ) fast_atof( m_buffer );
 
-        copyNextWord( m_buffer, BUFFERSIZE );
+        copyNextWord( m_buffer, Buffersize );
         z = ( float ) fast_atof( m_buffer );
     } else {
         throw DeadlyImportError( "OBJ: Invalid number of components" );
@@ -274,13 +274,13 @@ void ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
 //  Get values for a new 3D vector instance
 void ObjFileParser::getVector3(std::vector<aiVector3D> &point3d_array) {
     float x, y, z;
-    copyNextWord(m_buffer, BUFFERSIZE);
+    copyNextWord(m_buffer, Buffersize);
     x = (float) fast_atof(m_buffer);
 
-    copyNextWord(m_buffer, BUFFERSIZE);
+    copyNextWord(m_buffer, Buffersize);
     y = (float) fast_atof(m_buffer);
 
-    copyNextWord( m_buffer, BUFFERSIZE );
+    copyNextWord( m_buffer, Buffersize );
     z = ( float ) fast_atof( m_buffer );
 
     point3d_array.push_back( aiVector3D( x, y, z ) );
@@ -291,10 +291,10 @@ void ObjFileParser::getVector3(std::vector<aiVector3D> &point3d_array) {
 //  Get values for a new 2D vector instance
 void ObjFileParser::getVector2( std::vector<aiVector2D> &point2d_array ) {
     float x, y;
-    copyNextWord(m_buffer, BUFFERSIZE);
+    copyNextWord(m_buffer, Buffersize);
     x = (float) fast_atof(m_buffer);
 
-    copyNextWord(m_buffer, BUFFERSIZE);
+    copyNextWord(m_buffer, Buffersize);
     y = (float) fast_atof(m_buffer);
 
     point2d_array.push_back(aiVector2D(x, y));
@@ -306,12 +306,12 @@ void ObjFileParser::getVector2( std::vector<aiVector2D> &point2d_array ) {
 //  Get values for a new face instance
 void ObjFileParser::getFace(aiPrimitiveType type)
 {
-    copyNextLine(m_buffer, BUFFERSIZE);
+    copyNextLine(m_buffer, Buffersize);
     if (m_DataIt == m_DataItEnd)
         return;
 
     char *pPtr = m_buffer;
-    char *pEnd = &pPtr[BUFFERSIZE];
+    char *pEnd = &pPtr[Buffersize];
     pPtr = getNextToken<char*>(pPtr, pEnd);
     if (pPtr == pEnd || *pPtr == '\0')
         return;
@@ -468,8 +468,9 @@ void ObjFileParser::getMaterialDesc()
 
     // Get next data for material data
     m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
-    if (m_DataIt == m_DataItEnd)
+    if (m_DataIt == m_DataItEnd) {
         return;
+    }
 
     char *pStart = &(*m_DataIt);
     while( m_DataIt != m_DataItEnd && !IsLineEnd( *m_DataIt ) ) {
@@ -483,14 +484,11 @@ void ObjFileParser::getMaterialDesc()
 
     // Search for material
     std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( strName );
-    if ( it == m_pModel->m_MaterialMap.end() )
-    {
+    if ( it == m_pModel->m_MaterialMap.end() ) {
         // Not found, use default material
         m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial;
         DefaultLogger::get()->error("OBJ: failed to locate material " + strName + ", skipping");
-    }
-    else
-    {
+    } else {
         // Found, using detected material
         m_pModel->m_pCurrentMaterial = (*it).second;
         if ( needsNewMesh( strName ))
@@ -539,18 +537,26 @@ void ObjFileParser::getMaterialLib()
 
     // Check for existence
     const std::string strMatName(pStart, &(*m_DataIt));
-    IOStream *pFile = m_pIO->Open(strMatName);
+    std::string absName;
+    if ( m_pIO->StackSize() > 0 ) {
+        const std::string &path = m_pIO->CurrentDirectory();
+        absName = path + strMatName;
+    } else {
+        absName = strMatName;
+    }
+    //IOStream *pFile = m_pIO->Open( strMatName );
+    IOStream *pFile = m_pIO->Open( absName );
 
     if (!pFile )
     {
-        DefaultLogger::get()->error("OBJ: Unable to locate material file " + strMatName);
+        DefaultLogger::get()->error("OBJ: Unable to locate material file " + strMatName );
         m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
         return;
     }
 
     // Import material library data from file
     std::vector<char> buffer;
-    BaseImporter::TextFileToBuffer(pFile,buffer);
+    BaseImporter::TextFileToBuffer( pFile, buffer );
     m_pIO->Close( pFile );
 
     // Importing the material library

+ 4 - 4
code/ObjFileParser.h

@@ -62,10 +62,9 @@ class IOSystem;
 
 /// \class  ObjFileParser
 /// \brief  Parser for a obj waveform file
-class ObjFileParser
-{
+class ObjFileParser {
 public:
-    static const size_t BUFFERSIZE = 4096;
+    static const size_t Buffersize = 4096;
     typedef std::vector<char> DataArray;
     typedef std::vector<char>::iterator DataArrayIt;
     typedef std::vector<char>::const_iterator ConstDataArrayIt;
@@ -137,9 +136,10 @@ private:
     //! Current line (for debugging)
     unsigned int m_uiLine;
     //! Helper buffer
-    char m_buffer[BUFFERSIZE];
+    char m_buffer[Buffersize];
     /// Pointer to IO system instance.
     IOSystem *m_pIO;
+    /// Path to the current model
 };
 
 }   // Namespace Assimp

+ 50 - 1
include/assimp/IOSystem.hpp

@@ -53,6 +53,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endif
 
 #include "types.h"
+
+#include <vector>
+
 namespace Assimp    {
 class IOStream;
 
@@ -170,10 +173,19 @@ public:
      */
     inline bool ComparePaths (const std::string& one,
         const std::string& second) const;
+
+    virtual bool PushDirectory( const std::string &path );
+    virtual const std::string &CurrentDirectory() const;
+    virtual size_t StackSize() const;
+    virtual bool PopDirectory();
+
+private:
+    std::vector<std::string> m_pathStack;
 };
 
 // ----------------------------------------------------------------------------
-AI_FORCE_INLINE IOSystem::IOSystem()
+AI_FORCE_INLINE IOSystem::IOSystem() :
+    m_pathStack()
 {
     // empty
 }
@@ -220,6 +232,43 @@ inline bool IOSystem::ComparePaths (const std::string& one,
 }
 
 // ----------------------------------------------------------------------------
+inline bool IOSystem::PushDirectory( const std::string &path ) {
+    if ( path.empty() ) {
+        return false;
+    }
+
+    m_pathStack.push_back( path );
+
+    return true;
+}
+
+// ----------------------------------------------------------------------------
+inline const std::string &IOSystem::CurrentDirectory() const {
+    if ( m_pathStack.empty() ) {
+        static const std::string Dummy("");
+        return Dummy;
+    }
+    return m_pathStack[ m_pathStack.size()-1 ];
+}
+
+// ----------------------------------------------------------------------------
+inline size_t IOSystem::StackSize() const {
+    return m_pathStack.size();
+}
+
+// ----------------------------------------------------------------------------
+inline bool IOSystem::PopDirectory() {
+    if ( m_pathStack.empty() ) {
+        return false;
+    }
+
+    m_pathStack.pop_back();
+
+    return true;
+}
+
+// ----------------------------------------------------------------------------
+
 } //!ns Assimp
 
 #endif //AI_IOSYSTEM_H_INC

+ 1 - 0
test/CMakeLists.txt

@@ -24,6 +24,7 @@ SET( TEST_SRCS
   unit/utGenNormals.cpp
   unit/utImporter.cpp
   unit/utImproveCacheLocality.cpp
+  unit/utIOSystem.cpp
   unit/utJoinVertices.cpp
   unit/utLimitBoneWeights.cpp
   unit/utMaterialSystem.cpp

+ 94 - 0
test/unit/utIOSystem.cpp

@@ -0,0 +1,94 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2014, assimp 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 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.
+---------------------------------------------------------------------------
+*/
+#include "UnitTestPCH.h"
+
+#include <assimp/IOSystem.hpp>
+
+using namespace std;
+using namespace Assimp;
+
+static const string Sep = "/";
+class TestIOSystem : public IOSystem {
+public:
+    TestIOSystem() : IOSystem() {}
+    virtual ~TestIOSystem() {}
+    virtual bool Exists( const char* ) const {
+        return true;
+    }
+    virtual char getOsSeparator() const {
+        return Sep[ 0 ];
+    }
+
+    virtual IOStream* Open(const char* pFile, const char* pMode = "rb") {
+        return NULL;
+    }
+
+    virtual void Close( IOStream* pFile) {
+        // empty
+    }
+};
+
+class IOSystemTest : public ::testing::Test {
+public:
+    virtual void SetUp() { pImp = new TestIOSystem(); }
+    virtual void TearDown() { delete pImp; }
+
+protected:
+    TestIOSystem* pImp;
+};
+
+/*
+virtual bool PushDirectory( const std::string &path );
+virtual const std::string &CurrentDirectory() const;
+virtual bool PopDirectory();
+*/
+
+TEST_F( IOSystemTest, accessDirectoryStackTest ) {
+    EXPECT_FALSE( pImp->PopDirectory() );
+    EXPECT_EQ( 0, pImp->StackSize() );
+    EXPECT_FALSE( pImp->PushDirectory( "" ) );
+    std::string path = "test/";
+    EXPECT_TRUE( pImp->PushDirectory( path ) );
+    EXPECT_EQ( 1, pImp->StackSize() );
+    EXPECT_EQ( path, pImp->CurrentDirectory() );
+    EXPECT_TRUE( pImp->PopDirectory() );
+    EXPECT_EQ( 0, pImp->StackSize() );
+}