From 4b7b692e5e72f70ae001a274ccd78a4ffde35d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Korbinian=20W=C3=BCrl?= Date: Mon, 30 Apr 2018 16:27:34 +0200 Subject: [PATCH] Fix Issue #1923: OBJ Exporter can't correctly export vertex colors The indexMap for vertices now uses a combined vp + vc index --- code/ObjExporter.cpp | 90 ++++++++++------------------------------ code/ObjExporter.h | 99 +++++++++++++++++++++++++------------------- 2 files changed, 78 insertions(+), 111 deletions(-) diff --git a/code/ObjExporter.cpp b/code/ObjExporter.cpp index 6cd69f402..1542efebf 100644 --- a/code/ObjExporter.cpp +++ b/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& 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 &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 { diff --git a/code/ObjExporter.h b/code/ObjExporter.h index 7920598d0..bd745b593 100644 --- a/code/ObjExporter.h +++ b/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 vp, vn, vt; + + struct vertexData { + aiVector3D vp; + aiColor3D vc; // OBJ does not support 4D color + }; + + std::vector vn, vt; std::vector vc; + std::vector vp; + bool useVc; - 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 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 indexMap { int mNextIndex; - typedef std::map dataType; + typedef std::map dataType; dataType vecMap; public: - vecIndexMap() + indexMap() : mNextIndex(1) { // empty } - int getIndex(const aiVector3D& vec); - void getVectors( std::vector& vecs ); + 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& keys ) { + keys.resize(vecMap.size()); + for(typename dataType::iterator it = vecMap.begin(); it != vecMap.end(); ++it){ + keys[it->second-1] = it->first; + } + }; }; - class colIndexMap { - int mNextIndex; - typedef std::map dataType; - dataType colMap; - - public: - colIndexMap() - : mNextIndex( 1 ) { - // empty - } - - int getIndex( const aiColor4D& col ); - void getColors( std::vector &colors ); - }; - - vecIndexMap mVpMap, mVnMap, mVtMap; - colIndexMap mVcMap; + indexMap mVnMap, mVtMap; + indexMap mVpMap; std::vector mMeshes; // this endl() doesn't flush() the stream