export support.
pull/1180/head
Kim Kulling 2017-02-04 14:51:23 +01:00
parent f9fa95a7c2
commit 692fb216f7
4 changed files with 135 additions and 45 deletions

View File

@ -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_EXPORT
#ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER #ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER
@ -53,8 +51,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/scene.h> #include <assimp/scene.h>
#include <memory> #include <memory>
using namespace Assimp; using namespace Assimp;
namespace Assimp { namespace Assimp {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -90,7 +88,10 @@ ObjExporter :: ObjExporter(const char* _filename, const aiScene* pScene)
: filename(_filename) : filename(_filename)
, pScene(pScene) , pScene(pScene)
, endl("\n") , endl("\n")
{ , vp()
, vn()
, vt()
, vc() {
// 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
const std::locale& l = std::locale("C"); const std::locale& l = std::locale("C");
mOutput.imbue(l); mOutput.imbue(l);
@ -170,7 +171,6 @@ void ObjExporter::WriteMaterialFile()
mOutputMat << "Tf " << c.r << " " << c.g << " " << c.b << endl; mOutputMat << "Tf " << c.r << " " << c.g << " " << c.b << endl;
} }
ai_real o; ai_real o;
if(AI_SUCCESS == mat->Get(AI_MATKEY_OPACITY,o)) { if(AI_SUCCESS == mat->Get(AI_MATKEY_OPACITY,o)) {
mOutputMat << "d " << o << endl; mOutputMat << "d " << o << endl;
@ -213,8 +213,7 @@ void ObjExporter::WriteMaterialFile()
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ObjExporter :: WriteGeometryFile() void ObjExporter::WriteGeometryFile() {
{
WriteHeader(mOutput); WriteHeader(mOutput);
mOutput << "mtllib " << GetMaterialLibName() << endl << endl; mOutput << "mtllib " << GetMaterialLibName() << endl << endl;
@ -222,12 +221,22 @@ void ObjExporter :: WriteGeometryFile()
aiMatrix4x4 mBase; aiMatrix4x4 mBase;
AddNode(pScene->mRootNode, mBase); AddNode(pScene->mRootNode, mBase);
// write vertex positions // write vertex positions with colors, if any
vpMap.getVectors( vp ); vpMap.getVectors( vp );
vcMap.getColors( vc );
if ( vc.empty() ) {
mOutput << "# " << vp.size() << " vertex positions" << endl; mOutput << "# " << vp.size() << " vertex positions" << endl;
for ( const aiVector3D& v : vp ) { for ( const aiVector3D& v : vp ) {
mOutput << "v " << v.x << " " << v.y << " " << v.z << endl; 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; mOutput << endl;
// write uv coordinates // write uv coordinates
@ -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); vecIndexMap::dataType::iterator vertIt = vecMap.find(vec);
// vertex already exists, so reference it // vertex already exists, so reference it
if(vertIt != vecMap.end()){ 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()); vecs.resize(vecMap.size());
for(vecIndexMap::dataType::iterator it = vecMap.begin(); it != vecMap.end(); ++it){ for(vecIndexMap::dataType::iterator it = vecMap.begin(); it != vecMap.end(); ++it){
vecs[it->second-1] = it->first; 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()); meshes.push_back(MeshInstance());
MeshInstance& mesh = meshes.back(); MeshInstance& mesh = meshes.back();
@ -337,15 +365,20 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4
if (m->mNormals) { if (m->mNormals) {
aiVector3D norm = aiMatrix3x3(mat) * m->mNormals[idx]; aiVector3D norm = aiMatrix3x3(mat) * m->mNormals[idx];
face.indices[a].vn = vnMap.getIndex(norm); face.indices[a].vn = vnMap.getIndex(norm);
} } else {
else{
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 = vcMap.getIndex( col4 );
} else {
face.indices[ a ].vc = 0;
}
if ( m->mTextureCoords[ 0 ] ) { if ( m->mTextureCoords[ 0 ] ) {
face.indices[a].vt = vtMap.getIndex(m->mTextureCoords[0][idx]); face.indices[a].vt = vtMap.getIndex(m->mTextureCoords[0][idx]);
} } else {
else{
face.indices[a].vt = 0; face.indices[a].vt = 0;
} }
} }

View File

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

View File

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

View File

@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "AbstractImportExportBase.h" #include "AbstractImportExportBase.h"
#include <assimp/Importer.hpp> #include <assimp/Importer.hpp>
#include <assimp/Exporter.hpp>
#include <assimp/scene.h> #include <assimp/scene.h>
using namespace Assimp; using namespace Assimp;
@ -194,6 +195,16 @@ protected:
return nullptr != scene; 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: protected:
Assimp::Importer *m_im; Assimp::Importer *m_im;
aiScene *m_expectedScene; aiScene *m_expectedScene;
@ -203,6 +214,10 @@ TEST_F( utObjImportExport, importObjFromFileTest ) {
EXPECT_TRUE( importerTest() ); EXPECT_TRUE( importerTest() );
} }
TEST_F( utObjImportExport, exportObjFromFileTest ) {
EXPECT_TRUE( exporterTest() );
}
TEST_F( utObjImportExport, obj_import_test ) { TEST_F( utObjImportExport, obj_import_test ) {
const aiScene *scene = m_im->ReadFileFromMemory( (void*) ObjModel.c_str(), ObjModel.size(), 0 ); const aiScene *scene = m_im->ReadFileFromMemory( (void*) ObjModel.c_str(), ObjModel.size(), 0 );
aiScene *expected = createScene(); 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 ); const aiScene *scene = m_im->ReadFileFromMemory( ( void* ) ObjModel_Issue1111.c_str(), ObjModel_Issue1111.size(), 0 );
EXPECT_NE( nullptr, scene ); 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" ) );
}