stereokit_rust/
model.rs

1use crate::maths::{Bool32T, Matrix};
2use crate::sk::MainThreadToken;
3use crate::{
4    StereoKitError,
5    material::{Cull, Material, MaterialT},
6    maths::{Bounds, Ray, Vec3},
7    mesh::{Mesh, MeshT},
8    shader::{Shader, ShaderT},
9    system::{IAsset, Log, RenderLayer},
10    util::Color128,
11};
12use std::{
13    ffi::{CStr, CString, c_char, c_void},
14    path::Path,
15    ptr::{NonNull, null_mut},
16};
17
18/// A Model is a collection of meshes, materials, and transforms that make up a visual element! This is a great way to
19/// group together complex objects that have multiple parts in them, and in fact, most model formats are composed this
20/// way already!
21///
22/// This class contains a number of methods for creation. If you pass in a .obj, .stl, , .ply (ASCII), .gltf, or .glb,
23/// StereoKit will load that model from file, and assemble materials and transforms from the file information. But you
24/// can also assemble a model from procedurally generated meshes!
25///
26/// Because models include an offset transform for each mesh element, this does have the overhead of an extra matrix
27/// multiplication in order to execute a render command. So if you need speed, and only have a single mesh with a
28/// precalculated transform matrix, it can be faster to render a Mesh instead of a Model!
29/// <https://stereokit.net/Pages/StereoKit/Model.html>
30///
31/// ### Examples
32/// ```
33/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
34/// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model, mesh::Mesh, material::Material};
35///
36/// let sphere =       Mesh::generate_sphere(0.6, None);
37/// let rounded_cube = Mesh::generate_rounded_cube(Vec3::ONE * 0.6, 0.2, None);
38/// let cylinder =     Mesh::generate_cylinder(0.25, 0.6, Vec3::Z, None);
39///
40/// let transform1 = Matrix::t([-0.7,-0.5, 0.0]);
41/// let transform2 = Matrix::t([ 0.0, 0.0, 0.0]);
42/// let transform3 = Matrix::t([ 0.7, 0.5, 0.0]);
43///
44/// let material = Material::pbr();
45///
46/// let model = Model::new();
47/// let mut nodes = model.get_nodes();
48/// nodes.add("sphere",   transform1 , Some(&sphere),       Some(&material), true)
49///      .add("cube",     transform2 , Some(&rounded_cube), Some(&material), true)
50///      .add("cylinder", transform3 , Some(&cylinder),     Some(&material), true);
51///
52/// filename_scr = "screenshots/model.jpeg";
53/// test_screenshot!( // !!!! Get a proper main loop !!!!
54///     model.draw(token, Matrix::IDENTITY, None, None);
55/// );
56/// ```
57/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/model.jpeg" alt="screenshot" width="200">
58#[derive(Debug, PartialEq)]
59pub struct Model(pub NonNull<_ModelT>);
60impl Drop for Model {
61    fn drop(&mut self) {
62        unsafe { model_release(self.0.as_ptr()) }
63    }
64}
65/// AsRef
66impl AsRef<Model> for Model {
67    fn as_ref(&self) -> &Model {
68        self
69    }
70}
71/// From / Into
72impl From<Model> for ModelT {
73    fn from(val: Model) -> Self {
74        val.0.as_ptr()
75    }
76}
77
78/// StereoKit internal type.
79#[repr(C)]
80#[derive(Debug)]
81pub struct _ModelT {
82    _unused: [u8; 0],
83}
84/// StereoKit ffi type.
85pub type ModelT = *mut _ModelT;
86
87unsafe extern "C" {
88    pub fn model_find(id: *const c_char) -> ModelT;
89    pub fn model_copy(model: ModelT) -> ModelT;
90    pub fn model_create() -> ModelT;
91    pub fn model_create_mesh(mesh: MeshT, material: MaterialT) -> ModelT;
92    pub fn model_create_mem(
93        filename_utf8: *const c_char,
94        data: *const c_void,
95        data_size: usize,
96        shader: ShaderT,
97    ) -> ModelT;
98    pub fn model_create_file(filename_utf8: *const c_char, shader: ShaderT) -> ModelT;
99    pub fn model_set_id(model: ModelT, id: *const c_char);
100    pub fn model_get_id(model: ModelT) -> *const c_char;
101    pub fn model_addref(model: ModelT);
102    pub fn model_release(model: ModelT);
103    pub fn model_draw(model: ModelT, transform: Matrix, color_linear: Color128, layer: RenderLayer);
104    pub fn model_draw_mat(
105        model: ModelT,
106        material_override: MaterialT,
107        transform: Matrix,
108        color_linear: Color128,
109        layer: RenderLayer,
110    );
111    pub fn model_recalculate_bounds(model: ModelT);
112    pub fn model_recalculate_bounds_exact(model: ModelT);
113    pub fn model_set_bounds(model: ModelT, bounds: *const Bounds);
114    pub fn model_get_bounds(model: ModelT) -> Bounds;
115    pub fn model_ray_intersect(model: ModelT, model_space_ray: Ray, cull_mode: Cull, out_pt: *mut Ray) -> Bool32T;
116    pub fn model_ray_intersect_bvh(model: ModelT, model_space_ray: Ray, cull_mode: Cull, out_pt: *mut Ray) -> Bool32T;
117    pub fn model_ray_intersect_bvh_detailed(
118        model: ModelT,
119        model_space_ray: Ray,
120        cull_mode: Cull,
121        out_pt: *mut Ray,
122        out_mesh: *mut MeshT,
123        out_matrix: *mut Matrix,
124        out_start_inds: *mut u32,
125    ) -> Bool32T;
126    pub fn model_step_anim(model: ModelT);
127    pub fn model_play_anim(model: ModelT, animation_name: *const c_char, mode: AnimMode) -> Bool32T;
128    pub fn model_play_anim_idx(model: ModelT, index: i32, mode: AnimMode);
129    pub fn model_set_anim_time(model: ModelT, time: f32);
130    pub fn model_set_anim_completion(model: ModelT, percent: f32);
131    pub fn model_anim_find(model: ModelT, animation_name: *const c_char) -> i32;
132    pub fn model_anim_count(model: ModelT) -> i32;
133    pub fn model_anim_active(model: ModelT) -> i32;
134    pub fn model_anim_active_mode(model: ModelT) -> AnimMode;
135    pub fn model_anim_active_time(model: ModelT) -> f32;
136    pub fn model_anim_active_completion(model: ModelT) -> f32;
137    pub fn model_anim_get_name(model: ModelT, index: i32) -> *const c_char;
138    pub fn model_anim_get_duration(model: ModelT, index: i32) -> f32;
139    // Deprecated :pub fn model_get_name(model: ModelT, subset: i32) -> *const c_char;
140    // Deprecated :pub fn model_get_material(model: ModelT, subset: i32) -> MaterialT;
141    // Deprecated :pub fn model_get_mesh(model: ModelT, subset: i32) -> MeshT;
142    // Deprecated :pub fn model_get_transform(model: ModelT, subset: i32) -> Matrix;
143    // Deprecated :pub fn model_set_material(model: ModelT, subset: i32, material: MaterialT);
144    // Deprecated :pub fn model_set_mesh(model: ModelT, subset: i32, mesh: MeshT);
145    // Deprecated :pub fn model_set_transform(model: ModelT, subset: i32, transform: *const Matrix);
146    // Deprecated :pub fn model_remove_subset(model: ModelT, subset: i32);
147    // Deprecated :pub fn model_add_named_subset(
148    //     model: ModelT,
149    //     name: *const c_char,
150    //     mesh: MeshT,
151    //     material: MaterialT,
152    //     transform: *const Matrix,
153    // ) -> i32;
154    // Deprecated :pub fn model_add_subset(model: ModelT, mesh: MeshT, material: MaterialT, transform: *const Matrix) -> i32;
155}
156
157impl IAsset for Model {
158    // fn id(&mut self, id: impl AsRef<str>) {
159    //     self.id(id);
160    // }
161
162    fn get_id(&self) -> &str {
163        self.get_id()
164    }
165}
166
167impl Default for Model {
168    /// Create an empty model
169    /// <https://stereokit.net/Pages/StereoKit/Model/Model.html>
170    ///
171    /// see also [`Model::new`]
172    fn default() -> Self {
173        Self::new()
174    }
175}
176
177impl Model {
178    /// Create an empty model
179    /// <https://stereokit.net/Pages/StereoKit/Model/Model.html>
180    ///
181    /// see also [`model_create`] [`Model::default`]
182    /// ### Examples
183    /// ```
184    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
185    /// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model, mesh::Mesh, material::Material};
186    ///
187    /// // Let's create a model with two identical spheres.
188    /// let sphere =       Mesh::generate_sphere(0.6, None);
189    ///
190    /// let transform_mesh1  = Matrix::t([-0.7,-0.5, 0.0]);
191    /// let transform_mesh2  = Matrix::t([ 0.7, 0.5, 0.0]);
192    ///
193    /// let transform_model  = Matrix::r([ 0.0, 180.0, 0.0]);
194    ///
195    /// let material = Material::pbr();
196    ///
197    /// let model = Model::new();
198    /// let mut nodes = model.get_nodes();
199    /// assert_eq!(nodes.get_count(), 0);
200    ///
201    /// nodes.add("sphere1", transform_mesh1, Some(&sphere), Some(&material), true);
202    /// nodes.add("sphere2", transform_mesh2, Some(&sphere), Some(&material), true);
203    /// assert_eq!(nodes.get_count(), 2);
204    ///
205    /// test_steps!( // !!!! Get a proper main loop !!!!
206    ///     model.draw(token, transform_model, None, None);
207    /// );
208    /// ```
209    pub fn new() -> Model {
210        Model(NonNull::new(unsafe { model_create() }).unwrap())
211    }
212
213    /// Creates a single mesh subset Model using the indicated Mesh and Material!
214    /// An id will be automatically generated for this asset.
215    /// <https://stereokit.net/Pages/StereoKit/Model/FromMesh.html>
216    /// * `mesh` - The mesh to use for this model.
217    /// * `material` - The material to use for this mesh.
218    ///
219    /// see also [`model_create_mesh`]
220    /// ### Examples
221    /// ```
222    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
223    /// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model, mesh::Mesh, material::Material};
224    ///
225    /// // Let's create a model with two identical spheres.
226    /// let sphere =       Mesh::generate_sphere(0.8, None);
227    ///
228    /// let transform_mesh1  = Matrix::t([-0.5,-0.5, 0.0]);
229    /// let transform_mesh2  = Matrix::t([ 0.5, 0.5, 0.0]);
230    ///
231    /// let transform_model  = Matrix::r([ 0.0, 150.0, 0.0]);
232    ///
233    /// let material = Material::pbr();
234    ///
235    /// let model = Model::from_mesh(&sphere, &material);
236    /// let mut nodes = model.get_nodes();
237    /// assert_eq!(nodes.get_count(), 1);
238    ///
239    /// nodes.add("sphere2", transform_mesh1, Some(&sphere), Some(&material), true);
240    /// nodes.add("sphere3", transform_mesh2, Some(&sphere), Some(&material), true);
241    /// assert_eq!(nodes.get_count(), 3);
242    ///
243    /// filename_scr = "screenshots/model_from_mesh.jpeg";
244    /// test_screenshot!( // !!!! Get a proper main loop !!!!
245    ///     model.draw(token, transform_model, None, None);
246    /// );
247    /// ```
248    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/model_from_mesh.jpeg" alt="screenshot" width="200">
249    pub fn from_mesh<Me: AsRef<Mesh>, Ma: AsRef<Material>>(mesh: Me, material: Ma) -> Model {
250        Model(
251            NonNull::new(unsafe { model_create_mesh(mesh.as_ref().0.as_ptr(), material.as_ref().0.as_ptr()) }).unwrap(),
252        )
253    }
254
255    /// Loads a list of mesh and material subsets from a .obj, .stl, .ply (ASCII), .gltf, or .glb file stored in memory.
256    /// Note that this function won’t work well on files that reference other files, such as .gltf files with
257    /// references in them.
258    /// <https://stereokit.net/Pages/StereoKit/Model/FromMemory.html>
259    /// * `file_name` - StereoKit still uses the filename of the data for format discovery, but not asset Id creation.
260    ///   If you don’t have a real filename for the data, just pass in an extension with a leading ‘.’ character here,
261    ///   like “.glb”.
262    /// * `data` - The binary data of a model file, this is NOT a raw array of vertex and index data!
263    /// * `shader` - The shader to use for the model's materials!, if None, this will automatically determine the best
264    ///   available shader to use.
265    ///
266    /// see also [`model_create_mem`] [`Model::from_file`]
267    /// ### Examples
268    /// ```
269    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
270    /// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model, util::named_colors};
271    ///
272    /// let my_bytes = std::include_bytes!("../assets/plane.glb");
273    ///
274    /// let model = Model::from_memory("my_bytes_center.glb", my_bytes, None).unwrap().copy();
275    /// let transform = Matrix::t_r_s(Vec3::Y * 0.10, [0.0, 110.0, 0.0], Vec3::ONE * 0.09);
276    ///
277    /// filename_scr = "screenshots/model_from_memory.jpeg";
278    /// test_screenshot!( // !!!! Get a proper main loop !!!!
279    ///     model.draw(token, transform, Some(named_colors::GREEN.into()), None);
280    /// );
281    /// ```
282    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/model_from_memory.jpeg" alt="screenshot" width="200">    
283    pub fn from_memory<S: AsRef<str>>(
284        file_name: S,
285        data: &[u8],
286        shader: Option<Shader>,
287    ) -> Result<Model, StereoKitError> {
288        let c_file_name = CString::new(file_name.as_ref())?;
289        let shader = shader.map(|shader| shader.0.as_ptr()).unwrap_or(null_mut());
290        match NonNull::new(unsafe {
291            model_create_mem(c_file_name.as_ptr(), data.as_ptr() as *const c_void, data.len(), shader)
292        }) {
293            Some(model) => Ok(Model(model)),
294            None => Err(StereoKitError::ModelFromMem(file_name.as_ref().to_owned(), "file not found!".to_owned())),
295        }
296    }
297
298    /// Loads a list of mesh and material subsets from a .obj, .stl, .ply (ASCII), .gltf, or .glb file.
299    ///
300    /// **Important**: The model is loaded only once. If you open the same file a second time, it will return the model
301    /// loaded the first time and all its modifications afterwards. If you want two different instances, remember to
302    /// copy the model while not forgetting that the assets that the copies contain when loaded are the same.
303    /// <https://stereokit.net/Pages/StereoKit/Model/FromFile.html>
304    /// * `file` - Name of the file to load! This gets prefixed with the StereoKit asset folder if no drive letter
305    ///   is specified in the path.
306    /// * `shader` - The shader to use for the model’s materials! If None, this will automatically determine the best
307    ///   shader available to use.
308    ///
309    /// see also [`model_create_file`] [`Model::from_memory`]
310    /// ### Examples
311    /// ```
312    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
313    /// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model};
314    ///
315    /// let model = Model::from_file("center.glb", None)
316    ///                              .expect("Could not load model").copy();
317    /// let transform = Matrix::t_r_s(Vec3::NEG_Y * 0.40, [0.0, 190.0, 0.0], Vec3::ONE * 0.25);
318    ///
319    /// filename_scr = "screenshots/model_from_file.jpeg";
320    /// test_screenshot!( // !!!! Get a proper main loop !!!!
321    ///     model.draw(token, transform, None, None);
322    /// );
323    /// ```
324    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/model_from_file.jpeg" alt="screenshot" width="200">
325    pub fn from_file(file: impl AsRef<Path>, shader: Option<Shader>) -> Result<Model, StereoKitError> {
326        let path = file.as_ref();
327        let path_buf = path.to_path_buf();
328        let c_str = CString::new(path.to_str().unwrap())?;
329        let shader = shader.map(|shader| shader.0.as_ptr()).unwrap_or(null_mut());
330        match NonNull::new(unsafe { model_create_file(c_str.as_ptr(), shader) }) {
331            Some(model) => Ok(Model(model)),
332            None => Err(StereoKitError::ModelFromFile(path_buf.to_owned(), "file not found!".to_owned())),
333        }
334    }
335    /// Creates a shallow copy of a Model asset! Meshes and Materials referenced by this Model will be referenced, not
336    /// copied.
337    /// <https://stereokit.net/Pages/StereoKit/Model/Copy.html>
338    ///
339    /// see also [`model_copy()`]
340    /// ### Examples
341    /// ```
342    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
343    /// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model};
344    ///
345    /// let model = Model::from_file("center.glb", None).expect("Model should load");
346    /// let model_copy = model.copy();
347    ///
348    /// assert_ne!(model, model_copy);
349    /// assert_eq!(model.get_nodes().get_count(), model_copy.get_nodes().get_count());
350    ///
351    /// // The nodes contains the same meshes as these are not copied.
352    /// assert_eq!(model.get_nodes().get_index(0).expect("Node should exist").get_mesh(),
353    ///            model_copy.get_nodes().get_index(0).expect("Node should exist").get_mesh());
354    ///
355    /// assert_eq!(model.get_nodes().get_index(2).expect("Node should exist").get_mesh(),
356    ///            model_copy.get_nodes().get_index(2).expect("Node should exist").get_mesh());
357    /// ```
358    pub fn copy(&self) -> Model {
359        Model(NonNull::new(unsafe { model_copy(self.0.as_ptr()) }).unwrap())
360    }
361
362    /// Looks for a Model asset that’s already loaded, matching the given id!
363    /// <https://stereokit.net/Pages/StereoKit/Model/Find.html>
364    /// * `id` - Which Model are you looking for?
365    ///
366    /// see also [`model_find`] [`Model::clone_ref`]
367    /// ### Examples
368    /// ```
369    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
370    /// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model};
371    ///
372    /// let mut model = Model::from_file("center.glb", None).expect("Model should load");
373    /// model.id("my_model_id");
374    ///
375    /// let same_model = Model::find("my_model_id").expect("Model should be found");
376    ///
377    /// assert_eq!(model, same_model);
378    /// ```
379    pub fn find<S: AsRef<str>>(id: S) -> Result<Model, StereoKitError> {
380        let c_str = CString::new(id.as_ref())?;
381        match NonNull::new(unsafe { model_find(c_str.as_ptr()) }) {
382            Some(model) => Ok(Model(model)),
383            None => Err(StereoKitError::ModelFind(id.as_ref().to_owned())),
384        }
385    }
386
387    /// Creates a clone of the same reference. Basically, the new variable is the same asset. This is what you get by
388    /// calling find() method.
389    /// <https://stereokit.net/Pages/StereoKit/Model/Find.html>
390    ///
391    /// see also [`model_find`] [`Model::find`]
392    /// ### Examples
393    /// ```
394    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
395    /// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model};
396    ///
397    /// let mut model = Model::from_file("center.glb", None).expect("Model should load");
398    ///
399    /// let same_model = model.clone_ref();
400    ///
401    /// assert_eq!(model, same_model);
402    /// ```
403    pub fn clone_ref(&self) -> Model {
404        Model(NonNull::new(unsafe { model_find(model_get_id(self.0.as_ptr())) }).expect("<asset>::clone_ref failed!"))
405    }
406
407    //-----------------Modify Model :
408    /// Set a new id to the model.
409    /// <https://stereokit.net/Pages/StereoKit/Model/Id.html>
410    ///
411    /// see also [`model_set_id`]
412    /// ### Examples
413    /// ```
414    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
415    /// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model};
416    ///
417    /// let mut model = Model::new();
418    /// assert!(model.get_id().starts_with("auto/model_"));
419    ///
420    /// model.id("my_model_id");
421    /// assert_eq!(model.get_id(), "my_model_id");
422    /// ```
423    pub fn id<S: AsRef<str>>(&mut self, id: S) -> &mut Self {
424        let c_str = CString::new(id.as_ref()).unwrap();
425        unsafe { model_set_id(self.0.as_ptr(), c_str.as_ptr()) };
426        self
427    }
428
429    /// Set the bounds of this model. This is a bounding box that encapsulates the Model and all its subsets! It’s used for collision,
430    /// visibility testing, UI layout, and probably other things. While it’s normally calculated from the mesh bounds, you can also override this to suit your needs.
431    /// <https://stereokit.net/Pages/StereoKit/Model/Bounds.html>
432    ///
433    /// see also [`model_set_bounds`]
434    /// ### Examples
435    /// ```
436    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
437    /// use stereokit_rust::{maths::{Vec3, Matrix, Bounds}, model::Model, mesh::Mesh,
438    ///                      material::Material, util::named_colors};
439    ///
440    /// let cube_bounds  = Mesh::cube();
441    ///
442    /// let cube = Mesh::generate_cube(Vec3::ONE * 0.3, None);
443    ///
444    /// let transform1 = Matrix::t([-0.30,-0.30,-0.30]);
445    /// let transform2 = Matrix::t([ 0.30, 0.30, 0.30]);
446    ///
447    /// let material = Material::pbr();
448    /// let mut model = Model::new();
449    /// let mut nodes = model.get_nodes();
450    /// nodes.add("cube1", transform1, Some(&cube), Some(&material), true)
451    ///      .add("cube2", transform2, Some(&cube), Some(&material), true);
452    ///
453    /// let mut material_before = Material::ui_box();
454    /// material_before.color_tint(named_colors::GOLD)
455    ///                .border_size(0.025);
456    ///
457    /// let mut material_after = material_before.copy();
458    /// material_after.color_tint(named_colors::RED);
459    ///
460    /// let bounds = model.get_bounds();
461    /// let transform_before = Matrix::t_s( bounds.center, bounds.dimensions);
462    /// assert_eq!(bounds.center, Vec3::ZERO);
463    /// assert_eq!(bounds.dimensions, Vec3::ONE * 0.9);
464    ///
465    /// // let's reduce the bounds to the upper cube only
466    /// model.bounds( Bounds::new([0.30, 0.30, 0.30].into(), Vec3::ONE * 0.301));
467    /// let new_bounds = model.get_bounds();
468    /// let transform_after = Matrix::t_s( new_bounds.center, new_bounds.dimensions);
469    ///
470    /// filename_scr = "screenshots/model_bounds.jpeg";
471    /// test_screenshot!( // !!!! Get a proper main loop !!!!
472    ///     model.draw(token, Matrix::IDENTITY, None, None);
473    ///     cube_bounds.draw(  token, &material_before, transform_before, None, None);
474    ///     cube_bounds.draw(  token, &material_after,  transform_after,  None, None);
475    /// );
476    /// ```
477    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/model_bounds.jpeg" alt="screenshot" width="200">
478    pub fn bounds(&mut self, bounds: impl AsRef<Bounds>) -> &mut Self {
479        unsafe { model_set_bounds(self.0.as_ptr(), bounds.as_ref()) };
480        self
481    }
482
483    /// Adds this Model to the render queue for this frame! If the Hierarchy has a transform on it, that transform is
484    /// combined with the Matrix provided here.
485    /// <https://stereokit.net/Pages/StereoKit/Model/Draw.html>
486    /// * `token` - To be sure we are in the right thread, once per frame.
487    /// * `transform` - A Matrix that will transform the Model from Model Space into the current Hierarchy Space.
488    /// * `color_linear` - A per-instance linear space color value to pass into the shader! Normally this gets used like
489    ///   a material tint. If you’re adventurous and don’t need per-instance colors, this is a great spot to pack in
490    ///   extra per-instance data for the shader! If None has default value of WHITE.
491    /// * `layer` - All visuals are rendered using a layer bit-flag. By default, all layers are rendered, but this can
492    ///   be useful for filtering out objects for different rendering purposes! For example: rendering a mesh over the
493    ///   user’s head from a 3rd person perspective, but filtering it out from the 1st person perspective. If None has
494    ///   default value of Layer0.
495    ///
496    /// see also [`model_draw`] [`Model::draw_with_material`]
497    /// ### Examples
498    /// ```
499    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
500    /// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model, util::named_colors,
501    ///                      system::RenderLayer};
502    ///
503    /// let model = Model::from_file("center.glb", None)
504    ///                        .expect("Could not load model").copy();
505    /// let transform1 = Matrix::t_r_s([-0.70, -0.70, 0.0], [0.0, 190.0, 0.0], [0.25, 0.25, 0.25]);
506    /// let transform2 = transform1 * Matrix::t(Vec3::X * 0.70);
507    /// let transform3 = transform2 * Matrix::t(Vec3::X * 0.70);
508    ///
509    /// filename_scr = "screenshots/model_draw.jpeg";
510    /// test_screenshot!( // !!!! Get a proper main loop !!!!
511    ///     model.draw(token, transform1, None, None);
512    ///     model.draw(token, transform2, Some(named_colors::YELLOW.into()), None);
513    ///     model.draw(token, transform3, Some(named_colors::BLACK.into()),
514    ///                Some(RenderLayer::FirstPerson));
515    /// );
516    /// ```
517    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/model_draw.jpeg" alt="screenshot" width="200">
518    pub fn draw(
519        &self,
520        _token: &MainThreadToken,
521        transform: impl Into<Matrix>,
522        color_linear: Option<Color128>,
523        layer: Option<RenderLayer>,
524    ) {
525        let color_linear = color_linear.unwrap_or(Color128::WHITE);
526        let layer = layer.unwrap_or(RenderLayer::Layer0);
527        unsafe { model_draw(self.0.as_ptr(), transform.into(), color_linear, layer) };
528    }
529
530    /// Adds the model to the render queue of this frame overrided with the given material
531    /// <https://stereokit.net/Pages/StereoKit/Model/Draw.html>
532    /// * `token` - To be sure we are in the right thread, once per frame.
533    /// * `material_override` - the material that will override all materials of this model
534    /// * `transform` - A Matrix that will transform the Model from Model Space into the current Hierarchy Space.
535    /// * `color_linear` - A per-instance linear space color value to pass into the shader! Normally this gets used like
536    ///   a material tint. If you’re adventurous and don’t need per-instance colors, this is a great spot to pack in
537    ///   extra per-instance data for the shader! If None has default value of WHITE.
538    /// * `layer` - All visuals are rendered using a layer bit-flag. By default, all layers are rendered, but this can
539    ///   be useful for filtering out objects for different rendering purposes! For example: rendering a mesh over the
540    ///   user’s head from a 3rd person perspective, but filtering it out from the 1st person perspective. If None has
541    ///   default value of Layer0.
542    ///
543    /// see also [`model_draw`] [`Model::draw`]
544    /// ### Examples
545    /// ```
546    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
547    /// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model, util::named_colors,
548    ///                      material::Material, system::RenderLayer};
549    ///
550    /// let model = Model::from_file("cuve.glb", None)
551    ///                        .expect("Could not load model").copy();
552    /// let transform1 = Matrix::t_r_s([-0.50, -0.10, 0.0], [45.0, 0.0, 0.0], [0.07, 0.07, 0.07]);
553    /// let transform2 = transform1 * Matrix::t(Vec3::X * 0.70);
554    /// let transform3 = transform2 * Matrix::t(Vec3::X * 0.70);
555    ///
556    /// let mut material_ui = Material::ui_box();
557    /// material_ui.color_tint(named_colors::GOLD)
558    ///            .border_size(0.01);
559    ///
560    /// let material_brick =Material::from_file("shaders/brick_pbr.hlsl.sks",
561    ///                                         Some("my_material_brick")).unwrap();
562    ///
563    /// filename_scr = "screenshots/model_draw_with_material.jpeg";
564    /// test_screenshot!( // !!!! Get a proper main loop !!!!
565    ///     model.draw_with_material(token, &material_ui,    transform1, None, None);
566    ///     model.draw_with_material(token, &material_brick, transform2, None, None);
567    ///     model.draw_with_material(token, &material_ui,    transform3, Some(named_colors::RED.into()),
568    ///                Some(RenderLayer::FirstPerson));
569    /// );
570    /// ```
571    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/model_draw_with_material.jpeg" alt="screenshot" width="200">
572    pub fn draw_with_material<M: AsRef<Material>>(
573        &self,
574        _token: &MainThreadToken,
575        material_override: M,
576        transform: impl Into<Matrix>,
577        color_linear: Option<Color128>,
578        layer: Option<RenderLayer>,
579    ) {
580        let color_linear = match color_linear {
581            Some(c) => c,
582            None => Color128::WHITE,
583        };
584        let layer = layer.unwrap_or(RenderLayer::Layer0);
585        unsafe {
586            model_draw_mat(
587                self.0.as_ptr(),
588                material_override.as_ref().0.as_ptr(),
589                transform.into(),
590                color_linear,
591                layer,
592            )
593        };
594    }
595
596    /// Examines the visuals as they currently are, and rebuilds the bounds based on that! This is normally done automatically,
597    /// but if you modify a Mesh that this Model is using, the Model can’t see it, and you should call this manually.
598    /// <https://stereokit.net/Pages/StereoKit/Model/RecalculateBounds.html>
599    ///
600    /// see also [`model_recalculate_bounds`] [`Model::bounds`] [`Model::recalculate_bounds_exact`]
601    /// ### Examples
602    /// ```
603    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
604    /// use stereokit_rust::{maths::{Vec2, Vec3, Matrix, Bounds}, mesh::{Mesh, Vertex},
605    ///                      model::Model, material::Material, util::named_colors};
606    ///
607    /// let material = Material::pbr();
608    /// let mut square = Mesh::new();
609    /// square.set_verts(&[
610    ///     Vertex::new([-1.0, -1.0, 0.0].into(), Vec3::UP, None,            None),
611    ///     Vertex::new([ 1.0, -1.0, 0.0].into(), Vec3::UP, Some(Vec2::X),   None),
612    ///     Vertex::new([-1.0,  1.0, 0.0].into(), Vec3::UP, Some(Vec2::Y),   None),
613    ///     ], true)
614    ///    .set_inds(&[0, 1, 2]);
615    ///
616    /// let model = Model::from_mesh(&square, &material);
617    ///
618    /// assert_eq!(model.get_bounds(), Bounds::new(Vec3::ZERO, Vec3::new(2.0, 2.0, 0.0)));
619    ///
620    /// // We add a second vertex to our mesh adding the dimension-Z.
621    /// let mut vertices = square.get_verts_copy();
622    /// vertices.push(
623    ///     Vertex::new([ 1.0,  1.0, 1.0].into(), Vec3::UP, Some(Vec2::ONE), None));
624    ///
625    /// square.set_verts(&vertices, true)
626    ///       .set_inds(&[0, 1, 2, 2, 1, 3]);
627    ///
628    /// assert_eq!(model.get_bounds(), Bounds::new(Vec3::ZERO, Vec3::new(2.0, 2.0, 0.0)));
629    ///
630    /// model.recalculate_bounds();
631    ///
632    /// assert_eq!(model.get_bounds(), Bounds::new([0.0, 0.0, 0.5], [2.0, 2.0, 1.0]));
633    /// ```
634    pub fn recalculate_bounds(&self) {
635        unsafe { model_recalculate_bounds(self.0.as_ptr()) };
636    }
637
638    /// Examines the visuals as they currently are, and rebuilds the bounds based on all the vertices in the model!
639    /// This leads (in general) to a tighter bound than the default bound based on bounding boxes. However, computing
640    /// the exact bound can take much longer!
641    /// <https://stereokit.net/Pages/StereoKit/Model/RecalculateBoundsExact.html>
642    ///
643    /// see also [`model_recalculate_bounds_exact`] [`Model::bounds`] [`Model::recalculate_bounds`]
644    /// ### Examples
645    /// ```
646    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
647    /// use stereokit_rust::{maths::{Vec2, Vec3, Matrix, Bounds}, mesh::{Mesh, Vertex},
648    ///                      model::Model, material::Material, util::named_colors};
649    ///
650    /// let material = Material::pbr();
651    /// let mut square = Mesh::new();
652    /// square.set_verts(&[
653    ///     Vertex::new([-1.0, -1.0, 0.0].into(), Vec3::UP, None,            None),
654    ///     Vertex::new([ 1.0, -1.0, 0.0].into(), Vec3::UP, Some(Vec2::X),   None),
655    ///     Vertex::new([-1.0,  1.0, 0.0].into(), Vec3::UP, Some(Vec2::Y),   None),
656    ///     ], true)
657    ///    .set_inds(&[0, 1, 2]);
658    ///
659    /// let model = Model::from_mesh(&square, &material);
660    ///
661    /// assert_eq!(model.get_bounds(), Bounds::new(Vec3::ZERO, Vec3::new(2.0, 2.0, 0.0)));
662    ///
663    /// // We add a second vertex to our mesh adding the dimension-Z.
664    /// let mut vertices = square.get_verts_copy();
665    /// vertices.push(
666    ///     Vertex::new([ 1.0,  1.0, 1.0].into(), Vec3::UP, Some(Vec2::ONE), None));
667    ///
668    /// square.set_verts(&vertices, true)
669    ///       .set_inds(&[0, 1, 2, 2, 1, 3]);
670    ///
671    /// assert_eq!(model.get_bounds(), Bounds::new(Vec3::ZERO, Vec3::new(2.0, 2.0, 0.0)));
672    ///
673    /// model.recalculate_bounds_exact();
674    ///
675    /// assert_eq!(model.get_bounds(), Bounds::new([0.0, 0.0, 0.5], [2.0, 2.0, 1.0]));
676    /// ```
677    pub fn recalculate_bounds_exact(&self) {
678        unsafe { model_recalculate_bounds_exact(self.0.as_ptr()) };
679    }
680
681    /// Get the Id
682    /// <https://stereokit.net/Pages/StereoKit/Model/Id.html>
683    ///
684    /// see also [`model_get_id`] [`model_set_id`]
685    /// see example in [`Model::id`]
686    pub fn get_id(&self) -> &str {
687        unsafe { CStr::from_ptr(model_get_id(self.0.as_ptr())) }.to_str().unwrap()
688    }
689
690    /// Get the bounds
691    /// <https://stereokit.net/Pages/StereoKit/Model/Bounds.html>
692    ///
693    /// see also [`model_get_bounds`] [`model_set_bounds`]
694    /// see example in [`Model::bounds`]
695    pub fn get_bounds(&self) -> Bounds {
696        unsafe { model_get_bounds(self.0.as_ptr()) }
697    }
698
699    /// Get the nodes
700    /// <https://stereokit.net/Pages/StereoKit/ModelNodeCollection.html>
701    ///
702    /// see also [Nodes]
703    /// ### Examples
704    /// ```
705    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
706    /// use stereokit_rust::{maths::{Vec3, Matrix, Bounds}, model::Model, mesh::Mesh,
707    ///                      material::Material, util::named_colors};
708    ///
709    /// let cube_bounds  = Mesh::cube();
710    ///
711    /// let cube = Mesh::generate_cube(Vec3::ONE * 0.3, None);
712    ///
713    /// let transform1 = Matrix::t([-0.30,-0.30,-0.30]);
714    /// let transform2 = Matrix::t([ 0.30, 0.30, 0.30]);
715    /// let transform3 = Matrix::t([ 1.30, 1.30, 1.30]);
716    ///
717    /// let material = Material::pbr();
718    /// let mut model = Model::new();
719    /// let mut nodes = model.get_nodes();
720    /// nodes.add("cube1", transform1, Some(&cube), Some(&material), true)
721    ///      .add("cube2", transform2, Some(&cube), Some(&material), true)
722    ///      .add("not_a_mesh", transform3, None, None, true);
723    ///
724    /// for (iter, node) in nodes.all().enumerate() {
725    ///    match iter {
726    ///        0 => assert_eq!(node.get_name(), Some("cube1")),
727    ///        1 => assert_eq!(node.get_name(), Some("cube2")),
728    ///        _ => assert_eq!(node.get_name(), Some("not_a_mesh")),
729    ///    }
730    /// }
731    /// ```
732    pub fn get_nodes(&'_ self) -> Nodes<'_> {
733        Nodes::from(self)
734    }
735
736    /// Get the anims
737    /// <https://stereokit.net/Pages/StereoKit/ModelAnimCollection.html>
738    ///
739    /// see also [Anims]
740    /// ### Examples
741    /// ```
742    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
743    /// use stereokit_rust::model::{Model, Anims, AnimMode};
744    ///
745    /// let model = Model::from_file("mobiles.gltf", None)
746    ///                              .expect("Could not load model").copy();
747    /// let mut anims = model.get_anims();
748    /// assert_eq!(anims.get_count(), 3);
749    ///
750    /// for (iter, anim) in anims.enumerate() {
751    ///     match iter {
752    ///         0 => assert_eq!(anim.name, "rotate"),
753    ///         1 => assert_eq!(anim.name, "flyRotate"),
754    ///         _ => assert_eq!(anim.name, "fly"),
755    ///     }
756    /// }
757    ///
758    /// model.get_anims().play_anim_idx(0, AnimMode::Loop);
759    /// ```
760    pub fn get_anims(&'_ self) -> Anims<'_> {
761        Anims::from(self)
762    }
763
764    /// Checks the intersection point of a ray and the Solid flagged Meshes in the Model’s visual nodes. Ray must
765    /// be in model space, intersection point will be in model space too. You can use the inverse of the mesh’s world
766    /// transform matrix to bring the ray into model space, see the example in the docs!
767    /// <https://stereokit.net/Pages/StereoKit/Model/Intersect.html>
768    /// * `ray` - Ray must be in model space, the intersection point will be in model space too. You can use the inverse
769    ///   of the mesh’s world transform matrix to bring the ray into model space, see the example in the docs!
770    /// * `cull` - How should intersection work with respect to the direction the triangles are facing? Should we skip
771    ///   triangles that are facing away from the ray, or don’t skip anything? If None has default value of Cull::Back.
772    ///
773    /// see also [`model_ray_intersect`] [`Model::intersect_to_ptr`] same as [`Ray::intersect_model`]
774    /// ### Examples
775    /// ```
776    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
777    /// use stereokit_rust::{maths::{Vec3, Matrix, Bounds, Ray}, model::Model, mesh::Mesh,
778    ///                      material::{Material, Cull}, util::named_colors, system::Lines};
779    ///
780    /// let cube_bounds  = Mesh::cube();
781    ///
782    /// let cube = Mesh::generate_cube(Vec3::ONE * 0.4, None);
783    ///
784    /// let transform1 = Matrix::t([-0.30,-0.30,-0.30]);
785    /// let transform2 = Matrix::t([ 0.30, 0.30, 0.30]);
786    ///
787    /// let material = Material::pbr();
788    /// let mut model = Model::new();
789    /// let mut nodes = model.get_nodes();
790    /// nodes.add("cube1", transform1, Some(&cube), Some(&material), true)
791    ///      .add("cube2", transform2, Some(&cube), Some(&material), true);
792    /// let transform_model = Matrix::r([0.0, 15.0, 0.0]);
793    /// let inv = transform_model.get_inverse();
794    ///
795    /// let ray = Ray::from_to([-0.80, -2.8, -1.0],[0.35, 2.5, 1.0]);
796    /// let inv_ray = inv.transform_ray(ray);
797    ///
798    /// let contact_model = model.intersect(inv_ray, Some(Cull::Front))
799    ///           .expect("Intersection should be found");
800    ///
801    /// let transform_contact_model = Matrix::t(transform_model.transform_point(contact_model));
802    /// let point = Mesh::generate_sphere(0.1, Some(2));
803    /// let material = Material::pbr();
804    ///
805    /// let mut material_bounds = Material::ui_box();
806    /// material_bounds .color_tint(named_colors::GOLD)
807    ///                 .border_size(0.025);
808    ///
809    /// let bounds = model.get_bounds();
810    /// let transform_before = transform_model * Matrix::t_s( bounds.center, bounds.dimensions);
811    ///
812    /// filename_scr = "screenshots/model_intersect.jpeg";
813    /// test_screenshot!( // !!!! Get a proper main loop !!!!
814    ///     model.draw(token, transform_model, None, None);
815    ///     cube_bounds.draw( token, &material_bounds, transform_before, None, None);
816    ///     Lines::add_ray(token, ray, 7.2, named_colors::BLUE, Some(named_colors::RED.into()), 0.02);
817    ///     point.draw(token, &material, transform_contact_model,
818    ///                Some(named_colors::RED.into()), None );
819    /// );
820    /// ```
821    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/model_intersect.jpeg" alt="screenshot" width="200">
822    #[inline]
823    pub fn intersect(&self, ray: Ray, cull: Option<Cull>) -> Option<Vec3> {
824        ray.intersect_model(self, cull)
825    }
826
827    /// Checks the intersection point of a ray and the Solid flagged Meshes in the Model’s visual nodes. Ray must
828    /// be in model space, intersection point will be in model space too. You can use the inverse of the mesh’s world
829    /// transform matrix to bring the ray into model space, see the example in the docs!
830    /// <https://stereokit.net/Pages/StereoKit/Model/Intersect.html>
831    /// * `ray` - Ray must be in model space, the intersection point will be in model space too. You can use the inverse
832    ///   of the mesh’s world transform matrix to bring the ray into model space, see the example in the docs!
833    /// * `cull` - How should intersection work with respect to the direction the triangles are facing? Should we skip
834    ///   triangles that are facing away from the ray, or don’t skip anything? If None has default value of Cull::Back.
835    /// * `out_ray` - The intersection point and surface direction of the ray and the mesh, if an intersection occurs.
836    ///   This is in model space, and must be transformed back into world space later. Direction is not guaranteed to be
837    ///   normalized, especially if your own model->world transform contains scale/skew in it.
838    ///
839    /// see also [`model_ray_intersect`] [`Model::intersect`] same as [`Ray::intersect_model_to_ptr`]
840    /// ### Examples
841    /// ```
842    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
843    /// use stereokit_rust::{maths::{Vec3, Matrix, Bounds, Ray}, model::Model, mesh::Mesh,
844    ///                      material::{Material, Cull}, util::named_colors, system::Lines};
845    ///
846    /// let cube_bounds  = Mesh::cube();
847    ///
848    /// let cube = Mesh::generate_cube(Vec3::ONE * 0.4, None);
849    ///
850    /// let transform1 = Matrix::t([-0.30,-0.30,-0.30]);
851    /// let transform2 = Matrix::t([ 0.30, 0.30, 0.30]);
852    ///
853    /// let material = Material::pbr();
854    /// let mut model = Model::new();
855    /// let mut nodes = model.get_nodes();
856    /// nodes.add("cube1", transform1, Some(&cube), Some(&material), true)
857    ///      .add("cube2", transform2, Some(&cube), Some(&material), true);
858    /// let transform_model = Matrix::r([0.0, 15.0, 0.0]);
859    /// let inv = transform_model.get_inverse();
860    ///
861    /// let ray = Ray::from_to([-0.80, -2.8, -1.0],[0.35, 2.5, 1.0]);
862    /// let inv_ray = inv.transform_ray(ray);
863    ///
864    /// let mut inv_contact_model_ray = Ray::default();
865    /// assert!( model.intersect_to_ptr(inv_ray, Some(Cull::Front), &mut inv_contact_model_ray)
866    ///     ,"Ray should touch model");
867    ///
868    /// let contact_model_ray = transform_model.transform_ray(inv_contact_model_ray);
869    /// assert_eq!(contact_model_ray,
870    ///     Ray { position:  Vec3 {  x: -0.24654332, y: -0.24928647, z: -0.037466552 },
871    ///           direction: Vec3 {  x:  0.25881907, y:  0.0,        z:  0.9659258   } });
872    /// ```
873    #[inline]
874    #[allow(clippy::not_unsafe_ptr_arg_deref)]
875    pub fn intersect_to_ptr(&self, ray: Ray, cull: Option<Cull>, out_ray: *mut Ray) -> bool {
876        ray.intersect_model_to_ptr(self, cull, out_ray)
877    }
878}
879
880/// Animations of a Model
881/// <https://stereokit.net/Pages/StereoKit/ModelAnimCollection.html>
882///
883/// see also [`Model::get_anims`]
884/// ### Examples
885/// ```
886/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
887/// use stereokit_rust::{maths::{Vec3, Matrix}, model::{Model, Anims, AnimMode}};
888///
889/// let model = Model::from_file("center.glb", None)
890///                              .expect("Could not load model").copy();
891/// let transform = Matrix::t_r_s(Vec3::NEG_Y * 0.40, [0.0, 190.0, 0.0], Vec3::ONE * 0.25);
892///
893/// let mut anims = model.get_anims();
894/// assert_eq!(anims.get_count(), 1);
895/// anims.play_anim("SuzanneAction", AnimMode::Manual).anim_completion(0.80);
896///
897/// for (iter, anim) in anims.enumerate() {
898///     match iter {
899///         0 => assert_eq!(anim.name, "SuzanneAction"),
900///         _ => panic!("Unexpected animation name"),
901///     }
902/// }
903///
904/// let mut anims = model.get_anims();
905/// filename_scr = "screenshots/anims.jpeg";
906/// test_screenshot!( // !!!! Get a proper main loop !!!!
907///     model.draw(token, transform, None, None);
908/// );
909/// ```
910/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/model_from_file.jpeg" alt="screenshot" width="200">
911/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/anims.jpeg" alt="screenshot" width="200">
912pub struct Anims<'a> {
913    model: &'a Model,
914    curr: i32,
915}
916
917/// Describes how an animation is played back, and what to do when the animation hits the end.
918/// <https://stereokit.net/Pages/StereoKit/AnimMode.html>
919#[derive(Debug, Copy, Clone, PartialEq, Eq)]
920#[repr(u32)]
921pub enum AnimMode {
922    /// If the animation reaches the end, it will always loop back around to the start again.
923    Loop = 0,
924    /// When the animation reaches the end, it will freeze in-place.
925    Once = 1,
926    /// The animation will not progress on its own, and instead must be driven by providing information to the model’s
927    /// AnimTime or AnimCompletion properties.
928    Manual = 2,
929}
930
931impl Iterator for Anims<'_> {
932    type Item = Anim;
933
934    fn next(&mut self) -> Option<Self::Item> {
935        self.curr += 1;
936        if self.curr < self.get_count() {
937            Some(Anim {
938                name: match self.get_name_at_index(self.curr) {
939                    Some(name) => name.to_string(),
940                    None => {
941                        Log::err(format!("animation {:?}, is missing", self.curr));
942                        "<<error !!>>".to_string()
943                    }
944                },
945                duration: self.get_duration_at_index(self.curr),
946            })
947        } else {
948            None
949        }
950    }
951}
952
953/// A link to a Model’s animation! You can use this to get some basic information about the animation, or store it for
954/// reference. This maintains a link to the Model asset, and will keep it alive as long as this object lives.
955/// <https://stereokit.net/Pages/StereoKit/Anim.html>
956#[derive(Debug, Clone, PartialEq)]
957pub struct Anim {
958    pub name: String,
959    pub duration: f32,
960}
961
962impl<'a> Anims<'a> {
963    /// Same as [`Model::get_anims`]
964    pub fn from<M: AsRef<Model>>(model: &'a M) -> Anims<'a> {
965        Anims { model: model.as_ref(), curr: -1 }
966    }
967
968    /// Get the name of the animation at given index
969    ///
970    /// see also [`model_anim_get_name`]
971    /// ### Examples
972    /// ```
973    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
974    /// use stereokit_rust::{model::{Model, Anims, AnimMode}, system::Assets};
975    ///
976    /// let model = Model::from_file("mobiles.gltf", None)
977    ///                              .expect("Could not load model").copy();
978    /// Assets::block_for_priority(i32::MAX);
979    ///
980    /// let mut anims = model.get_anims();
981    /// assert_eq!(anims.get_count(), 3);
982    /// assert_eq!(anims.get_name_at_index(0), Some("rotate"));
983    /// assert_eq!(anims.get_name_at_index(1), Some("flyRotate"));
984    /// assert_eq!(anims.get_name_at_index(2), Some("fly"));
985    /// assert_eq!(anims.get_name_at_index(3), None);
986    /// ```
987    pub fn get_name_at_index(&self, index: i32) -> Option<&str> {
988        unsafe {
989            if model_anim_count(self.model.0.as_ptr()) > index {
990                CStr::from_ptr(model_anim_get_name(self.model.0.as_ptr(), index)).to_str().ok()
991            } else {
992                None
993            }
994        }
995    }
996
997    /// Get the duration of the animation at given index
998    ///
999    /// Returns `-0.01` if the index is out of bounds.
1000    /// see also [`model_anim_get_duration`]
1001    /// ### Examples
1002    /// ```
1003    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1004    /// use stereokit_rust::model::{Model, Anims, AnimMode};
1005    ///
1006    /// let model = Model::from_file("center.glb", None)
1007    ///                              .expect("Could not load model").copy();
1008    /// let mut anims = model.get_anims();
1009    /// assert_eq!(anims.get_count(), 1);
1010    /// assert_eq!(anims.get_duration_at_index(0), 2.5);
1011    /// assert_eq!(anims.get_duration_at_index(1), -0.01);
1012    /// ```
1013    pub fn get_duration_at_index(&self, index: i32) -> f32 {
1014        unsafe {
1015            if model_anim_count(self.model.0.as_ptr()) > index {
1016                model_anim_get_duration(self.model.0.as_ptr(), index)
1017            } else {
1018                -0.01
1019            }
1020        }
1021    }
1022
1023    /// Calling Draw will automatically step the Model’s animation, but if you don’t draw the Model, or need access to
1024    /// the animated nodes before drawing,
1025    /// then you can step the animation early manually via this method. Animation will only ever be stepped once per
1026    /// frame, so it’s okay to call this multiple times,
1027    /// or in addition to Draw.
1028    /// <https://stereokit.net/Pages/StereoKit/Model/StepAnim.html>
1029    ///
1030    /// see also [`model_step_anim`] [`model_play_anim`]
1031    /// ### Examples
1032    /// ```
1033    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1034    /// use stereokit_rust::{maths::{Vec3, Matrix}, model::{Model, Anims, AnimMode}};
1035    ///
1036    /// let model = Model::from_file("center.glb", None)
1037    ///                              .expect("Could not load model").copy();
1038    /// let mut anims = model.get_anims();
1039    /// assert_eq!(anims.get_count(), 1);
1040    /// anims.play_anim("SuzanneAction", AnimMode::Loop);
1041    ///
1042    /// number_of_steps = 20;
1043    /// test_steps!( // !!!! Get a proper main loop !!!!
1044    ///     if iter % 10 < 5 {
1045    ///         model.draw(token, Matrix::IDENTITY, None, None);
1046    ///     } else {
1047    ///         anims.step_anim();
1048    ///     }
1049    /// );
1050    /// ```
1051    pub fn step_anim(&mut self) -> &mut Self {
1052        unsafe { model_step_anim(self.model.0.as_ptr()) };
1053        self
1054    }
1055
1056    /// Searches for an animation with the given name, and if it’s found, sets it up as the active animation and begins
1057    /// laying it with the animation mode.
1058    /// <https://stereokit.net/Pages/StereoKit/Model/PlayAnim.html>
1059    /// * `name` - The name of the animation to play. Case sensitive.
1060    /// * `mode` - The animation mode to use.
1061    ///
1062    /// see also [`model_play_anim`] [`Anims::play_anim_idx`]
1063    /// ### Examples
1064    /// ```
1065    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1066    /// use stereokit_rust::model::{Model, Anims, AnimMode};
1067    ///
1068    /// let model = Model::from_file("center.glb", None)
1069    ///                              .expect("Could not load model").copy();
1070    /// let mut anims = model.get_anims();
1071    ///
1072    /// anims.play_anim("SuzanneAction", AnimMode::Loop);
1073    /// assert_eq!(anims.get_anim_mode(), AnimMode::Loop);
1074    ///
1075    /// anims.play_anim("SuzanneAction", AnimMode::Once);
1076    /// assert_eq!(anims.get_anim_mode(), AnimMode::Once);
1077    ///
1078    /// anims.play_anim("SuzanneAction", AnimMode::Manual);
1079    /// assert_eq!(anims.get_anim_mode(), AnimMode::Manual);
1080    ///
1081    /// // If anim does not exist:
1082    /// anims.play_anim("Not exist", AnimMode::Manual);
1083    /// assert_eq!(anims.get_anim_mode(), AnimMode::Manual);
1084    /// ```
1085    pub fn play_anim(&mut self, animation_name: impl AsRef<str>, mode: AnimMode) -> &mut Self {
1086        let c_str = CString::new(animation_name.as_ref()).unwrap();
1087        unsafe { model_play_anim(self.model.0.as_ptr(), c_str.as_ptr(), mode) };
1088        self
1089    }
1090
1091    /// Sets it up the animation at index idx as the active animation and begins playing it with the animation mode.
1092    /// <https://stereokit.net/Pages/StereoKit/Model/PlayAnim.html>
1093    /// * `idx` - index of the animation to play
1094    /// * `mode` - animation mode to play the animation with
1095    ///
1096    /// see also [`model_play_anim_idx`] [`Anims::play_anim`]
1097    /// ### Examples
1098    /// ```
1099    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1100    /// use stereokit_rust::model::{Model, Anims, AnimMode};
1101    ///
1102    /// let model = Model::from_file("center.glb", None)
1103    ///                              .expect("Could not load model").copy();
1104    /// let mut anims = model.get_anims();
1105    ///
1106    /// anims.play_anim_idx(0, AnimMode::Loop);
1107    /// assert_eq!(anims.get_anim_mode(), AnimMode::Loop);
1108    ///
1109    /// anims.play_anim_idx(0, AnimMode::Once);
1110    /// assert_eq!(anims.get_anim_mode(), AnimMode::Once);
1111    ///
1112    /// // If index does not exist:
1113    /// anims.play_anim_idx(102, AnimMode::Manual);
1114    /// assert_eq!(anims.get_anim_mode(), AnimMode::Once);
1115    /// ```
1116    pub fn play_anim_idx(&mut self, idx: i32, mode: AnimMode) -> &mut Self {
1117        unsafe { model_play_anim_idx(self.model.0.as_ptr(), idx, mode) };
1118        self
1119    }
1120
1121    /// Set the current time of the active animation in seconds, from the start of the animation. This may be a value
1122    /// superior to the animation’s Duration if the animation is a loop. For a percentage of completion,
1123    /// see anim_completion instead.
1124    /// <https://stereokit.net/Pages/StereoKit/Model/AnimTime.html>
1125    ///
1126    /// see also [`model_set_anim_time`] [`Anims::anim_completion`]
1127    /// ### Examples
1128    /// ```
1129    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1130    /// use stereokit_rust::model::{Model, Anims, AnimMode};
1131    ///
1132    /// let model = Model::from_file("center.glb", None)
1133    ///                              .expect("Could not load model").copy();
1134    /// let mut anims = model.get_anims();
1135    /// anims.play_anim_idx(0, AnimMode::Manual);
1136    /// assert_eq!(anims.get_duration_at_index(0), 2.5);
1137    ///
1138    /// anims.anim_time(1.0);
1139    /// assert_eq!(anims.get_anim_completion(), 0.4);
1140    ///
1141    /// anims.anim_time(2.0);
1142    /// assert_eq!(anims.get_anim_completion(), 0.8);
1143    ///
1144    /// // if the asking for animation longer than the duration (AnimMode::Manual):
1145    /// anims.anim_time(4.0);
1146    /// assert_eq!(anims.get_anim_completion(), 1.0);
1147    ///
1148    /// anims.play_anim_idx(0, AnimMode::Loop);
1149    /// // if the asking for animation longer than the duration (AnimMode::Loop):
1150    /// anims.anim_time(4.0);
1151    /// assert_eq!(anims.get_anim_completion(), 0.6);
1152    /// ```
1153    pub fn anim_time(&mut self, time: f32) -> &mut Self {
1154        unsafe { model_set_anim_time(self.model.0.as_ptr(), time) };
1155        self
1156    }
1157
1158    /// This set the percentage of completion of the active animation. This may be a value superior to 1.0 if the
1159    /// animation is a loop.
1160    /// <https://stereokit.net/Pages/StereoKit/Model/AnimCompletion.html>
1161    ///
1162    /// see also [`model_set_anim_completion`] [`Anims::anim_time`]
1163    /// ### Examples
1164    /// ```
1165    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1166    /// use stereokit_rust::model::{Model, Anims, AnimMode};
1167    ///
1168    /// let model = Model::from_file("center.glb", None)
1169    ///                              .expect("Could not load model").copy();
1170    /// let mut anims = model.get_anims();
1171    /// anims.play_anim_idx(0, AnimMode::Manual);
1172    /// assert_eq!(anims.get_duration_at_index(0), 2.5);
1173    ///
1174    /// anims.anim_completion(0.4);
1175    /// assert_eq!(anims.get_anim_time(), 1.0);
1176    ///
1177    /// anims.anim_completion(0.8);
1178    /// assert_eq!(anims.get_anim_time(), 2.0);
1179    ///
1180    /// // If asking for a completion over 100% (AnimMode::Manual):
1181    /// anims.anim_completion(1.8);
1182    /// assert_eq!(anims.get_anim_time(), 2.5);
1183    ///
1184    /// anims.play_anim_idx(0, AnimMode::Loop);
1185    /// // if the asking for a completion over 100% (AnimMode::Loop):
1186    /// anims.anim_completion(1.8);
1187    /// assert_eq!(anims.get_anim_time(), 2.0);
1188    /// ```
1189    pub fn anim_completion(&mut self, percent: f32) -> &mut Self {
1190        unsafe { model_set_anim_completion(self.model.0.as_ptr(), percent) };
1191        self
1192    }
1193
1194    /// get anim by name
1195    /// <https://stereokit.net/Pages/StereoKit/Model/FindAnim.html>
1196    ///
1197    /// see also [`model_anim_find`] [`Anims::play_anim`] [`Anims::play_anim_idx]
1198    /// ### Examples
1199    /// ```
1200    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1201    /// use stereokit_rust::model::{Model, Anims};
1202    /// let model = Model::from_file("center.glb", None)
1203    ///     .expect("Could not load model")
1204    ///     .copy();
1205    ///
1206    /// let anims = model.get_anims();
1207    ///
1208    /// assert_eq!(anims.find_anim("SuzanneAction"), Some(0));
1209    /// assert_eq!(anims.find_anim("Not exist"), None);
1210    /// ```
1211    pub fn find_anim<S: AsRef<str>>(&self, name: S) -> Option<i32> {
1212        let c_str = match CString::new(name.as_ref()) {
1213            Ok(c_str) => c_str,
1214            Err(..) => return None,
1215        };
1216        let index = unsafe { model_anim_find(self.model.0.as_ptr(), c_str.as_ptr()) };
1217        if index < 0 { None } else { Some(index) }
1218    }
1219
1220    /// Get the number of animations
1221    /// <https://stereokit.net/Pages/StereoKit/Model/ModelAnimCollection.html>
1222    ///
1223    /// see also [`model_anim_count`]
1224    /// ### Examples
1225    /// ```
1226    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1227    /// use stereokit_rust::model::{Model};
1228    ///
1229    /// let model = Model::from_file("mobiles.gltf", None)
1230    ///                             .expect("Could not load model").copy();
1231    ///
1232    /// let count = model.get_anims().get_count();
1233    ///
1234    /// assert_eq!(count, 3);
1235    /// ```
1236    pub fn get_count(&self) -> i32 {
1237        unsafe { model_anim_count(self.model.0.as_ptr()) }
1238    }
1239
1240    /// Get the current animation
1241    /// <https://stereokit.net/Pages/StereoKit/Model/ActiveAnim.html>
1242    ///
1243    /// see also [`model_anim_active`] [`Anims::play_anim`] [`Anims::play_anim_idx`]
1244    /// ### Examples
1245    /// ```
1246    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1247    /// use stereokit_rust::model::{Model, AnimMode};
1248    ///
1249    /// let model = Model::from_file("mobiles.gltf", None)
1250    ///                            .expect("Could not load model").copy();
1251    ///
1252    /// let mut anims = model.get_anims();
1253    /// assert_eq!(anims.get_active_anim(), -1);
1254    ///
1255    /// anims.play_anim("flyRotate", AnimMode::Loop);
1256    /// assert_eq!(anims.get_active_anim(), 1);
1257    /// ```
1258    pub fn get_active_anim(&self) -> i32 {
1259        unsafe { model_anim_active(self.model.0.as_ptr()) }
1260    }
1261
1262    /// Get the current animation, mode
1263    /// <https://stereokit.net/Pages/StereoKit/Model/AnimMode.html>
1264    ///
1265    /// see also [`model_anim_active_mode`] [`Anims::play_anim`] [`Anims::play_anim_idx`]
1266    /// ### Examples
1267    /// ```
1268    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1269    /// use stereokit_rust::model::{Model, AnimMode};
1270    ///
1271    /// let model = Model::from_file("mobiles.gltf", None)
1272    ///                           .expect("Could not load model").copy();
1273    ///
1274    /// let mut anims = model.get_anims();
1275    /// assert_eq!(anims.get_anim_mode(), AnimMode::Loop);
1276    ///
1277    /// anims.play_anim("flyRotate", AnimMode::Once);
1278    /// assert_eq!(anims.get_anim_mode(), AnimMode::Once);
1279    ///
1280    /// anims.play_anim("fly", AnimMode::Manual);
1281    /// assert_eq!(anims.get_anim_mode(), AnimMode::Manual);
1282    /// ```
1283    pub fn get_anim_mode(&self) -> AnimMode {
1284        unsafe { model_anim_active_mode(self.model.0.as_ptr()) }
1285    }
1286    /// Get the current animation duration
1287    /// <https://stereokit.net/Pages/StereoKit/Model/AnimTime.html>
1288    ///
1289    /// see also [`model_anim_active_time`] [`Anims::anim_time`] [`Anims::anim_completion`]
1290    /// ### Examples
1291    /// ```
1292    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1293    /// use stereokit_rust::model::{Model, AnimMode};
1294    ///
1295    /// let model = Model::from_file("center.glb", None)
1296    ///                           .expect("Could not load model").copy();
1297    ///
1298    /// let mut anims = model.get_anims();
1299    /// assert_eq!(anims.get_anim_time(), 0.0);
1300    ///
1301    /// anims.play_anim("SuzanneAction", AnimMode::Loop ).anim_completion(0.5);
1302    /// assert_eq!(anims.get_anim_time(), 1.25);
1303    /// ```
1304    pub fn get_anim_time(&self) -> f32 {
1305        unsafe { model_anim_active_time(self.model.0.as_ptr()) }
1306    }
1307
1308    /// Get the current animation completion %
1309    /// <https://stereokit.net/Pages/StereoKit/Model/AnimCompletion.html>
1310    ///
1311    /// see also [`model_anim_active_completion`] [`Anims::anim_time`] [`Anims::anim_completion`]
1312    /// ### Examples
1313    /// ```
1314    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1315    /// use stereokit_rust::model::{Model, AnimMode};
1316    ///
1317    /// let model = Model::from_file("center.glb", None)
1318    ///                          .expect("Could not load model").copy();
1319    /// let mut anims = model.get_anims();
1320    /// assert_eq!(anims.get_anim_completion(), 0.0);
1321    ///
1322    /// anims.play_anim("SuzanneAction", AnimMode::Loop);
1323    /// anims.anim_time(0.5);
1324    /// assert_eq!(anims.get_anim_completion(), 0.2);
1325    /// ```
1326    pub fn get_anim_completion(&self) -> f32 {
1327        unsafe { model_anim_active_completion(self.model.0.as_ptr()) }
1328    }
1329}
1330
1331/// Nodes of a Model
1332/// <https://stereokit.net/Pages/StereoKit/ModelNodeCollection.html>
1333/// <https://stereokit.net/Pages/StereoKit/ModelVisualCollection.html>
1334///
1335/// see also [`Model::get_nodes`]
1336/// ### Examples
1337/// ```
1338/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1339/// use stereokit_rust::{maths::{Vec3, Matrix} ,model::Model, util::Color128};
1340///
1341/// let model = Model::from_file("center.glb", None)
1342///                           .expect("Could not load model").copy();
1343/// let transform = Matrix::t_r_s(Vec3::NEG_Y * 0.40, [0.0, 190.0, 0.0], Vec3::ONE * 0.25);
1344///
1345/// let mut nodes = model.get_nodes();
1346///
1347/// // Duplicate Suzanne ModelNode 5 times at different positions.
1348/// let original_node = nodes.find("Suzanne").expect("Could not find Suzanne");
1349/// let mesh = original_node.get_mesh().expect("Could not get Suzanne's mesh");
1350/// let material_original = original_node.get_material().expect("Could not get Suzanne's material");
1351///
1352/// for i in -1..4 {
1353///     let coord = i as f32 * 1.25;
1354///     let color_idx = ((i+5) * 13694856) as u32;
1355///     let position = Matrix::t([coord, coord, coord]);
1356///     let name = format!("Suzanne_{}", i);
1357///     let mut material = material_original.copy();
1358///     material.color_tint(Color128::hex(color_idx));
1359///
1360///     nodes.add(name, position, Some(&mesh), Some(&material), true);
1361/// }
1362///
1363/// filename_scr = "screenshots/model_nodes.jpeg";
1364/// test_screenshot!( // !!!! Get a proper main loop !!!!
1365///     model.draw(token, transform, None, None);
1366/// );
1367/// ```
1368/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/model_nodes.jpeg" alt="screenshot" width="200">
1369#[derive(Debug, Copy, Clone)]
1370pub struct Nodes<'a> {
1371    model: &'a Model,
1372}
1373
1374unsafe extern "C" {
1375    pub fn model_subset_count(model: ModelT) -> i32;
1376    pub fn model_node_add(
1377        model: ModelT,
1378        name: *const c_char,
1379        model_transform: Matrix,
1380        mesh: MeshT,
1381        material: MaterialT,
1382        solid: Bool32T,
1383    ) -> ModelNodeId;
1384    pub fn model_node_add_child(
1385        model: ModelT,
1386        parent: ModelNodeId,
1387        name: *const c_char,
1388        local_transform: Matrix,
1389        mesh: MeshT,
1390        material: MaterialT,
1391        solid: Bool32T,
1392    ) -> ModelNodeId;
1393    pub fn model_node_find(model: ModelT, name: *const c_char) -> ModelNodeId;
1394    pub fn model_node_sibling(model: ModelT, node: ModelNodeId) -> ModelNodeId;
1395    pub fn model_node_parent(model: ModelT, node: ModelNodeId) -> ModelNodeId;
1396    pub fn model_node_child(model: ModelT, node: ModelNodeId) -> ModelNodeId;
1397    pub fn model_node_count(model: ModelT) -> i32;
1398    pub fn model_node_index(model: ModelT, index: i32) -> ModelNodeId;
1399    pub fn model_node_visual_count(model: ModelT) -> i32;
1400    pub fn model_node_visual_index(model: ModelT, index: i32) -> ModelNodeId;
1401    pub fn model_node_iterate(model: ModelT, node: ModelNodeId) -> ModelNodeId;
1402    pub fn model_node_get_root(model: ModelT) -> ModelNodeId;
1403    pub fn model_node_get_name(model: ModelT, node: ModelNodeId) -> *const c_char;
1404    pub fn model_node_get_solid(model: ModelT, node: ModelNodeId) -> Bool32T;
1405    pub fn model_node_get_visible(model: ModelT, node: ModelNodeId) -> Bool32T;
1406    pub fn model_node_get_material(model: ModelT, node: ModelNodeId) -> MaterialT;
1407    pub fn model_node_get_mesh(model: ModelT, node: ModelNodeId) -> MeshT;
1408    pub fn model_node_get_transform_model(model: ModelT, node: ModelNodeId) -> Matrix;
1409    pub fn model_node_get_transform_local(model: ModelT, node: ModelNodeId) -> Matrix;
1410    pub fn model_node_set_name(model: ModelT, node: ModelNodeId, name: *const c_char);
1411    pub fn model_node_set_solid(model: ModelT, node: ModelNodeId, solid: Bool32T);
1412    pub fn model_node_set_visible(model: ModelT, node: ModelNodeId, visible: Bool32T);
1413    pub fn model_node_set_material(model: ModelT, node: ModelNodeId, material: MaterialT);
1414    pub fn model_node_set_mesh(model: ModelT, node: ModelNodeId, mesh: MeshT);
1415    pub fn model_node_set_transform_model(model: ModelT, node: ModelNodeId, transform_model_space: Matrix);
1416    pub fn model_node_set_transform_local(model: ModelT, node: ModelNodeId, transform_local_space: Matrix);
1417    pub fn model_node_info_get(model: ModelT, node: ModelNodeId, info_key_utf8: *const c_char) -> *mut c_char;
1418    pub fn model_node_info_set(
1419        model: ModelT,
1420        node: ModelNodeId,
1421        info_key_utf8: *const c_char,
1422        info_value_utf8: *const c_char,
1423    );
1424    pub fn model_node_info_remove(model: ModelT, node: ModelNodeId, info_key_utf8: *const c_char) -> Bool32T;
1425    pub fn model_node_info_clear(model: ModelT, node: ModelNodeId);
1426    pub fn model_node_info_count(model: ModelT, node: ModelNodeId) -> i32;
1427    pub fn model_node_info_iterate(
1428        model: ModelT,
1429        node: ModelNodeId,
1430        ref_iterator: *mut i32,
1431        out_key_utf8: *mut *const c_char,
1432        out_value_utf8: *mut *const c_char,
1433    ) -> Bool32T;
1434
1435}
1436
1437/// Iterator of the nodes of a model. Can be instanciate from [Model]
1438///
1439/// see also [Nodes::all] [Nodes::visuals]
1440#[derive(Debug, Copy, Clone)]
1441pub struct NodeIter<'a> {
1442    model: &'a Model,
1443    index: i32,
1444    visual: bool,
1445}
1446
1447impl<'a> Iterator for NodeIter<'a> {
1448    type Item = ModelNode<'a>;
1449
1450    fn next(&mut self) -> Option<Self::Item> {
1451        self.index += 1;
1452        if self.visual {
1453            let count = unsafe { model_node_visual_count(self.model.0.as_ptr()) };
1454            if self.index < count {
1455                match unsafe { model_node_visual_index(self.model.0.as_ptr(), self.index) } {
1456                    -1 => {
1457                        Log::err(format!(
1458                            "node at index {:?}, is missing when {:?} visual nodes are expected for Model {:?}",
1459                            self.index,
1460                            count,
1461                            self.model.get_id()
1462                        ));
1463                        None
1464                    }
1465                    otherwise => Some(ModelNode { model: self.model, id: otherwise }),
1466                }
1467            } else {
1468                None
1469            }
1470        } else {
1471            let count = unsafe { model_node_count(self.model.0.as_ptr()) };
1472            if self.index < count {
1473                match unsafe { model_node_index(self.model.0.as_ptr(), self.index) } {
1474                    -1 => {
1475                        Log::err(format!(
1476                            "node at index {:?}, is missing when {:?} visual nodes are expected for Model {:?}",
1477                            self.index,
1478                            count,
1479                            self.model.get_id()
1480                        ));
1481                        None
1482                    }
1483                    otherwise => Some(ModelNode { model: self.model, id: otherwise }),
1484                }
1485            } else {
1486                None
1487            }
1488        }
1489    }
1490}
1491
1492impl<'a> NodeIter<'a> {
1493    /// Get an iterator for all node of the given model
1494    /// see also [Nodes::all]
1495    pub fn all_from(model: &'a impl AsRef<Model>) -> NodeIter<'a> {
1496        NodeIter { index: -1, model: model.as_ref(), visual: false }
1497    }
1498
1499    ///Get an iterator for all visual node of the given model
1500    /// see also [Nodes::visuals]
1501    pub fn visuals_from(model: &'a impl AsRef<Model>) -> NodeIter<'a> {
1502        NodeIter { index: -1, model: model.as_ref(), visual: true }
1503    }
1504}
1505
1506impl<'a> Nodes<'a> {
1507    pub fn from(model: &'a impl AsRef<Model>) -> Nodes<'a> {
1508        Nodes { model: model.as_ref() }
1509    }
1510
1511    /// This adds a root node to the Model’s node hierarchy! If There is already an initial root node,
1512    /// this node will still be a root node, but will be a Sibling of the Model’s RootNode. If this is the first root node added,
1513    /// you’ll be able to access it via [Nodes::get_root_node].
1514    /// <https://stereokit.net/Pages/StereoKit/Model/AddNode.html>
1515    ///
1516    /// see also [ModelNode::add_child] [`model_node_add`]
1517    /// ### Examples
1518    /// ```
1519    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1520    /// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model, mesh::Mesh, material::Material};
1521    ///
1522    /// let sphere =       Mesh::generate_sphere(0.6, None);
1523    /// let cylinder =     Mesh::generate_cylinder(0.25, 0.6, Vec3::Z, None);
1524    ///
1525    /// let transform1 = Matrix::t([-0.7,-0.5, 0.0]);
1526    /// let transform2 = Matrix::t([ 0.0, 0.0, 0.0]);
1527    ///
1528    /// let material = Material::pbr();
1529    ///
1530    /// let model = Model::new();
1531    /// let mut nodes = model.get_nodes();
1532    /// nodes.add("sphere",   transform1 , Some(&sphere),       Some(&material), true)
1533    ///      .add("cylinder", transform2 , Some(&cylinder),     Some(&material), true)
1534    ///      .add("A matrix", Matrix::IDENTITY, None, None, false);
1535    ///
1536    /// assert_eq!(nodes.get_count(), 3);
1537    /// assert_eq!(nodes.get_root_node().unwrap().get_name(), Some("sphere"));
1538    /// ```
1539    pub fn add<S: AsRef<str>>(
1540        &mut self,
1541        name: S,
1542        local_transform: impl Into<Matrix>,
1543        mesh: Option<&Mesh>,
1544        material: Option<&Material>,
1545        solid: bool,
1546    ) -> &mut Self {
1547        let c_str = CString::new(name.as_ref()).unwrap();
1548        let mesh = match mesh {
1549            Some(mesh) => mesh.0.as_ptr(),
1550            None => null_mut(),
1551        };
1552        let material = match material {
1553            Some(material) => material.0.as_ptr(),
1554            None => null_mut(),
1555        };
1556        unsafe {
1557            model_node_add(
1558                self.model.0.as_ptr(),
1559                c_str.as_ptr(),
1560                local_transform.into(),
1561                mesh,
1562                material,
1563                solid as Bool32T,
1564            )
1565        };
1566        self
1567    }
1568
1569    /// Get an iterator of all the nodes
1570    /// <https://stereokit.net/Pages/StereoKit/ModelNodeCollection.html>
1571    ///
1572    /// see also [NodeIter::all_from]
1573    /// ### Examples
1574    /// ```
1575    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1576    /// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model, mesh::Mesh, material::Material};
1577    ///
1578    /// let sphere =       Mesh::generate_sphere(0.6, None);
1579    /// let cylinder =     Mesh::generate_cylinder(0.25, 0.6, Vec3::Z, None);
1580    ///
1581    /// let transform1 = Matrix::t([-0.7,-0.5, 0.0]);
1582    /// let transform2 = Matrix::t([ 0.0, 0.0, 0.0]);
1583    ///
1584    /// let material = Material::pbr();
1585    ///
1586    /// let model = Model::new();
1587    /// let mut nodes = model.get_nodes();
1588    /// nodes.add("sphere",   transform1 , Some(&sphere),       Some(&material), true)
1589    ///      .add("cylinder", transform2 , Some(&cylinder),     Some(&material), true)
1590    ///      .add("A matrix", Matrix::IDENTITY, None, None, false);
1591    ///
1592    /// assert_eq!(nodes.get_count(), 3);
1593    /// assert_eq!(nodes.all().count(), 3);
1594    ///
1595    /// for (iter, node) in nodes.all().enumerate() {
1596    ///     match iter {
1597    ///         0 => assert_eq!(node.get_name(), Some("sphere")),
1598    ///         1 => assert_eq!(node.get_name(), Some("cylinder")),
1599    ///         _ => assert_eq!(node.get_name(), Some("A matrix")),
1600    ///     }
1601    /// }
1602    /// ```
1603    pub fn all(&'_ self) -> NodeIter<'_> {
1604        NodeIter::all_from(self.model)
1605    }
1606
1607    /// Get an iterator of all the visual nodes
1608    /// <https://stereokit.net/Pages/StereoKit/ModelVisualCollection.html>
1609    ///
1610    /// see also [NodeIter::visuals_from]
1611    /// ### Examples
1612    /// ```
1613    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1614    /// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model, mesh::Mesh, material::Material};
1615    ///
1616    /// let sphere =       Mesh::generate_sphere(0.6, None);
1617    /// let cylinder =     Mesh::generate_cylinder(0.25, 0.6, Vec3::Z, None);
1618    ///
1619    /// let transform1 = Matrix::t([-0.7,-0.5, 0.0]);
1620    /// let transform2 = Matrix::t([ 0.0, 0.0, 0.0]);
1621    ///
1622    /// let material = Material::pbr();
1623    ///
1624    /// let model = Model::new();
1625    /// let mut nodes = model.get_nodes();
1626    /// nodes.add("sphere",   transform1 , Some(&sphere),       Some(&material), true)
1627    ///      .add("cylinder", transform2 , Some(&cylinder),     Some(&material), true)
1628    ///      .add("A matrix", Matrix::IDENTITY, None, None, false);
1629    ///
1630    /// assert_eq!(nodes.get_count(), 3);
1631    /// assert_eq!(nodes.get_visual_count(), 2);
1632    /// assert_eq!(nodes.visuals().count(), 2);
1633    ///
1634    /// for (iter, node) in nodes.visuals().enumerate() {
1635    ///     match iter {
1636    ///         0 => assert_eq!(node.get_name(), Some("sphere")),
1637    ///         _ => assert_eq!(node.get_name(), Some("cylinder")),
1638    ///     }
1639    /// }
1640    /// ```
1641    pub fn visuals(&'_ self) -> NodeIter<'_> {
1642        NodeIter::visuals_from(self.model)
1643    }
1644
1645    /// get node by name
1646    /// <https://stereokit.net/Pages/StereoKit/Model/FindNode.html>
1647    /// * `name` - Exact name to match against. ASCII only for now.
1648    ///
1649    /// see also [`model_node_find`]
1650    /// ### Examples
1651    /// ```
1652    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1653    /// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model, mesh::Mesh, material::Material};
1654    ///
1655    /// let sphere =      Mesh::generate_sphere(0.6, None);
1656    /// let cylinder =    Mesh::generate_cylinder(0.25, 0.6, Vec3::Z, None);
1657    ///
1658    /// let transform1 = Matrix::t([-0.7,-0.5, 0.0]);
1659    /// let transform2 = Matrix::t([ 0.0, 0.0, 0.0]);
1660    ///
1661    /// let material = Material::pbr();
1662    ///
1663    /// let model = Model::new();
1664    /// let mut nodes = model.get_nodes();
1665    /// nodes.add("sphere",   transform1 , Some(&sphere),      Some(&material), true)
1666    ///      .add("cylinder", transform2 , Some(&cylinder),    Some(&material), true)
1667    ///      .add("A matrix", Matrix::IDENTITY, None, None, false);
1668    ///
1669    /// let found_sphere = nodes.find("sphere");
1670    /// assert!(found_sphere.is_some());
1671    /// assert_eq!(found_sphere.unwrap().get_name(), Some("sphere"));
1672    ///
1673    /// let found_non_existent = nodes.find("non_existent");
1674    /// assert!(found_non_existent.is_none());
1675    /// ```
1676    pub fn find<S: AsRef<str>>(&'_ self, name: S) -> Option<ModelNode<'_>> {
1677        let c_str = CString::new(name.as_ref()).unwrap();
1678        match unsafe { model_node_find(self.model.0.as_ptr(), c_str.as_ptr()) } {
1679            -1 => None,
1680            otherwise => Some(ModelNode { model: self.model, id: otherwise }),
1681        }
1682    }
1683
1684    /// Get the number of node.
1685    /// <https://stereokit.net/Pages/StereoKit/ModelNodeCollection.html>
1686    ///
1687    /// see also [NodeIter] [`model_node_count`]
1688    /// ### Examples
1689    /// ```
1690    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1691    /// use stereokit_rust::{maths::Matrix, model::Model};
1692    ///
1693    /// let model = Model::new();
1694    ///
1695    /// let mut nodes = model.get_nodes();
1696    /// assert_eq!(nodes.get_count(), 0);
1697    ///
1698    /// nodes.add("root", Matrix::IDENTITY, None, None, false);
1699    /// assert_eq!(nodes.get_count(), 1);
1700    /// ```
1701    pub fn get_count(&self) -> i32 {
1702        unsafe { model_node_count(self.model.0.as_ptr()) }
1703    }
1704
1705    /// Get the number of visual node
1706    /// <https://stereokit.net/Pages/StereoKit/ModelVisualCollection.html>
1707    ///
1708    /// see also [NodeIter] [`model_node_visual_count`]
1709    /// ### Examples
1710    /// ```
1711    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1712    /// use stereokit_rust::{maths::Matrix, model::Model};
1713    ///
1714    /// let model = Model::new();
1715    ///
1716    /// let mut nodes = model.get_nodes();
1717    /// assert_eq!(nodes.get_count(), 0);
1718    /// assert_eq!(nodes.get_visual_count(), 0);
1719    ///
1720    /// nodes.add("root", Matrix::IDENTITY, None, None, false);
1721    /// assert_eq!(nodes.get_count(), 1);
1722    /// assert_eq!(nodes.get_visual_count(), 0);
1723    /// ```
1724    pub fn get_visual_count(&self) -> i32 {
1725        unsafe { model_node_visual_count(self.model.0.as_ptr()) }
1726    }
1727
1728    /// Get the node at index
1729    /// <https://stereokit.net/Pages/StereoKit/ModelNodeCollection.html>
1730    ///
1731    /// see also [NodeIter] [`model_node_index`]
1732    /// ### Examples
1733    /// ```
1734    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1735    /// use stereokit_rust::{maths::Matrix, model::Model};
1736    ///
1737    /// let model = Model::new();
1738    ///
1739    /// let mut nodes = model.get_nodes();
1740    /// let node = nodes.get_index(0);
1741    /// assert!(node.is_none());
1742    ///
1743    /// nodes.add("root", Matrix::IDENTITY, None, None, false);
1744    /// let node = nodes.get_index(0).expect("Node should exist");
1745    /// assert_eq!(node.get_name(), Some("root"));
1746    /// ```
1747    pub fn get_index(&'_ self, index: i32) -> Option<ModelNode<'_>> {
1748        if unsafe { model_node_count(self.model.0.as_ptr()) } <= index {
1749            return None;
1750        }
1751        match unsafe { model_node_index(self.model.0.as_ptr(), index) } {
1752            -1 => None,
1753            otherwise => Some(ModelNode { model: self.model, id: otherwise }),
1754        }
1755    }
1756
1757    /// Get the visual node at index
1758    /// <https://stereokit.net/Pages/StereoKit/ModelVisualCollection.html>
1759    ///
1760    /// see also [NodeIter] [`model_node_visual_index`]
1761    /// ### Examples
1762    /// ```
1763    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1764    /// use stereokit_rust::{maths::Matrix, model::Model};
1765    ///
1766    /// let model = Model::new();
1767    ///
1768    /// let mut nodes = model.get_nodes();
1769    /// let node = nodes.get_visual_index(0);
1770    /// assert!(node.is_none());
1771    ///
1772    /// nodes.add("root", Matrix::IDENTITY, None, None, false);
1773    /// let node = nodes.get_visual_index(0);
1774    /// assert!(node.is_none());
1775    /// ```
1776    pub fn get_visual_index(&'_ self, index: i32) -> Option<ModelNode<'_>> {
1777        if unsafe { model_node_visual_count(self.model.0.as_ptr()) } <= index {
1778            return None;
1779        }
1780        match unsafe { model_node_visual_index(self.model.0.as_ptr(), index) } {
1781            -1 => None,
1782            otherwise => Some(ModelNode { model: self.model, id: otherwise }),
1783        }
1784    }
1785
1786    /// Get the root node
1787    /// <https://stereokit.net/Pages/StereoKit/Model/RootNode.html>
1788    ///
1789    /// see also [`model_node_get_root`]
1790    /// ### Examples
1791    /// ```
1792    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1793    /// use stereokit_rust::{maths::Matrix, model::Model};
1794    ///
1795    /// let model = Model::new();
1796    ///
1797    /// let mut nodes = model.get_nodes();
1798    /// let node = nodes.get_root_node();
1799    /// assert!(node.is_none());
1800    ///
1801    /// nodes.add("root", Matrix::IDENTITY, None, None, false);
1802    /// let node = nodes.get_root_node().expect("Node should exist");
1803    /// assert_eq!(node.get_name(), Some("root"));
1804    /// ```
1805    pub fn get_root_node(&'_ self) -> Option<ModelNode<'_>> {
1806        let id = unsafe { model_node_get_root(self.model.0.as_ptr()) };
1807        if id == -1 { None } else { Some(ModelNode { model: self.model, id }) }
1808    }
1809}
1810
1811/// This class is a link to a node in a Model’s internal hierarchy tree. It’s composed of node information, and links to
1812/// the directly adjacent tree nodes.
1813/// <https://stereokit.net/Pages/StereoKit/ModelNode.html>
1814///
1815/// ### Examples
1816/// ```
1817/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1818/// use stereokit_rust::{maths::{Vec3, Matrix}, model::Model, mesh::Mesh,
1819///                      material::Material, util::named_colors};
1820///
1821/// let sphere =       Mesh::generate_sphere(0.6, None);
1822/// let rounded_cube = Mesh::generate_rounded_cube(Vec3::ONE * 0.6, 0.2, None);
1823/// let cylinder =     Mesh::generate_cylinder(0.25, 0.6, Vec3::Z, None);
1824///
1825/// let transform1 = Matrix::t( [-0.7,-0.5, 0.0]);
1826/// let transform2 = Matrix::t( [ 0.0, 0.0, 0.0]);
1827/// let transform3 = Matrix::t( [ 0.7, 0.5, 0.0]);
1828/// let trans_mini = Matrix::t_s([ 0.0, 0.0, 0.5].into(), Vec3::ONE * 0.15);
1829///
1830/// let material = Material::pbr();
1831///
1832/// let model = Model::new();
1833/// let mut nodes = model.get_nodes();
1834/// nodes.add("sphere",   transform1 , Some(&sphere),       Some(&material), true)
1835///      .add("cube",     transform2 , Some(&rounded_cube), Some(&material), true)
1836///      .add("cylinder", transform3 , Some(&cylinder),     Some(&material), true)
1837///      .add("mini",     trans_mini, None, None, true);
1838///
1839/// let mut material = material.copy();
1840/// material.color_tint(named_colors::RED);
1841/// let mut mini = nodes.find("mini").expect("mini node should exist!");
1842/// mini.add_child("sphere",   transform1 , Some(&sphere),       Some(&material), true)
1843///     .add_child("cube",     transform2 , Some(&rounded_cube), Some(&material), true)
1844///     .add_child("cylinder", transform3 , Some(&cylinder),     Some(&material), true);
1845///
1846/// assert_eq!(nodes.get_visual_count(), 6);
1847/// assert_eq!(nodes.get_count(), 7);
1848///
1849/// filename_scr = "screenshots/model_node.jpeg";
1850/// test_screenshot!( // !!!! Get a proper main loop !!!!
1851///     model.draw(token, Matrix::IDENTITY, None, None);
1852/// );
1853/// ```
1854/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/model_node.jpeg" alt="screenshot" width="200">
1855#[derive(Debug, Copy, Clone, PartialEq)]
1856pub struct ModelNode<'a> {
1857    model: &'a Model,
1858    id: ModelNodeId,
1859}
1860pub type ModelNodeId = i32;
1861
1862impl ModelNode<'_> {
1863    /// Set the name of the node
1864    /// <https://stereokit.net/Pages/StereoKit/ModelNode/Name.html>
1865    ///
1866    /// see also [`model_node_set_name`]
1867    /// ### Examples
1868    /// ```
1869    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1870    /// use stereokit_rust::{maths::Matrix, model::Model};
1871    ///
1872    /// let model = Model::new();
1873    ///
1874    /// let mut nodes = model.get_nodes();
1875    /// nodes.add("root", Matrix::IDENTITY, None, None, false);
1876    ///
1877    /// let mut node = nodes.find("root").expect("A node should exist!");
1878    /// assert_eq!(node.get_name(), Some("root"));
1879    ///
1880    /// node.name("my_root_node");
1881    /// assert_eq!(node.get_name(), Some("my_root_node"));
1882    ///
1883    /// let node = nodes.find("my_root_node").expect("A node should exist!");
1884    /// assert!(nodes.find("root").is_none());
1885    /// ```
1886    pub fn name<S: AsRef<str>>(&mut self, name: S) -> &mut Self {
1887        let c_str = CString::new(name.as_ref()).unwrap();
1888        unsafe { model_node_set_name(self.model.0.as_ptr(), self.id, c_str.as_ptr()) };
1889        self
1890    }
1891
1892    /// Set the solid of the node. A flag that indicates the Mesh for this node will be used in ray intersection tests.
1893    /// This flag is ignored if no Mesh is attached.
1894    /// <https://stereokit.net/Pages/StereoKit/ModelNode/Solid.html>
1895    ///
1896    /// see also [`model_node_set_solid`] [`Nodes::add`] [`ModelNode::add_child`]
1897    /// ### Examples
1898    /// ```
1899    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1900    /// use stereokit_rust::{maths::Matrix, model::Model, mesh::Mesh, material::Material};
1901    ///
1902    /// let model = Model::new();
1903    ///
1904    /// let mut nodes = model.get_nodes();
1905    /// nodes.add("cube", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), false);
1906    ///
1907    /// let mut node = nodes.find("cube").expect("A node should exist!");
1908    /// assert_eq!(node.get_solid(), false);
1909    ///
1910    /// node.solid(true);
1911    /// assert_eq!(node.get_solid(), true);
1912    /// ```
1913    pub fn solid(&mut self, solid: bool) -> &mut Self {
1914        unsafe { model_node_set_solid(self.model.0.as_ptr(), self.id, solid as Bool32T) };
1915        self
1916    }
1917
1918    /// Is this node flagged as visible? By default, this is true for all nodes with visual elements attached. These
1919    /// nodes will not be drawn or skinned if you set this flag to false. If a ModelNode has no visual elements attached
1920    /// to it, it will always return false, and setting this value will have no effect.
1921    /// <https://stereokit.net/Pages/StereoKit/ModelNode/Visible.html>
1922    ///
1923    /// see also [`model_node_set_visible`]
1924    /// ### Examples
1925    /// ```
1926    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1927    /// use stereokit_rust::{maths::Matrix, model::Model, mesh::Mesh, material::Material};
1928    ///
1929    /// let model = Model::new();
1930    ///
1931    /// let mut nodes = model.get_nodes();
1932    /// nodes.add("cube", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
1933    ///
1934    /// let mut node = nodes.find("cube").expect("A node should exist!");
1935    /// assert_eq!(node.get_visible(), true);
1936    ///
1937    /// node.visible(false);
1938    /// assert_eq!(node.get_visible(), false);
1939    /// ```
1940    pub fn visible(&mut self, visible: bool) -> &mut Self {
1941        unsafe { model_node_set_visible(self.model.0.as_ptr(), self.id, visible as Bool32T) };
1942        self
1943    }
1944
1945    /// Set the material of the node
1946    /// <https://stereokit.net/Pages/StereoKit/ModelNode/Material.html>
1947    ///
1948    /// see also [`model_node_set_material`]
1949    /// ### Examples
1950    /// ```
1951    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1952    /// use stereokit_rust::{maths::Matrix, model::Model, mesh::Mesh, material::Material};
1953    ///
1954    /// let model = Model::new();
1955    ///
1956    /// let mut nodes = model.get_nodes();
1957    /// nodes.add("cube", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
1958    ///
1959    /// let mut node = nodes.find("cube").expect("A node should exist!");
1960    /// assert_eq!(node.get_material(), Some(Material::pbr()));
1961    ///
1962    /// node.material(Material::unlit());
1963    /// assert_eq!(node.get_material(), Some(Material::unlit()));
1964    /// ```
1965    pub fn material<M: AsRef<Material>>(&mut self, material: M) -> &mut Self {
1966        unsafe { model_node_set_material(self.model.0.as_ptr(), self.id, material.as_ref().0.as_ptr()) };
1967        self
1968    }
1969
1970    /// Set the mesh of the node
1971    /// <https://stereokit.net/Pages/StereoKit/ModelNode/Mesh.html>
1972    ///
1973    /// see also [`model_node_set_mesh`]
1974    /// ### Examples
1975    /// ```
1976    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1977    /// use stereokit_rust::{maths::Matrix, model::Model, mesh::Mesh, material::Material};
1978    ///
1979    /// let model = Model::new();
1980    ///
1981    /// let mut nodes = model.get_nodes();
1982    /// nodes.add("mesh", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
1983    ///
1984    /// let mut node = nodes.find("mesh").expect("A node should exist!");
1985    /// assert_eq!(node.get_mesh(), Some(Mesh::cube()));
1986    ///
1987    /// node.mesh(Mesh::sphere());
1988    /// assert_eq!(node.get_mesh(), Some(Mesh::sphere()));
1989    /// ```
1990    pub fn mesh<M: AsRef<Mesh>>(&mut self, mesh: M) -> &mut Self {
1991        unsafe { model_node_set_mesh(self.model.0.as_ptr(), self.id, mesh.as_ref().0.as_ptr()) };
1992        self
1993    }
1994
1995    /// Set the transform model of the node. The transform of this node relative to the Model itself. This incorporates
1996    /// transforms from all parent nodes.
1997    /// Setting this transform will update the LocalTransform, as well as all Child nodes below this one.
1998    /// <https://stereokit.net/Pages/StereoKit/ModelNode/ModelTransform.html>
1999    ///
2000    /// see also [`model_node_set_transform_model`]
2001    /// ### Examples
2002    /// ```
2003    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2004    /// use stereokit_rust::{maths::Matrix, model::Model, mesh::Mesh, material::Material};
2005    ///
2006    /// let model = Model::new();
2007    ///
2008    /// let mut nodes = model.get_nodes();
2009    /// nodes.add("root_mesh", Matrix::t([1.0, 1.0, 1.0]), Some(&Mesh::cube()), Some(&Material::pbr()), true);
2010    /// assert_eq!(model.get_bounds().center, [1.0, 1.0, 1.0].into());
2011    /// assert_eq!(model.get_bounds().dimensions, [1.0, 1.0, 1.0].into());
2012    ///
2013    /// let mut node = nodes.find("root_mesh").expect("A node should exist!");
2014    /// node.add_child("child_mesh", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), false);
2015    /// assert_eq!(model.get_bounds().center, [1.0, 1.0, 1.0].into());
2016    /// assert_eq!(model.get_bounds().dimensions, [1.0, 1.0, 1.0].into());
2017    ///
2018    /// // Model_transform!!!
2019    /// let mut node_child = nodes.find("child_mesh").expect("A node should exist!");
2020    /// node_child.model_transform(Matrix::t([-2.0, -2.0, -2.0]));
2021    /// assert_eq!(model.get_bounds().center, [-0.5, -0.5, -0.5].into());
2022    /// assert_eq!(model.get_bounds().dimensions, [4.0, 4.0, 4.0].into());
2023    ///
2024    /// // Local_transform!!!
2025    /// let mut node_child = nodes.find("child_mesh").expect("A node should exist!");
2026    /// node_child.local_transform(Matrix::t([-2.0, -2.0, -2.0]));
2027    /// assert_eq!(model.get_bounds().center, [0.0, 0.0, 0.0].into());
2028    /// assert_eq!(model.get_bounds().dimensions, [3.0, 3.0, 3.0].into());
2029    /// ```
2030    pub fn model_transform(&mut self, transform_model_space: impl Into<Matrix>) -> &mut Self {
2031        unsafe { model_node_set_transform_model(self.model.0.as_ptr(), self.id, transform_model_space.into()) };
2032        self
2033    }
2034
2035    /// Set the local transform  of the node. The transform of this node relative to the Parent node.
2036    /// Setting this transform will update the ModelTransform, as well as all Child nodes below this one.
2037    /// <https://stereokit.net/Pages/StereoKit/ModelNode/LocalTransform.html>
2038    ///
2039    /// see also [`model_node_set_transform_local`]
2040    /// see example [ModelNode::model_transform]
2041    pub fn local_transform(&mut self, transform_model_space: impl Into<Matrix>) -> &mut Self {
2042        unsafe { model_node_set_transform_local(self.model.0.as_ptr(), self.id, transform_model_space.into()) };
2043        self
2044    }
2045
2046    /// Adds a Child node below this node, at the end of the child chain! The local transform of the child will have
2047    /// this node as reference
2048    /// <https://stereokit.net/Pages/StereoKit/ModelNode/AddChild.html>
2049    /// * `name` - A text name to identify the node.
2050    /// * `local_transform` - A Matrix describing this node’s transform in local space relative to the currently selected
2051    ///   node.
2052    /// * `mesh` - The Mesh to attach to this Node’s visual. If None, the material must also be None.
2053    /// * `material` - The Material to attach to this Node’s visual. If None, the mesh must also be None.
2054    /// * `solid` - A flag that indicates the Mesh for this node will be used in ray intersection tests. This flag
2055    ///   is ignored if no Mesh is attached.
2056    ///
2057    /// see also [Nodes::add] [`model_node_add_child`]
2058    /// ### Examples
2059    /// ```
2060    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2061    /// use stereokit_rust::{maths::Matrix, model::Model, mesh::Mesh, material::Material};
2062    ///
2063    /// let model = Model::new();
2064    /// let cube = Mesh::generate_cube([0.1, 0.1, 0.1], None);
2065    /// let sphere = Mesh::generate_sphere(0.15, None);
2066    /// let material = Material::pbr();
2067    ///
2068    /// let mut nodes = model.get_nodes();
2069    /// nodes.add("root_mesh", Matrix::IDENTITY, Some(&cube), Some(&material), true);
2070    /// let mut root_node = nodes.get_root_node().expect("A node should exist!");
2071    ///
2072    /// root_node
2073    ///     .add_child("child_mesh2", Matrix::IDENTITY, Some(&sphere), Some(&material), true)
2074    ///     .add_child("child_no_mesh", Matrix::IDENTITY, None, None, false);
2075    /// ```
2076    pub fn add_child<S: AsRef<str>>(
2077        &mut self,
2078        name: S,
2079        local_transform: impl Into<Matrix>,
2080        mesh: Option<&Mesh>,
2081        material: Option<&Material>,
2082        solid: bool,
2083    ) -> &mut Self {
2084        let c_str = CString::new(name.as_ref()).unwrap();
2085        let mesh = match mesh {
2086            Some(mesh) => mesh.0.as_ptr(),
2087            None => null_mut(),
2088        };
2089        let material = match material {
2090            Some(material) => material.0.as_ptr(),
2091            None => null_mut(),
2092        };
2093        unsafe {
2094            model_node_add_child(
2095                self.model.0.as_ptr(),
2096                self.id,
2097                c_str.as_ptr(),
2098                local_transform.into(),
2099                mesh,
2100                material,
2101                solid as Bool32T,
2102            )
2103        };
2104        self
2105    }
2106
2107    /// Get the node Id
2108    ///
2109    /// ### Examples
2110    /// ```
2111    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2112    /// use stereokit_rust::{maths::Matrix, model::Model, mesh::Mesh, material::Material};
2113    ///
2114    /// let model = Model::new();
2115    ///
2116    /// let mut nodes = model.get_nodes();
2117    /// nodes.add("mosh", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2118    /// nodes.add("mesh", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2119    /// nodes.add("mush", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2120    ///
2121    /// let node = nodes.find("mesh").expect("Node mesh should exist");
2122    /// assert_eq!(node.get_id(), 1);
2123    ///
2124    /// let node = nodes.find("mush").expect("Node mesh should exist");
2125    /// assert_eq!(node.get_id(), 2);
2126    /// ```
2127    pub fn get_id(&self) -> ModelNodeId {
2128        self.id
2129    }
2130
2131    /// Get the node Name
2132    /// <https://stereokit.net/Pages/StereoKit/ModelNode/Name.html>
2133    ///
2134    /// see also [`model_node_get_name`]
2135    /// see example [`ModelNode::name`]
2136    pub fn get_name(&self) -> Option<&str> {
2137        unsafe { CStr::from_ptr(model_node_get_name(self.model.0.as_ptr(), self.id)).to_str().ok() }
2138    }
2139
2140    /// Get the solid of the node. A flag that indicates if the Mesh for this node will be used in ray intersection tests.
2141    /// This flag is ignored if no Mesh is attached.
2142    /// <https://stereokit.net/Pages/StereoKit/ModelNode/Solid.html>
2143    ///
2144    /// see also [`model_node_get_solid`]
2145    /// see example [`ModelNode::solid`]
2146    pub fn get_solid(&self) -> bool {
2147        unsafe { model_node_get_solid(self.model.0.as_ptr(), self.id) != 0 }
2148    }
2149
2150    /// Get the visibility of the node
2151    /// <https://stereokit.net/Pages/StereoKit/ModelNode/Visible.html>
2152    ///
2153    /// see also [`model_node_get_visible`]
2154    /// see example [`ModelNode::visible`]
2155    pub fn get_visible(&self) -> bool {
2156        unsafe { model_node_get_visible(self.model.0.as_ptr(), self.id) != 0 }
2157    }
2158
2159    /// Get the material of the node
2160    /// <https://stereokit.net/Pages/StereoKit/ModelNode/Material.html>
2161    ///
2162    /// see also [`model_node_get_material`]
2163    /// see example [`ModelNode::material`]
2164    pub fn get_material(&self) -> Option<Material> {
2165        NonNull::new(unsafe { model_node_get_material(self.model.0.as_ptr(), self.id) }).map(Material)
2166    }
2167
2168    /// Get the mesh of the node
2169    /// <https://stereokit.net/Pages/StereoKit/ModelNode/Mesh.html>
2170    ///
2171    /// see also [`model_node_get_mesh`]
2172    /// see example [`ModelNode::mesh`]
2173    pub fn get_mesh(&self) -> Option<Mesh> {
2174        NonNull::new(unsafe { model_node_get_mesh(self.model.0.as_ptr(), self.id) }).map(Mesh)
2175    }
2176
2177    /// Get the transform matrix of the node
2178    /// <https://stereokit.net/Pages/StereoKit/ModelNode/ModelTransform.html>
2179    ///
2180    /// see also [`model_node_get_transform_model`]
2181    /// see example [`ModelNode::model_transform`]
2182    pub fn get_model_transform(&self) -> Matrix {
2183        unsafe { model_node_get_transform_model(self.model.0.as_ptr(), self.id) }
2184    }
2185
2186    /// Get the local transform matrix of the node
2187    /// <https://stereokit.net/Pages/StereoKit/ModelNode/LocalTransform.html>
2188    ///
2189    /// see also [`model_node_get_transform_local`]
2190    /// see example [`ModelNode::model_transform`]
2191    pub fn get_local_transform(&self) -> Matrix {
2192        unsafe { model_node_get_transform_local(self.model.0.as_ptr(), self.id) }
2193    }
2194
2195    /// Iterate to the next node
2196    /// <https://stereokit.net/Pages/StereoKit/ModelNodeCollection.html>
2197    ///
2198    /// see also [`model_node_iterate`]
2199    /// ### Examples
2200    /// ```
2201    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2202    /// use stereokit_rust::{maths::Matrix, model::Model, mesh::Mesh, material::Material};
2203    ///
2204    /// let model = Model::new();
2205    ///
2206    /// let mut nodes = model.get_nodes();
2207    /// nodes.add("mosh", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2208    /// nodes.add("mesh", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2209    /// nodes.add("mush", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2210    ///
2211    /// let mut node = nodes.find("mosh").expect("Node mosh should exist");
2212    /// let mut next_node = node.iterate().expect("Node should have a follower");
2213    /// assert_eq!(next_node.get_name(), Some("mesh"));
2214    ///
2215    /// next_node.add_child("mesh child", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2216    /// let next_node = next_node.iterate().expect("Node should have a follower");
2217    /// assert_eq!(next_node.get_name(), Some("mesh child"));
2218    ///
2219    /// let next_node = next_node.iterate().expect("Node should have a follower");
2220    /// assert_eq!(next_node.get_name(), Some("mush"));
2221    ///
2222    /// let next_node = next_node.iterate();
2223    /// assert!(next_node.is_none());
2224    /// ```
2225    pub fn iterate(&'_ self) -> Option<ModelNode<'_>> {
2226        match unsafe { model_node_iterate(self.model.0.as_ptr(), self.id) } {
2227            -1 => None,
2228            otherwise => Some(ModelNode { model: self.model, id: otherwise }),
2229        }
2230    }
2231
2232    /// Get the The first child node “below” on the hierarchy tree, or null if there are none. To see all children,
2233    /// get the Child and then iterate through its Siblings.
2234    /// <https://stereokit.net/Pages/StereoKit/ModelNode/Child.html>
2235    ///
2236    /// see also [`model_node_child`]
2237    /// ### Examples
2238    /// ```
2239    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2240    /// use stereokit_rust::{maths::Matrix, model::Model, mesh::Mesh, material::Material};
2241    ///
2242    /// let model = Model::new();
2243    ///
2244    /// let mut nodes = model.get_nodes();
2245    /// nodes.add("mosh", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2246    /// nodes.add("mesh", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2247    /// nodes.add("mush", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2248    ///
2249    /// let mut node = nodes.find("mesh").expect("Node mosh should exist");
2250    /// node.add_child("mesh child1", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2251    /// node.add_child("mesh child2", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2252    ///
2253    /// let child_node = node.get_child().expect("Node should have a child");
2254    /// assert_eq!(child_node.get_name(), Some("mesh child1"));
2255    /// ```
2256    pub fn get_child(&'_ self) -> Option<ModelNode<'_>> {
2257        match unsafe { model_node_child(self.model.0.as_ptr(), self.id) } {
2258            -1 => None,
2259            otherwise => Some(ModelNode { model: self.model, id: otherwise }),
2260        }
2261    }
2262
2263    /// The next ModelNode in the hierarchy, at the same level as this one. To the “right” on a hierarchy tree.
2264    /// None if there are no more ModelNodes in the tree there.
2265    /// <https://stereokit.net/Pages/StereoKit/ModelNode/Sibling.html>
2266    ///
2267    /// see also [`model_node_sibling`]
2268    /// ### Examples
2269    /// ```
2270    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2271    /// use stereokit_rust::{maths::Matrix, model::Model, mesh::Mesh, material::Material};
2272    ///
2273    /// let model = Model::new();
2274    ///
2275    /// let mut nodes = model.get_nodes();
2276    /// nodes.add("mosh", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2277    /// nodes.add("mesh", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2278    /// nodes.add("mush", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2279    ///
2280    /// let mut node = nodes.find("mesh").expect("Node mosh should exist");
2281    /// node.add_child("mesh child1", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2282    /// node.add_child("mesh child2", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2283    ///
2284    /// let child_node = node.get_child().expect("Node should have a child");
2285    /// assert_eq!(child_node.get_name(), Some("mesh child1"));
2286    ///
2287    /// let child_node = child_node.get_sibling().expect("Child_node should have a sibling");
2288    /// assert_eq!(child_node.get_name(), Some("mesh child2"));
2289    ///
2290    /// let sibling_node = node.get_sibling().expect("Node should have a sibling");
2291    /// assert_eq!(sibling_node.get_name(), Some("mush"));
2292    /// ```
2293    pub fn get_sibling(&'_ self) -> Option<ModelNode<'_>> {
2294        match unsafe { model_node_sibling(self.model.0.as_ptr(), self.id) } {
2295            -1 => None,
2296            otherwise => Some(ModelNode { model: self.model, id: otherwise }),
2297        }
2298    }
2299
2300    /// The ModelNode above this one (“up”) in the hierarchy tree, or None if this is a root node.
2301    /// <https://stereokit.net/Pages/StereoKit/ModelNode/Parent.html>
2302    ///
2303    /// see also [`model_node_parent`]
2304    /// ### Examples
2305    /// ```
2306    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2307    /// use stereokit_rust::{maths::Matrix, model::Model, mesh::Mesh, material::Material};
2308    ///
2309    /// let model = Model::new();
2310    ///
2311    /// let mut nodes = model.get_nodes();
2312    /// nodes.add("mesh", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2313    /// nodes.add("mush", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2314    ///
2315    /// let mut node = nodes.find("mesh").expect("Node mosh should exist");
2316    /// node.add_child("mesh child1", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2317    /// node.add_child("mesh child2", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2318    ///
2319    /// let child_node = node.get_child().expect("Node should have a child");
2320    /// assert_eq!(child_node.get_name(), Some("mesh child1"));
2321    /// assert_eq!(child_node.get_parent().unwrap().get_name(), Some("mesh"));
2322    ///
2323    /// let child_node = child_node.get_sibling().expect("Child_node should have a sibling");
2324    /// assert_eq!(child_node.get_name(), Some("mesh child2"));
2325    /// assert_eq!(child_node.get_parent().unwrap().get_name(), Some("mesh"));
2326    ///
2327    /// // Mesh is it's own parent.
2328    /// assert_eq!(child_node.get_parent().unwrap().get_name(), Some("mesh"));
2329    /// ```
2330    pub fn get_parent(&'_ self) -> Option<ModelNode<'_>> {
2331        match unsafe { model_node_parent(self.model.0.as_ptr(), self.id) } {
2332            -1 => None,
2333            otherwise => Some(ModelNode { model: self.model, id: otherwise }),
2334        }
2335    }
2336
2337    /// The next ModelNode  in the hierarchy tree, or None if this is the last node.
2338    /// <https://stereokit.net/Pages/StereoKit/Model/ModelNodeCollection.html>
2339    ///
2340    /// see also [`model_node_iterate`]
2341    /// same as [`ModelNode::iterate`]
2342    pub fn get_next(&'_ self) -> Option<ModelNode<'_>> {
2343        self.iterate()
2344    }
2345
2346    /// The whole model in which this node belongs
2347    /// <https://stereokit.net/Pages/StereoKit/Model.html>
2348    ///
2349    /// see also [`Model`]
2350    /// ### Examples
2351    /// ```
2352    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2353    /// use stereokit_rust::{maths::Matrix, model::Model, mesh::Mesh, material::Material};
2354    ///
2355    /// let model = Model::new();
2356    ///
2357    /// let mut nodes = model.get_nodes();
2358    /// nodes.add("mesh", Matrix::IDENTITY, Some(&Mesh::cube()), Some(&Material::pbr()), true);
2359    /// let node = nodes.get_root_node().expect("We should have a root node");
2360    /// assert_eq!(node.get_name(), Some("mesh"));
2361    /// assert_eq!(node.get_model(), &model);
2362    /// ```
2363    pub fn get_model(&self) -> &Model {
2364        self.model
2365    }
2366
2367    /// Get Info for this node
2368    /// <https://stereokit.net/Pages/StereoKit/ModelNodeInfoCollection.html>
2369    ///
2370    /// ### Examples
2371    /// ```
2372    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2373    /// use stereokit_rust::{maths::Matrix, model::{Model, Info}};
2374    ///
2375    /// let model = Model::new();
2376    ///
2377    /// let mut nodes = model.get_nodes();
2378    /// nodes.add("some_info", Matrix::IDENTITY, None, None, false);
2379    ///
2380    /// let mut node = nodes.get_root_node().expect("We should have a root node");
2381    /// let mut infos = node.get_infos();
2382    /// infos.set_info( "name1", "value1");
2383    /// infos.set_info( "name2", "value2");
2384    ///
2385    /// assert_eq!(infos.get_count(), 2);
2386    ///
2387    /// for (item, info) in infos.enumerate() {
2388    ///    match item {
2389    ///        0 => assert_eq!(info, Info { name: "name2".to_string(), value: "value2".to_string() }),
2390    ///        _ => assert_eq!(info, Info { name: "name1".to_string(), value: "value1".to_string() }),
2391    ///    }
2392    /// }
2393    /// ```
2394    pub fn get_infos(&'_ self) -> Infos<'_> {
2395        Infos::from(self)
2396    }
2397}
2398
2399/// Infos of a ModelNode
2400/// <https://stereokit.net/Pages/StereoKit/ModelNodeInfoCollection.html>
2401///
2402/// see also [`ModelNode`]
2403pub struct Infos<'a> {
2404    model: &'a Model,
2405    node_id: ModelNodeId,
2406    curr: i32,
2407}
2408
2409impl Iterator for Infos<'_> {
2410    type Item = Info;
2411
2412    fn next(&mut self) -> Option<Self::Item> {
2413        if let Some(res) = Infos::info_iterate(self.model, self.curr, self.node_id) {
2414            self.curr = res.2;
2415            Some(Info { name: res.0.to_string(), value: res.1.to_string() })
2416        } else {
2417            None
2418        }
2419    }
2420}
2421
2422/// One Info of a ModelNode
2423/// <https://stereokit.net/Pages/StereoKit/ModelNodeInfoCollection.html>
2424#[derive(Debug, Clone, PartialEq)]
2425pub struct Info {
2426    pub name: String,
2427    pub value: String,
2428}
2429
2430impl<'a> Infos<'a> {
2431    /// Helper to get the collection struct same as [`ModelNode::get_infos`]
2432    pub fn from(node: &'a ModelNode) -> Infos<'a> {
2433        Infos { model: node.model, node_id: node.id, curr: 0 }
2434    }
2435
2436    /// iterator of the node infos
2437    fn info_iterate(model: &Model, mut iterator: i32, node: ModelNodeId) -> Option<(&str, &str, i32)> {
2438        let out_key_utf8 = CString::new("H").unwrap().into_raw() as *mut *const c_char;
2439        let out_value_utf8 = CString::new("H").unwrap().into_raw() as *mut *const c_char;
2440
2441        let ref_iterator = &mut iterator as *mut i32;
2442
2443        unsafe {
2444            let res = model_node_info_iterate(model.0.as_ptr(), node, ref_iterator, out_key_utf8, out_value_utf8);
2445            if res != 0 {
2446                let key = CStr::from_ptr(*out_key_utf8);
2447                let value = CStr::from_ptr(*out_value_utf8);
2448                Some((key.to_str().unwrap(), value.to_str().unwrap(), *ref_iterator))
2449            } else {
2450                None
2451            }
2452        }
2453    }
2454
2455    /// Clear all infos
2456    /// <https://stereokit.net/Pages/StereoKit/ModelNodeInfoCollection/Clear.html>
2457    ///
2458    /// see also [`model_node_info_clear`] [`ModelNode::get_infos`]
2459    /// ### Examples
2460    /// ```
2461    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2462    /// use stereokit_rust::{maths::Matrix, model::{Model, Info}};
2463    ///
2464    /// let model = Model::new();
2465    ///
2466    /// let mut nodes = model.get_nodes();
2467    /// nodes.add("some_info", Matrix::IDENTITY, None, None, false);
2468    ///
2469    /// let mut node = nodes.get_root_node().expect("We should have a root node");
2470    /// let mut infos = node.get_infos();
2471    /// infos.set_info( "name1", "value1");
2472    /// infos.set_info( "name2", "value2");
2473    ///
2474    /// assert_eq!(infos.get_count(), 2);
2475    ///
2476    /// infos.clear();
2477    /// assert_eq!(infos.get_count(), 0);
2478    /// ```
2479    pub fn clear(&mut self) -> &mut Self {
2480        unsafe { model_node_info_clear(self.model.0.as_ptr(), self.node_id) };
2481        self
2482    }
2483
2484    /// Remove the first occurence found of an info. An error is logged if the info do not exist
2485    /// <https://stereokit.net/Pages/StereoKit/ModelNodeInfoCollection/Remove.html>
2486    ///
2487    /// see also [`model_node_info_remove`] [`ModelNode::get_infos`]
2488    /// ### Examples
2489    /// ```
2490    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2491    /// use stereokit_rust::{maths::Matrix, model::{Model, Info}};
2492    ///
2493    /// let model = Model::new();
2494    ///
2495    /// let mut nodes = model.get_nodes();
2496    /// nodes.add("some_info", Matrix::IDENTITY, None, None, false);
2497    ///
2498    /// let mut node = nodes.get_root_node().expect("We should have a root node");
2499    /// let mut infos = node.get_infos();
2500    /// infos.set_info( "name1", "value1");
2501    /// infos.set_info( "name2", "value2");
2502    ///
2503    /// assert_eq!(infos.get_count(), 2);
2504    ///
2505    /// infos.remove_info("name1000");
2506    /// assert_eq!(infos.get_count(), 2);
2507    ///
2508    /// infos.remove_info("name1");
2509    /// assert_eq!(infos.get_count(), 1);
2510    ///
2511    /// assert_eq!(infos.get_info("name1"), None);
2512    /// assert_eq!(infos.get_info("name2"), Some("value2"));
2513    /// ```
2514    pub fn remove_info<S: AsRef<str>>(&mut self, info_key_utf8: S) -> &mut Self {
2515        let c_str = CString::new(info_key_utf8.as_ref()).unwrap();
2516        unsafe {
2517            if model_node_info_remove(self.model.0.as_ptr(), self.node_id, c_str.as_ptr()) == 0 {
2518                Log::err(format!("Info {:?} was not found during remove", info_key_utf8.as_ref()));
2519            }
2520        }
2521        self
2522    }
2523
2524    /// Set an info value to this node (key is unique). The last added key (if multiple) will be the first found.
2525    /// <https://stereokit.net/Pages/StereoKit/ModelNodeInfoCollection/Add.html>
2526    ///
2527    /// see also [`model_node_info_set`] [`ModelNode::get_infos`]
2528    /// ### Examples
2529    /// ```
2530    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2531    /// use stereokit_rust::{maths::Matrix, model::{Model, Info}};
2532    ///
2533    /// let model = Model::new();
2534    ///
2535    /// let mut nodes = model.get_nodes();
2536    /// nodes.add("some_info", Matrix::IDENTITY, None, None, false);
2537    ///
2538    /// let mut node = nodes.get_root_node().expect("We should have a root node");
2539    /// let mut infos = node.get_infos();
2540    /// infos.set_info( "name1", "value111");
2541    /// infos.set_info( "name1", "value1");
2542    /// infos.set_info( "name2", "value2");
2543    ///
2544    /// assert_eq!(infos.get_count(), 2);
2545    ///
2546    /// assert_eq!(infos.get_info("name1"), Some("value1"));
2547    /// assert_eq!(infos.get_info("name2"), Some("value2"));
2548    /// ```
2549    pub fn set_info<S: AsRef<str>>(&mut self, info_key_utf8: S, info_value_utf8: S) -> &mut Self {
2550        let c_str = CString::new(info_key_utf8.as_ref()).unwrap();
2551        let c_value = CString::new(info_value_utf8.as_ref()).unwrap();
2552        unsafe { model_node_info_set(self.model.0.as_ptr(), self.node_id, c_str.as_ptr(), c_value.as_ptr()) };
2553        self
2554    }
2555
2556    /// Get the first info value of this node corresponding to the key.
2557    /// Return None if the key doesn't exist
2558    /// <https://stereokit.net/Pages/StereoKit/ModelNodeInfoCollection/Get.html>
2559    ///
2560    /// see also [`model_node_info_get`] [`ModelNode::get_infos`]
2561    /// ### Examples
2562    /// ```
2563    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2564    /// use stereokit_rust::{maths::Matrix, model::{Model, Info}};
2565    ///
2566    /// let model = Model::new();
2567    ///
2568    /// let mut nodes = model.get_nodes();
2569    /// nodes.add("some_info", Matrix::IDENTITY, None, None, false);
2570    ///
2571    /// let mut node = nodes.get_root_node().expect("We should have a root node");
2572    /// let mut infos = node.get_infos();
2573    /// infos.set_info( "name1", "value1");
2574    /// infos.set_info( "name2", "value2");
2575    ///
2576    /// assert_eq!(infos.get_count(), 2);
2577    ///
2578    /// assert_eq!(infos.get_info("name1"), Some("value1"));
2579    /// assert_eq!(infos.get_info("name2"), Some("value2"));
2580    /// assert_eq!(infos.get_info("name3333"), None);
2581    /// ```
2582    pub fn get_info<S: AsRef<str>>(&self, info_key_utf8: S) -> Option<&str> {
2583        let c_str = CString::new(info_key_utf8.as_ref()).unwrap();
2584        match NonNull::new(unsafe { model_node_info_get(self.model.0.as_ptr(), self.node_id, c_str.as_ptr()) }) {
2585            Some(non_null) => unsafe { CStr::from_ptr(non_null.as_ref()).to_str().ok() },
2586            None => None,
2587        }
2588    }
2589
2590    /// Check if there is a node corresponding to the key.
2591    /// (get_info is more efficient, thanks to rust)
2592    /// <https://stereokit.net/Pages/StereoKit/ModelNodeInfoCollection/Contains.html>
2593    ///
2594    /// see also [`model_node_info_get`]
2595    /// ### Examples
2596    /// ```
2597    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2598    /// use stereokit_rust::{maths::Matrix, model::{Model, Info}};
2599    ///
2600    /// let model = Model::new();
2601    ///
2602    /// let mut nodes = model.get_nodes();
2603    /// nodes.add("some_info", Matrix::IDENTITY, None, None, false);
2604    ///
2605    /// let mut node = nodes.get_root_node().expect("We should have a root node");
2606    /// let mut infos = node.get_infos();
2607    /// infos.set_info( "name1", "value111");
2608    /// infos.set_info( "name1", "value1");
2609    /// infos.set_info( "name2", "value2");
2610    ///
2611    /// assert_eq!(infos.get_count(), 2);
2612    ///
2613    /// assert_eq!(infos.contains("name1"), true);
2614    /// assert_eq!(infos.contains("name2"), true);
2615    /// assert_eq!(infos.contains("name333"), false);
2616    /// ```
2617    pub fn contains<S: AsRef<str>>(&self, info_key_utf8: S) -> bool {
2618        self.get_info(info_key_utf8).is_some()
2619    }
2620
2621    /// Get the number of infos for this node
2622    /// <https://stereokit.net/Pages/StereoKit/ModelNodeInfoCollection/Count.html>
2623    ///
2624    /// see also [`model_node_info_count`]
2625    /// ### Examples
2626    /// ```
2627    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2628    /// use stereokit_rust::{maths::Matrix, model::{Model, Info}};
2629    ///
2630    /// let model = Model::new();
2631    ///
2632    /// let mut nodes = model.get_nodes();
2633    /// nodes.add("some_info", Matrix::IDENTITY, None, None, false);
2634    ///
2635    /// let mut node = nodes.get_root_node().expect("We should have a root node");
2636    /// let mut infos = node.get_infos();
2637    /// infos.set_info( "name0", "value0");
2638    /// assert_eq!(infos.get_count(), 1);
2639    ///
2640    /// infos.set_info( "name1", "value1");
2641    /// assert_eq!(infos.get_count(), 2);
2642    ///
2643    /// infos.set_info( "name2", "value2");
2644    /// assert_eq!(infos.get_count(), 3);
2645    ///
2646    /// infos.clear();
2647    /// assert_eq!(infos.get_count(), 0);
2648    /// ```
2649    pub fn get_count(&self) -> i32 {
2650        unsafe { model_node_info_count(self.model.0.as_ptr(), self.node_id) }
2651    }
2652}