1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2021, assimp team
7 
8 All rights reserved.
9 
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the following
12 conditions are met:
13 
14 * Redistributions of source code must retain the above
15   copyright notice, this list of conditions and the
16   following disclaimer.
17 
18 * Redistributions in binary form must reproduce the above
19   copyright notice, this list of conditions and the
20   following disclaimer in the documentation and/or other
21   materials provided with the distribution.
22 
23 * Neither the name of the assimp team, nor the names of its
24   contributors may be used to endorse or promote products
25   derived from this software without specific prior
26   written permission of the assimp team.
27 
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 ---------------------------------------------------------------------------
40 */
41 
42 /** @file Exporter.cpp
43 
44 Assimp export interface. While it's public interface bears many similarities
45 to the import interface (in fact, it is largely symmetric), the internal
46 implementations differs a lot. Exporters are considered stateless and are
47 simple callbacks which we maintain in a global list along with their
48 description strings.
49 
50 Here we implement only the C++ interface (Assimp::Exporter).
51 */
52 
53 #ifndef ASSIMP_BUILD_NO_EXPORT
54 
55 #include <assimp/BlobIOSystem.h>
56 #include <assimp/SceneCombiner.h>
57 #include <assimp/DefaultIOSystem.h>
58 #include <assimp/Exporter.hpp>
59 #include <assimp/mesh.h>
60 #include <assimp/postprocess.h>
61 #include <assimp/scene.h>
62 #include <assimp/Exceptional.h>
63 
64 #include "Common/DefaultProgressHandler.h"
65 #include "Common/BaseProcess.h"
66 #include "Common/ScenePrivate.h"
67 #include "PostProcessing/CalcTangentsProcess.h"
68 #include "PostProcessing/MakeVerboseFormat.h"
69 #include "PostProcessing/JoinVerticesProcess.h"
70 #include "PostProcessing/ConvertToLHProcess.h"
71 #include "PostProcessing/PretransformVertices.h"
72 
73 #include <memory>
74 
75 namespace Assimp {
76 
77 #ifdef _MSC_VER
78 #    pragma warning( disable : 4800 )
79 #endif // _MSC_VER
80 
81 
82 // PostStepRegistry.cpp
83 void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out);
84 
85 // ------------------------------------------------------------------------------------------------
86 // Exporter worker function prototypes. Do not use const, because some exporter need to convert
87 // the scene temporary
88 #ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER
89 void ExportSceneCollada(const char*,IOSystem*, const aiScene*, const ExportProperties*);
90 #endif
91 #ifndef ASSIMP_BUILD_NO_X_EXPORTER
92 void ExportSceneXFile(const char*,IOSystem*, const aiScene*, const ExportProperties*);
93 #endif
94 #ifndef ASSIMP_BUILD_NO_STEP_EXPORTER
95 void ExportSceneStep(const char*,IOSystem*, const aiScene*, const ExportProperties*);
96 #endif
97 #ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER
98 void ExportSceneObj(const char*,IOSystem*, const aiScene*, const ExportProperties*);
99 void ExportSceneObjNoMtl(const char*,IOSystem*, const aiScene*, const ExportProperties*);
100 #endif
101 #ifndef ASSIMP_BUILD_NO_STL_EXPORTER
102 void ExportSceneSTL(const char*,IOSystem*, const aiScene*, const ExportProperties*);
103 void ExportSceneSTLBinary(const char*,IOSystem*, const aiScene*, const ExportProperties*);
104 #endif
105 #ifndef ASSIMP_BUILD_NO_PLY_EXPORTER
106 void ExportScenePly(const char*,IOSystem*, const aiScene*, const ExportProperties*);
107 void ExportScenePlyBinary(const char*, IOSystem*, const aiScene*, const ExportProperties*);
108 #endif
109 #ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
110 void ExportScene3DS(const char*, IOSystem*, const aiScene*, const ExportProperties*);
111 #endif
112 #ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER
113 void ExportSceneGLTF(const char*, IOSystem*, const aiScene*, const ExportProperties*);
114 void ExportSceneGLB(const char*, IOSystem*, const aiScene*, const ExportProperties*);
115 void ExportSceneGLTF2(const char*, IOSystem*, const aiScene*, const ExportProperties*);
116 void ExportSceneGLB2(const char*, IOSystem*, const aiScene*, const ExportProperties*);
117 #endif
118 #ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
119 void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*);
120 #endif
121 #ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
122 void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*);
123 #endif
124 #ifndef ASSIMP_BUILD_NO_X3D_EXPORTER
125 void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
126 #endif
127 #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
128 void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*);
129 void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*);
130 #endif
131 #ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
132 void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* );
133 #endif
134 #ifndef ASSIMP_BUILD_NO_M3D_EXPORTER
135 void ExportSceneM3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
136 void ExportSceneM3DA(const char*, IOSystem*, const aiScene*, const ExportProperties*);
137 #endif
138 #ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
139 void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*);
140 #endif
141 #ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER
142 void ExportScenePbrt(const char*, IOSystem*, const aiScene*, const ExportProperties*);
143 #endif
144 
setupExporterArray(std::vector<Exporter::ExportFormatEntry> & exporters)145 static void setupExporterArray(std::vector<Exporter::ExportFormatEntry> &exporters) {
146 	(void)exporters;
147 
148 #ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER
149 	exporters.push_back(Exporter::ExportFormatEntry("collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada));
150 #endif
151 
152 #ifndef ASSIMP_BUILD_NO_X_EXPORTER
153 	exporters.push_back(Exporter::ExportFormatEntry("x", "X Files", "x", &ExportSceneXFile,
154 			aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs));
155 #endif
156 
157 #ifndef ASSIMP_BUILD_NO_STEP_EXPORTER
158 	exporters.push_back(Exporter::ExportFormatEntry("stp", "Step Files", "stp", &ExportSceneStep, 0));
159 #endif
160 
161 #ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER
162 	exporters.push_back(Exporter::ExportFormatEntry("obj", "Wavefront OBJ format", "obj", &ExportSceneObj,
163 			aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */));
164 	exporters.push_back(Exporter::ExportFormatEntry("objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl,
165 			aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */));
166 #endif
167 
168 #ifndef ASSIMP_BUILD_NO_STL_EXPORTER
169 	exporters.push_back(Exporter::ExportFormatEntry("stl", "Stereolithography", "stl", &ExportSceneSTL,
170 			aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices));
171 	exporters.push_back(Exporter::ExportFormatEntry("stlb", "Stereolithography (binary)", "stl", &ExportSceneSTLBinary,
172 			aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices));
173 #endif
174 
175 #ifndef ASSIMP_BUILD_NO_PLY_EXPORTER
176 	exporters.push_back(Exporter::ExportFormatEntry("ply", "Stanford Polygon Library", "ply", &ExportScenePly,
177 			aiProcess_PreTransformVertices));
178 	exporters.push_back(Exporter::ExportFormatEntry("plyb", "Stanford Polygon Library (binary)", "ply", &ExportScenePlyBinary,
179 			aiProcess_PreTransformVertices));
180 #endif
181 
182 #ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
183 	exporters.push_back(Exporter::ExportFormatEntry("3ds", "Autodesk 3DS (legacy)", "3ds", &ExportScene3DS,
184 			aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices));
185 #endif
186 
187 #if !defined(ASSIMP_BUILD_NO_GLTF_EXPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_EXPORTER)
188 	exporters.push_back(Exporter::ExportFormatEntry("gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2,
189 			aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
190 	exporters.push_back(Exporter::ExportFormatEntry("glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2,
191 			aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
192 #endif
193 
194 #if !defined(ASSIMP_BUILD_NO_GLTF_EXPORTER) && !defined(ASSIMP_BUILD_NO_GLTF1_EXPORTER)
195 	exporters.push_back(Exporter::ExportFormatEntry("gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF,
196 			aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
197 	exporters.push_back(Exporter::ExportFormatEntry("glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
198 			aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
199 #endif
200 
201 #ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
202 	exporters.push_back(Exporter::ExportFormatEntry("assbin", "Assimp Binary File", "assbin", &ExportSceneAssbin, 0));
203 #endif
204 
205 #ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
206 	exporters.push_back(Exporter::ExportFormatEntry("assxml", "Assimp XML Document", "assxml", &ExportSceneAssxml, 0));
207 #endif
208 
209 #ifndef ASSIMP_BUILD_NO_X3D_EXPORTER
210 	exporters.push_back(Exporter::ExportFormatEntry("x3d", "Extensible 3D", "x3d", &ExportSceneX3D, 0));
211 #endif
212 
213 #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
214 	exporters.push_back(Exporter::ExportFormatEntry("fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0));
215 	exporters.push_back(Exporter::ExportFormatEntry("fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0));
216 #endif
217 
218 #ifndef ASSIMP_BUILD_NO_M3D_EXPORTER
219 	exporters.push_back(Exporter::ExportFormatEntry("m3d", "Model 3D (binary)", "m3d", &ExportSceneM3D, 0));
220 	exporters.push_back(Exporter::ExportFormatEntry("m3da", "Model 3D (ascii)", "a3d", &ExportSceneM3DA, 0));
221 #endif
222 
223 #ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
224 	exporters.push_back(Exporter::ExportFormatEntry("3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0));
225 #endif
226 
227 #ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER
228 	exporters.push_back(Exporter::ExportFormatEntry("pbrt", "pbrt-v4 scene description file", "pbrt", &ExportScenePbrt, aiProcess_Triangulate | aiProcess_SortByPType));
229 #endif
230 
231 #ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
232 	exporters.push_back(Exporter::ExportFormatEntry("assjson", "Assimp JSON Document", "json", &ExportAssimp2Json, 0));
233 #endif
234 }
235 
236 class ExporterPimpl {
237 public:
ExporterPimpl()238     ExporterPimpl()
239     : blob()
240     , mIOSystem(new Assimp::DefaultIOSystem())
241     , mIsDefaultIOHandler(true)
242     , mProgressHandler( nullptr )
243     , mIsDefaultProgressHandler( true )
244     , mPostProcessingSteps()
245     , mError()
246     , mExporters() {
247         GetPostProcessingStepInstanceList(mPostProcessingSteps);
248 
249         // grab all built-in exporters
250 		setupExporterArray(mExporters);
251     }
252 
~ExporterPimpl()253     ~ExporterPimpl() {
254         delete blob;
255 
256         // Delete all post-processing plug-ins
257         for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) {
258             delete mPostProcessingSteps[a];
259         }
260         delete mProgressHandler;
261     }
262 
263 public:
264     aiExportDataBlob* blob;
265     std::shared_ptr< Assimp::IOSystem > mIOSystem;
266     bool mIsDefaultIOHandler;
267 
268     /** The progress handler */
269     ProgressHandler *mProgressHandler;
270     bool mIsDefaultProgressHandler;
271 
272     /** Post processing steps we can apply at the imported data. */
273     std::vector< BaseProcess* > mPostProcessingSteps;
274 
275     /** Last fatal export error */
276     std::string mError;
277 
278     /** Exporters, this includes those registered using #Assimp::Exporter::RegisterExporter */
279     std::vector<Exporter::ExportFormatEntry> mExporters;
280 };
281 
282 } // end of namespace Assimp
283 
284 using namespace Assimp;
285 
286 // ------------------------------------------------------------------------------------------------
Exporter()287 Exporter :: Exporter()
288 : pimpl(new ExporterPimpl()) {
289     pimpl->mProgressHandler = new DefaultProgressHandler();
290 }
291 
292 // ------------------------------------------------------------------------------------------------
~Exporter()293 Exporter::~Exporter() {
294 	ai_assert(nullptr != pimpl);
295 	FreeBlob();
296     delete pimpl;
297 }
298 
299 // ------------------------------------------------------------------------------------------------
SetIOHandler(IOSystem * pIOHandler)300 void Exporter::SetIOHandler( IOSystem* pIOHandler) {
301 	ai_assert(nullptr != pimpl);
302 	pimpl->mIsDefaultIOHandler = !pIOHandler;
303     pimpl->mIOSystem.reset(pIOHandler);
304 }
305 
306 // ------------------------------------------------------------------------------------------------
GetIOHandler() const307 IOSystem* Exporter::GetIOHandler() const {
308 	ai_assert(nullptr != pimpl);
309 	return pimpl->mIOSystem.get();
310 }
311 
312 // ------------------------------------------------------------------------------------------------
IsDefaultIOHandler() const313 bool Exporter::IsDefaultIOHandler() const {
314 	ai_assert(nullptr != pimpl);
315 	return pimpl->mIsDefaultIOHandler;
316 }
317 
318 // ------------------------------------------------------------------------------------------------
SetProgressHandler(ProgressHandler * pHandler)319 void Exporter::SetProgressHandler(ProgressHandler* pHandler) {
320     ai_assert(nullptr != pimpl);
321 
322     if ( nullptr == pHandler) {
323         // Release pointer in the possession of the caller
324         pimpl->mProgressHandler = new DefaultProgressHandler();
325         pimpl->mIsDefaultProgressHandler = true;
326         return;
327     }
328 
329     if (pimpl->mProgressHandler == pHandler) {
330         return;
331     }
332 
333     delete pimpl->mProgressHandler;
334     pimpl->mProgressHandler = pHandler;
335     pimpl->mIsDefaultProgressHandler = false;
336 }
337 
338 // ------------------------------------------------------------------------------------------------
ExportToBlob(const aiScene * pScene,const char * pFormatId,unsigned int pPreprocessing,const ExportProperties * pProperties)339 const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId,
340                                                 unsigned int pPreprocessing, const ExportProperties* pProperties) {
341 	ai_assert(nullptr != pimpl);
342     if (pimpl->blob) {
343         delete pimpl->blob;
344         pimpl->blob = nullptr;
345     }
346 
347     auto baseName = pProperties ? pProperties->GetPropertyString(AI_CONFIG_EXPORT_BLOB_NAME, AI_BLOBIO_MAGIC) : AI_BLOBIO_MAGIC;
348 
349     std::shared_ptr<IOSystem> old = pimpl->mIOSystem;
350     BlobIOSystem *blobio = new BlobIOSystem(baseName);
351     pimpl->mIOSystem = std::shared_ptr<IOSystem>( blobio );
352 
353     if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName(), pPreprocessing, pProperties)) {
354         pimpl->mIOSystem = old;
355         return nullptr;
356     }
357 
358     pimpl->blob = blobio->GetBlobChain();
359     pimpl->mIOSystem = old;
360 
361     return pimpl->blob;
362 }
363 
364 // ------------------------------------------------------------------------------------------------
Export(const aiScene * pScene,const char * pFormatId,const char * pPath,unsigned int pPreprocessing,const ExportProperties * pProperties)365 aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath,
366         unsigned int pPreprocessing, const ExportProperties* pProperties) {
367     ASSIMP_BEGIN_EXCEPTION_REGION();
368 	ai_assert(nullptr != pimpl);
369     // when they create scenes from scratch, users will likely create them not in verbose
370     // format. They will likely not be aware that there is a flag in the scene to indicate
371     // this, however. To avoid surprises and bug reports, we check for duplicates in
372     // meshes upfront.
373     const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || MakeVerboseFormatProcess::IsVerboseFormat(pScene);
374 
375     pimpl->mProgressHandler->UpdateFileWrite(0, 4);
376 
377     pimpl->mError = "";
378     for (size_t i = 0; i < pimpl->mExporters.size(); ++i) {
379         const Exporter::ExportFormatEntry& exp = pimpl->mExporters[i];
380         if (!strcmp(exp.mDescription.id,pFormatId)) {
381             try {
382                 // Always create a full copy of the scene. We might optimize this one day,
383                 // but for now it is the most pragmatic way.
384                 aiScene* scenecopy_tmp = nullptr;
385                 SceneCombiner::CopyScene(&scenecopy_tmp,pScene);
386 
387                 pimpl->mProgressHandler->UpdateFileWrite(1, 4);
388 
389                 std::unique_ptr<aiScene> scenecopy(scenecopy_tmp);
390                 const ScenePrivateData* const priv = ScenePriv(pScene);
391 
392                 // steps that are not idempotent, i.e. we might need to run them again, usually to get back to the
393                 // original state before the step was applied first. When checking which steps we don't need
394                 // to run, those are excluded.
395                 const unsigned int nonIdempotentSteps = aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_MakeLeftHanded;
396 
397                 // Erase all pp steps that were already applied to this scene
398                 const unsigned int pp = (exp.mEnforcePP | pPreprocessing) & ~(priv && !priv->mIsCopy
399                     ? (priv->mPPStepsApplied & ~nonIdempotentSteps)
400                     : 0u);
401 
402                 // If no extra post-processing was specified, and we obtained this scene from an
403                 // Assimp importer, apply the reverse steps automatically.
404                 // TODO: either drop this, or document it. Otherwise it is just a bad surprise.
405                 //if (!pPreprocessing && priv) {
406                 //  pp |= (nonIdempotentSteps & priv->mPPStepsApplied);
407                 //}
408 
409                 // If the input scene is not in verbose format, but there is at least post-processing step that relies on it,
410                 // we need to run the MakeVerboseFormat step first.
411                 bool must_join_again = false;
412                 if (!is_verbose_format) {
413                     bool verbosify = false;
414                     for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
415                         BaseProcess* const p = pimpl->mPostProcessingSteps[a];
416 
417                         if (p->IsActive(pp) && p->RequireVerboseFormat()) {
418                             verbosify = true;
419                             break;
420                         }
421                     }
422 
423                     if (verbosify || (exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) {
424                         ASSIMP_LOG_DEBUG("export: Scene data not in verbose format, applying MakeVerboseFormat step first");
425 
426                         MakeVerboseFormatProcess proc;
427                         proc.Execute(scenecopy.get());
428 
429                         if(!(exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) {
430                             must_join_again = true;
431                         }
432                     }
433                 }
434 
435                 pimpl->mProgressHandler->UpdateFileWrite(2, 4);
436 
437                 if (pp) {
438                     // the three 'conversion' steps need to be executed first because all other steps rely on the standard data layout
439                     {
440                         FlipWindingOrderProcess step;
441                         if (step.IsActive(pp)) {
442                             step.Execute(scenecopy.get());
443                         }
444                     }
445 
446                     {
447                         FlipUVsProcess step;
448                         if (step.IsActive(pp)) {
449                             step.Execute(scenecopy.get());
450                         }
451                     }
452 
453                     {
454                         MakeLeftHandedProcess step;
455                         if (step.IsActive(pp)) {
456                             step.Execute(scenecopy.get());
457                         }
458                     }
459 
460                     bool exportPointCloud(false);
461                     if (nullptr != pProperties) {
462                         exportPointCloud = pProperties->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS);
463                     }
464 
465                     // dispatch other processes
466                     for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
467                         BaseProcess* const p = pimpl->mPostProcessingSteps[a];
468 
469                         if (p->IsActive(pp)
470                             && !dynamic_cast<FlipUVsProcess*>(p)
471                             && !dynamic_cast<FlipWindingOrderProcess*>(p)
472                             && !dynamic_cast<MakeLeftHandedProcess*>(p)) {
473                             if (dynamic_cast<PretransformVertices*>(p) && exportPointCloud) {
474                                 continue;
475                             }
476                             p->Execute(scenecopy.get());
477                         }
478                     }
479                     ScenePrivateData* const privOut = ScenePriv(scenecopy.get());
480                     ai_assert(nullptr != privOut);
481 
482                     privOut->mPPStepsApplied |= pp;
483                 }
484 
485                 pimpl->mProgressHandler->UpdateFileWrite(3, 4);
486 
487                 if(must_join_again) {
488                     JoinVerticesProcess proc;
489                     proc.Execute(scenecopy.get());
490                 }
491 
492                 ExportProperties emptyProperties;  // Never pass nullptr ExportProperties so Exporters don't have to worry.
493                 ExportProperties* pProp = pProperties ? (ExportProperties*)pProperties : &emptyProperties;
494         		pProp->SetPropertyBool("bJoinIdenticalVertices", pp & aiProcess_JoinIdenticalVertices);
495                 exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
496 
497                 pimpl->mProgressHandler->UpdateFileWrite(4, 4);
498             } catch (DeadlyExportError& err) {
499                 pimpl->mError = err.what();
500                 return AI_FAILURE;
501             }
502             return AI_SUCCESS;
503         }
504     }
505 
506     pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId;
507     ASSIMP_END_EXCEPTION_REGION(aiReturn);
508 
509     return AI_FAILURE;
510 }
511 
512 // ------------------------------------------------------------------------------------------------
GetErrorString() const513 const char* Exporter::GetErrorString() const {
514 	ai_assert(nullptr != pimpl);
515     return pimpl->mError.c_str();
516 }
517 
518 // ------------------------------------------------------------------------------------------------
FreeBlob()519 void Exporter::FreeBlob() {
520 	ai_assert(nullptr != pimpl);
521     delete pimpl->blob;
522     pimpl->blob = nullptr;
523 
524     pimpl->mError = "";
525 }
526 
527 // ------------------------------------------------------------------------------------------------
GetBlob() const528 const aiExportDataBlob* Exporter::GetBlob() const {
529 	ai_assert(nullptr != pimpl);
530 	return pimpl->blob;
531 }
532 
533 // ------------------------------------------------------------------------------------------------
GetOrphanedBlob() const534 const aiExportDataBlob* Exporter::GetOrphanedBlob() const {
535 	ai_assert(nullptr != pimpl);
536 	const aiExportDataBlob *tmp = pimpl->blob;
537     pimpl->blob = nullptr;
538     return tmp;
539 }
540 
541 // ------------------------------------------------------------------------------------------------
GetExportFormatCount() const542 size_t Exporter::GetExportFormatCount() const {
543 	ai_assert(nullptr != pimpl);
544     return pimpl->mExporters.size();
545 }
546 
547 // ------------------------------------------------------------------------------------------------
GetExportFormatDescription(size_t index) const548 const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const {
549 	ai_assert(nullptr != pimpl);
550 	if (index >= GetExportFormatCount()) {
551         return nullptr;
552     }
553 
554     // Return from static storage if the requested index is built-in.
555 	if (index < pimpl->mExporters.size()) {
556 		return &pimpl->mExporters[index].mDescription;
557     }
558 
559     return &pimpl->mExporters[index].mDescription;
560 }
561 
562 // ------------------------------------------------------------------------------------------------
RegisterExporter(const ExportFormatEntry & desc)563 aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
564 	ai_assert(nullptr != pimpl);
565 	for (const ExportFormatEntry &e : pimpl->mExporters) {
566         if (!strcmp(e.mDescription.id,desc.mDescription.id)) {
567             return aiReturn_FAILURE;
568         }
569     }
570 
571     pimpl->mExporters.push_back(desc);
572     return aiReturn_SUCCESS;
573 }
574 
575 // ------------------------------------------------------------------------------------------------
UnregisterExporter(const char * id)576 void Exporter::UnregisterExporter(const char* id) {
577 	ai_assert(nullptr != pimpl);
578 	for (std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin();
579             it != pimpl->mExporters.end(); ++it) {
580         if (!strcmp((*it).mDescription.id,id)) {
581             pimpl->mExporters.erase(it);
582             break;
583         }
584     }
585 }
586 
587 // ------------------------------------------------------------------------------------------------
ExportProperties()588 ExportProperties::ExportProperties() {
589     // empty
590 }
591 
592 // ------------------------------------------------------------------------------------------------
ExportProperties(const ExportProperties & other)593 ExportProperties::ExportProperties(const ExportProperties &other)
594 : mIntProperties(other.mIntProperties)
595 , mFloatProperties(other.mFloatProperties)
596 , mStringProperties(other.mStringProperties)
597 , mMatrixProperties(other.mMatrixProperties)
598 , mCallbackProperties(other.mCallbackProperties){
599     // empty
600 }
601 
SetPropertyCallback(const char * szName,const std::function<void * (void *)> & f)602 bool ExportProperties::SetPropertyCallback(const char *szName, const std::function<void *(void *)> &f) {
603     return SetGenericProperty<std::function<void *(void *)>>(mCallbackProperties, szName, f);
604 }
605 
GetPropertyCallback(const char * szName) const606 std::function<void *(void *)> ExportProperties::GetPropertyCallback(const char *szName) const {
607     return GetGenericProperty<std::function<void *(void *)>>(mCallbackProperties, szName, 0);
608 }
609 
HasPropertyCallback(const char * szName) const610 bool ExportProperties::HasPropertyCallback(const char *szName) const {
611     return HasGenericProperty<std::function<void *(void *)>>(mCallbackProperties, szName);
612 }
613 
614 // ------------------------------------------------------------------------------------------------
615 // Set a configuration property
SetPropertyInteger(const char * szName,int iValue)616 bool ExportProperties::SetPropertyInteger(const char* szName, int iValue) {
617     return SetGenericProperty<int>(mIntProperties, szName,iValue);
618 }
619 
620 // ------------------------------------------------------------------------------------------------
621 // Set a configuration property
SetPropertyFloat(const char * szName,ai_real iValue)622 bool ExportProperties::SetPropertyFloat(const char* szName, ai_real iValue) {
623     return SetGenericProperty<ai_real>(mFloatProperties, szName,iValue);
624 }
625 
626 // ------------------------------------------------------------------------------------------------
627 // Set a configuration property
SetPropertyString(const char * szName,const std::string & value)628 bool ExportProperties::SetPropertyString(const char* szName, const std::string& value) {
629     return SetGenericProperty<std::string>(mStringProperties, szName,value);
630 }
631 
632 // ------------------------------------------------------------------------------------------------
633 // Set a configuration property
SetPropertyMatrix(const char * szName,const aiMatrix4x4 & value)634 bool ExportProperties::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) {
635     return SetGenericProperty<aiMatrix4x4>(mMatrixProperties, szName,value);
636 }
637 
638 // ------------------------------------------------------------------------------------------------
639 // Get a configuration property
GetPropertyInteger(const char * szName,int iErrorReturn) const640 int ExportProperties::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const {
641     return GetGenericProperty<int>(mIntProperties,szName,iErrorReturn);
642 }
643 
644 // ------------------------------------------------------------------------------------------------
645 // Get a configuration property
GetPropertyFloat(const char * szName,ai_real iErrorReturn) const646 ai_real ExportProperties::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const {
647     return GetGenericProperty<ai_real>(mFloatProperties,szName,iErrorReturn);
648 }
649 
650 // ------------------------------------------------------------------------------------------------
651 // Get a configuration property
GetPropertyString(const char * szName,const std::string & iErrorReturn) const652 const std::string ExportProperties::GetPropertyString(const char* szName,
653         const std::string& iErrorReturn /*= ""*/) const {
654     return GetGenericProperty<std::string>(mStringProperties,szName,iErrorReturn);
655 }
656 
657 // ------------------------------------------------------------------------------------------------
658 // Has a configuration property
GetPropertyMatrix(const char * szName,const aiMatrix4x4 & iErrorReturn) const659 const aiMatrix4x4 ExportProperties::GetPropertyMatrix(const char* szName,
660         const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const {
661     return GetGenericProperty<aiMatrix4x4>(mMatrixProperties,szName,iErrorReturn);
662 }
663 
664 // ------------------------------------------------------------------------------------------------
665 // Has a configuration property
HasPropertyInteger(const char * szName) const666 bool ExportProperties::HasPropertyInteger(const char* szName) const {
667     return HasGenericProperty<int>(mIntProperties, szName);
668 }
669 
670 // ------------------------------------------------------------------------------------------------
671 // Has a configuration property
HasPropertyBool(const char * szName) const672 bool ExportProperties::HasPropertyBool(const char* szName) const {
673     return HasGenericProperty<int>(mIntProperties, szName);
674 }
675 
676 // ------------------------------------------------------------------------------------------------
677 // Has a configuration property
HasPropertyFloat(const char * szName) const678 bool ExportProperties::HasPropertyFloat(const char* szName) const {
679     return HasGenericProperty<ai_real>(mFloatProperties, szName);
680 }
681 
682 // ------------------------------------------------------------------------------------------------
683 // Has a configuration property
HasPropertyString(const char * szName) const684 bool ExportProperties::HasPropertyString(const char* szName) const {
685     return HasGenericProperty<std::string>(mStringProperties, szName);
686 }
687 
688 // ------------------------------------------------------------------------------------------------
689 // Has a configuration property
HasPropertyMatrix(const char * szName) const690 bool ExportProperties::HasPropertyMatrix(const char* szName) const {
691     return HasGenericProperty<aiMatrix4x4>(mMatrixProperties, szName);
692 }
693 
694 
695 #endif // !ASSIMP_BUILD_NO_EXPORT
696