assimp/import/
mod.rs

1//! The `import` module contains functionality for importing scenes.
2//!
3//! TODO write better documentation, at the moment it's mostly copied from Assimp and some of it
4//! is incorrect/irrelevant.
5//!
6//! # Examples
7//! ```
8//! use assimp::import::Importer;
9//!
10//! fn main() {
11//!     let importer = Importer::new();
12//!     let scene = importer.read_file("examples/box.obj");
13//! }
14//! ```
15
16use std::ffi::{CStr, CString};
17use std::mem;
18use std::ptr;
19use std::str;
20
21use ffi::*;
22use ffi::config::*;
23
24use math::matrix4::*;
25use scene::*;
26
27pub mod structs;
28use self::structs::*;
29
30/// The `Importer` type.
31///
32/// See [module-level documentation](index.html) for examples.
33pub struct Importer {
34    property_store: *mut AiPropertyStore,
35    flags: AiPostProcessSteps
36}
37
38impl Importer {
39    /// Create a new Importer.
40    pub fn new() -> Importer {
41        Importer {
42            property_store: unsafe { aiCreatePropertyStore() },
43            flags: AiPostProcessSteps::empty()
44        }
45    }
46
47    /// Load a scene from the specified file.
48    ///
49    /// If the call succeeds, return value is `Ok`, containing the loaded `Scene` structure.
50    /// If the call fails, return value is `Err`, containing the error string returned from
51    /// the Assimp library.
52    pub fn read_file<'a>(&self, file: &str) -> Result<Scene<'a>, &str> {
53        let cstr = CString::new(file).unwrap();
54        let raw_scene = unsafe {
55            aiImportFileExWithProperties(
56                cstr.as_ptr(),
57                self.flags,
58                ptr::null_mut(),
59                self.property_store)
60        };
61        if !raw_scene.is_null() {
62            Ok(Scene::from_raw(raw_scene))
63        } else {
64            let error_str = unsafe { aiGetErrorString() };
65            if error_str.is_null() {
66                Err("Unknown error")
67            } else {
68                unsafe {
69                    let cstr = CStr::from_ptr(error_str);
70                    match str::from_utf8(cstr.to_bytes()) {
71                        Ok(s) => Err(s),
72                        Err(_) => Err("Unknown error")
73                    }
74                }
75            }
76        }
77    }
78
79    /// Load a scene from a string.
80    ///
81    /// If the call succeeds, return value is `Ok`, containing the loaded `Scene` structure.
82    /// If the call fails, return value is `Err`, containing the error string returned from
83    /// the Assimp library.
84    pub fn read_string<'a>(&self, data: &str) -> Result<Scene<'a>, &str> {
85        let cstr = CString::new(data).unwrap();
86        let raw_scene = unsafe {
87            aiImportFileFromMemoryWithProperties(
88                cstr.as_ptr(),
89                data.len() as u32,
90                self.flags,
91                ptr::null_mut(),
92                self.property_store)
93        };
94        if !raw_scene.is_null() {
95            Ok(Scene::from_raw(raw_scene))
96        } else {
97            let error_str = unsafe { aiGetErrorString() };
98            if error_str.is_null() {
99                Err("Unknown error")
100            } else {
101                unsafe {
102                    let cstr = CStr::from_ptr(error_str);
103                    match str::from_utf8(cstr.to_bytes()) {
104                        Ok(s) => Err(s),
105                        Err(_) => Err("Unknown error")
106                    }
107                }
108            }
109        }
110    }
111
112    /// Apply post-processing to an already-imported scene.
113    ///
114    /// This performs all enabled post-processing steps on an already imported scene. The main
115    /// use case for this is to inspect the scene returned by `read_file` before choosing which
116    /// additional post-process steps to apply.
117    ///
118    /// Due to how the Assimp C API works, this isn't as useful as it should be. Currently it isn't
119    /// possible to configure properties of post-processing steps after the initial import.
120    ///
121    /// # Return value
122    /// The new scene, with new post-processing steps applied. Note that it is possible for this
123    /// method to fail, in which case the return value is `Err`.
124    pub fn apply_postprocessing<'a>(&'a self, scene: Scene<'a>) -> Result<Scene, &str> {
125        let raw_scene = unsafe { aiApplyPostProcessing(scene.to_raw(), self.flags) };
126        if !raw_scene.is_null() {
127            // Return original scene, Assimp applies post-processing in-place so returning
128            // a new scene object would cause the scene to get double-dropped.
129            Ok(scene)
130        } else {
131            // Assimp frees the scene on failure, dropping would cause the memory to be
132            // freed twice so use mem::forget to prevent that happening.
133            mem::forget(scene);
134            Err("apply_postprocessing failed, see output log for errors.")
135        }
136    }
137
138    /// Enables time measurements.
139    ///
140    /// If enabled, measures the time needed for each part of the loading process (i.e. IO time,
141    /// importing, postprocessing, ..) and dumps these timings to the output log.
142    pub fn measure_time(&mut self, enable: bool) {
143        self.set_bool_property(GLOB_MEASURE_TIME, enable);
144    }
145
146    /// A hint to Assimp to favour speed against import quality.
147    ///
148    /// Enabling this option may result in faster loading, but it needn't. It represents just a hint
149    /// to loaders and post-processing steps to use faster code paths, if possible.
150    pub fn favour_speed(&mut self, enable: bool) {
151        self.set_bool_property(FAVOUR_SPEED, enable);
152    }
153
154    /// Helper method to set or clear the appropriate import flag
155    fn set_import_flag(&mut self, flag: AiPostProcessSteps, value: bool) {
156        if value {
157            self.flags.insert(flag)
158        } else {
159            self.flags.remove(flag)
160        }
161    }
162
163    /// Helper method to set a boolean import property.
164    fn set_bool_property(&mut self, name: &str, value: bool) {
165        self.set_int_property(name, value as i32)
166    }
167
168    /// Helper method to set an integer import property.
169    fn set_int_property(&mut self, name: &str, value: i32) {
170        let cstr = CString::new(name).unwrap();
171        unsafe { aiSetImportPropertyInteger(self.property_store, cstr.as_ptr(), value); }
172    }
173
174    /// Helper method to set a floating point import property.
175    fn set_float_property(&mut self, name: &str, value: f32) {
176        let cstr = CString::new(name).unwrap();
177        unsafe { aiSetImportPropertyFloat(self.property_store, cstr.as_ptr(), value); }
178    }
179
180    /// Helper method to set a 4x4 matrix import property.
181    fn set_matrix_property(&mut self, name: &str, value: Matrix4x4) {
182        let cstr = CString::new(name).unwrap();
183        unsafe { aiSetImportPropertyMatrix(self.property_store, cstr.as_ptr(), &*value); }
184    }
185
186    /// Helper method to set a string import property.
187    fn set_string_property(&mut self, name: &str, value: &str) {
188        let cstr = CString::new(name).unwrap();
189        let aistr: AiString = From::from(value);
190        unsafe { aiSetImportPropertyString(self.property_store, cstr.as_ptr(), &aistr) }
191    }
192
193    /// Calculates the tangents and bitangents for the imported meshes.
194    ///
195    /// Does nothing if a mesh does not have normals. You might want this post processing step to be
196    /// executed if you plan to use tangent space calculations such as normal mapping applied to the
197    /// meshes. The `max_smoothing_angle` property allows you to specify a maximum smoothing angle
198    /// for the algorithm. However, usually you'll want to leave it at the default value.
199    pub fn calc_tangent_space<F: Fn(&mut CalcTangentSpace)>(&mut self, closure: F) {
200        let mut args = CalcTangentSpace::default();
201        closure(&mut args);
202
203        self.set_import_flag(AIPROCESS_CALC_TANGENT_SPACE, args.enable);
204        if args.enable {
205            self.set_float_property(PP_CT_MAX_SMOOTHING_ANGLE, args.max_smoothing_angle);
206            self.set_int_property(PP_CT_TEXTURE_CHANNEL_INDEX, args.texture_channel);
207        }
208    }
209
210    /// Identifies and joins identical vertex data sets within all imported meshes.
211    ///
212    /// After this step is run, each mesh contains unique vertices, so a vertex may be used by
213    /// multiple faces. You usually want to use this post processing step. If your application deals
214    /// with indexed geometry, this step is compulsory or you'll just waste rendering time.
215    /// If this flag is not specified</b>, no vertices are referenced by more than one face and
216    /// no index buffer is required for rendering.
217    pub fn join_identical_vertices(&mut self, enable: bool) {
218        self.set_import_flag(AIPROCESS_JOIN_IDENTICAL_VERTICES, enable);
219    }
220
221    /// Converts all the imported data to a left-handed coordinate space.
222    ///
223    /// By default the data is returned in a right-handed coordinate space (which OpenGL prefers).
224    /// In this space, +X points to the right, +Z points towards the viewer, and +Y points upwards.
225    /// In the DirectX coordinate space +X points to the right, +Y points upwards, and +Z points
226    /// away from the viewer.
227    ///
228    /// You'll probably want to consider this flag if you use Direct3D for rendering.
229    pub fn make_left_handed(&mut self, enable: bool) {
230        self.set_import_flag(AIPROCESS_MAKE_LEFT_HANDED, enable);
231    }
232
233    /// Triangulates all faces of all meshes.
234    ///
235    /// By default the imported mesh data might contain faces with more than 3 indices. For
236    /// rendering you'll usually want all faces to be triangles. This post processing step splits up
237    /// faces with more than 3 indices into triangles. Line and point primitives are *not* modified!
238    /// If you want 'triangles only' with no other kinds of primitives, try the following solution:
239    ///
240    /// * Enable both `triangulate` and `sort_by_primitive_type`
241    /// * Ignore all point and line meshes when you process assimp's output
242    pub fn triangulate(&mut self, enable: bool) {
243        self.set_import_flag(AIPROCESS_TRIANGULATE, enable);
244    }
245
246    /// Removes some parts of the data structure (animations, materials, light sources, cameras,
247    /// textures, vertex components).
248    ///
249    /// The components to be removed are specified in the `flags` property. This is quite useful
250    /// if you don't need all parts of the output structure. Vertex colors are rarely used today for
251    /// example... Calling this step to remove unneeded data from the pipeline as early as possible
252    /// results in increased performance and a more optimized output data structure.
253    ///
254    /// This step is also useful if you want to force Assimp to recompute normals or tangents.
255    /// The corresponding steps don't recompute them if they're already there (loaded from the
256    /// source asset). By using this step you can make sure they are NOT there.
257    ///
258    /// This flag is a poor one, mainly because its purpose is usually misunderstood. Consider the
259    /// following case: a 3D model has been exported from a CAD app, and it has per-face vertex
260    /// colors. Vertex positions can't be shared, thus the `join_identical_vertices` step fails to
261    /// optimize the data because of these nasty little vertex colors. Most apps don't even process
262    /// them, so it's all for nothing. By using this step, unneeded components are excluded as early
263    /// as possible, thus opening more room for internal optimizations.
264    pub fn remove_component<F: Fn(&mut RemoveComponent)>(&mut self, closure: F) {
265        use self::structs::ComponentType::*;
266
267        let mut args = RemoveComponent::default();
268        closure(&mut args);
269
270        self.set_import_flag(AIPROCESS_REMOVE_COMPONENT, args.enable);
271        if args.enable {
272            let flags = args.components.iter().fold(0, |x, &c|
273                x | match c {
274                    Normals => AICOMPONENT_NORMALS,
275                    TangentsAndBitangents => AICOMPONENT_TANGENTS_AND_BITANGENTS,
276                    Colors => AICOMPONENT_COLORS,
277                    TexCoords => AICOMPONENT_TEXCOORDS,
278                    BoneWeights => AICOMPONENT_BONE_WEIGHTS,
279                    Animations => AICOMPONENT_ANIMATIONS,
280                    Textures => AICOMPONENT_TEXTURES,
281                    Lights => AICOMPONENT_LIGHTS,
282                    Cameras => AICOMPONENT_CAMERAS,
283                    Meshes => AICOMPONENT_MESHES,
284                    Materials => AICOMPONENT_MATERIALS
285                }.bits()
286            );
287            self.set_int_property(PP_RVC_FLAGS, flags as i32);
288        }
289    }
290
291    /// Generates normals for imported meshes.
292    ///
293    /// This is ignored if normals are already there at the time this flag is evaluated. Model
294    /// importers try to load them from the source file, so they're usually already there.
295    ///
296    /// The `smooth` property specifies how normals are calculated. When set to false, normals are
297    /// calculated per face, and shared between all points of a single face, so a single point can
298    /// have multiple normals, which forces the library to duplicate vertices in some cases.
299    ///
300    /// When set to true, normals are calculated per vertex. The `max_smoothing_angle` property
301    /// allows you to specify an angle maximum for the normal smoothing algorithm. Normals exceeding
302    /// this limit are not smoothed, resulting in a hard seam between two faces. Using a decent
303    /// angle here (e.g. 80 degrees) results in very good visual appearance.
304    pub fn generate_normals<F: Fn(&mut GenerateNormals)>(&mut self, closure: F) {
305        let mut args = GenerateNormals::default();
306        closure(&mut args);
307
308        if args.enable {
309            if args.smooth {
310                self.flags.insert(AIPROCESS_GEN_SMOOTH_NORMALS);
311                self.set_float_property(PP_GSN_MAX_SMOOTHING_ANGLE, args.max_smoothing_angle);
312            } else {
313                self.flags.insert(AIPROCESS_GEN_NORMALS);
314            }
315        } else {
316            self.flags.remove(AIPROCESS_GEN_NORMALS | AIPROCESS_GEN_SMOOTH_NORMALS);
317        }
318    }
319
320    /// Splits large meshes into smaller sub-meshes.
321    ///
322    /// This is quite useful for real-time rendering, where the number of triangles which can be
323    /// maximally processed in a single draw-call is limited by the video driver/hardware. The
324    /// maximum vertex buffer is usually limited too. Both requirements can be met with this step:
325    /// you may specify both a triangle and vertex limit for a single mesh.
326    ///
327    /// The split limits can (and should!) be set through the `vertex_limit` and `triangle_limit`
328    /// properties.
329    ///
330    /// Note that splitting is generally a time-consuming task, but only if there's something to
331    /// split. The use of this step is recommended for most users.
332    pub fn split_large_meshes<F: Fn(&mut SplitLargeMeshes)>(&mut self, closure: F) {
333        let mut args = SplitLargeMeshes::default();
334        closure(&mut args);
335
336        self.set_import_flag(AIPROCESS_SPLIT_LARGE_MESHES, args.enable);
337        if args.enable {
338            self.set_int_property(PP_SLM_TRIANGLE_LIMIT, args.triangle_limit);
339            self.set_int_property(PP_SLM_VERTEX_LIMIT, args.vertex_limit);
340        }
341    }
342
343    /// Removes the node graph and pre-transforms all vertices with the local transformation
344    /// matrices of their nodes.
345    ///
346    /// The output scene still contains nodes, however there is only a root node with children, each
347    /// one referencing only one mesh, and each mesh referencing one material. For rendering, you
348    /// can simply render all meshes in order - you don't need to pay attention to local
349    /// transformations and the node hierarchy. Animations are removed during this step.
350    ///
351    /// This step is intended for applications without a scenegraph. The step CAN cause some
352    /// problems: if e.g. a mesh of the asset contains normals and another, using the same material
353    /// index, does not, they will be brought together, but the first meshes's part of the normal
354    /// list is zeroed. However, these artifacts are rare.
355    pub fn pre_transform_vertices<F: Fn(&mut PreTransformVertices)>(&mut self, closure: F) {
356        let mut args = PreTransformVertices::default();
357        closure(&mut args);
358
359        self.set_import_flag(AIPROCESS_PRE_TRANSFORM_VERTICES, args.enable);
360        if args.enable {
361            self.set_bool_property(PP_PTV_KEEP_HIERARCHY, args.keep_hierarchy);
362            self.set_bool_property(PP_PTV_NORMALIZE, args.normalize);
363            self.set_bool_property(PP_PTV_ADD_ROOT_TRANSFORMATION, args.add_root_transformation);
364            self.set_matrix_property(PP_PTV_ROOT_TRANSFORMATION, args.root_transformation);
365        }
366    }
367
368    /// Limits the number of bones simultaneously affecting a single vertex to a maximum value.
369    ///
370    /// If any vertex is affected by more than the maximum number of bones, the least important
371    /// vertex weights are removed and the remaining vertex weights are renormalized so that the
372    /// weights still sum up to 1. The default bone weight limit is 4, but you can use the
373    /// `max_weights` property to supply your own limit to the post processing step.
374    ///
375    /// If you intend to perform the skinning in hardware, this post processing step might be of
376    /// interest to you.
377    pub fn limit_bone_weights<F: Fn(&mut LimitBoneWeights)>(&mut self, closure: F) {
378        let mut args = LimitBoneWeights::default();
379        closure(&mut args);
380
381        self.set_import_flag(AIPROCESS_LIMIT_BONE_WEIGHTS, args.enable);
382        if args.enable {
383            self.set_int_property(PP_LBW_MAX_WEIGHTS, args.max_weights);
384        }
385    }
386
387    /// Validates the imported scene data structure.
388    ///
389    /// This makes sure that all indices are valid, all animations and bones are linked correctly,
390    /// all material references are correct .. etc.
391    ///
392    /// It is recommended that you capture Assimp's log output if you use this flag, so you can
393    /// easily find out what's wrong if a file fails the validation. The validator is quite strict
394    /// and will find *all* inconsistencies in the data structure... It is recommended that plugin
395    /// developers use it to debug their loaders. There are two types of validation failures:
396    ///
397    /// * Error: Error: There's something wrong with the imported data. Further postprocessing is
398    ///   not possible and the data is not usable at all. The import fails.
399    ///   #Importer::GetErrorString() or #aiGetErrorString() carry the error message around.
400    /// * Warning: There are some minor issues (e.g. 1000000 animation keyframes with the same
401    ///   time), but further postprocessing and use of the data structure is still safe. Warning
402    ///   details are written to the log file, <tt>#AI_SCENE_FLAGS_VALIDATION_WARNING</tt> is set
403    ///   in #aiScene::mFlags</li>
404    ///
405    /// This post-processing step is not time-consuming. Its use is not compulsory, but recommended.
406    pub fn validate_data_structure(&mut self, enable: bool) {
407        self.set_import_flag(AIPROCESS_VALIDATE_DATA_STRUCTURE, enable);
408    }
409
410    /// Reorders triangles for better vertex cache locality.
411    ///
412    /// The step tries to improve the ACMR (average post-transform vertex cache miss ratio) for all
413    /// meshes. The implementation runs in O(n) and is roughly based on the 'tipsify' algorithm (see
414    /// [this paper](http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf)).
415    ///
416    /// If you intend to render huge models in hardware, this step might be of interest to you.
417    /// The `cache_size` property can be used to fine-tune the cache optimization.
418    pub fn improve_cache_locality<F: Fn(&mut ImproveCacheLocality)>(&mut self, closure: F) {
419        let mut args = ImproveCacheLocality::default();
420        closure(&mut args);
421
422        self.set_import_flag(AIPROCESS_IMPROVE_CACHE_LOCALITY, args.enable);
423        if args.enable {
424            self.set_int_property(PP_ICL_PTCACHE_SIZE, args.cache_size);
425        }
426    }
427
428    /// Searches for redundant/unreferenced materials and removes them.
429    ///
430    /// This is especially useful in combination with the `pre_transform_vertices` and
431    /// `optimize_meshes` steps. Both join small meshes with equal characteristics, but they can't
432    /// do  their work if two meshes have different materials. Because several material settings are
433    /// lost during Assimp's import filters, (and because many exporters don't check for redundant
434    /// materials), huge models often have materials which are are defined several times with
435    /// exactly the same settings.
436    ///
437    /// Several material settings not contributing to the final appearance of a surface are ignored
438    /// in all comparisons (e.g. the material name). So, if you're passing additional information
439    /// through the content pipeline (probably using *magic* material names), don't specify this
440    /// flag. Alternatively take a look at the  exclude_list` property.
441    pub fn remove_redudant_materials<F: Fn(&mut RemoveRedundantMaterials)>(&mut self, closure: F) {
442        let mut args = RemoveRedundantMaterials::default();
443        closure(&mut args);
444
445        self.set_import_flag(AIPROCESS_REMOVE_REDUNDANT_MATERIALS, args.enable);
446        if args.enable {
447            self.set_string_property(PP_RRM_EXCLUDE_LIST, &args.exclude_list);
448        }
449    }
450
451    /// This step tries to determine which meshes have normal vectors that are facing inwards and
452    /// inverts them.
453    ///
454    /// The algorithm is simple but effective:
455    /// the bounding box of all vertices + their normals is compared against the volume of the
456    /// bounding box of all vertices without their normals. This works well for most objects,
457    /// problems might occur with planar surfaces. However, the step tries to filter such cases.
458    /// The step inverts all in-facing normals. Generally it is recommended to enable this step,
459    /// although the result is not always correct.
460    pub fn fix_infacing_normals(&mut self, enable: bool) {
461        self.set_import_flag(AIPROCESS_FIX_INFACING_NORMALS, enable);
462    }
463
464    /// This step splits meshes with more than one primitive type in homogeneous sub-meshes.
465    ///
466    /// The step is executed after the triangulation step. After the step returns, just one bit is
467    /// set in aiMesh::mPrimitiveTypes. This is especially useful for real-time rendering where
468    /// point and line primitives are often ignored or rendered separately.
469    ///
470    /// You can use the `types` property to specify which primitive types you need. This can be
471    /// used to easily exclude lines and points, which are rarely used, from the import.
472    ///
473    /// # Panics
474    /// Specifying all possible primitive types for removal is illegal and causes a panic.
475    pub fn sort_by_primitive_type<F: Fn(&mut SortByPrimitiveType)>(&mut self, closure: F) {
476        use self::structs::PrimitiveType::*;
477
478        let mut args = SortByPrimitiveType::default();
479        closure(&mut args);
480
481        self.set_import_flag(AIPROCESS_SORT_BY_PTYPE, args.enable);
482        if args.enable {
483            let flags = args.remove.iter().fold(0, |x, &t|
484                x | match t {
485                    Point => AIPRIMITIVETYPE_POINT,
486                    Line => AIPRIMITIVETYPE_LINE,
487                    Triangle => AIPRIMITIVETYPE_TRIANGLE,
488                    Polygon => AIPRIMITIVETYPE_POLYGON
489                }.bits()
490            );
491
492            // Removing all primitives is a bad thing and causes Assimp to segfault when
493            // used in combination with `validate_data_structure` and `apply_postprocessing`.
494            if flags == (AIPRIMITIVETYPE_POINT |
495                         AIPRIMITIVETYPE_LINE |
496                         AIPRIMITIVETYPE_TRIANGLE |
497                         AIPRIMITIVETYPE_POLYGON)
498                         .bits() {
499                panic!("Trying to remove all possible primitive types is illegal.");
500            }
501
502            self.set_int_property(PP_SBP_REMOVE, flags as i32);
503        }
504    }
505
506    /// This step searches all meshes for degenerate primitives and converts them to proper lines
507    /// or points.
508    ///
509    /// A face is 'degenerate' if one or more of its points are identical. To have the degenerate
510    /// stuff not only detected and collapsed but removed, try one of the following procedures:
511    ///
512    /// 1. If you support lines and points for rendering but don't want the degenerates:
513    ///    * Enable the `find_degenerates` step.
514    ///    * Set the `remove` property to true. This will cause the step to remove degenerate
515    ///      triangles from the import as soon as they're detected. They won't pass any further
516    ///      pipeline steps.
517    /// 2. If you don't support lines and points at all:
518    ///    * Enable the `find_degenerates` step.
519    ///    * Enable the `sort_by_primitive_type` step. This moves line and point primitives to
520    ///      separate meshes.
521    ///    * Set the `components` property to aiPrimitiveType_POINTS | aiPrimitiveType_LINES
522    ///      to cause `sort_by_primitive_type` to reject point and line meshes from the scene.
523    ///
524    /// Degenerate polygons are not necessarily evil and that's why they're not removed by default.
525    /// There are several file formats which don't support lines or points, and some exporters
526    /// bypass the format specification and write them as degenerate triangles instead.
527    pub fn find_degenerates<F: Fn(&mut FindDegenerates)>(&mut self, closure: F) {
528        let mut args = FindDegenerates::default();
529        closure(&mut args);
530
531        self.set_import_flag(AIPROCESS_FIND_DEGENERATES, args.enable);
532        if args.enable {
533            self.set_bool_property(PP_FD_REMOVE, args.remove);
534        }
535    }
536
537    /// This step searches all meshes for invalid data, such as zeroed normal vectors or invalid UV
538    /// coords and removes/fixes them. This is intended to get rid of some common exporter errors.
539    ///
540    /// This is especially useful for normals. If they are invalid, and the step recognizes this,
541    /// they will be removed and can later be recomputed, i.e. by the `gen_normals` step.
542    ///
543    /// The step will also remove meshes that are infinitely small and reduce animation tracks
544    /// consisting of hundreds if redundant keys to a single key.
545    /// The `accuracy` property decides the accuracy of the check for duplicate animation tracks.
546    pub fn find_invalid_data<F: Fn(&mut FindInvalidData)>(&mut self, closure: F) {
547        let mut args = FindInvalidData::default();
548        closure(&mut args);
549
550        self.set_import_flag(AIPROCESS_FIND_INVALID_DATA, args.enable);
551        if args.enable {
552            self.set_float_property(PP_FID_ANIM_ACCURACY, args.accuracy);
553        }
554    }
555
556    /// This step converts non-UV mappings (such as spherical or cylindrical mapping) to proper
557    /// texture coordinate channels.
558    ///
559    /// Most applications will support UV mapping only, so you will probably want to specify this
560    /// step in every case. Note that Assimp is not always able to match the original mapping
561    /// implementation of the 3D app which produced a model perfectly. It's always better to let the
562    /// modelling app compute the UV channels - 3ds max, Maya, Blender, LightWave, and Modo do this
563    /// for example.
564    ///
565    /// If this step is not requested, you'll need to process the `AI_MATKEY_MAPPING` material
566    /// property in order to display all assets properly.
567    pub fn gen_uv_coords(&mut self, enable: bool) {
568        self.set_import_flag(AIPROCESS_GEN_UV_COORDS, enable);
569    }
570
571    /// This step applies per-texture UV transformations and bakes them into stand-alone vtexture
572    /// coordinate channels.
573    ///
574    /// UV transformations are specified per-texture - see the `AI_MATKEY_UVTRANSFORM` material key
575    /// for more information. This step processes all textures with transformed input UV coordinates
576    /// and generates a new (pre-transformed) UV channel which replaces the old channel. Most
577    /// applications won't support UV transformations, so you will probably want to specify
578    /// this step.
579    ///
580    /// UV transformations are usually implemented in real-time apps by transforming texture
581    /// coordinates at vertex shader stage with a 3x3 (homogenous) transformation matrix.
582    pub fn transform_uv_coords<F: Fn(&mut TransformUVCoords)>(&mut self, closure: F) {
583        use self::structs::UVTransformFlag::*;
584
585        let mut args = TransformUVCoords::default();
586        closure(&mut args);
587
588        self.set_import_flag(AIPROCESS_TRANSFORM_UV_COORDS, args.enable);
589        if args.enable {
590            let flags = args.flags.iter().fold(0, |x, &f|
591                x | match f {
592                    Scaling => AI_UVTRAFO_SCALING,
593                    Rotation => AI_UVTRAFO_ROTATION,
594                    Translation => AI_UVTRAFO_TRANSLATION,
595                    All => AI_UVTRAFO_ALL
596                }.bits()
597            );
598            self.set_int_property(PP_TUV_EVALUATE, flags as i32);
599        }
600    }
601
602    /// This step searches for duplicate meshes and replaces them with references to the first mesh.
603    ///
604    /// This step takes a while, so don't use it if speed is a concern. Its main purpose is to
605    /// workaround the fact that many export file formats don't support instanced meshes, so
606    /// exporters need to duplicate meshes. This step removes the duplicates again. Please note that
607    /// Assimp does not currently support per-node material assignment to meshes, which means that
608    /// identical meshes with different materials are currently *not* joined, although this is
609    /// planned for future versions.
610    pub fn find_instances(&mut self, enable: bool) {
611        self.set_import_flag(AIPROCESS_FIND_INSTANCES, enable);
612    }
613
614    /// A postprocessing step to reduce the number of meshes.
615    ///
616    /// This will, in fact, reduce the number of draw calls.
617    ///
618    /// This is a very effective optimization and is recommended to be used together with
619    /// `optimize_graph`, if possible. The flag is fully compatible with both `split_large_meshes`
620    /// and `sort_by_primitive_type`.
621    pub fn optimize_meshes(&mut self, enable: bool) {
622        self.set_import_flag(AIPROCESS_OPTIMIZE_MESHES, enable);
623    }
624
625    /// A postprocessing step to optimize the scene hierarchy.
626    ///
627    /// Nodes without animations, bones, lights or cameras assigned are collapsed and joined.
628    ///
629    /// Node names can be lost during this step. If you use special 'tag nodes' to pass additional
630    /// information through your content pipeline, use the `exclude_list` property to specify a
631    /// list of node names you want to be kept. Nodes matching one of the names in this list won't
632    /// be touched or modified.
633    ///
634    /// Use this flag with caution. Most simple files will be collapsed to a single node, so
635    /// complex hierarchies are usually completely lost. This is not useful for editor environments,
636    /// but probably a very effective optimization if you just want to get the model data, convert
637    /// it to your own format, and render it as fast as possible.
638    ///
639    /// This flag is designed to be used with `optimize_meshes` for best results.
640    ///
641    /// 'Crappy' scenes with thousands of extremely small meshes packed in deeply nested nodes exist
642    /// for almost all file formats. `optimize_meshes` in combination with `optimize_graph`
643    /// usually fixes them all and makes them renderable.
644    pub fn optimize_graph<F: Fn(&mut OptimizeGraph)>(&mut self, closure: F) {
645        let mut args = OptimizeGraph::default();
646        closure(&mut args);
647
648        self.set_import_flag(AIPROCESS_OPTIMIZE_GRAPH, args.enable);
649        if args.enable {
650            self.set_string_property(PP_OG_EXCLUDE_LIST, &args.exclude_list);
651        }
652    }
653
654    /// This step flips all UV coordinates along the y-axis and adjusts material settings and
655    /// bitangents accordingly.
656    ///
657    /// *Output UV coordinate system:*
658    ///
659    /// ```text
660    /// 0y|0y ---------- 1x|0y
661    /// |                 |
662    /// |                 |
663    /// |                 |
664    /// 0x|1y ---------- 1x|1y
665    /// ```
666    ///
667    /// You'll probably want to consider this flag if you use Direct3D for rendering.
668    pub fn flip_uvs(&mut self, enable: bool) {
669        self.set_import_flag(AIPROCESS_FLIP_UVS, enable);
670    }
671
672    /// This step adjusts the output face winding order to be CW.
673    ///
674    /// The default face winding order is counter clockwise (CCW).
675    ///
676    /// *Output face order:*
677    ///
678    /// ```text
679    ///       x2
680    ///
681    ///                         x0
682    ///  x1
683    /// ```
684    pub fn flip_winding_order(&mut self, enable: bool) {
685        self.set_import_flag(AIPROCESS_FLIP_WINDING_ORDER, enable);
686    }
687
688    /// This step splits meshes with many bones into sub-meshes so that each submesh has fewer or
689    /// as many bones as a given limit.
690    pub fn split_by_bone_count<F: Fn(&mut SplitByBoneCount)>(&mut self, closure: F) {
691        let mut args = SplitByBoneCount::default();
692        closure(&mut args);
693
694        self.set_import_flag(AIPROCESS_SPLIT_BY_BONE_COUNT, args.enable);
695        if args.enable {
696            self.set_int_property(PP_SBBC_MAX_BONES, args.max_bones);
697        }
698    }
699
700    /// This step removes bones losslessly or according to some threshold.
701    ///
702    /// In some cases (i.e. formats that require it) exporters are forced to assign dummy bone
703    /// weights to otherwise static meshes assigned to animated meshes. Full, weight-based skinning
704    /// is expensive while animating nodes is extremely cheap, so this step is offered to clean up
705    /// the data in that regard.
706    ///
707    /// Use the `threshold` property to control this.
708    /// Use the `all_or_none` property if you want bones removed if and only if all bones within the
709    /// scene qualify for removal.
710    pub fn debone<F: Fn(&mut Debone)>(&mut self, closure: F) {
711        let mut args = Debone::default();
712        closure(&mut args);
713
714        self.set_import_flag(AIPROCESS_DEBONE, args.enable);
715        if args.enable {
716            self.set_float_property(PP_DB_THRESHOLD, args.threshold);
717            self.set_bool_property(PP_DB_ALL_OR_NONE, args.all_or_none);
718        }
719    }
720
721    /// Global setting to disable generation of skeleton dummy meshes
722    ///
723    /// Skeleton dummy meshes are generated as a visualization aid in cases which the input data
724    /// contains no geometry, but only animation data.
725    pub fn import_no_skeleton_meshes(&mut self, enable: bool) {
726        self.set_bool_property(IMPORT_NO_SKELETON_MESHES, enable);
727    }
728
729    /// Sets the colormap to be used to decode embedded textures in MDL (Quake or 3DGS) files.
730    ///
731    /// This must be a valid path to a file. The file is 768 (256*3) bytes large and contains RGB
732    /// triplets for each of the 256 palette entries. If the file is not found, a default palette
733    /// (from Quake 1) is used.
734    ///
735    /// Default: colormap.lmp
736    pub fn import_mdl_colormap(&mut self, path: &str) {
737        self.set_string_property(IMPORT_MDL_COLORMAP, path);
738    }
739
740    /// Set whether the FBX importer will merge all geometry layers present in the source file or
741    /// take only the first.
742    ///
743    /// Default: true.
744    pub fn fbx_read_all_geometry_layers(&mut self, enable: bool) {
745        self.set_bool_property(IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, enable);
746    }
747
748    /// Set whether the FBX importer will read all materials present in the source file or take only
749    /// the referenced materials. This has no effect if `fbx_read_materials` is false.
750    ///
751    /// Default: false.
752    pub fn fbx_read_all_materials(&mut self, enable: bool) {
753        self.set_bool_property(IMPORT_FBX_READ_ALL_MATERIALS, enable);
754    }
755
756    /// Set whether the FBX importer will read materials.
757    ///
758    /// Default: true.
759    pub fn fbx_read_materials(&mut self, enable: bool) {
760        self.set_bool_property(IMPORT_FBX_READ_MATERIALS, enable);
761    }
762
763    /// Set whether the FBX importer will read embedded textures.
764    ///
765    /// Default: true.
766    pub fn fbx_read_textures(&mut self, enable: bool) {
767        self.set_bool_property(IMPORT_FBX_READ_TEXTURES, enable);
768    }
769
770    /// Set whether the FBX importer will read cameras.
771    ///
772    /// Default: true.
773    pub fn fbx_read_cameras(&mut self, enable: bool) {
774        self.set_bool_property(IMPORT_FBX_READ_CAMERAS, enable);
775    }
776
777    /// Set whether the FBX importer will read light sources.
778    ///
779    /// Default: true.
780    pub fn fbx_read_lights(&mut self, enable: bool) {
781        self.set_bool_property(IMPORT_FBX_READ_LIGHTS, enable);
782    }
783
784    /// Set whether the FBX importer will read animations.
785    ///
786    /// Default: true.
787    pub fn fbx_read_animations(&mut self, enable: bool) {
788        self.set_bool_property(IMPORT_FBX_READ_ANIMATIONS, enable);
789    }
790
791    /// Set whether the FBX importer will act in strict mode in which only FBX 2013 is supported and
792    /// any other sub formats are rejected. FBX 2013 is the primary target for the importer, so this
793    /// format is best supported and well-tested.
794    ///
795    /// Default: false.
796    pub fn fbx_strict_mode(&mut self, enable: bool) {
797        self.set_bool_property(IMPORT_FBX_STRICT_MODE, enable);
798    }
799
800    /// Set whether the FBX importer will preserve pivot points for transformations (as extra
801    /// nodes). If set to false, pivots and offsets will be evaluated whenever possible.
802    ///
803    /// Default: true.
804    pub fn fbx_preserve_pivots(&mut self, enable: bool) {
805        self.set_bool_property(IMPORT_FBX_PRESERVE_PIVOTS, enable);
806    }
807
808    /// Specifies whether the FBX importer will drop empty animation curves or animation curves
809    /// which match the bind pose transformation over their entire defined range.
810    ///
811    /// Default: true.
812    pub fn fbx_optimize_empty_animation_curves(&mut self, enable: bool) {
813        self.set_bool_property(IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, enable);
814    }
815
816    /// Set the vertex animation keyframe to be imported
817    ///
818    /// Assimp does not support vertex keyframes (only bone animation is supported). The library
819    /// reads only one frame of models with vertex animations. This option applies to all importers,
820    /// unless overridden for a specific loader.
821    ///
822    /// Default: first frame.
823    pub fn global_keyframe(&mut self, value: i32) {
824        self.set_int_property(IMPORT_GLOBAL_KEYFRAME, value);
825    }
826
827    /// Override [`global_keyframe`](#method.global_keyframe) property for the MD3 importer.
828    pub fn md3_keyframe(&mut self, value: i32) {
829        self.set_int_property(IMPORT_MD3_KEYFRAME, value);
830    }
831
832    /// Override [`global_keyframe`](#method.global_keyframe) property for the MD2 importer.
833    pub fn md2_keyframe(&mut self, value: i32) {
834        self.set_int_property(IMPORT_MD2_KEYFRAME, value);
835    }
836
837    /// Override [`global_keyframe`](#method.global_keyframe) property for the MDL importer.
838    pub fn mdl_keyframe(&mut self, value: i32) {
839        self.set_int_property(IMPORT_MDL_KEYFRAME, value);
840    }
841
842    /// Override [`global_keyframe`](#method.global_keyframe) property for the MDC importer.
843    pub fn mdc_keyframe(&mut self, value: i32) {
844        self.set_int_property(IMPORT_MDC_KEYFRAME, value);
845    }
846
847    /// Override [`global_keyframe`](#method.global_keyframe) property for the SMD importer.
848    pub fn smd_keyframe(&mut self, value: i32) {
849        self.set_int_property(IMPORT_SMD_KEYFRAME, value);
850    }
851
852    /// Override [`global_keyframe`](#method.global_keyframe) property for the Unreal importer.
853    pub fn unreal_keyframe(&mut self, value: i32) {
854        self.set_int_property(IMPORT_UNREAL_KEYFRAME, value);
855    }
856
857    /// Configures the AC importer to collect all surfaces which have the "Backface cull" flag set
858    /// in separate meshes.
859    ///
860    /// Default: true.
861    pub fn ac_separate_bf_cull(&mut self, enable: bool) {
862        self.set_bool_property(IMPORT_AC_SEPARATE_BFCULL, enable);
863    }
864
865    /// Configures whether the AC importer evaluates subdivision surfaces (indicated by the presence
866    /// of the 'subdiv' attribute in the file). By default, Assimp performs the subdivision using
867    /// the standard Catmull-Clark algorithm.
868    ///
869    /// Default: true.
870    pub fn ac_eval_subdivision(&mut self, enable: bool) {
871        self.set_bool_property(IMPORT_AC_EVAL_SUBDIVISION, enable);
872    }
873
874    /// Configures the Unreal importer to separate faces with different surface flags (e.g.
875    /// two-sided vs. single-sided).
876    ///
877    /// Default: true.
878    pub fn unreal_handle_flags(&mut self, enable: bool) {
879        self.set_bool_property(UNREAL_HANDLE_FLAGS, enable);
880    }
881
882    /// Configures the terragen importer to compute UVs for terrains, if not given.
883    /// Furthermore a default texture is assigned.
884    ///
885    /// Default: false.
886    pub fn ter_make_uvs(&mut self, enable: bool) {
887        self.set_bool_property(IMPORT_TER_MAKE_UVS, enable);
888    }
889
890    /// Configures the ASE importer to always reconstruct normal vectors based on the smoothing
891    /// groups loaded from the file.
892    ///
893    /// Default: true.
894    pub fn ase_reconstruct_normals(&mut self, enable: bool) {
895        self.set_bool_property(IMPORT_ASE_RECONSTRUCT_NORMALS, enable);
896    }
897
898    /// Configures the MD3 importer to detect and process multi-part Quake player models.
899    ///
900    /// These models usually consist of 3 files, lower.md3, upper.md3 and head.md3. If this property
901    /// is set to true, Assimp will try to load and combine all three files if one of them is
902    /// loaded.
903    ///
904    /// Default: true.
905    pub fn md3_handle_multipart(&mut self, enable: bool) {
906        self.set_bool_property(IMPORT_MD3_HANDLE_MULTIPART, enable);
907    }
908
909    /// Tells the MD3 importer which skin files to load.
910    ///
911    /// When loading MD3 files, Assimp checks whether a file `<md3_file_name>_<skin_name>.skin` is
912    /// existing. These files are used by Quake III to be able to assign different skins (e.g. red
913    /// and blue team) to models. 'default', 'red', 'blue' are typical skin names.
914    ///
915    /// Default: "default".
916    pub fn md3_skin_name(&mut self, name: &str) {
917        self.set_string_property(IMPORT_MD3_SKIN_NAME, name);
918    }
919
920    /// Specify the Quake 3 shader file to be used for a particular MD3 file. This can also be a
921    /// search path.
922    ///
923    /// By default Assimp's behaviour is as follows: If a MD3 file
924    /// `<any_path>/models/<any_q3_subdir>/<model_name>/<file_name>.md3` is loaded, the library
925    /// tries to locate the corresponding shader file in `<any_path>/scripts/<model_name>.shader`.
926    /// This property overrides this behaviour. It can either specify a full path to the shader to
927    /// be loaded or alternatively the path (relative or absolute) to the directory where the
928    /// shaders for all MD3s to be loaded reside. Assimp attempts to open
929    /// `<dir>/<model_name>.shader` first, `<dir>/<file_name>.shader` is the fallback file.
930    /// Note that `<dir>` should have a terminal (back)slash.
931    pub fn md3_shader_src(&mut self, path: &str) {
932        self.set_string_property(IMPORT_MD3_SHADER_SRC, path);
933    }
934
935    /// Configures the LWO importer to load just one layer from the model.
936    ///
937    /// LWO files consist of layers and in some cases it could be useful to load only one of them.
938    /// This property is a string which specifies the name of the layer. If the property is not set
939    /// the whole LWO model is loaded. Loading fails if the requested layer is not available.
940    /// The layer name may not be empty.
941    ///
942    /// Default: all layers are loaded.
943    pub fn lwo_one_layer_only_str(&mut self, name: &str) {
944        self.set_string_property(IMPORT_LWO_ONE_LAYER_ONLY, name);
945    }
946
947    /// Configures the LWO importer to load just one layer from the model.
948    ///
949    /// LWO files consist of layers and in some cases it could be useful to load only one of them.
950    /// This property is an integer which specifies the index of the layer. If the property is not
951    /// set the whole LWO model is loaded. Loading fails if the requested layer is not available.
952    /// The layer index is zero-based.
953    ///
954    /// Default: all layers are loaded.
955    pub fn lwo_one_layer_only_int(&mut self, index: i32) {
956        self.set_int_property(IMPORT_LWO_ONE_LAYER_ONLY, index);
957    }
958
959    /// Configures the MD5 loader to not load the MD5ANIM file for a MD5MESH file automatically.
960    ///
961    /// The default strategy is to look for a file with the same name but the MD5ANIM extension in
962    /// the same directory. If it is found, it is loaded and combined with the MD5MESH file. This
963    /// configuration option can be used to disable this behaviour.
964    ///
965    /// Default: false.
966    pub fn md5_no_anim_autoload(&mut self, enable: bool) {
967        self.set_bool_property(IMPORT_MD5_NO_ANIM_AUTOLOAD, enable);
968    }
969
970    /// Defines the begin of the time range for which the LWS loader evaluates animations and
971    /// computes aiNodeAnims.
972    ///
973    /// Assimp provides full conversion of LightWave's envelope system, including pre and post
974    /// conditions. The loader computes linearly subsampled animation channels with the frame rate
975    /// given in the LWS file. This property defines the start time. Note: animation channels are
976    /// only generated if a node has at least one envelope with more than one key assigned. This
977    /// property is given in frames, '0' is the first frame. By default, if this property is not
978    /// set, the importer takes the animation start from the input LWS file ('FirstFrame' line)
979    ///
980    /// Default: taken from file.
981    pub fn lws_anim_start(&mut self, frame: i32) {
982        self.set_int_property(IMPORT_LWS_ANIM_START, frame);
983    }
984
985    /// Defines the end of the time range for which the LWS loader evaluates animations and
986    /// computs aiNodeAnims. See [`lws_anim_start`](#method.lws_anim_start) for more info.
987    ///
988    /// Default: taken from file.
989    pub fn lws_anim_end(&mut self, frame: i32) {
990        self.set_int_property(IMPORT_LWS_ANIM_END, frame);
991    }
992
993    /// Defines the output frame rate of the IRR loader.
994    ///
995    /// IRR animations are difficult to convert for Assimp and there will always be a loss of
996    /// quality. This setting defines how many keys per second are returned by the converter.
997    ///
998    /// Default: 100.
999    pub fn irr_anim_fps(&mut self, fps: i32) {
1000        self.set_int_property(IMPORT_IRR_ANIM_FPS, fps);
1001    }
1002
1003    /// Ogre Importer will try to find referenced materials from this file.
1004    ///
1005    /// Ogre meshes reference with material names, this does not tell Assimp the file where it is
1006    /// located in. Assimp will try to find the source file in the following order:
1007    ///
1008    /// 1. `<material-name>.material`
1009    /// 2. `<mesh-filename-base>.material`
1010    /// 3. The material name defined by this config property.
1011    ///
1012    /// Default value: Scene.material.
1013    pub fn ogre_material_file(&mut self, file: &str) {
1014        self.set_string_property(IMPORT_OGRE_MATERIAL_FILE, file);
1015    }
1016
1017    /// Ogre Importer detect the texture usage from its filename.
1018    ///
1019    /// Ogre material texture units do not define texture type, the textures usage depends on the
1020    /// used shader or Ogres fixed pipeline. If this config property is true Assimp will try to
1021    /// detect the type from the textures filename postfix:
1022    ///
1023    /// * _n, _nrm, _nrml, _normal, _normals and _normalmap for normal map
1024    /// * _s, _spec, _specular and _specularmap for specular map
1025    /// * _l, _light, _lightmap, _occ and _occlusion for light map
1026    /// * _disp and _displacement for displacement map
1027    ///
1028    /// The matching is case insensitive. Post fix is taken between last "_" and last ".". Default
1029    /// behavior is to detect type from lower cased texture unit name by matching against:
1030    /// normalmap, specularmap, lightmap and displacementmap. For both cases if no match is found
1031    /// aiTextureType_DIFFUSE is used.
1032    ///
1033    /// Default: false.
1034    pub fn ogre_texture_type_from_filename(&mut self, enable: bool) {
1035        self.set_bool_property(IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME, enable);
1036    }
1037
1038    /// Specifies whether the IFC loader skips over IfcSpace elements.
1039    ///
1040    /// IfcSpace elements (and their geometric representations) are used to represent, well, free
1041    /// space in a building storey.
1042    ///
1043    /// Default: true.
1044    pub fn ifc_skip_space_representations(&mut self, enable: bool) {
1045        self.set_bool_property(IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS, enable);
1046    }
1047
1048    /// Specifies whether the IFC loader skips over shape representations of type 'Curve2D'.
1049    ///
1050    /// A lot of files contain both a faceted mesh representation and a outline with a presentation
1051    /// type of 'Curve2D'. Currently Assimp doesn't convert those, so turning this option off just
1052    // clutters the log with errors.
1053    ///
1054    /// Default: true.
1055    pub fn ifc_skip_curve_representations(&mut self, enable: bool) {
1056        self.set_bool_property(IMPORT_IFC_SKIP_CURVE_REPRESENTATIONS, enable);
1057    }
1058
1059    /// Specifies whether the IFC loader will use its own, custom triangulation algorithm to
1060    /// triangulate wall and floor meshes.
1061    ///
1062    /// If this property is set to false, walls will be either triangulated by `triangulate`
1063    /// [`triangulate`](#method.triangulate) or will be passed through as huge polygons with
1064    /// faked holes (i.e. holes that are connected with the outer boundary using a dummy edge).
1065    /// It is highly recommended to set this property to true if you want triangulated data because
1066    /// `triangulate` is known to have problems with the kind of polygons that the IFC loader spits
1067    /// out for complicated meshes.
1068    ///
1069    /// Default: true.
1070    pub fn ifc_custom_triangulation(&mut self, enable: bool) {
1071        self.set_bool_property(IMPORT_IFC_CUSTOM_TRIANGULATION, enable);
1072    }
1073
1074    /// Tells the Collada importer to ignore the up direction specified in the file.
1075    ///
1076    /// Default: false.
1077    pub fn collada_ignore_up_direction(&mut self, enable: bool) {
1078        self.set_bool_property(IMPORT_COLLADA_IGNORE_UP_DIRECTION, enable);
1079    }
1080
1081    /// Get a list of all file extensions supported by Assimp.
1082    ///
1083    /// If a file extension is contained in the list this does, of course, not mean that Assimp is
1084    /// able to load all files with this extension.
1085    ///
1086    /// # Return value
1087    /// `Vec<String>` containing the supported file extensions in lower-case with no leading
1088    /// wildcard or period characters, e.g. "3ds", "obj", "fbx".
1089    pub fn get_extension_list() -> Vec<String> {
1090        let mut ext_list = AiString::default();
1091        unsafe { aiGetExtensionList(&mut ext_list) };
1092
1093        let extensions = ext_list.as_ref().split(';');
1094        extensions.map(|x| x.trim_left_matches("*.").to_owned()).collect()
1095    }
1096}
1097
1098impl Drop for Importer {
1099    fn drop(&mut self) {
1100        unsafe { aiReleasePropertyStore(self.property_store) }
1101    }
1102}