/src/assimp/code/Common/Exporter.cpp
Line | Count | Source |
1 | | /* |
2 | | --------------------------------------------------------------------------- |
3 | | Open Asset Import Library (assimp) |
4 | | --------------------------------------------------------------------------- |
5 | | |
6 | | Copyright (c) 2006-2025, 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 | | |
145 | 56 | static void setupExporterArray(std::vector<Exporter::ExportFormatEntry> &exporters) { |
146 | 56 | (void)exporters; |
147 | | |
148 | 56 | #ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER |
149 | 56 | exporters.emplace_back("collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada); |
150 | 56 | #endif |
151 | | |
152 | 56 | #ifndef ASSIMP_BUILD_NO_X_EXPORTER |
153 | 56 | exporters.emplace_back("x", "X Files", "x", &ExportSceneXFile, |
154 | 56 | aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs); |
155 | 56 | #endif |
156 | | |
157 | 56 | #ifndef ASSIMP_BUILD_NO_STEP_EXPORTER |
158 | 56 | exporters.emplace_back("stp", "Step Files", "stp", &ExportSceneStep, 0); |
159 | 56 | #endif |
160 | | |
161 | 56 | #ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER |
162 | 56 | exporters.emplace_back("obj", "Wavefront OBJ format", "obj", &ExportSceneObj); |
163 | 56 | exporters.emplace_back("objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl); |
164 | 56 | #endif |
165 | | |
166 | 56 | #ifndef ASSIMP_BUILD_NO_STL_EXPORTER |
167 | 56 | exporters.emplace_back("stl", "Stereolithography", "stl", &ExportSceneSTL, |
168 | 56 | aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices); |
169 | 56 | exporters.emplace_back("stlb", "Stereolithography (binary)", "stl", &ExportSceneSTLBinary, |
170 | 56 | aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices); |
171 | 56 | #endif |
172 | | |
173 | 56 | #ifndef ASSIMP_BUILD_NO_PLY_EXPORTER |
174 | 56 | exporters.emplace_back("ply", "Stanford Polygon Library", "ply", &ExportScenePly, |
175 | 56 | aiProcess_PreTransformVertices); |
176 | 56 | exporters.emplace_back("plyb", "Stanford Polygon Library (binary)", "ply", &ExportScenePlyBinary, |
177 | 56 | aiProcess_PreTransformVertices); |
178 | 56 | #endif |
179 | | |
180 | 56 | #ifndef ASSIMP_BUILD_NO_3DS_EXPORTER |
181 | 56 | exporters.emplace_back("3ds", "Autodesk 3DS (legacy)", "3ds", &ExportScene3DS, |
182 | 56 | aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices); |
183 | 56 | #endif |
184 | | |
185 | 56 | #if !defined(ASSIMP_BUILD_NO_GLTF_EXPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_EXPORTER) |
186 | 56 | exporters.emplace_back("gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2, |
187 | 56 | aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType); |
188 | 56 | exporters.emplace_back("glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2, |
189 | 56 | aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType); |
190 | 56 | #endif |
191 | | |
192 | 56 | #if !defined(ASSIMP_BUILD_NO_GLTF_EXPORTER) && !defined(ASSIMP_BUILD_NO_GLTF1_EXPORTER) |
193 | 56 | exporters.emplace_back("gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF, |
194 | 56 | aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType); |
195 | 56 | exporters.emplace_back("glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB, |
196 | 56 | aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType); |
197 | 56 | #endif |
198 | | |
199 | 56 | #ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER |
200 | 56 | exporters.emplace_back("assbin", "Assimp Binary File", "assbin", &ExportSceneAssbin, 0); |
201 | 56 | #endif |
202 | | |
203 | 56 | #ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER |
204 | 56 | exporters.emplace_back("assxml", "Assimp XML Document", "assxml", &ExportSceneAssxml, 0); |
205 | 56 | #endif |
206 | | |
207 | 56 | #ifndef ASSIMP_BUILD_NO_X3D_EXPORTER |
208 | 56 | exporters.emplace_back("x3d", "Extensible 3D", "x3d", &ExportSceneX3D, 0); |
209 | 56 | #endif |
210 | | |
211 | 56 | #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER |
212 | 56 | exporters.emplace_back("fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0); |
213 | 56 | exporters.emplace_back("fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0); |
214 | 56 | #endif |
215 | | |
216 | | #ifndef ASSIMP_BUILD_NO_M3D_EXPORTER |
217 | | exporters.push_back(Exporter::ExportFormatEntry("m3d", "Model 3D (binary)", "m3d", &ExportSceneM3D, 0)); |
218 | | exporters.push_back(Exporter::ExportFormatEntry("m3da", "Model 3D (ascii)", "a3d", &ExportSceneM3DA, 0)); |
219 | | #endif |
220 | | |
221 | 56 | #ifndef ASSIMP_BUILD_NO_3MF_EXPORTER |
222 | 56 | exporters.emplace_back("3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0); |
223 | 56 | #endif |
224 | | |
225 | 56 | #ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER |
226 | 56 | exporters.emplace_back("pbrt", "pbrt-v4 scene description file", "pbrt", &ExportScenePbrt, aiProcess_ConvertToLeftHanded | aiProcess_Triangulate | aiProcess_SortByPType); |
227 | 56 | #endif |
228 | | |
229 | 56 | #ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER |
230 | 56 | exporters.emplace_back("assjson", "Assimp JSON Document", "json", &ExportAssimp2Json, 0); |
231 | 56 | #endif |
232 | 56 | } |
233 | | |
234 | | class ExporterPimpl { |
235 | | public: |
236 | | ExporterPimpl() |
237 | | : blob() |
238 | 56 | , mIOSystem(new Assimp::DefaultIOSystem()) |
239 | 56 | , mIsDefaultIOHandler(true) |
240 | 56 | , mProgressHandler( nullptr ) |
241 | 56 | , mIsDefaultProgressHandler( true ) |
242 | 56 | , mPostProcessingSteps() |
243 | 56 | , mError() |
244 | 56 | , mExporters() { |
245 | 56 | GetPostProcessingStepInstanceList(mPostProcessingSteps); |
246 | | |
247 | | // grab all built-in exporters |
248 | 56 | setupExporterArray(mExporters); |
249 | 56 | } |
250 | | |
251 | 56 | ~ExporterPimpl() { |
252 | 56 | delete blob; |
253 | | |
254 | | // Delete all post-processing plug-ins |
255 | 1.90k | for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) { |
256 | 1.84k | delete mPostProcessingSteps[a]; |
257 | 1.84k | } |
258 | 56 | delete mProgressHandler; |
259 | 56 | } |
260 | | |
261 | | public: |
262 | | aiExportDataBlob* blob; |
263 | | std::shared_ptr< Assimp::IOSystem > mIOSystem; |
264 | | bool mIsDefaultIOHandler; |
265 | | |
266 | | /** The progress handler */ |
267 | | ProgressHandler *mProgressHandler; |
268 | | bool mIsDefaultProgressHandler; |
269 | | |
270 | | /** Post processing steps we can apply at the imported data. */ |
271 | | std::vector< BaseProcess* > mPostProcessingSteps; |
272 | | |
273 | | /** Last fatal export error */ |
274 | | std::string mError; |
275 | | |
276 | | /** Exporters, this includes those registered using #Assimp::Exporter::RegisterExporter */ |
277 | | std::vector<Exporter::ExportFormatEntry> mExporters; |
278 | | }; |
279 | | |
280 | | } // end of namespace Assimp |
281 | | |
282 | | using namespace Assimp; |
283 | | |
284 | | // ------------------------------------------------------------------------------------------------ |
285 | | Exporter :: Exporter() |
286 | 56 | : pimpl(new ExporterPimpl()) { |
287 | 56 | pimpl->mProgressHandler = new DefaultProgressHandler(); |
288 | 56 | } |
289 | | |
290 | | // ------------------------------------------------------------------------------------------------ |
291 | 56 | Exporter::~Exporter() { |
292 | 56 | ai_assert(nullptr != pimpl); |
293 | 56 | FreeBlob(); |
294 | 56 | delete pimpl; |
295 | 56 | } |
296 | | |
297 | | // ------------------------------------------------------------------------------------------------ |
298 | 0 | void Exporter::SetIOHandler( IOSystem* pIOHandler) { |
299 | 0 | ai_assert(nullptr != pimpl); |
300 | 0 | pimpl->mIsDefaultIOHandler = !pIOHandler; |
301 | 0 | pimpl->mIOSystem.reset(pIOHandler); |
302 | 0 | } |
303 | | |
304 | | // ------------------------------------------------------------------------------------------------ |
305 | 0 | IOSystem* Exporter::GetIOHandler() const { |
306 | 0 | ai_assert(nullptr != pimpl); |
307 | 0 | return pimpl->mIOSystem.get(); |
308 | 0 | } |
309 | | |
310 | | // ------------------------------------------------------------------------------------------------ |
311 | 0 | bool Exporter::IsDefaultIOHandler() const { |
312 | 0 | ai_assert(nullptr != pimpl); |
313 | 0 | return pimpl->mIsDefaultIOHandler; |
314 | 0 | } |
315 | | |
316 | | // ------------------------------------------------------------------------------------------------ |
317 | 0 | void Exporter::SetProgressHandler(ProgressHandler* pHandler) { |
318 | 0 | ai_assert(nullptr != pimpl); |
319 | |
|
320 | 0 | if ( nullptr == pHandler) { |
321 | | // Release pointer in the possession of the caller |
322 | 0 | pimpl->mProgressHandler = new DefaultProgressHandler(); |
323 | 0 | pimpl->mIsDefaultProgressHandler = true; |
324 | 0 | return; |
325 | 0 | } |
326 | | |
327 | 0 | if (pimpl->mProgressHandler == pHandler) { |
328 | 0 | return; |
329 | 0 | } |
330 | | |
331 | 0 | delete pimpl->mProgressHandler; |
332 | 0 | pimpl->mProgressHandler = pHandler; |
333 | 0 | pimpl->mIsDefaultProgressHandler = false; |
334 | 0 | } |
335 | | |
336 | | // ------------------------------------------------------------------------------------------------ |
337 | | const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId, |
338 | 56 | unsigned int pPreprocessing, const ExportProperties* pProperties) { |
339 | 56 | ai_assert(nullptr != pimpl); |
340 | 56 | if (pimpl->blob) { |
341 | 0 | delete pimpl->blob; |
342 | 0 | pimpl->blob = nullptr; |
343 | 0 | } |
344 | | |
345 | 56 | auto baseName = pProperties ? pProperties->GetPropertyString(AI_CONFIG_EXPORT_BLOB_NAME, AI_BLOBIO_MAGIC) : AI_BLOBIO_MAGIC; |
346 | | |
347 | 56 | std::shared_ptr<IOSystem> old = pimpl->mIOSystem; |
348 | 56 | BlobIOSystem *blobio = new BlobIOSystem(baseName); |
349 | 56 | pimpl->mIOSystem = std::shared_ptr<IOSystem>( blobio ); |
350 | | |
351 | 56 | if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName(), pPreprocessing, pProperties)) { |
352 | 3 | pimpl->mIOSystem = old; |
353 | 3 | return nullptr; |
354 | 3 | } |
355 | | |
356 | 53 | pimpl->blob = blobio->GetBlobChain(); |
357 | 53 | pimpl->mIOSystem = old; |
358 | | |
359 | 53 | return pimpl->blob; |
360 | 56 | } |
361 | | |
362 | | // ------------------------------------------------------------------------------------------------ |
363 | | aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath, |
364 | 56 | unsigned int pPreprocessing, const ExportProperties* pProperties) { |
365 | 56 | ASSIMP_BEGIN_EXCEPTION_REGION(); |
366 | 56 | ai_assert(nullptr != pimpl); |
367 | | // when they create scenes from scratch, users will likely create them not in verbose |
368 | | // format. They will likely not be aware that there is a flag in the scene to indicate |
369 | | // this, however. To avoid surprises and bug reports, we check for duplicates in |
370 | | // meshes upfront. |
371 | 56 | const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || MakeVerboseFormatProcess::IsVerboseFormat(pScene); |
372 | | |
373 | 56 | pimpl->mProgressHandler->UpdateFileWrite(0, 4); |
374 | | |
375 | 56 | pimpl->mError = ""; |
376 | 1.00k | for (size_t i = 0; i < pimpl->mExporters.size(); ++i) { |
377 | 1.00k | const Exporter::ExportFormatEntry& exp = pimpl->mExporters[i]; |
378 | 1.00k | if (!strcmp(exp.mDescription.id,pFormatId)) { |
379 | 56 | try { |
380 | | // Always create a full copy of the scene. We might optimize this one day, |
381 | | // but for now it is the most pragmatic way. |
382 | 56 | aiScene* scenecopy_tmp = nullptr; |
383 | 56 | SceneCombiner::CopyScene(&scenecopy_tmp,pScene); |
384 | | |
385 | 56 | pimpl->mProgressHandler->UpdateFileWrite(1, 4); |
386 | | |
387 | 56 | std::unique_ptr<aiScene> scenecopy(scenecopy_tmp); |
388 | 56 | const ScenePrivateData* const priv = ScenePriv(pScene); |
389 | | |
390 | | // steps that are not idempotent, i.e. we might need to run them again, usually to get back to the |
391 | | // original state before the step was applied first. When checking which steps we don't need |
392 | | // to run, those are excluded. |
393 | 56 | const unsigned int nonIdempotentSteps = aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_MakeLeftHanded; |
394 | | |
395 | | // Erase all pp steps that were already applied to this scene |
396 | 56 | const unsigned int pp = (exp.mEnforcePP | pPreprocessing) & ~(priv && !priv->mIsCopy |
397 | 56 | ? (priv->mPPStepsApplied & ~nonIdempotentSteps) |
398 | 56 | : 0u); |
399 | | |
400 | | // If no extra post-processing was specified, and we obtained this scene from an |
401 | | // Assimp importer, apply the reverse steps automatically. |
402 | | // TODO: either drop this, or document it. Otherwise it is just a bad surprise. |
403 | | //if (!pPreprocessing && priv) { |
404 | | // pp |= (nonIdempotentSteps & priv->mPPStepsApplied); |
405 | | //} |
406 | | |
407 | | // If the input scene is not in verbose format, but there is at least post-processing step that relies on it, |
408 | | // we need to run the MakeVerboseFormat step first. |
409 | 56 | bool must_join_again = false; |
410 | 56 | if (!is_verbose_format) { |
411 | 37 | bool verbosify = false; |
412 | 1.25k | for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { |
413 | 1.22k | BaseProcess* const p = pimpl->mPostProcessingSteps[a]; |
414 | | |
415 | 1.22k | if (p->IsActive(pp) && p->RequireVerboseFormat()) { |
416 | 0 | verbosify = true; |
417 | 0 | break; |
418 | 0 | } |
419 | 1.22k | } |
420 | | |
421 | 37 | if (verbosify || (exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) { |
422 | 0 | ASSIMP_LOG_DEBUG("export: Scene data not in verbose format, applying MakeVerboseFormat step first"); |
423 | |
|
424 | 0 | MakeVerboseFormatProcess proc; |
425 | 0 | proc.Execute(scenecopy.get()); |
426 | |
|
427 | 0 | if(!(exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) { |
428 | 0 | must_join_again = true; |
429 | 0 | } |
430 | 0 | } |
431 | 37 | } |
432 | | |
433 | 56 | pimpl->mProgressHandler->UpdateFileWrite(2, 4); |
434 | | |
435 | 56 | if (pp) { |
436 | | // the three 'conversion' steps need to be executed first because all other steps rely on the standard data layout |
437 | 0 | { |
438 | 0 | FlipWindingOrderProcess step; |
439 | 0 | if (step.IsActive(pp)) { |
440 | 0 | step.Execute(scenecopy.get()); |
441 | 0 | } |
442 | 0 | } |
443 | |
|
444 | 0 | { |
445 | 0 | FlipUVsProcess step; |
446 | 0 | if (step.IsActive(pp)) { |
447 | 0 | step.Execute(scenecopy.get()); |
448 | 0 | } |
449 | 0 | } |
450 | |
|
451 | 0 | { |
452 | 0 | MakeLeftHandedProcess step; |
453 | 0 | if (step.IsActive(pp)) { |
454 | 0 | step.Execute(scenecopy.get()); |
455 | 0 | } |
456 | 0 | } |
457 | |
|
458 | 0 | bool exportPointCloud(false); |
459 | 0 | if (nullptr != pProperties) { |
460 | 0 | exportPointCloud = pProperties->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS); |
461 | 0 | } |
462 | | |
463 | | // dispatch other processes |
464 | 0 | for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { |
465 | 0 | BaseProcess* const p = pimpl->mPostProcessingSteps[a]; |
466 | |
|
467 | 0 | if (p->IsActive(pp) |
468 | 0 | && !dynamic_cast<FlipUVsProcess*>(p) |
469 | 0 | && !dynamic_cast<FlipWindingOrderProcess*>(p) |
470 | 0 | && !dynamic_cast<MakeLeftHandedProcess*>(p)) { |
471 | 0 | if (dynamic_cast<PretransformVertices*>(p) && exportPointCloud) { |
472 | 0 | continue; |
473 | 0 | } |
474 | 0 | p->Execute(scenecopy.get()); |
475 | 0 | } |
476 | 0 | } |
477 | 0 | ScenePrivateData* const privOut = ScenePriv(scenecopy.get()); |
478 | 0 | ai_assert(nullptr != privOut); |
479 | |
|
480 | 0 | privOut->mPPStepsApplied |= pp; |
481 | 0 | } |
482 | | |
483 | 56 | pimpl->mProgressHandler->UpdateFileWrite(3, 4); |
484 | | |
485 | 56 | if(must_join_again) { |
486 | 0 | JoinVerticesProcess proc; |
487 | 0 | proc.Execute(scenecopy.get()); |
488 | 0 | } |
489 | | |
490 | 56 | ExportProperties emptyProperties; // Never pass nullptr ExportProperties so Exporters don't have to worry. |
491 | 56 | ExportProperties* pProp = pProperties ? (ExportProperties*)pProperties : &emptyProperties; |
492 | 56 | pProp->SetPropertyBool("bJoinIdenticalVertices", pp & aiProcess_JoinIdenticalVertices); |
493 | 56 | exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp); |
494 | | |
495 | 56 | pimpl->mProgressHandler->UpdateFileWrite(4, 4); |
496 | 56 | } catch (DeadlyExportError& err) { |
497 | 3 | pimpl->mError = err.what(); |
498 | 3 | return AI_FAILURE; |
499 | 3 | } |
500 | 53 | return AI_SUCCESS; |
501 | 56 | } |
502 | 1.00k | } |
503 | | |
504 | 0 | pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId; |
505 | 0 | ASSIMP_END_EXCEPTION_REGION(aiReturn); |
506 | |
|
507 | 0 | return AI_FAILURE; |
508 | 56 | } |
509 | | |
510 | | // ------------------------------------------------------------------------------------------------ |
511 | 0 | const char* Exporter::GetErrorString() const { |
512 | 0 | ai_assert(nullptr != pimpl); |
513 | 0 | return pimpl->mError.c_str(); |
514 | 0 | } |
515 | | |
516 | | // ------------------------------------------------------------------------------------------------ |
517 | 56 | void Exporter::FreeBlob() { |
518 | 56 | ai_assert(nullptr != pimpl); |
519 | 56 | delete pimpl->blob; |
520 | 56 | pimpl->blob = nullptr; |
521 | | |
522 | 56 | pimpl->mError = ""; |
523 | 56 | } |
524 | | |
525 | | // ------------------------------------------------------------------------------------------------ |
526 | 0 | const aiExportDataBlob* Exporter::GetBlob() const { |
527 | 0 | ai_assert(nullptr != pimpl); |
528 | 0 | return pimpl->blob; |
529 | 0 | } |
530 | | |
531 | | // ------------------------------------------------------------------------------------------------ |
532 | 0 | const aiExportDataBlob* Exporter::GetOrphanedBlob() const { |
533 | 0 | ai_assert(nullptr != pimpl); |
534 | 0 | const aiExportDataBlob *tmp = pimpl->blob; |
535 | 0 | pimpl->blob = nullptr; |
536 | 0 | return tmp; |
537 | 0 | } |
538 | | |
539 | | // ------------------------------------------------------------------------------------------------ |
540 | 0 | size_t Exporter::GetExportFormatCount() const { |
541 | 0 | ai_assert(nullptr != pimpl); |
542 | 0 | return pimpl->mExporters.size(); |
543 | 0 | } |
544 | | |
545 | | // ------------------------------------------------------------------------------------------------ |
546 | 0 | const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const { |
547 | 0 | ai_assert(nullptr != pimpl); |
548 | 0 | if (index >= GetExportFormatCount()) { |
549 | 0 | return nullptr; |
550 | 0 | } |
551 | | |
552 | | // Return from static storage if the requested index is built-in. |
553 | 0 | if (index < pimpl->mExporters.size()) { |
554 | 0 | return &pimpl->mExporters[index].mDescription; |
555 | 0 | } |
556 | | |
557 | 0 | return &pimpl->mExporters[index].mDescription; |
558 | 0 | } |
559 | | |
560 | | // ------------------------------------------------------------------------------------------------ |
561 | 0 | aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) { |
562 | 0 | ai_assert(nullptr != pimpl); |
563 | 0 | for (const ExportFormatEntry &e : pimpl->mExporters) { |
564 | 0 | if (!strcmp(e.mDescription.id,desc.mDescription.id)) { |
565 | 0 | return aiReturn_FAILURE; |
566 | 0 | } |
567 | 0 | } |
568 | | |
569 | 0 | pimpl->mExporters.push_back(desc); |
570 | 0 | return aiReturn_SUCCESS; |
571 | 0 | } |
572 | | |
573 | | // ------------------------------------------------------------------------------------------------ |
574 | 0 | void Exporter::UnregisterExporter(const char* id) { |
575 | 0 | ai_assert(nullptr != pimpl); |
576 | 0 | for (std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin(); |
577 | 0 | it != pimpl->mExporters.end(); ++it) { |
578 | 0 | if (!strcmp((*it).mDescription.id,id)) { |
579 | 0 | pimpl->mExporters.erase(it); |
580 | 0 | break; |
581 | 0 | } |
582 | 0 | } |
583 | 0 | } |
584 | | |
585 | | // ------------------------------------------------------------------------------------------------ |
586 | 56 | ExportProperties::ExportProperties() = default; |
587 | | |
588 | | // ------------------------------------------------------------------------------------------------ |
589 | 0 | ExportProperties::ExportProperties(const ExportProperties &other) = default; |
590 | | |
591 | 0 | bool ExportProperties::SetPropertyCallback(const char *szName, const std::function<void *(void *)> &f) { |
592 | 0 | return SetGenericProperty<std::function<void *(void *)>>(mCallbackProperties, szName, f); |
593 | 0 | } |
594 | | |
595 | 0 | std::function<void *(void *)> ExportProperties::GetPropertyCallback(const char *szName) const { |
596 | 0 | return GetGenericProperty<std::function<void *(void *)>>(mCallbackProperties, szName, nullptr); |
597 | 0 | } |
598 | | |
599 | 0 | bool ExportProperties::HasPropertyCallback(const char *szName) const { |
600 | 0 | return HasGenericProperty<std::function<void *(void *)>>(mCallbackProperties, szName); |
601 | 0 | } |
602 | | |
603 | | // ------------------------------------------------------------------------------------------------ |
604 | | // Set a configuration property |
605 | 56 | bool ExportProperties::SetPropertyInteger(const char* szName, int iValue) { |
606 | 56 | return SetGenericProperty<int>(mIntProperties, szName,iValue); |
607 | 56 | } |
608 | | |
609 | | // ------------------------------------------------------------------------------------------------ |
610 | | // Set a configuration property |
611 | 0 | bool ExportProperties::SetPropertyFloat(const char* szName, ai_real iValue) { |
612 | 0 | return SetGenericProperty<ai_real>(mFloatProperties, szName,iValue); |
613 | 0 | } |
614 | | |
615 | | // ------------------------------------------------------------------------------------------------ |
616 | | // Set a configuration property |
617 | 0 | bool ExportProperties::SetPropertyString(const char* szName, const std::string& value) { |
618 | 0 | return SetGenericProperty<std::string>(mStringProperties, szName,value); |
619 | 0 | } |
620 | | |
621 | | // ------------------------------------------------------------------------------------------------ |
622 | | // Set a configuration property |
623 | 0 | bool ExportProperties::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) { |
624 | 0 | return SetGenericProperty<aiMatrix4x4>(mMatrixProperties, szName,value); |
625 | 0 | } |
626 | | |
627 | | // ------------------------------------------------------------------------------------------------ |
628 | | // Get a configuration property |
629 | 112 | int ExportProperties::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const { |
630 | 112 | return GetGenericProperty<int>(mIntProperties,szName,iErrorReturn); |
631 | 112 | } |
632 | | |
633 | | // ------------------------------------------------------------------------------------------------ |
634 | | // Get a configuration property |
635 | 0 | ai_real ExportProperties::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const { |
636 | 0 | return GetGenericProperty<ai_real>(mFloatProperties,szName,iErrorReturn); |
637 | 0 | } |
638 | | |
639 | | // ------------------------------------------------------------------------------------------------ |
640 | | // Get a configuration property |
641 | | const std::string ExportProperties::GetPropertyString(const char* szName, |
642 | 0 | const std::string& iErrorReturn /*= ""*/) const { |
643 | 0 | return GetGenericProperty<std::string>(mStringProperties,szName,iErrorReturn); |
644 | 0 | } |
645 | | |
646 | | // ------------------------------------------------------------------------------------------------ |
647 | | // Has a configuration property |
648 | | const aiMatrix4x4 ExportProperties::GetPropertyMatrix(const char* szName, |
649 | 0 | const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const { |
650 | 0 | return GetGenericProperty<aiMatrix4x4>(mMatrixProperties,szName,iErrorReturn); |
651 | 0 | } |
652 | | |
653 | | // ------------------------------------------------------------------------------------------------ |
654 | | // Has a configuration property |
655 | 0 | bool ExportProperties::HasPropertyInteger(const char* szName) const { |
656 | 0 | return HasGenericProperty<int>(mIntProperties, szName); |
657 | 0 | } |
658 | | |
659 | | // ------------------------------------------------------------------------------------------------ |
660 | | // Has a configuration property |
661 | 0 | bool ExportProperties::HasPropertyBool(const char* szName) const { |
662 | 0 | return HasGenericProperty<int>(mIntProperties, szName); |
663 | 0 | } |
664 | | |
665 | | // ------------------------------------------------------------------------------------------------ |
666 | | // Has a configuration property |
667 | 0 | bool ExportProperties::HasPropertyFloat(const char* szName) const { |
668 | 0 | return HasGenericProperty<ai_real>(mFloatProperties, szName); |
669 | 0 | } |
670 | | |
671 | | // ------------------------------------------------------------------------------------------------ |
672 | | // Has a configuration property |
673 | 0 | bool ExportProperties::HasPropertyString(const char* szName) const { |
674 | 0 | return HasGenericProperty<std::string>(mStringProperties, szName); |
675 | 0 | } |
676 | | |
677 | | // ------------------------------------------------------------------------------------------------ |
678 | | // Has a configuration property |
679 | 0 | bool ExportProperties::HasPropertyMatrix(const char* szName) const { |
680 | 0 | return HasGenericProperty<aiMatrix4x4>(mMatrixProperties, szName); |
681 | 0 | } |
682 | | |
683 | | |
684 | | #endif // !ASSIMP_BUILD_NO_EXPORT |