Parcourir la source

closes https://github.com/assimp/assimp/issues/809: add vertex color
export support.

Kim Kulling il y a 8 ans
Parent
commit
692fb216f7
4 fichiers modifiés avec 135 ajouts et 45 suppressions
  1. 57 24
      code/ObjExporter.cpp
  2. 48 21
      code/ObjExporter.h
  3. 6 0
      test/unit/AbstractImportExportBase.h
  4. 24 0
      test/unit/utObjImportExport.cpp

+ 57 - 24
code/ObjExporter.cpp

@@ -38,8 +38,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
-
-
 #ifndef ASSIMP_BUILD_NO_EXPORT
 #ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER
 
@@ -53,9 +51,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/scene.h>
 #include <memory>
 
-
 using namespace Assimp;
-namespace Assimp    {
+
+namespace Assimp {
 
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to Wavefront OBJ. Prototyped and registered in Exporter.cpp
@@ -90,7 +88,10 @@ ObjExporter :: ObjExporter(const char* _filename, const aiScene* pScene)
 : filename(_filename)
 , pScene(pScene)
 , endl("\n")
-{
+, vp()
+, vn()
+, vt()
+, vc() {
     // make sure that all formatting happens using the standard, C locale and not the user's current locale
     const std::locale& l = std::locale("C");
     mOutput.imbue(l);
@@ -170,7 +171,6 @@ void ObjExporter::WriteMaterialFile()
             mOutputMat << "Tf " << c.r << " " << c.g << " " << c.b << endl;
         }
         
-        
         ai_real o;
         if(AI_SUCCESS == mat->Get(AI_MATKEY_OPACITY,o)) {
             mOutputMat << "d " << o << endl;
@@ -213,8 +213,7 @@ void ObjExporter::WriteMaterialFile()
 }
 
 // ------------------------------------------------------------------------------------------------
-void ObjExporter :: WriteGeometryFile()
-{
+void ObjExporter::WriteGeometryFile() {
     WriteHeader(mOutput);
     mOutput << "mtllib "  << GetMaterialLibName() << endl << endl;
 
@@ -222,11 +221,21 @@ void ObjExporter :: WriteGeometryFile()
     aiMatrix4x4 mBase;
     AddNode(pScene->mRootNode, mBase);
 
-    // write vertex positions
-    vpMap.getVectors(vp);
-    mOutput << "# " << vp.size() << " vertex positions" << endl;
-    for(const aiVector3D& v : vp) {
-        mOutput << "v  " << v.x << " " << v.y << " " << v.z << endl;
+    // write vertex positions with colors, if any
+    vpMap.getVectors( vp );
+    vcMap.getColors( vc );
+    if ( vc.empty() ) {
+        mOutput << "# " << vp.size() << " vertex positions" << endl;
+        for ( const aiVector3D& v : vp ) {
+            mOutput << "v  " << v.x << " " << v.y << " " << v.z << endl;
+        }
+    } else {
+        mOutput << "# " << vp.size() << " vertex positions and colors" << endl;
+        size_t colIdx = 0;
+        for ( const aiVector3D& v : vp ) {
+            mOutput << "v  " << v.x << " " << v.y << " " << v.z << " " << vc[ colIdx ].r << " " << vc[ colIdx ].g << " " << vc[ colIdx ].b << endl;
+            colIdx++;
+        }
     }
     mOutput << endl;
 
@@ -279,8 +288,7 @@ void ObjExporter :: WriteGeometryFile()
 }
 
 // ------------------------------------------------------------------------------------------------
-int ObjExporter::vecIndexMap::getIndex(const aiVector3D& vec)
-{
+int ObjExporter::vecIndexMap::getIndex(const aiVector3D& vec) {
     vecIndexMap::dataType::iterator vertIt = vecMap.find(vec);
     // vertex already exists, so reference it
     if(vertIt != vecMap.end()){
@@ -293,8 +301,7 @@ int ObjExporter::vecIndexMap::getIndex(const aiVector3D& vec)
 }
 
 // ------------------------------------------------------------------------------------------------
-void ObjExporter::vecIndexMap::getVectors( std::vector<aiVector3D>& vecs )
-{
+void ObjExporter::vecIndexMap::getVectors( std::vector<aiVector3D>& vecs ) {
     vecs.resize(vecMap.size());
     for(vecIndexMap::dataType::iterator it = vecMap.begin(); it != vecMap.end(); ++it){
         vecs[it->second-1] = it->first;
@@ -302,8 +309,29 @@ void ObjExporter::vecIndexMap::getVectors( std::vector<aiVector3D>& vecs )
 }
 
 // ------------------------------------------------------------------------------------------------
-void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat)
-{
+int ObjExporter::colIndexMap::getIndex( const aiColor4D& col ) {
+    colIndexMap::dataType::iterator vertIt = colMap.find( col );
+    // vertex already exists, so reference it
+    if ( vertIt != colMap.end() ) {
+        return vertIt->second;
+    }
+    colMap[ col ] = mNextIndex;
+    int ret = mNextIndex;
+    mNextIndex++;
+    
+    return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+void ObjExporter::colIndexMap::getColors( std::vector<aiColor4D> &colors ) {
+    colors.resize( colMap.size() );
+    for ( colIndexMap::dataType::iterator it = colMap.begin(); it != colMap.end(); ++it ) {
+        colors[ it->second - 1 ] = it->first;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat) {
     meshes.push_back(MeshInstance());
     MeshInstance& mesh = meshes.back();
 
@@ -337,15 +365,20 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4
             if (m->mNormals) {
                 aiVector3D norm = aiMatrix3x3(mat) * m->mNormals[idx];
                 face.indices[a].vn = vnMap.getIndex(norm);
-            }
-            else{
+            } else {
                 face.indices[a].vn = 0;
             }
 
-            if (m->mTextureCoords[0]) {
-                face.indices[a].vt = vtMap.getIndex(m->mTextureCoords[0][idx]);
+            if ( nullptr != m->mColors[ 0 ] ) {
+                aiColor4D col4 = m->mColors[ 0 ][ idx ];
+                face.indices[ a ].vc = vcMap.getIndex( col4 );
+            } else {
+                face.indices[ a ].vc = 0;
             }
-            else{
+
+            if ( m->mTextureCoords[ 0 ] ) {
+                face.indices[a].vt = vtMap.getIndex(m->mTextureCoords[0][idx]);
+            } else {
                 face.indices[a].vt = 0;
             }
         }

+ 48 - 21
code/ObjExporter.h

@@ -59,34 +59,33 @@ namespace Assimp
 // ------------------------------------------------------------------------------------------------
 /** Helper class to export a given scene to an OBJ file. */
 // ------------------------------------------------------------------------------------------------
-class ObjExporter
-{
+class ObjExporter {
 public:
     /// Constructor for a specific scene to export
     ObjExporter(const char* filename, const aiScene* pScene);
 
 public:
-
     std::string GetMaterialLibName();
     std::string GetMaterialLibFileName();
 
 public:
-
-    /// public stringstreams to write all output into
+    /// public string-streams to write all output into
     std::ostringstream mOutput, mOutputMat;
 
 private:
 
     // intermediate data structures
-    struct FaceVertex
-    {
+    struct FaceVertex {
         FaceVertex()
-            : vp(),vn(),vt()
-        {
+        : vp()
+        , vn()
+        , vt()
+        , vc() {
+            // empty
         }
 
         // one-based, 0 means: 'does not exist'
-        unsigned int vp,vn,vt;
+        unsigned int vp, vn, vt, vc;
     };
 
     struct Face {
@@ -111,17 +110,14 @@ private:
     void AddNode(const aiNode* nd, const aiMatrix4x4& mParent);
 
 private:
-
     const std::string filename;
     const aiScene* const pScene;
 
     std::vector<aiVector3D> vp, vn, vt;
+    std::vector<aiColor4D> vc;
 
-
-    struct aiVectorCompare
-    {
-        bool operator() (const aiVector3D& a, const aiVector3D& b) const
-        {
+    struct aiVectorCompare {
+        bool operator() (const aiVector3D& a, const aiVector3D& b) const {
             if(a.x < b.x) return true;
             if(a.x > b.x) return false;
             if(a.y < b.y) return true;
@@ -131,21 +127,52 @@ private:
         }
     };
 
-    class vecIndexMap
-    {
+    struct aiColor4Compare {
+        bool operator() ( const aiColor4D& a, const aiColor4D& b ) const {
+            if ( a.r < b.r ) return true;
+            if ( a.r > b.r ) return false;
+            if ( a.g < b.g ) return true;
+            if ( a.g > b.g ) return false;
+            if ( a.b < b.b ) return true;
+            if ( a.b > b.b ) return false;
+            if ( a.a < b.a ) return true;
+            if ( a.a > b.a ) return false;
+            return false;
+        }
+    };
+
+    class vecIndexMap {
         int mNextIndex;
         typedef std::map<aiVector3D, int, aiVectorCompare> dataType;
         dataType vecMap;
+    
     public:
-
-        vecIndexMap():mNextIndex(1)
-        {}
+        vecIndexMap()
+        : mNextIndex(1) {
+            // empty
+        }
 
         int getIndex(const aiVector3D& vec);
         void getVectors( std::vector<aiVector3D>& vecs );
     };
 
+    class colIndexMap {
+        int mNextIndex;
+        typedef std::map<aiColor4D, int, aiColor4Compare> dataType;
+        dataType colMap;
+
+    public:
+        colIndexMap()
+        : mNextIndex( 1 ) {
+            // empty
+        }
+
+        int getIndex( const aiColor4D& col );
+        void getColors( std::vector<aiColor4D> &colors );
+    };
+
     vecIndexMap vpMap, vnMap, vtMap;
+    colIndexMap vcMap;
     std::vector<MeshInstance> meshes;
 
     // this endl() doesn't flush() the stream

+ 6 - 0
test/unit/AbstractImportExportBase.h

@@ -46,4 +46,10 @@ class AbstractImportExportBase : public ::testing::Test {
 public:
     virtual ~AbstractImportExportBase();
     virtual bool importerTest() = 0;
+    virtual bool exporterTest();
 };
+
+inline 
+bool AbstractImportExportBase::exporterTest() {
+    return true;
+}

+ 24 - 0
test/unit/utObjImportExport.cpp

@@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "AbstractImportExportBase.h"
 
 #include <assimp/Importer.hpp>
+#include <assimp/Exporter.hpp>
 #include <assimp/scene.h>
 
 using namespace Assimp;
@@ -194,6 +195,16 @@ protected:
         return nullptr != scene;
     }
 
+    virtual bool exporterTest() {
+        Assimp::Importer importer;
+        Assimp::Exporter exporter;
+        const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", 0 );
+        EXPECT_NE( nullptr, scene );
+        EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "obj", ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj" ) );
+        
+        return true;
+    }
+
 protected:
     Assimp::Importer *m_im;
     aiScene *m_expectedScene;
@@ -203,6 +214,10 @@ TEST_F( utObjImportExport, importObjFromFileTest ) {
     EXPECT_TRUE( importerTest() );
 }
 
+TEST_F( utObjImportExport, exportObjFromFileTest ) {
+    EXPECT_TRUE( exporterTest() );
+}
+
 TEST_F( utObjImportExport, obj_import_test ) {
     const aiScene *scene = m_im->ReadFileFromMemory( (void*) ObjModel.c_str(), ObjModel.size(), 0 );
     aiScene *expected = createScene();
@@ -219,3 +234,12 @@ TEST_F( utObjImportExport, issue1111_no_mat_name_Test ) {
     const aiScene *scene = m_im->ReadFileFromMemory( ( void* ) ObjModel_Issue1111.c_str(), ObjModel_Issue1111.size(), 0 );
     EXPECT_NE( nullptr, scene );
 }
+
+TEST_F( utObjImportExport, issue809_vertex_color_Test ) {
+    Assimp::Importer importer;
+    const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/cube_with_vertexcolors.obj", 0 );
+    EXPECT_NE( nullptr, scene );
+
+    Assimp::Exporter exporter;
+    EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "obj", ASSIMP_TEST_MODELS_DIR "/OBJ/test.obj" ) );
+}