diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 0a8f054b7..bd533f3b3 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; @@ -470,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. @@ -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; @@ -531,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/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 3d681f8c7..e4a1dbb66 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)); @@ -95,9 +100,11 @@ 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 binary) +STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool exportPointClouds, bool binary) : filename(_filename) , endl("\n") { @@ -118,22 +125,55 @@ 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"; - mOutput << "solid " << name << endl; + // Exporting only point clouds + if (exportPointClouds) { + WritePointCloud("Assimp_Pointcloud", pScene ); + return; + } + + // 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 :: 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) { 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) { nor += m->mNormals[f.mIndices[a]]; } - nor.Normalize(); + nor.NormalizeSafe(); } mOutput << " facet normal " << nor.x << " " << nor.y << " " << nor.z << 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) { const aiFace& f = m->mFaces[i]; diff --git a/code/STLExporter.h b/code/STLExporter.h index 1bd589627..6e8f90915 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. */ @@ -62,15 +61,13 @@ 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; private: - + void WritePointCloud(const std::string &name, const aiScene* pScene); void WriteMesh(const aiMesh* m); void WriteMeshBinary(const aiMesh* m); 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..3d1a9ea85 100644 --- a/include/assimp/Exporter.hpp +++ b/include/assimp/Exporter.hpp @@ -115,12 +115,16 @@ public: } }; - -public: + /** + * @brief The class constructor. + */ Exporter(); + + /** + * @brief The class destructor. + */ ~Exporter(); -public: // ------------------------------------------------------------------- /** Supplies a custom IO handler to the exporter to use to open and * access files. @@ -172,8 +176,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 +214,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/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/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 */ 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..181862560 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,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 ); 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 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 ); + + delete properties; +} + +#endif