Merge branch 'master' into fix_fbx_empty_names

pull/1929/head
Kim Kulling 2018-05-02 14:57:08 +02:00 committed by GitHub
commit 7bb2d6271b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 356 additions and 200 deletions

View File

@ -62,6 +62,7 @@ Here we implement only the C++ interface (Assimp::Exporter).
#include "JoinVerticesProcess.h" #include "JoinVerticesProcess.h"
#include "MakeVerboseFormat.h" #include "MakeVerboseFormat.h"
#include "ConvertToLHProcess.h" #include "ConvertToLHProcess.h"
#include "PretransformVertices.h"
#include <assimp/Exceptional.h> #include <assimp/Exceptional.h>
#include "ScenePrivate.h" #include "ScenePrivate.h"
#include <memory> #include <memory>
@ -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 // dispatch other processes
for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
BaseProcess* const p = pimpl->mPostProcessingSteps[a]; BaseProcess* const p = pimpl->mPostProcessingSteps[a];
@ -405,7 +411,9 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c
&& !dynamic_cast<FlipUVsProcess*>(p) && !dynamic_cast<FlipUVsProcess*>(p)
&& !dynamic_cast<FlipWindingOrderProcess*>(p) && !dynamic_cast<FlipWindingOrderProcess*>(p)
&& !dynamic_cast<MakeLeftHandedProcess*>(p)) { && !dynamic_cast<MakeLeftHandedProcess*>(p)) {
if (dynamic_cast<PretransformVertices*>(p) && exportPointCloud) {
continue;
}
p->Execute(scenecopy.get()); p->Execute(scenecopy.get());
} }
} }
@ -441,7 +449,6 @@ const char* Exporter::GetErrorString() const {
return pimpl->mError.c_str(); return pimpl->mError.c_str();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Exporter::FreeBlob() { void Exporter::FreeBlob() {
delete pimpl->blob; delete pimpl->blob;
@ -470,7 +477,7 @@ size_t Exporter::GetExportFormatCount() const {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const { const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const {
if (index >= GetExportFormatCount()) { if (index >= GetExportFormatCount()) {
return NULL; return nullptr;
} }
// Return from static storage if the requested index is built-in. // Return from static storage if the requested index is built-in.
@ -495,7 +502,8 @@ aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Exporter::UnregisterExporter(const char* id) { void Exporter::UnregisterExporter(const char* id) {
for(std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin(); it != pimpl->mExporters.end(); ++it) { for(std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin();
it != pimpl->mExporters.end(); ++it) {
if (!strcmp((*it).mDescription.id,id)) { if (!strcmp((*it).mDescription.id,id)) {
pimpl->mExporters.erase(it); pimpl->mExporters.erase(it);
break; break;
@ -531,8 +539,7 @@ bool ExportProperties::SetPropertyFloat(const char* szName, ai_real iValue) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Set a configuration property // 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<std::string>(mStringProperties, szName,value); return SetGenericProperty<std::string>(mStringProperties, szName,value);
} }

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

View File

@ -60,14 +60,17 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
PretransformVertices::PretransformVertices() PretransformVertices::PretransformVertices()
: configKeepHierarchy (false), configNormalize(false), configTransform(false), configTransformation() : configKeepHierarchy (false)
{ , configNormalize(false)
, configTransform(false)
, configTransformation()
, mConfigPointCloud( false ) {
// empty
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Destructor, private as well // Destructor, private as well
PretransformVertices::~PretransformVertices() PretransformVertices::~PretransformVertices() {
{
// nothing to do here // 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)); configTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION,0));
configTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4()); 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]->mBones = NULL;
pScene->mMeshes[i]->mNumBones = 0; pScene->mMeshes[i]->mNumBones = 0;
} }
} } else {
else {
apcOutMeshes.reserve(pScene->mNumMaterials<<1u); apcOutMeshes.reserve(pScene->mNumMaterials<<1u);
std::list<unsigned int> aiVFormats; std::list<unsigned int> 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 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"); throw DeadlyImportError("No output meshes: all meshes are orphaned and are not referenced by any nodes");
} }
else else

View File

@ -61,15 +61,11 @@ namespace Assimp {
* and removes the whole graph. The output is a list of meshes, one for * and removes the whole graph. The output is a list of meshes, one for
* each material. * each material.
*/ */
class ASSIMP_API PretransformVertices : public BaseProcess class ASSIMP_API PretransformVertices : public BaseProcess {
{
public: public:
PretransformVertices (); PretransformVertices ();
~PretransformVertices (); ~PretransformVertices ();
public:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Check whether step is active // Check whether step is active
bool IsActive( unsigned int pFlags) const; bool IsActive( unsigned int pFlags) const;
@ -82,7 +78,6 @@ public:
// Setup import settings // Setup import settings
void SetupProperties(const Importer* pImp); void SetupProperties(const Importer* pImp);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** @brief Toggle the 'keep hierarchy' option /** @brief Toggle the 'keep hierarchy' option
* @param d hm ... difficult to guess what this means, hu!? * @param d hm ... difficult to guess what this means, hu!?
@ -100,7 +95,6 @@ public:
} }
private: private:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Count the number of nodes // Count the number of nodes
unsigned int CountNodes( aiNode* pcNode ); unsigned int CountNodes( aiNode* pcNode );
@ -161,6 +155,7 @@ private:
bool configNormalize; bool configNormalize;
bool configTransform; bool configTransform;
aiMatrix4x4 configTransformation; aiMatrix4x4 configTransformation;
bool mConfigPointCloud;
}; };
} // end of namespace Assimp } // end of namespace Assimp

View File

@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/mesh.h> #include <assimp/mesh.h>
class RemoveRedundantMatsTest; class RemoveRedundantMatsTest;
namespace Assimp { namespace Assimp {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -54,14 +54,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/ByteSwapper.h> #include <assimp/ByteSwapper.h>
using namespace Assimp; using namespace Assimp;
namespace Assimp { namespace Assimp {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Worker function for exporting a scene to Stereolithograpy. Prototyped and registered in Exporter.cpp // 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 // invoke the exporter
STLExporter exporter(pFile, pScene); STLExporter exporter(pFile, pScene, exportPointClouds );
if (exporter.mOutput.fail()) { if (exporter.mOutput.fail()) {
throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); 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<size_t>(exporter.mOutput.tellp()),1); outfile->Write( exporter.mOutput.str().c_str(), static_cast<size_t>(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 // invoke the exporter
STLExporter exporter(pFile, pScene, true); STLExporter exporter(pFile, pScene, exportPointClouds, true);
if (exporter.mOutput.fail()) { if (exporter.mOutput.fail()) {
throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile));
@ -95,9 +100,11 @@ void ExportSceneSTLBinary(const char* pFile,IOSystem* pIOSystem, const aiScene*
} // end of namespace Assimp } // end of namespace Assimp
static const char *SolidToken = "solid";
static const char *EndSolidToken = "endsolid";
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
STLExporter :: STLExporter(const char* _filename, const aiScene* pScene, bool binary) STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool exportPointClouds, bool binary)
: filename(_filename) : filename(_filename)
, endl("\n") , endl("\n")
{ {
@ -118,22 +125,55 @@ STLExporter :: STLExporter(const char* _filename, const aiScene* pScene, bool bi
} }
AI_SWAP4(meshnum); AI_SWAP4(meshnum);
mOutput.write((char *)&meshnum, 4); mOutput.write((char *)&meshnum, 4);
if (exportPointClouds) {
}
for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
WriteMeshBinary(pScene->mMeshes[i]); WriteMeshBinary(pScene->mMeshes[i]);
} }
} else { } else {
const std::string& name = "AssimpScene";
mOutput << "solid " << name << endl; // Exporting only point clouds
for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { if (exportPointClouds) {
WriteMesh(pScene->mMeshes[i]); WritePointCloud("Assimp_Pointcloud", pScene );
return;
} }
mOutput << "endsolid " << 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 ]);
}
mOutput << EndSolidToken << name << endl;
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void STLExporter :: WriteMesh(const aiMesh* m) 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)
{ {
for (unsigned int i = 0; i < m->mNumFaces; ++i) { for (unsigned int i = 0; i < m->mNumFaces; ++i) {
const aiFace& f = m->mFaces[i]; const aiFace& f = m->mFaces[i];
@ -145,7 +185,7 @@ void STLExporter :: WriteMesh(const aiMesh* m)
for(unsigned int a = 0; a < f.mNumIndices; ++a) { for(unsigned int a = 0; a < f.mNumIndices; ++a) {
nor += m->mNormals[f.mIndices[a]]; nor += m->mNormals[f.mIndices[a]];
} }
nor.Normalize(); nor.NormalizeSafe();
} }
mOutput << " facet normal " << nor.x << " " << nor.y << " " << nor.z << endl; mOutput << " facet normal " << nor.x << " " << nor.y << " " << nor.z << endl;
mOutput << " outer loop" << endl; mOutput << " outer loop" << endl;
@ -159,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) { for (unsigned int i = 0; i < m->mNumFaces; ++i) {
const aiFace& f = m->mFaces[i]; const aiFace& f = m->mFaces[i];

View File

@ -52,8 +52,7 @@ struct aiScene;
struct aiNode; struct aiNode;
struct aiMesh; struct aiMesh;
namespace Assimp namespace Assimp {
{
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
/** Helper class to export a given scene to a STL file. */ /** Helper class to export a given scene to a STL file. */
@ -62,15 +61,13 @@ class STLExporter
{ {
public: public:
/// Constructor for a specific scene to export /// Constructor for a specific scene to export
STLExporter(const char* filename, const aiScene* pScene, bool binary = false); STLExporter(const char* filename, const aiScene* pScene, bool exportPOintClouds, bool binary = false);
public:
/// public stringstreams to write all output into /// public stringstreams to write all output into
std::ostringstream mOutput; std::ostringstream mOutput;
private: private:
void WritePointCloud(const std::string &name, const aiScene* pScene);
void WriteMesh(const aiMesh* m); void WriteMesh(const aiMesh* m);
void WriteMeshBinary(const aiMesh* m); void WriteMeshBinary(const aiMesh* m);

View File

@ -75,4 +75,5 @@ bool CPUSupportsSSE2() {
#endif #endif
} }
} // Namespace Assimp } // Namespace Assimp

View File

@ -115,12 +115,16 @@ public:
} }
}; };
/**
public: * @brief The class constructor.
*/
Exporter(); Exporter();
/**
* @brief The class destructor.
*/
~Exporter(); ~Exporter();
public:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Supplies a custom IO handler to the exporter to use to open and /** Supplies a custom IO handler to the exporter to use to open and
* access files. * access files.
@ -172,8 +176,10 @@ public:
* Any IO handlers set via #SetIOHandler are ignored here. * Any IO handlers set via #SetIOHandler are ignored here.
* @note Use aiCopyScene() to get a modifiable copy of a previously * @note Use aiCopyScene() to get a modifiable copy of a previously
* imported scene. */ * 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 char* pFormatId,
const aiExportDataBlob* ExportToBlob( const aiScene* pScene, const std::string& pFormatId, unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = NULL); 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 /** Convenience function to export directly to a file. Use
@ -208,8 +214,10 @@ public:
* @return AI_SUCCESS if everything was fine. * @return AI_SUCCESS if everything was fine.
* @note Use aiCopyScene() to get a modifiable copy of a previously * @note Use aiCopyScene() to get a modifiable copy of a previously
* imported scene.*/ * 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 char* pFormatId, const char* pPath,
aiReturn Export( const aiScene* pScene, const std::string& pFormatId, const std::string& pPath, unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = NULL); 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 /** Returns an error description of an error that occurred in #Export

View File

@ -46,15 +46,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/Importer.hpp> #include <assimp/Importer.hpp>
#include <assimp/ai_assert.h> #include <assimp/ai_assert.h>
#include "Hash.h" #include "Hash.h"
#include <map>
#include <map>
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
template <class T> template <class T>
inline bool SetGenericProperty(std::map< unsigned int, T >& list, inline
const char* szName, const T& value) bool SetGenericProperty(std::map< unsigned int, T >& list,
{ const char* szName, const T& value) {
ai_assert(NULL != szName); ai_assert(nullptr != szName);
const uint32_t hash = SuperFastHash(szName); const uint32_t hash = SuperFastHash(szName);
typename std::map<unsigned int, T>::iterator it = list.find(hash); typename std::map<unsigned int, T>::iterator it = list.find(hash);
@ -63,20 +63,22 @@ inline bool SetGenericProperty(std::map< unsigned int, T >& list,
return false; return false;
} }
(*it).second = value; (*it).second = value;
return true; return true;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
template <class T> template <class T>
inline const T& GetGenericProperty(const std::map< unsigned int, T >& list, inline
const char* szName, const T& errorReturn) const T& GetGenericProperty(const std::map< unsigned int, T >& list,
{ const char* szName, const T& errorReturn) {
ai_assert(NULL != szName); ai_assert(nullptr != szName);
const uint32_t hash = SuperFastHash(szName); const uint32_t hash = SuperFastHash(szName);
typename std::map<unsigned int, T>::const_iterator it = list.find(hash); typename std::map<unsigned int, T>::const_iterator it = list.find(hash);
if (it == list.end()) if (it == list.end()) {
return errorReturn; return errorReturn;
}
return (*it).second; 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 // Special version for pointer types - they will be deleted when replaced with another value
// passing NULL removes the whole property // passing NULL removes the whole property
template <class T> template <class T>
inline void SetGenericPropertyPtr(std::map< unsigned int, T* >& list, inline
const char* szName, T* value, bool* bWasExisting = NULL) void SetGenericPropertyPtr(std::map< unsigned int, T* >& list,
{ const char* szName, T* value, bool* bWasExisting = nullptr ) {
ai_assert(NULL != szName); ai_assert(nullptr != szName);
const uint32_t hash = SuperFastHash(szName); const uint32_t hash = SuperFastHash(szName);
typename std::map<unsigned int, T*>::iterator it = list.find(hash); typename std::map<unsigned int, T*>::iterator it = list.find(hash);
if (it == list.end()) { if (it == list.end()) {
if (bWasExisting) if (bWasExisting) {
*bWasExisting = false; *bWasExisting = false;
}
list.insert(std::pair<unsigned int,T*>( hash, value )); list.insert(std::pair<unsigned int,T*>( hash, value ));
return; return;
@ -106,20 +109,23 @@ inline void SetGenericPropertyPtr(std::map< unsigned int, T* >& list,
if (!value) { if (!value) {
list.erase(it); list.erase(it);
} }
if (bWasExisting) if (bWasExisting) {
*bWasExisting = true; *bWasExisting = true;
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
template <class T> template <class T>
inline bool HasGenericProperty(const std::map< unsigned int, T >& list, inline
const char* szName) bool HasGenericProperty(const std::map< unsigned int, T >& list,
{ const char* szName) {
ai_assert(NULL != szName); ai_assert(nullptr != szName);
const uint32_t hash = SuperFastHash(szName); const uint32_t hash = SuperFastHash(szName);
typename std::map<unsigned int, T>::const_iterator it = list.find(hash); typename std::map<unsigned int, T>::const_iterator it = list.find(hash);
if (it == list.end()) return false; if (it == list.end()) {
return false;
}
return true; return true;
} }

View File

@ -85,7 +85,6 @@ struct aiExportFormatDesc
*/ */
ASSIMP_API size_t aiGetExportFormatCount(void); ASSIMP_API size_t aiGetExportFormatCount(void);
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
/** Returns a description of the nth export file format. Use #aiGetExportFormatCount() /** 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 * 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, C_STRUCT aiFileIO* pIO,
unsigned int pPreprocessing ); unsigned int pPreprocessing );
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
/** Describes a blob of exported scene data. Use #aiExportSceneToBlob() to create a blob containing an /** 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. * 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 * @param pPreprocessing Please see the documentation for #aiExportScene
* @return the exported data or NULL in case of error * @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 /** Releases the memory associated with the given exported data. Use this function to free a data blob

View File

@ -953,6 +953,11 @@ enum aiComponent
#define AI_CONFIG_EXPORT_XFILE_64BIT "EXPORT_XFILE_64BIT" #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 * @brief Specifies a gobal key factor for scale, float value
*/ */

View File

@ -1,13 +1,13 @@
g cube g cube
v 0.0 0.0 0.0 124 110 120 v 0.0 0.0 0.0 0.48627 0.43137 0.47059
v 0.0 0.0 1.0 24 0 121 v 0.0 0.0 1.0 0.09412 0.00000 0.47451
v 0.0 1.0 0.0 4 0 44 v 0.0 1.0 0.0 0.01569 0.00000 0.17255
v 0.0 1.0 1.0 224 0 10 v 0.0 1.0 1.0 0.87843 0.00000 0.03922
v 1.0 0.0 0.0 24 200 25 v 1.0 0.0 0.0 0.09412 0.78431 0.09804
v 1.0 0.0 1.0 124 10 56 v 1.0 0.0 1.0 0.48627 0.03922 0.21961
v 1.0 1.0 0.0 78 10 50 v 1.0 1.0 0.0 0.30588 0.03922 0.19608
v 1.0 1.0 1.0 23 0 200 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
vn 0.0 0.0 -1.0 vn 0.0 0.0 -1.0

View File

@ -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

View File

@ -267,6 +267,28 @@ TEST_F( utObjImportExport, issue809_vertex_color_Test ) {
#endif // ASSIMP_BUILD_NO_EXPORT #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 );
scene = importer.GetOrphanedScene();
#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
delete scene;
}
TEST_F( utObjImportExport, issue1453_segfault ) { TEST_F( utObjImportExport, issue1453_segfault ) {
static const std::string ObjModel = static const std::string ObjModel =
"v 0.0 0.0 0.0\n" "v 0.0 0.0 0.0\n"

View File

@ -138,18 +138,19 @@ TEST_F( utPLYImportExport, vertexColorTest ) {
EXPECT_EQ(2u, first_face.mIndices[2]); 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) { TEST_F(utPLYImportExport, pointcloudTest) {
Assimp::Importer importer; 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);
EXPECT_EQ(1u, scene->mNumMeshes); //Could not use aiProcess_ValidateDataStructure since it's missing faces.
EXPECT_NE(nullptr, scene->mMeshes[0]); const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/issue623.ply", 0);
EXPECT_EQ(24u, scene->mMeshes[0]->mNumVertices); EXPECT_NE(nullptr, scene);
EXPECT_EQ(aiPrimitiveType::aiPrimitiveType_POINT, scene->mMeshes[0]->mPrimitiveTypes);
EXPECT_EQ(0u, scene->mMeshes[0]->mNumFaces); 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 = static const char *test_file =

View File

@ -47,6 +47,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/Importer.hpp> #include <assimp/Importer.hpp>
#include <assimp/postprocess.h> #include <assimp/postprocess.h>
#include <assimp/Exporter.hpp>
#include <assimp/scene.h>
#include <vector>
using namespace Assimp; using namespace Assimp;
@ -68,3 +72,73 @@ TEST_F( utSTLImporterExporter, test_with_two_solids ) {
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/STL/triangle_with_two_solids.stl", aiProcess_ValidateDataStructure ); const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/STL/triangle_with_two_solids.stl", aiProcess_ValidateDataStructure );
EXPECT_NE( nullptr, scene ); EXPECT_NE( nullptr, scene );
} }
#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;
};
std::vector<XYZ> points;
for (size_t i = 0; i < 10; ++i) {
XYZ current;
current.x = static_cast<float>(i);
current.y = static_cast<float>(i);
current.z = static_cast<float>(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 );
delete properties;
}
#endif