Skip to main content

modelio/
material.rs

1use std::path::Path;
2use std::ptr;
3
4use crate::asset_resolver::AssetResolver;
5use crate::error::Result;
6use crate::ffi;
7use crate::handle::ObjectHandle;
8use crate::protocols::Named;
9use crate::texture::Texture;
10use crate::transform::Transform;
11use crate::types::{
12    MaterialFace, MaterialInfo, MaterialMipMapFilterMode, MaterialPropertyInfo, MaterialSemantic,
13    MaterialTextureFilterMode, MaterialTextureWrapMode, TextureFilterInfo, TextureSamplerInfo,
14};
15use crate::util::{c_string, parse_json, path_to_c_string, required_handle, take_string};
16
17fn array_objects<T, F>(
18    array_ptr: *mut core::ffi::c_void,
19    context: &'static str,
20    mut map: F,
21) -> Result<Vec<T>>
22where
23    F: FnMut(ObjectHandle) -> T,
24{
25    let array = required_handle(array_ptr, context)?;
26    // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
27    let count = unsafe { ffi::mdl_array_count(array.as_ptr()) as usize };
28    let mut values = Vec::with_capacity(count);
29    for index in 0..count {
30        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
31        let ptr = unsafe { ffi::mdl_array_object_at(array.as_ptr(), index as u64) };
32        // SAFETY: The unsafe operation is valid in this context.
33        if let Some(handle) = unsafe { ObjectHandle::from_retained_ptr(ptr) } {
34            values.push(map(handle));
35        }
36    }
37    Ok(values)
38}
39
40#[derive(Debug, Clone)]
41/// Wraps the corresponding Model I/O material counterpart.
42pub struct Material {
43    handle: ObjectHandle,
44}
45
46impl Named for Material {
47    fn name(&self) -> Option<String> {
48        self.name()
49    }
50
51    fn set_name(&self, name: &str) -> Result<()> {
52        self.set_name(name)
53    }
54}
55
56impl Material {
57    /// Builds this wrapper from the retained handle of the wrapped Model I/O material counterpart.
58    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
59        Self { handle }
60    }
61
62    /// Returns the opaque pointer used to call the wrapped Model I/O material counterpart.
63    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
64        self.handle.as_ptr()
65    }
66
67    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O material counterpart.
68    pub fn new(name: &str, physically_plausible: bool) -> Result<Self> {
69        let name = c_string(name)?;
70        let mut out_material = ptr::null_mut();
71        let mut out_error = ptr::null_mut();
72        // SAFETY: The unsafe operation is valid in this context.
73        let status = unsafe {
74            ffi::mdl_material_new(
75                name.as_ptr(),
76                i32::from(physically_plausible),
77                &mut out_material,
78                &mut out_error,
79            )
80        };
81        crate::util::status_result(status, out_error)?;
82        Ok(Self::from_handle(required_handle(
83            out_material,
84            "MDLMaterial",
85        )?))
86    }
87
88    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
89    pub fn info(&self) -> Result<MaterialInfo> {
90        parse_json(
91            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
92            unsafe { ffi::mdl_material_info_json(self.handle.as_ptr()) },
93            "MDLMaterial",
94        )
95    }
96
97    #[must_use]
98    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
99    pub fn count(&self) -> usize {
100        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
101        unsafe { ffi::mdl_material_count(self.handle.as_ptr()) as usize }
102    }
103
104    #[must_use]
105    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
106    pub fn name(&self) -> Option<String> {
107        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
108        take_string(unsafe { ffi::mdl_material_name_string(self.handle.as_ptr()) })
109    }
110
111    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
112    pub fn set_name(&self, name: &str) -> Result<()> {
113        let name = c_string(name)?;
114        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
115        unsafe { ffi::mdl_material_set_name(self.handle.as_ptr(), name.as_ptr()) };
116        Ok(())
117    }
118
119    #[must_use]
120    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
121    pub fn material_face(&self) -> Option<MaterialFace> {
122        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
123        MaterialFace::from_raw(unsafe { ffi::mdl_material_material_face(self.handle.as_ptr()) })
124    }
125
126    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
127    pub fn set_material_face(&self, face: MaterialFace) {
128        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
129        unsafe { ffi::mdl_material_set_material_face(self.handle.as_ptr(), face.as_raw()) };
130    }
131
132    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
133    pub fn remove_all_properties(&self) {
134        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
135        unsafe { ffi::mdl_material_remove_all_properties(self.handle.as_ptr()) };
136    }
137
138    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
139    pub fn load_textures_using_resolver(&self, resolver: &AssetResolver) {
140        // SAFETY: The unsafe operation is valid in this context.
141        unsafe {
142            ffi::mdl_material_load_textures_using_resolver(self.handle.as_ptr(), resolver.as_ptr());
143        };
144    }
145
146    #[must_use]
147    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
148    pub fn property(&self, index: usize) -> Option<MaterialProperty> {
149        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
150        let ptr = unsafe { ffi::mdl_material_property_at(self.handle.as_ptr(), index as u64) };
151        // SAFETY: The unsafe operation is valid in this context.
152        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
153    }
154
155    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
156    pub fn property_named(&self, name: &str) -> Result<Option<MaterialProperty>> {
157        let name = c_string(name)?;
158        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
159        let ptr = unsafe { ffi::mdl_material_property_named(self.handle.as_ptr(), name.as_ptr()) };
160        // SAFETY: The unsafe operation is valid in this context.
161        Ok(unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle))
162    }
163
164    #[must_use]
165    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
166    pub fn property_with_semantic(&self, semantic: MaterialSemantic) -> Option<MaterialProperty> {
167        // SAFETY: The unsafe operation is valid in this context.
168        let ptr = unsafe {
169            ffi::mdl_material_property_with_semantic(self.handle.as_ptr(), semantic as u32)
170        };
171        // SAFETY: The unsafe operation is valid in this context.
172        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
173    }
174
175    #[must_use]
176    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
177    pub fn properties(&self) -> Vec<MaterialProperty> {
178        (0..self.count())
179            .filter_map(|index| self.property(index))
180            .collect()
181    }
182}
183
184#[derive(Debug, Clone)]
185/// Wraps the corresponding Model I/O material property counterpart.
186pub struct MaterialProperty {
187    handle: ObjectHandle,
188}
189
190impl Named for MaterialProperty {
191    fn name(&self) -> Option<String> {
192        self.name()
193    }
194
195    fn set_name(&self, name: &str) -> Result<()> {
196        self.set_name(name)
197    }
198}
199
200impl MaterialProperty {
201    /// Builds this wrapper from the retained handle of the wrapped Model I/O material property counterpart.
202    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
203        Self { handle }
204    }
205
206    /// Returns the opaque pointer used to call the wrapped Model I/O material property counterpart.
207    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
208        self.handle.as_ptr()
209    }
210
211    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O material property counterpart.
212    pub fn new(name: &str, semantic: MaterialSemantic) -> Result<Self> {
213        let name = c_string(name)?;
214        let mut out_property = ptr::null_mut();
215        let mut out_error = ptr::null_mut();
216        // SAFETY: The unsafe operation is valid in this context.
217        let status = unsafe {
218            ffi::mdl_material_property_new(
219                name.as_ptr(),
220                semantic as u32,
221                &mut out_property,
222                &mut out_error,
223            )
224        };
225        crate::util::status_result(status, out_error)?;
226        Ok(Self::from_handle(required_handle(
227            out_property,
228            "MDLMaterialProperty",
229        )?))
230    }
231
232    #[must_use]
233    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
234    pub fn name(&self) -> Option<String> {
235        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
236        take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
237    }
238
239    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
240    pub fn set_name(&self, name: &str) -> Result<()> {
241        let name = c_string(name)?;
242        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
243        unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
244        Ok(())
245    }
246
247    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
248    pub fn info(&self) -> Result<MaterialPropertyInfo> {
249        parse_json(
250            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
251            unsafe { ffi::mdl_material_property_info_json(self.handle.as_ptr()) },
252            "MDLMaterialProperty",
253        )
254    }
255
256    #[must_use]
257    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
258    pub fn texture(&self) -> Option<Texture> {
259        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
260        let ptr = unsafe { ffi::mdl_material_property_texture(self.handle.as_ptr()) };
261        // SAFETY: The unsafe operation is valid in this context.
262        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Texture::from_handle)
263    }
264
265    #[must_use]
266    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
267    pub fn texture_sampler(&self) -> Option<TextureSampler> {
268        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
269        let ptr = unsafe { ffi::mdl_material_property_texture_sampler(self.handle.as_ptr()) };
270        // SAFETY: The unsafe operation is valid in this context.
271        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(TextureSampler::from_handle)
272    }
273
274    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
275    pub fn set_texture_sampler(&self, sampler: Option<&TextureSampler>) {
276        // SAFETY: The unsafe operation is valid in this context.
277        unsafe {
278            ffi::mdl_material_property_set_texture_sampler(
279                self.handle.as_ptr(),
280                sampler.map_or(ptr::null_mut(), TextureSampler::as_ptr),
281            );
282        };
283    }
284
285    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
286    pub fn set_float(&self, value: f32) {
287        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
288        unsafe { ffi::mdl_material_property_set_float(self.handle.as_ptr(), value) };
289    }
290
291    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
292    pub fn set_float2(&self, value: [f32; 2]) {
293        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
294        unsafe { ffi::mdl_material_property_set_float2(self.handle.as_ptr(), value[0], value[1]) };
295    }
296
297    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
298    pub fn set_float3(&self, value: [f32; 3]) {
299        // SAFETY: The unsafe operation is valid in this context.
300        unsafe {
301            ffi::mdl_material_property_set_float3(
302                self.handle.as_ptr(),
303                value[0],
304                value[1],
305                value[2],
306            );
307        };
308    }
309
310    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
311    pub fn set_float4(&self, value: [f32; 4]) {
312        // SAFETY: The unsafe operation is valid in this context.
313        unsafe {
314            ffi::mdl_material_property_set_float4(
315                self.handle.as_ptr(),
316                value[0],
317                value[1],
318                value[2],
319                value[3],
320            );
321        };
322    }
323
324    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
325    pub fn set_matrix4x4(&self, value: [f32; 16]) {
326        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
327        unsafe { ffi::mdl_material_property_set_matrix4x4(self.handle.as_ptr(), value.as_ptr()) };
328    }
329
330    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
331    pub fn set_string(&self, value: Option<&str>) -> Result<()> {
332        let value = value.map(c_string).transpose()?;
333        // SAFETY: The unsafe operation is valid in this context.
334        unsafe {
335            ffi::mdl_material_property_set_string(
336                self.handle.as_ptr(),
337                value.as_ref().map_or(ptr::null(), |value| value.as_ptr()),
338            );
339        };
340        Ok(())
341    }
342
343    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
344    pub fn set_url(&self, path: Option<impl AsRef<Path>>) -> Result<()> {
345        let path = path
346            .map(|path| path_to_c_string(path.as_ref()))
347            .transpose()?;
348        // SAFETY: The unsafe operation is valid in this context.
349        unsafe {
350            ffi::mdl_material_property_set_url(
351                self.handle.as_ptr(),
352                path.as_ref().map_or(ptr::null(), |path| path.as_ptr()),
353            );
354        };
355        Ok(())
356    }
357
358    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
359    pub fn set_color(&self, color: [f32; 4]) {
360        // SAFETY: The unsafe operation is valid in this context.
361        unsafe {
362            ffi::mdl_material_property_set_color(
363                self.handle.as_ptr(),
364                color[0],
365                color[1],
366                color[2],
367                color[3],
368            );
369        };
370    }
371
372    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
373    pub fn set_luminance(&self, value: f32) {
374        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
375        unsafe { ffi::mdl_material_property_set_luminance(self.handle.as_ptr(), value) };
376    }
377}
378
379#[derive(Debug, Clone)]
380/// Wraps the corresponding Model I/O texture filter counterpart.
381pub struct TextureFilter {
382    handle: ObjectHandle,
383}
384
385impl TextureFilter {
386    /// Builds this wrapper from the retained handle of the wrapped Model I/O texture filter counterpart.
387    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
388        Self { handle }
389    }
390
391    /// Returns the opaque pointer used to call the wrapped Model I/O texture filter counterpart.
392    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
393        self.handle.as_ptr()
394    }
395
396    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O texture filter counterpart.
397    pub fn new() -> Result<Self> {
398        let mut out_filter = ptr::null_mut();
399        let mut out_error = ptr::null_mut();
400        // SAFETY: Output pointers are initialized and managed; FFI function is called safely.
401        let status = unsafe { ffi::mdl_texture_filter_new(&mut out_filter, &mut out_error) };
402        crate::util::status_result(status, out_error)?;
403        Ok(Self::from_handle(required_handle(
404            out_filter,
405            "MDLTextureFilter",
406        )?))
407    }
408
409    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture filter counterpart.
410    pub fn info(&self) -> Result<TextureFilterInfo> {
411        parse_json(
412            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
413            unsafe { ffi::mdl_texture_filter_info_json(self.handle.as_ptr()) },
414            "MDLTextureFilter",
415        )
416    }
417
418    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture filter counterpart.
419    pub fn set_s_wrap_mode(&self, value: MaterialTextureWrapMode) {
420        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
421        unsafe { ffi::mdl_texture_filter_set_s_wrap_mode(self.handle.as_ptr(), value.as_raw()) };
422    }
423
424    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture filter counterpart.
425    pub fn set_t_wrap_mode(&self, value: MaterialTextureWrapMode) {
426        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
427        unsafe { ffi::mdl_texture_filter_set_t_wrap_mode(self.handle.as_ptr(), value.as_raw()) };
428    }
429
430    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture filter counterpart.
431    pub fn set_r_wrap_mode(&self, value: MaterialTextureWrapMode) {
432        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
433        unsafe { ffi::mdl_texture_filter_set_r_wrap_mode(self.handle.as_ptr(), value.as_raw()) };
434    }
435
436    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture filter counterpart.
437    pub fn set_min_filter(&self, value: MaterialTextureFilterMode) {
438        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
439        unsafe { ffi::mdl_texture_filter_set_min_filter(self.handle.as_ptr(), value.as_raw()) };
440    }
441
442    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture filter counterpart.
443    pub fn set_mag_filter(&self, value: MaterialTextureFilterMode) {
444        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
445        unsafe { ffi::mdl_texture_filter_set_mag_filter(self.handle.as_ptr(), value.as_raw()) };
446    }
447
448    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture filter counterpart.
449    pub fn set_mip_filter(&self, value: MaterialMipMapFilterMode) {
450        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
451        unsafe { ffi::mdl_texture_filter_set_mip_filter(self.handle.as_ptr(), value.as_raw()) };
452    }
453}
454
455#[derive(Debug, Clone)]
456/// Wraps the corresponding Model I/O texture sampler counterpart.
457pub struct TextureSampler {
458    handle: ObjectHandle,
459}
460
461impl TextureSampler {
462    /// Builds this wrapper from the retained handle of the wrapped Model I/O texture sampler counterpart.
463    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
464        Self { handle }
465    }
466
467    /// Returns the opaque pointer used to call the wrapped Model I/O texture sampler counterpart.
468    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
469        self.handle.as_ptr()
470    }
471
472    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O texture sampler counterpart.
473    pub fn new() -> Result<Self> {
474        let mut out_sampler = ptr::null_mut();
475        let mut out_error = ptr::null_mut();
476        // SAFETY: Output pointers are initialized and managed; FFI function is called safely.
477        let status = unsafe { ffi::mdl_texture_sampler_new(&mut out_sampler, &mut out_error) };
478        crate::util::status_result(status, out_error)?;
479        Ok(Self::from_handle(required_handle(
480            out_sampler,
481            "MDLTextureSampler",
482        )?))
483    }
484
485    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture sampler counterpart.
486    pub fn info(&self) -> Result<TextureSamplerInfo> {
487        parse_json(
488            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
489            unsafe { ffi::mdl_texture_sampler_info_json(self.handle.as_ptr()) },
490            "MDLTextureSampler",
491        )
492    }
493
494    #[must_use]
495    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture sampler counterpart.
496    pub fn texture(&self) -> Option<Texture> {
497        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
498        let ptr = unsafe { ffi::mdl_texture_sampler_texture(self.handle.as_ptr()) };
499        // SAFETY: The unsafe operation is valid in this context.
500        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Texture::from_handle)
501    }
502
503    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture sampler counterpart.
504    pub fn set_texture(&self, texture: Option<&Texture>) {
505        // SAFETY: The unsafe operation is valid in this context.
506        unsafe {
507            ffi::mdl_texture_sampler_set_texture(
508                self.handle.as_ptr(),
509                texture.map_or(ptr::null_mut(), Texture::as_ptr),
510            );
511        };
512    }
513
514    #[must_use]
515    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture sampler counterpart.
516    pub fn hardware_filter(&self) -> Option<TextureFilter> {
517        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
518        let ptr = unsafe { ffi::mdl_texture_sampler_hardware_filter(self.handle.as_ptr()) };
519        // SAFETY: The unsafe operation is valid in this context.
520        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(TextureFilter::from_handle)
521    }
522
523    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture sampler counterpart.
524    pub fn set_hardware_filter(&self, filter: Option<&TextureFilter>) {
525        // SAFETY: The unsafe operation is valid in this context.
526        unsafe {
527            ffi::mdl_texture_sampler_set_hardware_filter(
528                self.handle.as_ptr(),
529                filter.map_or(ptr::null_mut(), TextureFilter::as_ptr),
530            );
531        };
532    }
533
534    #[must_use]
535    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture sampler counterpart.
536    pub fn transform(&self) -> Option<Transform> {
537        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
538        let ptr = unsafe { ffi::mdl_texture_sampler_transform(self.handle.as_ptr()) };
539        // SAFETY: The unsafe operation is valid in this context.
540        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Transform::from_handle)
541    }
542
543    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture sampler counterpart.
544    pub fn set_transform(&self, transform: Option<&Transform>) {
545        // SAFETY: The unsafe operation is valid in this context.
546        unsafe {
547            ffi::mdl_texture_sampler_set_transform(
548                self.handle.as_ptr(),
549                transform.map_or(ptr::null_mut(), Transform::as_ptr),
550            );
551        };
552    }
553}
554
555#[derive(Debug, Clone)]
556/// Wraps the corresponding Model I/O material property connection counterpart.
557pub struct MaterialPropertyConnection {
558    handle: ObjectHandle,
559}
560
561impl Named for MaterialPropertyConnection {
562    fn name(&self) -> Option<String> {
563        self.name()
564    }
565
566    fn set_name(&self, name: &str) -> Result<()> {
567        self.set_name(name)
568    }
569}
570
571impl MaterialPropertyConnection {
572    /// Builds this wrapper from the retained handle of the wrapped Model I/O material property connection counterpart.
573    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
574        Self { handle }
575    }
576
577    /// Returns the opaque pointer used to call the wrapped Model I/O material property connection counterpart.
578    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
579        self.handle.as_ptr()
580    }
581
582    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O material property connection counterpart.
583    pub fn new(output: &MaterialProperty, input: &MaterialProperty) -> Result<Self> {
584        let mut out_connection = ptr::null_mut();
585        let mut out_error = ptr::null_mut();
586        // SAFETY: The unsafe operation is valid in this context.
587        let status = unsafe {
588            ffi::mdl_material_property_connection_new(
589                output.as_ptr(),
590                input.as_ptr(),
591                &mut out_connection,
592                &mut out_error,
593            )
594        };
595        crate::util::status_result(status, out_error)?;
596        Ok(Self::from_handle(required_handle(
597            out_connection,
598            "MDLMaterialPropertyConnection",
599        )?))
600    }
601
602    #[must_use]
603    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property connection counterpart.
604    pub fn name(&self) -> Option<String> {
605        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
606        take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
607    }
608
609    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property connection counterpart.
610    pub fn set_name(&self, name: &str) -> Result<()> {
611        let name = c_string(name)?;
612        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
613        unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
614        Ok(())
615    }
616
617    #[must_use]
618    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property connection counterpart.
619    pub fn output(&self) -> Option<MaterialProperty> {
620        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
621        let ptr = unsafe { ffi::mdl_material_property_connection_output(self.handle.as_ptr()) };
622        // SAFETY: The unsafe operation is valid in this context.
623        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
624    }
625
626    #[must_use]
627    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property connection counterpart.
628    pub fn input(&self) -> Option<MaterialProperty> {
629        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
630        let ptr = unsafe { ffi::mdl_material_property_connection_input(self.handle.as_ptr()) };
631        // SAFETY: The unsafe operation is valid in this context.
632        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
633    }
634}
635
636#[derive(Debug, Clone)]
637/// Wraps the corresponding Model I/O material property node counterpart.
638pub struct MaterialPropertyNode {
639    handle: ObjectHandle,
640}
641
642impl Named for MaterialPropertyNode {
643    fn name(&self) -> Option<String> {
644        self.name()
645    }
646
647    fn set_name(&self, name: &str) -> Result<()> {
648        self.set_name(name)
649    }
650}
651
652impl MaterialPropertyNode {
653    /// Builds this wrapper from the retained handle of the wrapped Model I/O material property node counterpart.
654    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
655        Self { handle }
656    }
657
658    /// Returns the opaque pointer used to call the wrapped Model I/O material property node counterpart.
659    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
660        self.handle.as_ptr()
661    }
662
663    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O material property node counterpart.
664    pub fn new(inputs: &[&MaterialProperty], outputs: &[&MaterialProperty]) -> Result<Self> {
665        let input_ptrs = inputs
666            .iter()
667            .map(|property| property.as_ptr())
668            .collect::<Vec<_>>();
669        let output_ptrs = outputs
670            .iter()
671            .map(|property| property.as_ptr())
672            .collect::<Vec<_>>();
673        let mut out_node = ptr::null_mut();
674        let mut out_error = ptr::null_mut();
675        // SAFETY: The unsafe operation is valid in this context.
676        let status = unsafe {
677            ffi::mdl_material_property_node_new(
678                input_ptrs.as_ptr(),
679                input_ptrs.len() as u64,
680                output_ptrs.as_ptr(),
681                output_ptrs.len() as u64,
682                &mut out_node,
683                &mut out_error,
684            )
685        };
686        crate::util::status_result(status, out_error)?;
687        Ok(Self::from_handle(required_handle(
688            out_node,
689            "MDLMaterialPropertyNode",
690        )?))
691    }
692
693    #[must_use]
694    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property node counterpart.
695    pub fn name(&self) -> Option<String> {
696        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
697        take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
698    }
699
700    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property node counterpart.
701    pub fn set_name(&self, name: &str) -> Result<()> {
702        let name = c_string(name)?;
703        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
704        unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
705        Ok(())
706    }
707
708    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property node counterpart.
709    pub fn inputs(&self) -> Result<Vec<MaterialProperty>> {
710        array_objects(
711            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
712            unsafe { ffi::mdl_material_property_node_inputs(self.handle.as_ptr()) },
713            "MDLMaterialPropertyNode inputs",
714            MaterialProperty::from_handle,
715        )
716    }
717
718    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property node counterpart.
719    pub fn outputs(&self) -> Result<Vec<MaterialProperty>> {
720        array_objects(
721            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
722            unsafe { ffi::mdl_material_property_node_outputs(self.handle.as_ptr()) },
723            "MDLMaterialPropertyNode outputs",
724            MaterialProperty::from_handle,
725        )
726    }
727}
728
729#[derive(Debug, Clone)]
730/// Wraps the corresponding Model I/O material property graph counterpart.
731pub struct MaterialPropertyGraph {
732    handle: ObjectHandle,
733}
734
735impl Named for MaterialPropertyGraph {
736    fn name(&self) -> Option<String> {
737        self.name()
738    }
739
740    fn set_name(&self, name: &str) -> Result<()> {
741        self.set_name(name)
742    }
743}
744
745impl MaterialPropertyGraph {
746    /// Builds this wrapper from the retained handle of the wrapped Model I/O material property graph counterpart.
747    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
748        Self { handle }
749    }
750
751    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O material property graph counterpart.
752    pub fn new(
753        nodes: &[&MaterialPropertyNode],
754        connections: &[&MaterialPropertyConnection],
755    ) -> Result<Self> {
756        let node_ptrs = nodes.iter().map(|node| node.as_ptr()).collect::<Vec<_>>();
757        let connection_ptrs = connections
758            .iter()
759            .map(|connection| connection.as_ptr())
760            .collect::<Vec<_>>();
761        let mut out_graph = ptr::null_mut();
762        let mut out_error = ptr::null_mut();
763        // SAFETY: The unsafe operation is valid in this context.
764        let status = unsafe {
765            ffi::mdl_material_property_graph_new(
766                node_ptrs.as_ptr(),
767                node_ptrs.len() as u64,
768                connection_ptrs.as_ptr(),
769                connection_ptrs.len() as u64,
770                &mut out_graph,
771                &mut out_error,
772            )
773        };
774        crate::util::status_result(status, out_error)?;
775        Ok(Self::from_handle(required_handle(
776            out_graph,
777            "MDLMaterialPropertyGraph",
778        )?))
779    }
780
781    #[must_use]
782    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property graph counterpart.
783    pub fn name(&self) -> Option<String> {
784        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
785        take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
786    }
787
788    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property graph counterpart.
789    pub fn set_name(&self, name: &str) -> Result<()> {
790        let name = c_string(name)?;
791        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
792        unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
793        Ok(())
794    }
795
796    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property graph counterpart.
797    pub fn evaluate(&self) {
798        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
799        unsafe { ffi::mdl_material_property_graph_evaluate(self.handle.as_ptr()) };
800    }
801
802    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property graph counterpart.
803    pub fn nodes(&self) -> Result<Vec<MaterialPropertyNode>> {
804        array_objects(
805            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
806            unsafe { ffi::mdl_material_property_graph_nodes(self.handle.as_ptr()) },
807            "MDLMaterialPropertyGraph nodes",
808            MaterialPropertyNode::from_handle,
809        )
810    }
811
812    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property graph counterpart.
813    pub fn connections(&self) -> Result<Vec<MaterialPropertyConnection>> {
814        array_objects(
815            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
816            unsafe { ffi::mdl_material_property_graph_connections(self.handle.as_ptr()) },
817            "MDLMaterialPropertyGraph connections",
818            MaterialPropertyConnection::from_handle,
819        )
820    }
821
822    #[must_use]
823    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property graph counterpart.
824    pub fn as_node(&self) -> MaterialPropertyNode {
825        MaterialPropertyNode::from_handle(self.handle.clone())
826    }
827}