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}