From 1533aec21c4d0aefba4810d2dc9fddf4990ab2a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Korbinian=20W=C3=BCrl?= Date: Fri, 27 Apr 2018 22:11:36 +0200 Subject: [PATCH 01/13] Fix ply exporter to be conformant to spec respecting vertex colors. Vertex colors should be defined as red, green, blue, alpha instead of r, g, b, a and should have the type 'uchar' instead of 'float' --- code/PlyExporter.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/code/PlyExporter.cpp b/code/PlyExporter.cpp index 2d528c96c..31c64a4d1 100644 --- a/code/PlyExporter.cpp +++ b/code/PlyExporter.cpp @@ -194,16 +194,16 @@ PlyExporter::PlyExporter(const char* _filename, const aiScene* pScene, bool bina for (unsigned int n = PLY_EXPORT_HAS_COLORS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_COLOR_SETS; n <<= 1, ++c) { if (!c) { - mOutput << "property " << typeName << " r" << endl; - mOutput << "property " << typeName << " g" << endl; - mOutput << "property " << typeName << " b" << endl; - mOutput << "property " << typeName << " a" << endl; + mOutput << "property " << "uchar" << " red" << endl; + mOutput << "property " << "uchar" << " green" << endl; + mOutput << "property " << "uchar" << " blue" << endl; + mOutput << "property " << "uchar" << " alpha" << endl; } else { - mOutput << "property " << typeName << " r" << c << endl; - mOutput << "property " << typeName << " g" << c << endl; - mOutput << "property " << typeName << " b" << c << endl; - mOutput << "property " << typeName << " a" << c << endl; + mOutput << "property " << "uchar" << " red" << c << endl; + mOutput << "property " << "uchar" << " green" << c << endl; + mOutput << "property " << "uchar" << " blue" << c << endl; + mOutput << "property " << "uchar" << " alpha" << c << endl; } } @@ -288,13 +288,13 @@ void PlyExporter::WriteMeshVerts(const aiMesh* m, unsigned int components) for (unsigned int n = PLY_EXPORT_HAS_COLORS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_COLOR_SETS; n <<= 1, ++c) { if (m->HasVertexColors(c)) { mOutput << - " " << m->mColors[c][i].r << - " " << m->mColors[c][i].g << - " " << m->mColors[c][i].b << - " " << m->mColors[c][i].a; + " " << (int)(m->mColors[c][i].r * 255) << + " " << (int)(m->mColors[c][i].g * 255) << + " " << (int)(m->mColors[c][i].b * 255) << + " " << (int)(m->mColors[c][i].a * 255); } else { - mOutput << " -1.0 -1.0 -1.0 -1.0"; + mOutput << " 0 0 0"; } } From 6fb9ebe3c891f05acccb08e7c89d70077409b2bd Mon Sep 17 00:00:00 2001 From: Trylz Date: Sat, 28 Apr 2018 23:34:07 -0400 Subject: [PATCH 02/13] Apply fix --- code/FBXConverter.cpp | 27 ++++++++++++++++----------- code/FBXConverter.h | 10 +++++----- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 2c9818a16..d6529d119 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -874,7 +874,7 @@ void Converter::ConvertModel( const Model& model, aiNode& nd, const aiMatrix4x4& const MeshGeometry* const mesh = dynamic_cast< const MeshGeometry* >( geo ); if ( mesh ) { - const std::vector& indices = ConvertMesh( *mesh, model, node_global_transform ); + const std::vector& indices = ConvertMesh( *mesh, model, node_global_transform, nd); std::copy( indices.begin(), indices.end(), std::back_inserter( meshes ) ); } else { @@ -891,7 +891,7 @@ void Converter::ConvertModel( const Model& model, aiNode& nd, const aiMatrix4x4& } std::vector Converter::ConvertMesh( const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform ) + const aiMatrix4x4& node_global_transform, aiNode& nd) { std::vector temp; @@ -915,17 +915,17 @@ std::vector Converter::ConvertMesh( const MeshGeometry& mesh, cons const MatIndexArray::value_type base = mindices[ 0 ]; for( MatIndexArray::value_type index : mindices ) { if ( index != base ) { - return ConvertMeshMultiMaterial( mesh, model, node_global_transform ); + return ConvertMeshMultiMaterial( mesh, model, node_global_transform, nd); } } } // faster code-path, just copy the data - temp.push_back( ConvertMeshSingleMaterial( mesh, model, node_global_transform ) ); + temp.push_back( ConvertMeshSingleMaterial( mesh, model, node_global_transform, nd) ); return temp; } -aiMesh* Converter::SetupEmptyMesh( const MeshGeometry& mesh ) +aiMesh* Converter::SetupEmptyMesh( const MeshGeometry& mesh, aiNode& nd) { aiMesh* const out_mesh = new aiMesh(); meshes.push_back( out_mesh ); @@ -940,15 +940,19 @@ aiMesh* Converter::SetupEmptyMesh( const MeshGeometry& mesh ) if ( name.length() ) { out_mesh->mName.Set( name ); } + else + { + out_mesh->mName = nd.mName; + } return out_mesh; } unsigned int Converter::ConvertMeshSingleMaterial( const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform ) + const aiMatrix4x4& node_global_transform, aiNode& nd) { const MatIndexArray& mindices = mesh.GetMaterialIndices(); - aiMesh* const out_mesh = SetupEmptyMesh( mesh ); + aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); const std::vector& vertices = mesh.GetVertices(); const std::vector& faces = mesh.GetFaceIndexCounts(); @@ -1072,7 +1076,7 @@ unsigned int Converter::ConvertMeshSingleMaterial( const MeshGeometry& mesh, con } std::vector Converter::ConvertMeshMultiMaterial( const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform ) + const aiMatrix4x4& node_global_transform, aiNode& nd) { const MatIndexArray& mindices = mesh.GetMaterialIndices(); ai_assert( mindices.size() ); @@ -1083,7 +1087,7 @@ std::vector Converter::ConvertMeshMultiMaterial( const MeshGeometr for( MatIndexArray::value_type index : mindices ) { if ( had.find( index ) == had.end() ) { - indices.push_back( ConvertMeshMultiMaterial( mesh, model, index, node_global_transform ) ); + indices.push_back( ConvertMeshMultiMaterial( mesh, model, index, node_global_transform, nd) ); had.insert( index ); } } @@ -1093,9 +1097,10 @@ std::vector Converter::ConvertMeshMultiMaterial( const MeshGeometr unsigned int Converter::ConvertMeshMultiMaterial( const MeshGeometry& mesh, const Model& model, MatIndexArray::value_type index, - const aiMatrix4x4& node_global_transform ) + const aiMatrix4x4& node_global_transform, + aiNode& nd) { - aiMesh* const out_mesh = SetupEmptyMesh( mesh ); + aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); const MatIndexArray& mindices = mesh.GetMaterialIndices(); const std::vector& vertices = mesh.GetVertices(); diff --git a/code/FBXConverter.h b/code/FBXConverter.h index 60fd04e2c..71d93d339 100644 --- a/code/FBXConverter.h +++ b/code/FBXConverter.h @@ -172,23 +172,23 @@ private: // ------------------------------------------------------------------------------------------------ // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed std::vector ConvertMesh(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform); + const aiMatrix4x4& node_global_transform, aiNode& nd); // ------------------------------------------------------------------------------------------------ - aiMesh* SetupEmptyMesh(const MeshGeometry& mesh); + aiMesh* SetupEmptyMesh(const MeshGeometry& mesh, aiNode& nd); // ------------------------------------------------------------------------------------------------ unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform); + const aiMatrix4x4& node_global_transform, aiNode& nd); // ------------------------------------------------------------------------------------------------ std::vector ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform); + const aiMatrix4x4& node_global_transform, aiNode& nd); // ------------------------------------------------------------------------------------------------ unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, MatIndexArray::value_type index, - const aiMatrix4x4& node_global_transform); + const aiMatrix4x4& node_global_transform, aiNode& nd); // ------------------------------------------------------------------------------------------------ static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits::max() */ From 9e484daa65dc7993d39e89fa612f91b59f43bc2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Korbinian=20W=C3=BCrl?= Date: Mon, 30 Apr 2018 15:51:48 +0200 Subject: [PATCH 03/13] Unit test for Issue #1923: OBJ Exporter can't correctly export vertex colors --- .../models/OBJ/cube_with_vertexcolors_uni.obj | 30 +++++++++++++++++++ test/unit/utObjImportExport.cpp | 18 +++++++++++ 2 files changed, 48 insertions(+) create mode 100644 test/models/OBJ/cube_with_vertexcolors_uni.obj diff --git a/test/models/OBJ/cube_with_vertexcolors_uni.obj b/test/models/OBJ/cube_with_vertexcolors_uni.obj new file mode 100644 index 000000000..c698b9489 --- /dev/null +++ b/test/models/OBJ/cube_with_vertexcolors_uni.obj @@ -0,0 +1,30 @@ +g cube + +v 0.0 0.0 0.0 0.0 0.0 0.0 +v 0.0 0.0 1.0 1.0 0.6 0.3 +v 0.0 1.0 0.0 0.0 0.0 0.0 +v 0.0 1.0 1.0 0.3 0.6 1.0 +v 1.0 0.0 0.0 0.0 0.0 0.0 +v 1.0 0.0 1.0 1.0 0.6 0.3 +v 1.0 1.0 0.0 0.0 0.0 0.0 +v 1.0 1.0 1.0 0.3 0.6 1.0 + +vn 0.0 0.0 1.0 +vn 0.0 0.0 -1.0 +vn 0.0 1.0 0.0 +vn 0.0 -1.0 0.0 +vn 1.0 0.0 0.0 +vn -1.0 0.0 0.0 + +f 1//2 7//2 5//2 +f 1//2 3//2 7//2 +f 1//6 4//6 3//6 +f 1//6 2//6 4//6 +f 3//3 8//3 7//3 +f 3//3 4//3 8//3 +f 5//5 7//5 8//5 +f 5//5 8//5 6//5 +f 1//4 5//4 6//4 +f 1//4 6//4 2//4 +f 2//1 6//1 8//1 +f 2//1 8//1 4//1 \ No newline at end of file diff --git a/test/unit/utObjImportExport.cpp b/test/unit/utObjImportExport.cpp index 8aec9c443..e879c350e 100644 --- a/test/unit/utObjImportExport.cpp +++ b/test/unit/utObjImportExport.cpp @@ -267,6 +267,24 @@ TEST_F( utObjImportExport, issue809_vertex_color_Test ) { #endif // ASSIMP_BUILD_NO_EXPORT } +TEST_F( utObjImportExport, issue1923_vertex_color_Test ) { + ::Assimp::Importer importer; + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/cube_with_vertexcolors_uni.obj", aiProcess_ValidateDataStructure ); + EXPECT_NE( nullptr, scene ); + +#ifndef ASSIMP_BUILD_NO_EXPORT + ::Assimp::Exporter exporter; + const aiExportDataBlob* blob = exporter.ExportToBlob( scene, "obj"); + EXPECT_NE( nullptr, blob ); + + const aiScene *sceneReImport = importer.ReadFileFromMemory( blob->data, blob->size, aiProcess_ValidateDataStructure ); + EXPECT_NE( nullptr, scene ); + + SceneDiffer differ; + EXPECT_TRUE( differ.isEqual( scene, sceneReImport ) ); +#endif // ASSIMP_BUILD_NO_EXPORT +} + TEST_F( utObjImportExport, issue1453_segfault ) { static const std::string ObjModel = "v 0.0 0.0 0.0\n" From 9cfdb8d3658f306163d2eb33eaaeb16c5a2eb429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Korbinian=20W=C3=BCrl?= Date: Mon, 30 Apr 2018 16:23:29 +0200 Subject: [PATCH 04/13] Fixed test .obj file: OBJ Vertex Colors are expected to be floats 0-1 See https://github.com/cnr-isti-vclab/vcglib/blob/master/wrap/io_trimesh/import_obj.h#L326 --- test/models/OBJ/cube_with_vertexcolors.obj | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/models/OBJ/cube_with_vertexcolors.obj b/test/models/OBJ/cube_with_vertexcolors.obj index d78d3a555..1541e0823 100644 --- a/test/models/OBJ/cube_with_vertexcolors.obj +++ b/test/models/OBJ/cube_with_vertexcolors.obj @@ -1,13 +1,13 @@ g cube -v 0.0 0.0 0.0 124 110 120 -v 0.0 0.0 1.0 24 0 121 -v 0.0 1.0 0.0 4 0 44 -v 0.0 1.0 1.0 224 0 10 -v 1.0 0.0 0.0 24 200 25 -v 1.0 0.0 1.0 124 10 56 -v 1.0 1.0 0.0 78 10 50 -v 1.0 1.0 1.0 23 0 200 +v 0.0 0.0 0.0 0.48627 0.43137 0.47059 +v 0.0 0.0 1.0 0.09412 0.00000 0.47451 +v 0.0 1.0 0.0 0.01569 0.00000 0.17255 +v 0.0 1.0 1.0 0.87843 0.00000 0.03922 +v 1.0 0.0 0.0 0.09412 0.78431 0.09804 +v 1.0 0.0 1.0 0.48627 0.03922 0.21961 +v 1.0 1.0 0.0 0.30588 0.03922 0.19608 +v 1.0 1.0 1.0 0.09020 0.00000 0.78431 vn 0.0 0.0 1.0 vn 0.0 0.0 -1.0 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 05/13] 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 From 9f835ea8435ddb7999a4d57eb388ada230db1ef3 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 1 May 2018 09:06:22 +0200 Subject: [PATCH 06/13] closes https://github.com/assimp/assimp/issues/842: experimental suppor for ascii stl pointcloud export. --- code/STLExporter.cpp | 40 ++++++++++++++++++++++++++++++++----- code/STLExporter.h | 4 +--- code/simd.cpp | 1 + include/assimp/Exporter.hpp | 14 +++++++------ include/assimp/cexport.h | 6 ++---- include/assimp/config.h.in | 5 +++++ 6 files changed, 52 insertions(+), 18 deletions(-) diff --git a/code/STLExporter.cpp b/code/STLExporter.cpp index 3d681f8c7..5f9bfbde2 100644 --- a/code/STLExporter.cpp +++ b/code/STLExporter.cpp @@ -54,14 +54,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include using namespace Assimp; + namespace Assimp { // ------------------------------------------------------------------------------------------------ // Worker function for exporting a scene to Stereolithograpy. Prototyped and registered in Exporter.cpp -void ExportSceneSTL(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) +void ExportSceneSTL(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties ) { + bool exportPointClouds = pProperties->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS); + // invoke the exporter - STLExporter exporter(pFile, pScene); + STLExporter exporter(pFile, pScene, exportPointClouds ); if (exporter.mOutput.fail()) { throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); @@ -75,10 +78,12 @@ void ExportSceneSTL(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene outfile->Write( exporter.mOutput.str().c_str(), static_cast(exporter.mOutput.tellp()),1); } -void ExportSceneSTLBinary(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) +void ExportSceneSTLBinary(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties ) { + bool exportPointClouds = pProperties->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS); + // invoke the exporter - STLExporter exporter(pFile, pScene, true); + STLExporter exporter(pFile, pScene, exportPointClouds, true); if (exporter.mOutput.fail()) { throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); @@ -97,7 +102,7 @@ void ExportSceneSTLBinary(const char* pFile,IOSystem* pIOSystem, const aiScene* // ------------------------------------------------------------------------------------------------ -STLExporter :: STLExporter(const char* _filename, const aiScene* pScene, bool binary) +STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool exportPointClouds, bool binary) : filename(_filename) , endl("\n") { @@ -118,12 +123,37 @@ STLExporter :: STLExporter(const char* _filename, const aiScene* pScene, bool bi } AI_SWAP4(meshnum); mOutput.write((char *)&meshnum, 4); + + if (exportPointClouds) { + + } + for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { WriteMeshBinary(pScene->mMeshes[i]); } } else { const std::string& name = "AssimpScene"; + // Exporting only point clouds + if (exportPointClouds) { + mOutput << "solid " << name << endl; + aiVector3D nor; + mOutput << " facet normal " << nor.x << " " << nor.y << " " << nor.z << endl; + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + aiMesh *mesh = pScene->mMeshes[i]; + if (mesh->mNormals) { + for (unsigned int a = 0; a < mesh->mNumVertices; ++a) { + const aiVector3D& v = mesh->mVertices[a]; + mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; + mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; + mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; + } + } + } + mOutput << "endsolid " << name << endl; + return; + } + mOutput << "solid " << name << endl; for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { WriteMesh(pScene->mMeshes[i]); diff --git a/code/STLExporter.h b/code/STLExporter.h index 1bd589627..a1d306944 100644 --- a/code/STLExporter.h +++ b/code/STLExporter.h @@ -62,9 +62,7 @@ class STLExporter { public: /// Constructor for a specific scene to export - STLExporter(const char* filename, const aiScene* pScene, bool binary = false); - -public: + STLExporter(const char* filename, const aiScene* pScene, bool exportPOintClouds, bool binary = false); /// public stringstreams to write all output into std::ostringstream mOutput; diff --git a/code/simd.cpp b/code/simd.cpp index bd951bffa..9e2a83a60 100644 --- a/code/simd.cpp +++ b/code/simd.cpp @@ -75,4 +75,5 @@ bool CPUSupportsSSE2() { #endif } + } // Namespace Assimp diff --git a/include/assimp/Exporter.hpp b/include/assimp/Exporter.hpp index e7e43d8b7..dfbac9371 100644 --- a/include/assimp/Exporter.hpp +++ b/include/assimp/Exporter.hpp @@ -115,12 +115,10 @@ public: } }; - public: Exporter(); ~Exporter(); -public: // ------------------------------------------------------------------- /** Supplies a custom IO handler to the exporter to use to open and * access files. @@ -172,8 +170,10 @@ public: * Any IO handlers set via #SetIOHandler are ignored here. * @note Use aiCopyScene() to get a modifiable copy of a previously * imported scene. */ - const aiExportDataBlob* ExportToBlob(const aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing = 0u, const ExportProperties* = NULL); - const aiExportDataBlob* ExportToBlob( const aiScene* pScene, const std::string& pFormatId, unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = NULL); + const aiExportDataBlob* ExportToBlob(const aiScene* pScene, const char* pFormatId, + unsigned int pPreprocessing = 0u, const ExportProperties* = nullptr); + const aiExportDataBlob* ExportToBlob( const aiScene* pScene, const std::string& pFormatId, + unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr); // ------------------------------------------------------------------- /** Convenience function to export directly to a file. Use @@ -208,8 +208,10 @@ public: * @return AI_SUCCESS if everything was fine. * @note Use aiCopyScene() to get a modifiable copy of a previously * imported scene.*/ - aiReturn Export( const aiScene* pScene, const char* pFormatId, const char* pPath, unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = NULL); - aiReturn Export( const aiScene* pScene, const std::string& pFormatId, const std::string& pPath, unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = NULL); + aiReturn Export( const aiScene* pScene, const char* pFormatId, const char* pPath, + unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr); + aiReturn Export( const aiScene* pScene, const std::string& pFormatId, const std::string& pPath, + unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr); // ------------------------------------------------------------------- /** Returns an error description of an error that occurred in #Export diff --git a/include/assimp/cexport.h b/include/assimp/cexport.h index 44b06fb3c..1d62dc26b 100644 --- a/include/assimp/cexport.h +++ b/include/assimp/cexport.h @@ -85,7 +85,6 @@ struct aiExportFormatDesc */ ASSIMP_API size_t aiGetExportFormatCount(void); - // -------------------------------------------------------------------------------- /** Returns a description of the nth export file format. Use #aiGetExportFormatCount() * to learn how many export formats are supported. The description must be released by @@ -186,7 +185,6 @@ ASSIMP_API aiReturn aiExportSceneEx( const C_STRUCT aiScene* pScene, C_STRUCT aiFileIO* pIO, unsigned int pPreprocessing ); - // -------------------------------------------------------------------------------- /** Describes a blob of exported scene data. Use #aiExportSceneToBlob() to create a blob containing an * exported scene. The memory referred by this structure is owned by Assimp. @@ -245,8 +243,8 @@ private: * @param pPreprocessing Please see the documentation for #aiExportScene * @return the exported data or NULL in case of error */ -ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const C_STRUCT aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing ); - +ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const C_STRUCT aiScene* pScene, const char* pFormatId, + unsigned int pPreprocessing ); // -------------------------------------------------------------------------------- /** Releases the memory associated with the given exported data. Use this function to free a data blob diff --git a/include/assimp/config.h.in b/include/assimp/config.h.in index c9555fb38..8de2ea43e 100644 --- a/include/assimp/config.h.in +++ b/include/assimp/config.h.in @@ -953,6 +953,11 @@ enum aiComponent #define AI_CONFIG_EXPORT_XFILE_64BIT "EXPORT_XFILE_64BIT" +/** + * + */ +#define AI_CONFIG_EXPORT_POINT_CLOUDS "EXPORT_POINT_CLOUDS" + /** * @brief Specifies a gobal key factor for scale, float value */ From 22475130649c3d356c507a7a420ce176d0b45d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Korbinian=20W=C3=BCrl?= Date: Tue, 1 May 2018 11:23:38 +0200 Subject: [PATCH 07/13] Fix for TravisCI --- test/unit/utObjImportExport.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/unit/utObjImportExport.cpp b/test/unit/utObjImportExport.cpp index e879c350e..5a10ae17b 100644 --- a/test/unit/utObjImportExport.cpp +++ b/test/unit/utObjImportExport.cpp @@ -272,6 +272,8 @@ TEST_F( utObjImportExport, issue1923_vertex_color_Test ) { const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/cube_with_vertexcolors_uni.obj", aiProcess_ValidateDataStructure ); EXPECT_NE( nullptr, scene ); + scene = importer.GetOrphanedScene(); + #ifndef ASSIMP_BUILD_NO_EXPORT ::Assimp::Exporter exporter; const aiExportDataBlob* blob = exporter.ExportToBlob( scene, "obj"); @@ -283,6 +285,8 @@ TEST_F( utObjImportExport, issue1923_vertex_color_Test ) { SceneDiffer differ; EXPECT_TRUE( differ.isEqual( scene, sceneReImport ) ); #endif // ASSIMP_BUILD_NO_EXPORT + + delete scene; } TEST_F( utObjImportExport, issue1453_segfault ) { From 2c47717ca6cb54c9da9623edaf2da44da430bc63 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 1 May 2018 13:28:53 +0200 Subject: [PATCH 08/13] Introduce export with test for point clouds. --- code/Exporter.cpp | 14 ++++++-- code/PretransformVertices.cpp | 20 ++++++----- code/PretransformVertices.h | 9 ++--- code/RemoveRedundantMaterials.h | 1 + code/STLExporter.cpp | 18 +++++----- code/STLExporter.h | 3 +- test/unit/utPLYImportExport.cpp | 21 ++++++------ test/unit/utSTLImportExport.cpp | 61 +++++++++++++++++++++++++++++++++ 8 files changed, 109 insertions(+), 38 deletions(-) diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 0a8f054b7..53a623ecd 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -62,6 +62,7 @@ Here we implement only the C++ interface (Assimp::Exporter). #include "JoinVerticesProcess.h" #include "MakeVerboseFormat.h" #include "ConvertToLHProcess.h" +#include "PretransformVertices.h" #include #include "ScenePrivate.h" #include @@ -397,6 +398,11 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c } } + bool exportPointCloud(false); + if (nullptr != pProperties) { + exportPointCloud = pProperties->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS); + } + // dispatch other processes for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { BaseProcess* const p = pimpl->mPostProcessingSteps[a]; @@ -405,7 +411,9 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c && !dynamic_cast(p) && !dynamic_cast(p) && !dynamic_cast(p)) { - + if (dynamic_cast(p) && exportPointCloud) { + continue; + } p->Execute(scenecopy.get()); } } @@ -441,7 +449,6 @@ const char* Exporter::GetErrorString() const { return pimpl->mError.c_str(); } - // ------------------------------------------------------------------------------------------------ void Exporter::FreeBlob() { delete pimpl->blob; @@ -495,7 +502,8 @@ aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) { // ------------------------------------------------------------------------------------------------ void Exporter::UnregisterExporter(const char* id) { - for(std::vector::iterator it = pimpl->mExporters.begin(); it != pimpl->mExporters.end(); ++it) { + for(std::vector::iterator it = pimpl->mExporters.begin(); + it != pimpl->mExporters.end(); ++it) { if (!strcmp((*it).mDescription.id,id)) { pimpl->mExporters.erase(it); break; diff --git a/code/PretransformVertices.cpp b/code/PretransformVertices.cpp index 0623f00ef..9745abd60 100644 --- a/code/PretransformVertices.cpp +++ b/code/PretransformVertices.cpp @@ -60,14 +60,17 @@ using namespace Assimp; // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer PretransformVertices::PretransformVertices() -: configKeepHierarchy (false), configNormalize(false), configTransform(false), configTransformation() -{ +: configKeepHierarchy (false) +, configNormalize(false) +, configTransform(false) +, configTransformation() +, mConfigPointCloud( false ) { + // empty } // ------------------------------------------------------------------------------------------------ // Destructor, private as well -PretransformVertices::~PretransformVertices() -{ +PretransformVertices::~PretransformVertices() { // nothing to do here } @@ -89,6 +92,8 @@ void PretransformVertices::SetupProperties(const Importer* pImp) configTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION,0)); configTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4()); + + mConfigPointCloud = pImp->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS); } // ------------------------------------------------------------------------------------------------ @@ -502,9 +507,7 @@ void PretransformVertices::Execute( aiScene* pScene) pScene->mMeshes[i]->mBones = NULL; pScene->mMeshes[i]->mNumBones = 0; } - } - else { - + } else { apcOutMeshes.reserve(pScene->mNumMaterials<<1u); std::list aiVFormats; @@ -556,7 +559,8 @@ void PretransformVertices::Execute( aiScene* pScene) } // If no meshes are referenced in the node graph it is possible that we get no output meshes. - if (apcOutMeshes.empty()) { + if (apcOutMeshes.empty()) { + throw DeadlyImportError("No output meshes: all meshes are orphaned and are not referenced by any nodes"); } else diff --git a/code/PretransformVertices.h b/code/PretransformVertices.h index 8b400dc0c..a8196289d 100644 --- a/code/PretransformVertices.h +++ b/code/PretransformVertices.h @@ -61,15 +61,11 @@ namespace Assimp { * and removes the whole graph. The output is a list of meshes, one for * each material. */ -class ASSIMP_API PretransformVertices : public BaseProcess -{ +class ASSIMP_API PretransformVertices : public BaseProcess { public: - PretransformVertices (); ~PretransformVertices (); -public: - // ------------------------------------------------------------------- // Check whether step is active bool IsActive( unsigned int pFlags) const; @@ -82,7 +78,6 @@ public: // Setup import settings void SetupProperties(const Importer* pImp); - // ------------------------------------------------------------------- /** @brief Toggle the 'keep hierarchy' option * @param d hm ... difficult to guess what this means, hu!? @@ -100,7 +95,6 @@ public: } private: - // ------------------------------------------------------------------- // Count the number of nodes unsigned int CountNodes( aiNode* pcNode ); @@ -161,6 +155,7 @@ private: bool configNormalize; bool configTransform; aiMatrix4x4 configTransformation; + bool mConfigPointCloud; }; } // end of namespace Assimp diff --git a/code/RemoveRedundantMaterials.h b/code/RemoveRedundantMaterials.h index 37b69ffbe..314bbf345 100644 --- a/code/RemoveRedundantMaterials.h +++ b/code/RemoveRedundantMaterials.h @@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include class RemoveRedundantMatsTest; + namespace Assimp { // --------------------------------------------------------------------------- diff --git a/code/STLExporter.cpp b/code/STLExporter.cpp index 5f9bfbde2..dd4df9212 100644 --- a/code/STLExporter.cpp +++ b/code/STLExporter.cpp @@ -141,13 +141,15 @@ STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool expo mOutput << " facet normal " << nor.x << " " << nor.y << " " << nor.z << endl; for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { aiMesh *mesh = pScene->mMeshes[i]; - if (mesh->mNormals) { - for (unsigned int a = 0; a < mesh->mNumVertices; ++a) { - const aiVector3D& v = mesh->mVertices[a]; - mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; - mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; - mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; - } + if (nullptr == mesh) { + continue; + } + + for (unsigned int a = 0; a < mesh->mNumVertices; ++a) { + const aiVector3D& v = mesh->mVertices[a]; + mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; + mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; + mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; } } mOutput << "endsolid " << name << endl; @@ -163,7 +165,7 @@ STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool expo } // ------------------------------------------------------------------------------------------------ -void STLExporter :: WriteMesh(const aiMesh* m) +void STLExporter::WriteMesh(const aiMesh* m) { for (unsigned int i = 0; i < m->mNumFaces; ++i) { const aiFace& f = m->mFaces[i]; diff --git a/code/STLExporter.h b/code/STLExporter.h index a1d306944..51a440de7 100644 --- a/code/STLExporter.h +++ b/code/STLExporter.h @@ -52,8 +52,7 @@ struct aiScene; struct aiNode; struct aiMesh; -namespace Assimp -{ +namespace Assimp { // ------------------------------------------------------------------------------------------------ /** Helper class to export a given scene to a STL file. */ diff --git a/test/unit/utPLYImportExport.cpp b/test/unit/utPLYImportExport.cpp index c009bda39..5aabe0805 100644 --- a/test/unit/utPLYImportExport.cpp +++ b/test/unit/utPLYImportExport.cpp @@ -138,18 +138,19 @@ TEST_F( utPLYImportExport, vertexColorTest ) { EXPECT_EQ(2u, first_face.mIndices[2]); } -//Test issue #623, PLY importer should not automatically create faces +// Test issue #623, PLY importer should not automatically create faces TEST_F(utPLYImportExport, pointcloudTest) { - Assimp::Importer importer; - //Could not use aiProcess_ValidateDataStructure since it's missing faces. - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/issue623.ply", 0); - EXPECT_NE(nullptr, scene); + Assimp::Importer importer; - EXPECT_EQ(1u, scene->mNumMeshes); - EXPECT_NE(nullptr, scene->mMeshes[0]); - EXPECT_EQ(24u, scene->mMeshes[0]->mNumVertices); - EXPECT_EQ(aiPrimitiveType::aiPrimitiveType_POINT, scene->mMeshes[0]->mPrimitiveTypes); - EXPECT_EQ(0u, scene->mMeshes[0]->mNumFaces); + //Could not use aiProcess_ValidateDataStructure since it's missing faces. + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/issue623.ply", 0); + EXPECT_NE(nullptr, scene); + + EXPECT_EQ(1u, scene->mNumMeshes); + EXPECT_NE(nullptr, scene->mMeshes[0]); + EXPECT_EQ(24u, scene->mMeshes[0]->mNumVertices); + EXPECT_EQ(aiPrimitiveType::aiPrimitiveType_POINT, scene->mMeshes[0]->mPrimitiveTypes); + EXPECT_EQ(0u, scene->mMeshes[0]->mNumFaces); } static const char *test_file = diff --git a/test/unit/utSTLImportExport.cpp b/test/unit/utSTLImportExport.cpp index 0cb9f73ee..aad6b0fa1 100644 --- a/test/unit/utSTLImportExport.cpp +++ b/test/unit/utSTLImportExport.cpp @@ -47,6 +47,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include +#include + +#include using namespace Assimp; @@ -68,3 +72,60 @@ TEST_F( utSTLImporterExporter, test_with_two_solids ) { const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/STL/triangle_with_two_solids.stl", aiProcess_ValidateDataStructure ); EXPECT_NE( nullptr, scene ); } + +#ifndef ASSIMP_BUILD_NO_EXPORT + +TEST_F(utSTLImporterExporter, test_export_pointclouds) { + struct XYZ { + float x, y, z; + }; + + std::vector points; + + for (size_t i = 0; i < 10; ++i) { + XYZ current; + current.x = static_cast(i); + current.y = static_cast(i); + current.z = static_cast(i); + points.push_back(current); + } + aiScene scene; + scene.mRootNode = new aiNode(); + + scene.mMeshes = new aiMesh*[1]; + scene.mMeshes[0] = nullptr; + scene.mNumMeshes = 1; + + scene.mMaterials = new aiMaterial*[1]; + scene.mMaterials[0] = nullptr; + scene.mNumMaterials = 1; + + scene.mMaterials[0] = new aiMaterial(); + + scene.mMeshes[0] = new aiMesh(); + scene.mMeshes[0]->mMaterialIndex = 0; + + scene.mRootNode->mMeshes = new unsigned int[1]; + scene.mRootNode->mMeshes[0] = 0; + scene.mRootNode->mNumMeshes = 1; + + auto pMesh = scene.mMeshes[0]; + + long numValidPoints = points.size(); + + pMesh->mVertices = new aiVector3D[numValidPoints]; + pMesh->mNumVertices = numValidPoints; + + int i = 0; + for (XYZ &p : points) { + pMesh->mVertices[i] = aiVector3D(p.x, p.y, p.z); + ++i; + } + + Assimp::Exporter mAiExporter; + ExportProperties *properties = new ExportProperties; + properties->SetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS, true); + mAiExporter.Export(&scene, "stl", "testExport.stl", 0, properties ); +} + +#endif From eced86b949c8c1a9fc2b74eae531b8b9b456cf68 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 1 May 2018 13:41:39 +0200 Subject: [PATCH 09/13] some refactorings. --- code/STLExporter.cpp | 50 +++++++++++++++++++++++++------------------- code/STLExporter.h | 2 +- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/code/STLExporter.cpp b/code/STLExporter.cpp index dd4df9212..7b38125ef 100644 --- a/code/STLExporter.cpp +++ b/code/STLExporter.cpp @@ -100,6 +100,8 @@ void ExportSceneSTLBinary(const char* pFile,IOSystem* pIOSystem, const aiScene* } // end of namespace Assimp +static const char *SolidToken = "solid"; +static const char *EndSolidToken = "endsolid"; // ------------------------------------------------------------------------------------------------ STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool exportPointClouds, bool binary) @@ -132,38 +134,44 @@ STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool expo WriteMeshBinary(pScene->mMeshes[i]); } } else { - const std::string& name = "AssimpScene"; // Exporting only point clouds if (exportPointClouds) { - mOutput << "solid " << name << endl; - aiVector3D nor; - mOutput << " facet normal " << nor.x << " " << nor.y << " " << nor.z << endl; - for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { - aiMesh *mesh = pScene->mMeshes[i]; - if (nullptr == mesh) { - continue; - } - - for (unsigned int a = 0; a < mesh->mNumVertices; ++a) { - const aiVector3D& v = mesh->mVertices[a]; - mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; - mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; - mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; - } - } - mOutput << "endsolid " << name << endl; + WritePointCloud("Assimp_Pointcloud", pScene ); return; } - mOutput << "solid " << name << endl; + // Export the assimp mesh + const std::string name = "AssimpScene"; + mOutput << SolidToken << name << endl; for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { - WriteMesh(pScene->mMeshes[i]); + WriteMesh(pScene->mMeshes[ i ]); } - mOutput << "endsolid " << name << endl; + mOutput << EndSolidToken << name << endl; } } +// ------------------------------------------------------------------------------------------------ +void STLExporter::WritePointCloud(const std::string &name, const aiScene* pScene) { + mOutput << " " << SolidToken << " " << name << endl; + aiVector3D nor; + mOutput << " facet normal " << nor.x << " " << nor.y << " " << nor.z << endl; + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + aiMesh *mesh = pScene->mMeshes[i]; + if (nullptr == mesh) { + continue; + } + + for (unsigned int a = 0; a < mesh->mNumVertices; ++a) { + const aiVector3D& v = mesh->mVertices[a]; + mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; + mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; + mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; + } + } + mOutput << EndSolidToken << name << endl; +} + // ------------------------------------------------------------------------------------------------ void STLExporter::WriteMesh(const aiMesh* m) { diff --git a/code/STLExporter.h b/code/STLExporter.h index 51a440de7..6e8f90915 100644 --- a/code/STLExporter.h +++ b/code/STLExporter.h @@ -67,7 +67,7 @@ public: std::ostringstream mOutput; private: - + void WritePointCloud(const std::string &name, const aiScene* pScene); void WriteMesh(const aiMesh* m); void WriteMeshBinary(const aiMesh* m); From 037a213bb4cd1e5853df390b4a64d663efde2fe2 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 1 May 2018 15:06:56 +0200 Subject: [PATCH 10/13] STL-Exporter: fix division by zero in normalize method during update --- code/Exporter.cpp | 5 ++-- code/STLExporter.cpp | 8 +++--- include/assimp/Exporter.hpp | 8 +++++- include/assimp/GenericProperty.h | 48 ++++++++++++++++++-------------- test/unit/utSTLImportExport.cpp | 13 +++++++++ 5 files changed, 53 insertions(+), 29 deletions(-) diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 53a623ecd..bd533f3b3 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -477,7 +477,7 @@ size_t Exporter::GetExportFormatCount() const { // ------------------------------------------------------------------------------------------------ const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const { if (index >= GetExportFormatCount()) { - return NULL; + return nullptr; } // Return from static storage if the requested index is built-in. @@ -539,8 +539,7 @@ bool ExportProperties::SetPropertyFloat(const char* szName, ai_real iValue) { // ------------------------------------------------------------------------------------------------ // Set a configuration property -bool ExportProperties :: SetPropertyString(const char* szName, const std::string& value) -{ +bool ExportProperties::SetPropertyString(const char* szName, const std::string& value) { return SetGenericProperty(mStringProperties, szName,value); } diff --git a/code/STLExporter.cpp b/code/STLExporter.cpp index 7b38125ef..e4a1dbb66 100644 --- a/code/STLExporter.cpp +++ b/code/STLExporter.cpp @@ -143,7 +143,7 @@ STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool expo // Export the assimp mesh const std::string name = "AssimpScene"; - mOutput << SolidToken << name << endl; + mOutput << SolidToken << " " << name << endl; for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { WriteMesh(pScene->mMeshes[ i ]); } @@ -169,7 +169,7 @@ void STLExporter::WritePointCloud(const std::string &name, const aiScene* pScene mOutput << " vertex " << v.x << " " << v.y << " " << v.z << endl; } } - mOutput << EndSolidToken << name << endl; + mOutput << EndSolidToken << " " << name << endl; } // ------------------------------------------------------------------------------------------------ @@ -185,7 +185,7 @@ void STLExporter::WriteMesh(const aiMesh* m) for(unsigned int a = 0; a < f.mNumIndices; ++a) { nor += m->mNormals[f.mIndices[a]]; } - nor.Normalize(); + nor.NormalizeSafe(); } mOutput << " facet normal " << nor.x << " " << nor.y << " " << nor.z << endl; mOutput << " outer loop" << endl; @@ -199,7 +199,7 @@ void STLExporter::WriteMesh(const aiMesh* m) } } -void STLExporter :: WriteMeshBinary(const aiMesh* m) +void STLExporter::WriteMeshBinary(const aiMesh* m) { for (unsigned int i = 0; i < m->mNumFaces; ++i) { const aiFace& f = m->mFaces[i]; diff --git a/include/assimp/Exporter.hpp b/include/assimp/Exporter.hpp index dfbac9371..3d1a9ea85 100644 --- a/include/assimp/Exporter.hpp +++ b/include/assimp/Exporter.hpp @@ -115,8 +115,14 @@ public: } }; -public: + /** + * @brief The class constructor. + */ Exporter(); + + /** + * @brief The class destructor. + */ ~Exporter(); // ------------------------------------------------------------------- diff --git a/include/assimp/GenericProperty.h b/include/assimp/GenericProperty.h index 96c74b4c4..7b75cb1c8 100644 --- a/include/assimp/GenericProperty.h +++ b/include/assimp/GenericProperty.h @@ -46,15 +46,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include "Hash.h" -#include +#include // ------------------------------------------------------------------------------------------------ template -inline bool SetGenericProperty(std::map< unsigned int, T >& list, - const char* szName, const T& value) -{ - ai_assert(NULL != szName); +inline +bool SetGenericProperty(std::map< unsigned int, T >& list, + const char* szName, const T& value) { + ai_assert(nullptr != szName); const uint32_t hash = SuperFastHash(szName); typename std::map::iterator it = list.find(hash); @@ -63,20 +63,22 @@ inline bool SetGenericProperty(std::map< unsigned int, T >& list, return false; } (*it).second = value; + return true; } // ------------------------------------------------------------------------------------------------ template -inline const T& GetGenericProperty(const std::map< unsigned int, T >& list, - const char* szName, const T& errorReturn) -{ - ai_assert(NULL != szName); +inline +const T& GetGenericProperty(const std::map< unsigned int, T >& list, + const char* szName, const T& errorReturn) { + ai_assert(nullptr != szName); const uint32_t hash = SuperFastHash(szName); typename std::map::const_iterator it = list.find(hash); - if (it == list.end()) + if (it == list.end()) { return errorReturn; + } return (*it).second; } @@ -85,16 +87,17 @@ inline const T& GetGenericProperty(const std::map< unsigned int, T >& list, // Special version for pointer types - they will be deleted when replaced with another value // passing NULL removes the whole property template -inline void SetGenericPropertyPtr(std::map< unsigned int, T* >& list, - const char* szName, T* value, bool* bWasExisting = NULL) -{ - ai_assert(NULL != szName); +inline +void SetGenericPropertyPtr(std::map< unsigned int, T* >& list, + const char* szName, T* value, bool* bWasExisting = nullptr ) { + ai_assert(nullptr != szName); const uint32_t hash = SuperFastHash(szName); typename std::map::iterator it = list.find(hash); if (it == list.end()) { - if (bWasExisting) + if (bWasExisting) { *bWasExisting = false; + } list.insert(std::pair( hash, value )); return; @@ -106,20 +109,23 @@ inline void SetGenericPropertyPtr(std::map< unsigned int, T* >& list, if (!value) { list.erase(it); } - if (bWasExisting) + if (bWasExisting) { *bWasExisting = true; + } } // ------------------------------------------------------------------------------------------------ template -inline bool HasGenericProperty(const std::map< unsigned int, T >& list, - const char* szName) -{ - ai_assert(NULL != szName); +inline +bool HasGenericProperty(const std::map< unsigned int, T >& list, + const char* szName) { + ai_assert(nullptr != szName); const uint32_t hash = SuperFastHash(szName); typename std::map::const_iterator it = list.find(hash); - if (it == list.end()) return false; + if (it == list.end()) { + return false; + } return true; } diff --git a/test/unit/utSTLImportExport.cpp b/test/unit/utSTLImportExport.cpp index aad6b0fa1..be73c5421 100644 --- a/test/unit/utSTLImportExport.cpp +++ b/test/unit/utSTLImportExport.cpp @@ -75,6 +75,17 @@ TEST_F( utSTLImporterExporter, test_with_two_solids ) { #ifndef ASSIMP_BUILD_NO_EXPORT +TEST_F(utSTLImporterExporter, exporterTest) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/STL/Spider_ascii.stl", aiProcess_ValidateDataStructure); + + Assimp::Exporter mAiExporter; + mAiExporter.Export( scene, "stl", "spiderExport.stl" ); + + const aiScene *scene2 = importer.ReadFile("spiderExport.stl", aiProcess_ValidateDataStructure); + //EXPECT_NE(nullptr, scene2); +} + TEST_F(utSTLImporterExporter, test_export_pointclouds) { struct XYZ { float x, y, z; @@ -126,6 +137,8 @@ TEST_F(utSTLImporterExporter, test_export_pointclouds) { ExportProperties *properties = new ExportProperties; properties->SetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS, true); mAiExporter.Export(&scene, "stl", "testExport.stl", 0, properties ); + + delete properties; } #endif From 1679c8b08e9186133dd9bdea5cc26e7548fd8a68 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 1 May 2018 16:03:56 +0200 Subject: [PATCH 11/13] STL: reenable unittest. --- test/unit/utSTLImportExport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/utSTLImportExport.cpp b/test/unit/utSTLImportExport.cpp index be73c5421..181862560 100644 --- a/test/unit/utSTLImportExport.cpp +++ b/test/unit/utSTLImportExport.cpp @@ -83,7 +83,7 @@ TEST_F(utSTLImporterExporter, exporterTest) { mAiExporter.Export( scene, "stl", "spiderExport.stl" ); const aiScene *scene2 = importer.ReadFile("spiderExport.stl", aiProcess_ValidateDataStructure); - //EXPECT_NE(nullptr, scene2); + EXPECT_NE(nullptr, scene2); } TEST_F(utSTLImporterExporter, test_export_pointclouds) { From a0bf664695dd9a4ec46b7a136cdef2003d690056 Mon Sep 17 00:00:00 2001 From: kimkulling Date: Wed, 2 May 2018 16:42:22 +0200 Subject: [PATCH 12/13] closes code/SortByPTypeProcess.cpp: fix memory leak. --- code/SortByPTypeProcess.cpp | 41 +++++++++++++++---------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/code/SortByPTypeProcess.cpp b/code/SortByPTypeProcess.cpp index ee18585e2..b67da5b95 100644 --- a/code/SortByPTypeProcess.cpp +++ b/code/SortByPTypeProcess.cpp @@ -85,8 +85,6 @@ void SortByPTypeProcess::SetupProperties(const Importer* pImp) // Update changed meshes in all nodes void UpdateNodes(const std::vector& replaceMeshIndex, aiNode* node) { -// std::vector::const_iterator it; - if (node->mNumMeshes) { unsigned int newSize = 0; @@ -133,10 +131,8 @@ void UpdateNodes(const std::vector& replaceMeshIndex, aiNode* node // ------------------------------------------------------------------------------------------------ // Executes the post processing step on the given imported data. -void SortByPTypeProcess::Execute( aiScene* pScene) -{ - if (!pScene->mNumMeshes) - { +void SortByPTypeProcess::Execute( aiScene* pScene) { + if ( 0 == pScene->mNumMeshes) { ASSIMP_LOG_DEBUG("SortByPTypeProcess skipped, there are no meshes"); return; } @@ -152,42 +148,38 @@ void SortByPTypeProcess::Execute( aiScene* pScene) std::vector replaceMeshIndex(pScene->mNumMeshes*4,UINT_MAX); std::vector::iterator meshIdx = replaceMeshIndex.begin(); - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) - { + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { aiMesh* const mesh = pScene->mMeshes[i]; ai_assert(0 != mesh->mPrimitiveTypes); // if there's just one primitive type in the mesh there's nothing to do for us unsigned int num = 0; - if (mesh->mPrimitiveTypes & aiPrimitiveType_POINT) - { + if (mesh->mPrimitiveTypes & aiPrimitiveType_POINT) { ++aiNumMeshesPerPType[0]; ++num; } - if (mesh->mPrimitiveTypes & aiPrimitiveType_LINE) - { + if (mesh->mPrimitiveTypes & aiPrimitiveType_LINE) { ++aiNumMeshesPerPType[1]; ++num; } - if (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) - { + if (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) { ++aiNumMeshesPerPType[2]; ++num; } - if (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON) - { + if (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON) { ++aiNumMeshesPerPType[3]; ++num; } - if (1 == num) - { - if (!(configRemoveMeshes & mesh->mPrimitiveTypes)) - { - *meshIdx = (unsigned int) outMeshes.size(); + if (1 == num) { + if (!(configRemoveMeshes & mesh->mPrimitiveTypes)) { + *meshIdx = static_cast( outMeshes.size() ); outMeshes.push_back(mesh); + } else { + delete mesh; + pScene->mMeshes[ i ] = nullptr; + bAnyChanges = true; } - else bAnyChanges = true; meshIdx += 4; continue; @@ -195,14 +187,13 @@ void SortByPTypeProcess::Execute( aiScene* pScene) bAnyChanges = true; // reuse our current mesh arrays for the submesh - // with the largest numer of primitives + // with the largest number of primitives unsigned int aiNumPerPType[4] = {0,0,0,0}; aiFace* pFirstFace = mesh->mFaces; aiFace* const pLastFace = pFirstFace + mesh->mNumFaces; unsigned int numPolyVerts = 0; - for (;pFirstFace != pLastFace; ++pFirstFace) - { + for (;pFirstFace != pLastFace; ++pFirstFace) { if (pFirstFace->mNumIndices <= 3) ++aiNumPerPType[pFirstFace->mNumIndices-1]; else From 4aaf7ad5474c14affaa4d6971d7a9ff6c538173b Mon Sep 17 00:00:00 2001 From: Alexis Breust Date: Thu, 3 May 2018 11:07:39 +0200 Subject: [PATCH 13/13] Added name to nodes --- code/glTF2Exporter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index fcf8005ae..bd33848d9 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -759,7 +759,7 @@ void glTF2Exporter::ExportMeshes() if (c) p.attributes.color.push_back(c); } - + /*************** Vertices indices ****************/ if (aim->mNumFaces > 0) { std::vector indices; @@ -891,6 +891,8 @@ unsigned int glTF2Exporter::ExportNodeHierarchy(const aiNode* n) { Ref node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node")); + node->name = n->mName.C_Str(); + if (!n->mTransformation.IsIdentity()) { node->matrix.isPresent = true; CopyValue(n->mTransformation, node->matrix.value);