Browse Source

Fix Issue #1923: OBJ Exporter can't correctly export vertex colors

The indexMap for vertices now uses a combined vp + vc index
Korbinian Würl 7 years ago
parent
commit
4b7b692e5e
2 changed files with 79 additions and 112 deletions
  1. 22 68
      code/ObjExporter.cpp
  2. 57 44
      code/ObjExporter.h

+ 22 - 68
code/ObjExporter.cpp

@@ -114,14 +114,13 @@ static const std::string MaterialExt = ".mtl";
 ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene, bool noMtl)
 : filename(_filename)
 , pScene(pScene)
-, vp()
 , vn()
 , vt()
-, vc()
-, mVpMap()
+, vp()
+, useVc(false)
 , mVnMap()
 , mVtMap()
-, mVcMap()
+, mVpMap()
 , mMeshes()
 , endl("\n") {
     // make sure that all formatting happens using the standard, C locale and not the user's current locale
@@ -268,27 +267,22 @@ void ObjExporter::WriteGeometryFile(bool noMtl) {
     AddNode(pScene->mRootNode, mBase);
 
     // write vertex positions with colors, if any
-    mVpMap.getVectors( vp );
-    mVcMap.getColors( vc );
-    if ( vc.empty() ) {
+    mVpMap.getKeys( vp );
+    if ( !useVc ) {
         mOutput << "# " << vp.size() << " vertex positions" << endl;
-        for ( const aiVector3D& v : vp ) {
-            mOutput << "v  " << v.x << " " << v.y << " " << v.z << endl;
+        for ( const vertexData& v : vp ) {
+            mOutput << "v  " << v.vp.x << " " << v.vp.y << " " << v.vp.z << endl;
         }
     } else {
         mOutput << "# " << vp.size() << " vertex positions and colors" << endl;
-        size_t colIdx = 0;
-        for ( const aiVector3D& v : vp ) {
-            if ( colIdx < vc.size() ) {
-                mOutput << "v  " << v.x << " " << v.y << " " << v.z << " " << vc[ colIdx ].r << " " << vc[ colIdx ].g << " " << vc[ colIdx ].b << endl;
-            }
-            ++colIdx;
+        for ( const vertexData& v : vp ) {
+            mOutput << "v  " << v.vp.x << " " << v.vp.y << " " << v.vp.z << " " << v.vc.r << " " << v.vc.g << " " << v.vc.b << endl;
         }
     }
     mOutput << endl;
 
     // write uv coordinates
-    mVtMap.getVectors(vt);
+    mVtMap.getKeys(vt);
     mOutput << "# " << vt.size() << " UV coordinates" << endl;
     for(const aiVector3D& v : vt) {
         mOutput << "vt " << v.x << " " << v.y << " " << v.z << endl;
@@ -296,7 +290,7 @@ void ObjExporter::WriteGeometryFile(bool noMtl) {
     mOutput << endl;
 
     // write vertex normals
-    mVnMap.getVectors(vn);
+    mVnMap.getKeys(vn);
     mOutput << "# " << vn.size() << " vertex normals" << endl;
     for(const aiVector3D& v : vn) {
         mOutput << "vn " << v.x << " " << v.y << " " << v.z << endl;
@@ -337,54 +331,15 @@ void ObjExporter::WriteGeometryFile(bool noMtl) {
     }
 }
 
-// ------------------------------------------------------------------------------------------------
-int ObjExporter::vecIndexMap::getIndex(const aiVector3D& vec) {
-    vecIndexMap::dataType::iterator vertIt = vecMap.find(vec);
-    // vertex already exists, so reference it
-    if(vertIt != vecMap.end()){
-        return vertIt->second;
-    }
-    vecMap[vec] = mNextIndex;
-    int ret = mNextIndex;
-    mNextIndex++;
-    return ret;
-}
-
-// ------------------------------------------------------------------------------------------------
-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;
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-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) {
     mMeshes.push_back(MeshInstance() );
     MeshInstance& mesh = mMeshes.back();
 
+    if ( nullptr != m->mColors[ 0 ] ) {
+        useVc = true;
+    }
+
     mesh.name = std::string( name.data, name.length );
     mesh.matname = GetMaterialName(m->mMaterialIndex);
 
@@ -410,7 +365,13 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4
             const unsigned int idx = f.mIndices[a];
 
             aiVector3D vert = mat * m->mVertices[idx];
-            face.indices[a].vp = mVpMap.getIndex(vert);
+
+            if ( nullptr != m->mColors[ 0 ] ) {
+                aiColor4D col4 = m->mColors[ 0 ][ idx ];
+                face.indices[a].vp = mVpMap.getIndex({vert, aiColor3D(col4.r, col4.g, col4.b)});
+            } else {
+                face.indices[a].vp = mVpMap.getIndex({vert, aiColor3D(0,0,0)});
+            }
 
             if (m->mNormals) {
                 aiVector3D norm = aiMatrix3x3(mat) * m->mNormals[idx];
@@ -419,13 +380,6 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4
                 face.indices[a].vn = 0;
             }
 
-            if ( nullptr != m->mColors[ 0 ] ) {
-                aiColor4D col4 = m->mColors[ 0 ][ idx ];
-                face.indices[ a ].vc = mVcMap.getIndex( col4 );
-            } else {
-                face.indices[ a ].vc = 0;
-            }
-
             if ( m->mTextureCoords[ 0 ] ) {
                 face.indices[a].vt = mVtMap.getIndex(m->mTextureCoords[0][idx]);
             } else {

+ 57 - 44
code/ObjExporter.h

@@ -77,13 +77,12 @@ private:
         FaceVertex()
         : vp()
         , vn()
-        , vt()
-        , vc() {
+        , vt() {
             // empty
         }
 
         // one-based, 0 means: 'does not exist'
-        unsigned int vp, vn, vt, vc;
+        unsigned int vp, vn, vt;
     };
 
     struct Face {
@@ -106,66 +105,80 @@ private:
 private:
     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 {
-            if(a.x < b.x) return true;
-            if(a.x > b.x) return false;
-            if(a.y < b.y) return true;
-            if(a.y > b.y) return false;
-            if(a.z < b.z) return true;
+    struct vertexData {
+        aiVector3D vp;
+        aiColor3D vc; // OBJ does not support 4D color
+    };
+
+    std::vector<aiVector3D> vn, vt;
+    std::vector<aiColor4D> vc;
+    std::vector<vertexData> vp;
+    bool useVc;
+
+    struct vertexDataCompare {
+        bool operator() ( const vertexData& a, const vertexData& b ) const {
+            // position
+            if (a.vp.x < b.vp.x) return true;
+            if (a.vp.x > b.vp.x) return false;
+            if (a.vp.y < b.vp.y) return true;
+            if (a.vp.y > b.vp.y) return false;
+            if (a.vp.z < b.vp.z) return true;
+            if (a.vp.z > b.vp.z) return false;
+
+            // color
+            if (a.vc.r < b.vc.r) return true;
+            if (a.vc.r > b.vc.r) return false;
+            if (a.vc.g < b.vc.g) return true;
+            if (a.vc.g > b.vc.g) return false;
+            if (a.vc.b < b.vc.b) return true;
+            if (a.vc.b > b.vc.b) return false;
             return false;
         }
     };
 
-    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;
+    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; 
+            if(a.y > b.y) return false; 
+            if(a.z < b.z) return true; 
             return false;
         }
     };
 
-    class vecIndexMap {
+    template <class T, class Compare = std::less<T>>
+    class indexMap {
         int mNextIndex;
-        typedef std::map<aiVector3D, int, aiVectorCompare> dataType;
+        typedef std::map<T, int, Compare> dataType;
         dataType vecMap;
     
     public:
-        vecIndexMap()
+        indexMap()
         : 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 );
+        int getIndex(const T& key) {
+            typename dataType::iterator vertIt = vecMap.find(key);
+            // vertex already exists, so reference it
+            if(vertIt != vecMap.end()){
+                return vertIt->second;
+            }
+            return vecMap[key] = mNextIndex++;
+        };
+
+        void getKeys( std::vector<T>& keys ) {
+            keys.resize(vecMap.size());
+            for(typename dataType::iterator it = vecMap.begin(); it != vecMap.end(); ++it){
+                keys[it->second-1] = it->first;
+            }
+        };
     };
 
-    vecIndexMap mVpMap, mVnMap, mVtMap;
-    colIndexMap mVcMap;
+    indexMap<aiVector3D, aiVectorCompare> mVnMap, mVtMap;
+    indexMap<vertexData, vertexDataCompare> mVpMap;
     std::vector<MeshInstance> mMeshes;
 
     // this endl() doesn't flush() the stream