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