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

The indexMap for vertices now uses a combined vp + vc index
pull/1931/head
Korbinian Würl 2018-04-30 16:27:34 +02:00
parent 9cfdb8d365
commit 4b7b692e5e
2 changed files with 78 additions and 111 deletions

View File

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

View File

@ -77,13 +77,12 @@ private:
FaceVertex() FaceVertex()
: vp() : vp()
, vn() , vn()
, vt() , vt() {
, vc() {
// empty // empty
} }
// one-based, 0 means: 'does not exist' // one-based, 0 means: 'does not exist'
unsigned int vp, vn, vt, vc; unsigned int vp, vn, vt;
}; };
struct Face { struct Face {
@ -106,8 +105,37 @@ private:
private: private:
std::string filename; std::string filename;
const aiScene* const pScene; const aiScene* const pScene;
std::vector<aiVector3D> vp, vn, vt;
struct vertexData {
aiVector3D vp;
aiColor3D vc; // OBJ does not support 4D color
};
std::vector<aiVector3D> vn, vt;
std::vector<aiColor4D> vc; 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 aiVectorCompare { struct aiVectorCompare {
bool operator() (const aiVector3D& a, const aiVector3D& b) const { bool operator() (const aiVector3D& a, const aiVector3D& b) const {
@ -120,52 +148,37 @@ private:
} }
}; };
struct aiColor4Compare { template <class T, class Compare = std::less<T>>
bool operator() ( const aiColor4D& a, const aiColor4D& b ) const { class indexMap {
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; int mNextIndex;
typedef std::map<aiVector3D, int, aiVectorCompare> dataType; typedef std::map<T, int, Compare> dataType;
dataType vecMap; dataType vecMap;
public: public:
vecIndexMap() indexMap()
: mNextIndex(1) { : mNextIndex(1) {
// empty // empty
} }
int getIndex(const aiVector3D& vec); int getIndex(const T& key) {
void getVectors( std::vector<aiVector3D>& vecs ); 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;
}
};
}; };
class colIndexMap { indexMap<aiVector3D, aiVectorCompare> mVnMap, mVtMap;
int mNextIndex; indexMap<vertexData, vertexDataCompare> mVpMap;
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 mVpMap, mVnMap, mVtMap;
colIndexMap mVcMap;
std::vector<MeshInstance> mMeshes; std::vector<MeshInstance> mMeshes;
// this endl() doesn't flush() the stream // this endl() doesn't flush() the stream