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}