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    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O material counterpart.
89    pub fn new_with_scattering_function(
90        name: &str,
91        scattering_function: &ScatteringFunction,
92    ) -> Result<Self> {
93        let name = c_string(name)?;
94        let mut out_material = ptr::null_mut();
95        let mut out_error = ptr::null_mut();
96        // SAFETY: The unsafe operation is valid in this context.
97        let status = unsafe {
98            ffi::mdl_material_new_with_scattering_function(
99                name.as_ptr(),
100                scattering_function.as_ptr(),
101                &mut out_material,
102                &mut out_error,
103            )
104        };
105        crate::util::status_result(status, out_error)?;
106        Ok(Self::from_handle(required_handle(
107            out_material,
108            "MDLMaterial",
109        )?))
110    }
111
112    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
113    pub fn info(&self) -> Result<MaterialInfo> {
114        parse_json(
115            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
116            unsafe { ffi::mdl_material_info_json(self.handle.as_ptr()) },
117            "MDLMaterial",
118        )
119    }
120
121    #[must_use]
122    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
123    pub fn count(&self) -> usize {
124        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
125        unsafe { ffi::mdl_material_count(self.handle.as_ptr()) as usize }
126    }
127
128    #[must_use]
129    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
130    pub fn name(&self) -> Option<String> {
131        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
132        take_string(unsafe { ffi::mdl_material_name_string(self.handle.as_ptr()) })
133    }
134
135    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
136    pub fn set_name(&self, name: &str) -> Result<()> {
137        let name = c_string(name)?;
138        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
139        unsafe { ffi::mdl_material_set_name(self.handle.as_ptr(), name.as_ptr()) };
140        Ok(())
141    }
142
143    #[must_use]
144    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
145    pub fn material_face(&self) -> Option<MaterialFace> {
146        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
147        MaterialFace::from_raw(unsafe { ffi::mdl_material_material_face(self.handle.as_ptr()) })
148    }
149
150    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
151    pub fn set_material_face(&self, face: MaterialFace) {
152        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
153        unsafe { ffi::mdl_material_set_material_face(self.handle.as_ptr(), face.as_raw()) };
154    }
155
156    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
157    pub fn remove_all_properties(&self) {
158        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
159        unsafe { ffi::mdl_material_remove_all_properties(self.handle.as_ptr()) };
160    }
161
162    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
163    pub fn load_textures_using_resolver(&self, resolver: &AssetResolver) {
164        // SAFETY: The unsafe operation is valid in this context.
165        unsafe {
166            ffi::mdl_material_load_textures_using_resolver(self.handle.as_ptr(), resolver.as_ptr());
167        };
168    }
169
170    #[must_use]
171    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
172    pub fn property(&self, index: usize) -> Option<MaterialProperty> {
173        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
174        let ptr = unsafe { ffi::mdl_material_property_at(self.handle.as_ptr(), index as u64) };
175        // SAFETY: The unsafe operation is valid in this context.
176        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
177    }
178
179    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
180    pub fn property_named(&self, name: &str) -> Result<Option<MaterialProperty>> {
181        let name = c_string(name)?;
182        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
183        let ptr = unsafe { ffi::mdl_material_property_named(self.handle.as_ptr(), name.as_ptr()) };
184        // SAFETY: The unsafe operation is valid in this context.
185        Ok(unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle))
186    }
187
188    #[must_use]
189    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
190    pub fn property_with_semantic(&self, semantic: MaterialSemantic) -> Option<MaterialProperty> {
191        // SAFETY: The unsafe operation is valid in this context.
192        let ptr = unsafe {
193            ffi::mdl_material_property_with_semantic(self.handle.as_ptr(), semantic as u32)
194        };
195        // SAFETY: The unsafe operation is valid in this context.
196        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
197    }
198
199    #[must_use]
200    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
201    pub fn properties(&self) -> Vec<MaterialProperty> {
202        (0..self.count())
203            .filter_map(|index| self.property(index))
204            .collect()
205    }
206
207    #[must_use]
208    /// Calls the corresponding Model I/O method on the wrapped Model I/O material counterpart.
209    pub fn scattering_function(&self) -> Option<ScatteringFunction> {
210        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
211        let ptr = unsafe { ffi::mdl_material_scattering_function(self.handle.as_ptr()) };
212        // SAFETY: The unsafe operation is valid in this context.
213        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(ScatteringFunction::from_handle)
214    }
215}
216
217mod scattering_property_code {
218    pub const BASE_COLOR: u32 = 1;
219    pub const EMISSION: u32 = 2;
220    pub const SPECULAR: u32 = 3;
221    pub const MATERIAL_INDEX_OF_REFRACTION: u32 = 4;
222    pub const INTERFACE_INDEX_OF_REFRACTION: u32 = 5;
223    pub const NORMAL: u32 = 6;
224    pub const AMBIENT_OCCLUSION: u32 = 7;
225    pub const AMBIENT_OCCLUSION_SCALE: u32 = 8;
226}
227
228mod physically_plausible_scattering_property_code {
229    pub const SUBSURFACE: u32 = 1;
230    pub const METALLIC: u32 = 2;
231    pub const SPECULAR_AMOUNT: u32 = 3;
232    pub const SPECULAR_TINT: u32 = 4;
233    pub const ROUGHNESS: u32 = 5;
234    pub const ANISOTROPIC: u32 = 6;
235    pub const ANISOTROPIC_ROTATION: u32 = 7;
236    pub const SHEEN: u32 = 8;
237    pub const SHEEN_TINT: u32 = 9;
238    pub const CLEARCOAT: u32 = 10;
239    pub const CLEARCOAT_GLOSS: u32 = 11;
240}
241
242#[derive(Debug, Clone)]
243/// Wraps the corresponding Model I/O scattering function counterpart.
244pub struct ScatteringFunction {
245    handle: ObjectHandle,
246}
247
248impl Named for ScatteringFunction {
249    fn name(&self) -> Option<String> {
250        self.name()
251    }
252
253    fn set_name(&self, name: &str) -> Result<()> {
254        self.set_name(name)
255    }
256}
257
258impl ScatteringFunction {
259    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O scattering function counterpart.
260    pub fn new() -> Result<Self> {
261        let mut out_scattering = ptr::null_mut();
262        let mut out_error = ptr::null_mut();
263        // SAFETY: The unsafe operation is valid in this context.
264        let status = unsafe { ffi::mdl_scattering_function_new(&mut out_scattering, &mut out_error) };
265        crate::util::status_result(status, out_error)?;
266        Ok(Self::from_handle(required_handle(
267            out_scattering,
268            "MDLScatteringFunction",
269        )?))
270    }
271
272    /// Builds this wrapper from the retained handle of the wrapped Model I/O scattering function counterpart.
273    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
274        Self { handle }
275    }
276
277    /// Returns the opaque pointer used to call the wrapped Model I/O scattering function counterpart.
278    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
279        self.handle.as_ptr()
280    }
281
282    fn property(&self, code: u32) -> Option<MaterialProperty> {
283        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
284        let ptr = unsafe { ffi::mdl_scattering_function_property(self.handle.as_ptr(), code) };
285        // SAFETY: The unsafe operation is valid in this context.
286        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
287    }
288
289    #[must_use]
290    /// Calls the corresponding Model I/O method on the wrapped Model I/O scattering function counterpart.
291    pub fn name(&self) -> Option<String> {
292        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
293        take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
294    }
295
296    /// Calls the corresponding Model I/O method on the wrapped Model I/O scattering function counterpart.
297    pub fn set_name(&self, name: &str) -> Result<()> {
298        let name = c_string(name)?;
299        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
300        unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
301        Ok(())
302    }
303
304    #[must_use]
305    /// Calls the corresponding Model I/O method on the wrapped Model I/O scattering function counterpart.
306    pub fn base_color(&self) -> Option<MaterialProperty> {
307        self.property(scattering_property_code::BASE_COLOR)
308    }
309
310    #[must_use]
311    /// Calls the corresponding Model I/O method on the wrapped Model I/O scattering function counterpart.
312    pub fn emission(&self) -> Option<MaterialProperty> {
313        self.property(scattering_property_code::EMISSION)
314    }
315
316    #[must_use]
317    /// Calls the corresponding Model I/O method on the wrapped Model I/O scattering function counterpart.
318    pub fn specular(&self) -> Option<MaterialProperty> {
319        self.property(scattering_property_code::SPECULAR)
320    }
321
322    #[must_use]
323    /// Calls the corresponding Model I/O method on the wrapped Model I/O scattering function counterpart.
324    pub fn material_index_of_refraction(&self) -> Option<MaterialProperty> {
325        self.property(scattering_property_code::MATERIAL_INDEX_OF_REFRACTION)
326    }
327
328    #[must_use]
329    /// Calls the corresponding Model I/O method on the wrapped Model I/O scattering function counterpart.
330    pub fn interface_index_of_refraction(&self) -> Option<MaterialProperty> {
331        self.property(scattering_property_code::INTERFACE_INDEX_OF_REFRACTION)
332    }
333
334    #[must_use]
335    /// Calls the corresponding Model I/O method on the wrapped Model I/O scattering function counterpart.
336    pub fn normal(&self) -> Option<MaterialProperty> {
337        self.property(scattering_property_code::NORMAL)
338    }
339
340    #[must_use]
341    /// Calls the corresponding Model I/O method on the wrapped Model I/O scattering function counterpart.
342    pub fn ambient_occlusion(&self) -> Option<MaterialProperty> {
343        self.property(scattering_property_code::AMBIENT_OCCLUSION)
344    }
345
346    #[must_use]
347    /// Calls the corresponding Model I/O method on the wrapped Model I/O scattering function counterpart.
348    pub fn ambient_occlusion_scale(&self) -> Option<MaterialProperty> {
349        self.property(scattering_property_code::AMBIENT_OCCLUSION_SCALE)
350    }
351
352    #[must_use]
353    /// Calls the corresponding Model I/O method on the wrapped Model I/O scattering function counterpart.
354    pub fn as_physically_plausible_scattering_function(
355        &self,
356    ) -> Option<PhysicallyPlausibleScatteringFunction> {
357        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
358        (unsafe { ffi::mdl_scattering_function_is_physically_plausible(self.handle.as_ptr()) != 0 })
359            .then(|| PhysicallyPlausibleScatteringFunction::from_handle(self.handle.clone()))
360    }
361}
362
363#[derive(Debug, Clone)]
364/// Wraps the corresponding Model I/O physically plausible scattering function counterpart.
365pub struct PhysicallyPlausibleScatteringFunction {
366    handle: ObjectHandle,
367}
368
369impl Named for PhysicallyPlausibleScatteringFunction {
370    fn name(&self) -> Option<String> {
371        self.name()
372    }
373
374    fn set_name(&self, name: &str) -> Result<()> {
375        self.set_name(name)
376    }
377}
378
379impl PhysicallyPlausibleScatteringFunction {
380    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O physically plausible scattering function counterpart.
381    pub fn new() -> Result<Self> {
382        let mut out_scattering = ptr::null_mut();
383        let mut out_error = ptr::null_mut();
384        // SAFETY: The unsafe operation is valid in this context.
385        let status = unsafe {
386            ffi::mdl_physically_plausible_scattering_function_new(
387                &mut out_scattering,
388                &mut out_error,
389            )
390        };
391        crate::util::status_result(status, out_error)?;
392        Ok(Self::from_handle(required_handle(
393            out_scattering,
394            "MDLPhysicallyPlausibleScatteringFunction",
395        )?))
396    }
397
398    /// Builds this wrapper from the retained handle of the wrapped Model I/O physically plausible scattering function counterpart.
399    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
400        Self { handle }
401    }
402
403    fn property(&self, code: u32) -> Option<MaterialProperty> {
404        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
405        let ptr = unsafe {
406            ffi::mdl_physically_plausible_scattering_function_property(self.handle.as_ptr(), code)
407        };
408        // SAFETY: The unsafe operation is valid in this context.
409        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
410    }
411
412    #[must_use]
413    /// Calls the corresponding Model I/O method on the wrapped Model I/O physically plausible scattering function counterpart.
414    pub fn version(&self) -> i64 {
415        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
416        unsafe { ffi::mdl_physically_plausible_scattering_function_version(self.handle.as_ptr()) }
417    }
418
419    #[must_use]
420    /// Calls the corresponding Model I/O method on the wrapped Model I/O physically plausible scattering function counterpart.
421    pub fn name(&self) -> Option<String> {
422        self.as_scattering_function().name()
423    }
424
425    /// Calls the corresponding Model I/O method on the wrapped Model I/O physically plausible scattering function counterpart.
426    pub fn set_name(&self, name: &str) -> Result<()> {
427        self.as_scattering_function().set_name(name)
428    }
429
430    #[must_use]
431    /// Calls the corresponding Model I/O method on the wrapped Model I/O physically plausible scattering function counterpart.
432    pub fn subsurface(&self) -> Option<MaterialProperty> {
433        self.property(physically_plausible_scattering_property_code::SUBSURFACE)
434    }
435
436    #[must_use]
437    /// Calls the corresponding Model I/O method on the wrapped Model I/O physically plausible scattering function counterpart.
438    pub fn metallic(&self) -> Option<MaterialProperty> {
439        self.property(physically_plausible_scattering_property_code::METALLIC)
440    }
441
442    #[must_use]
443    /// Calls the corresponding Model I/O method on the wrapped Model I/O physically plausible scattering function counterpart.
444    pub fn specular_amount(&self) -> Option<MaterialProperty> {
445        self.property(physically_plausible_scattering_property_code::SPECULAR_AMOUNT)
446    }
447
448    #[must_use]
449    /// Calls the corresponding Model I/O method on the wrapped Model I/O physically plausible scattering function counterpart.
450    pub fn specular_tint(&self) -> Option<MaterialProperty> {
451        self.property(physically_plausible_scattering_property_code::SPECULAR_TINT)
452    }
453
454    #[must_use]
455    /// Calls the corresponding Model I/O method on the wrapped Model I/O physically plausible scattering function counterpart.
456    pub fn roughness(&self) -> Option<MaterialProperty> {
457        self.property(physically_plausible_scattering_property_code::ROUGHNESS)
458    }
459
460    #[must_use]
461    /// Calls the corresponding Model I/O method on the wrapped Model I/O physically plausible scattering function counterpart.
462    pub fn anisotropic(&self) -> Option<MaterialProperty> {
463        self.property(physically_plausible_scattering_property_code::ANISOTROPIC)
464    }
465
466    #[must_use]
467    /// Calls the corresponding Model I/O method on the wrapped Model I/O physically plausible scattering function counterpart.
468    pub fn anisotropic_rotation(&self) -> Option<MaterialProperty> {
469        self.property(physically_plausible_scattering_property_code::ANISOTROPIC_ROTATION)
470    }
471
472    #[must_use]
473    /// Calls the corresponding Model I/O method on the wrapped Model I/O physically plausible scattering function counterpart.
474    pub fn sheen(&self) -> Option<MaterialProperty> {
475        self.property(physically_plausible_scattering_property_code::SHEEN)
476    }
477
478    #[must_use]
479    /// Calls the corresponding Model I/O method on the wrapped Model I/O physically plausible scattering function counterpart.
480    pub fn sheen_tint(&self) -> Option<MaterialProperty> {
481        self.property(physically_plausible_scattering_property_code::SHEEN_TINT)
482    }
483
484    #[must_use]
485    /// Calls the corresponding Model I/O method on the wrapped Model I/O physically plausible scattering function counterpart.
486    pub fn clearcoat(&self) -> Option<MaterialProperty> {
487        self.property(physically_plausible_scattering_property_code::CLEARCOAT)
488    }
489
490    #[must_use]
491    /// Calls the corresponding Model I/O method on the wrapped Model I/O physically plausible scattering function counterpart.
492    pub fn clearcoat_gloss(&self) -> Option<MaterialProperty> {
493        self.property(physically_plausible_scattering_property_code::CLEARCOAT_GLOSS)
494    }
495
496    #[must_use]
497    /// Calls the corresponding Model I/O method on the wrapped Model I/O physically plausible scattering function counterpart.
498    pub fn as_scattering_function(&self) -> ScatteringFunction {
499        ScatteringFunction::from_handle(self.handle.clone())
500    }
501}
502
503#[derive(Debug, Clone)]
504/// Wraps the corresponding Model I/O material property counterpart.
505pub struct MaterialProperty {
506    handle: ObjectHandle,
507}
508
509impl Named for MaterialProperty {
510    fn name(&self) -> Option<String> {
511        self.name()
512    }
513
514    fn set_name(&self, name: &str) -> Result<()> {
515        self.set_name(name)
516    }
517}
518
519impl MaterialProperty {
520    /// Builds this wrapper from the retained handle of the wrapped Model I/O material property counterpart.
521    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
522        Self { handle }
523    }
524
525    /// Returns the opaque pointer used to call the wrapped Model I/O material property counterpart.
526    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
527        self.handle.as_ptr()
528    }
529
530    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O material property counterpart.
531    pub fn new(name: &str, semantic: MaterialSemantic) -> Result<Self> {
532        let name = c_string(name)?;
533        let mut out_property = ptr::null_mut();
534        let mut out_error = ptr::null_mut();
535        // SAFETY: The unsafe operation is valid in this context.
536        let status = unsafe {
537            ffi::mdl_material_property_new(
538                name.as_ptr(),
539                semantic as u32,
540                &mut out_property,
541                &mut out_error,
542            )
543        };
544        crate::util::status_result(status, out_error)?;
545        Ok(Self::from_handle(required_handle(
546            out_property,
547            "MDLMaterialProperty",
548        )?))
549    }
550
551    #[must_use]
552    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
553    pub fn name(&self) -> Option<String> {
554        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
555        take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
556    }
557
558    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
559    pub fn set_name(&self, name: &str) -> Result<()> {
560        let name = c_string(name)?;
561        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
562        unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
563        Ok(())
564    }
565
566    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
567    pub fn info(&self) -> Result<MaterialPropertyInfo> {
568        parse_json(
569            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
570            unsafe { ffi::mdl_material_property_info_json(self.handle.as_ptr()) },
571            "MDLMaterialProperty",
572        )
573    }
574
575    #[must_use]
576    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
577    pub fn texture(&self) -> Option<Texture> {
578        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
579        let ptr = unsafe { ffi::mdl_material_property_texture(self.handle.as_ptr()) };
580        // SAFETY: The unsafe operation is valid in this context.
581        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Texture::from_handle)
582    }
583
584    #[must_use]
585    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
586    pub fn texture_sampler(&self) -> Option<TextureSampler> {
587        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
588        let ptr = unsafe { ffi::mdl_material_property_texture_sampler(self.handle.as_ptr()) };
589        // SAFETY: The unsafe operation is valid in this context.
590        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(TextureSampler::from_handle)
591    }
592
593    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
594    pub fn set_texture_sampler(&self, sampler: Option<&TextureSampler>) {
595        // SAFETY: The unsafe operation is valid in this context.
596        unsafe {
597            ffi::mdl_material_property_set_texture_sampler(
598                self.handle.as_ptr(),
599                sampler.map_or(ptr::null_mut(), TextureSampler::as_ptr),
600            );
601        };
602    }
603
604    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
605    pub fn set_float(&self, value: f32) {
606        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
607        unsafe { ffi::mdl_material_property_set_float(self.handle.as_ptr(), value) };
608    }
609
610    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
611    pub fn set_float2(&self, value: [f32; 2]) {
612        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
613        unsafe { ffi::mdl_material_property_set_float2(self.handle.as_ptr(), value[0], value[1]) };
614    }
615
616    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
617    pub fn set_float3(&self, value: [f32; 3]) {
618        // SAFETY: The unsafe operation is valid in this context.
619        unsafe {
620            ffi::mdl_material_property_set_float3(
621                self.handle.as_ptr(),
622                value[0],
623                value[1],
624                value[2],
625            );
626        };
627    }
628
629    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
630    pub fn set_float4(&self, value: [f32; 4]) {
631        // SAFETY: The unsafe operation is valid in this context.
632        unsafe {
633            ffi::mdl_material_property_set_float4(
634                self.handle.as_ptr(),
635                value[0],
636                value[1],
637                value[2],
638                value[3],
639            );
640        };
641    }
642
643    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
644    pub fn set_matrix4x4(&self, value: [f32; 16]) {
645        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
646        unsafe { ffi::mdl_material_property_set_matrix4x4(self.handle.as_ptr(), value.as_ptr()) };
647    }
648
649    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
650    pub fn set_string(&self, value: Option<&str>) -> Result<()> {
651        let value = value.map(c_string).transpose()?;
652        // SAFETY: The unsafe operation is valid in this context.
653        unsafe {
654            ffi::mdl_material_property_set_string(
655                self.handle.as_ptr(),
656                value.as_ref().map_or(ptr::null(), |value| value.as_ptr()),
657            );
658        };
659        Ok(())
660    }
661
662    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
663    pub fn set_url(&self, path: Option<impl AsRef<Path>>) -> Result<()> {
664        let path = path
665            .map(|path| path_to_c_string(path.as_ref()))
666            .transpose()?;
667        // SAFETY: The unsafe operation is valid in this context.
668        unsafe {
669            ffi::mdl_material_property_set_url(
670                self.handle.as_ptr(),
671                path.as_ref().map_or(ptr::null(), |path| path.as_ptr()),
672            );
673        };
674        Ok(())
675    }
676
677    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
678    pub fn set_color(&self, color: [f32; 4]) {
679        // SAFETY: The unsafe operation is valid in this context.
680        unsafe {
681            ffi::mdl_material_property_set_color(
682                self.handle.as_ptr(),
683                color[0],
684                color[1],
685                color[2],
686                color[3],
687            );
688        };
689    }
690
691    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property counterpart.
692    pub fn set_luminance(&self, value: f32) {
693        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
694        unsafe { ffi::mdl_material_property_set_luminance(self.handle.as_ptr(), value) };
695    }
696}
697
698#[derive(Debug, Clone)]
699/// Wraps the corresponding Model I/O texture filter counterpart.
700pub struct TextureFilter {
701    handle: ObjectHandle,
702}
703
704impl TextureFilter {
705    /// Builds this wrapper from the retained handle of the wrapped Model I/O texture filter counterpart.
706    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
707        Self { handle }
708    }
709
710    /// Returns the opaque pointer used to call the wrapped Model I/O texture filter counterpart.
711    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
712        self.handle.as_ptr()
713    }
714
715    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O texture filter counterpart.
716    pub fn new() -> Result<Self> {
717        let mut out_filter = ptr::null_mut();
718        let mut out_error = ptr::null_mut();
719        // SAFETY: Output pointers are initialized and managed; FFI function is called safely.
720        let status = unsafe { ffi::mdl_texture_filter_new(&mut out_filter, &mut out_error) };
721        crate::util::status_result(status, out_error)?;
722        Ok(Self::from_handle(required_handle(
723            out_filter,
724            "MDLTextureFilter",
725        )?))
726    }
727
728    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture filter counterpart.
729    pub fn info(&self) -> Result<TextureFilterInfo> {
730        parse_json(
731            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
732            unsafe { ffi::mdl_texture_filter_info_json(self.handle.as_ptr()) },
733            "MDLTextureFilter",
734        )
735    }
736
737    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture filter counterpart.
738    pub fn set_s_wrap_mode(&self, value: MaterialTextureWrapMode) {
739        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
740        unsafe { ffi::mdl_texture_filter_set_s_wrap_mode(self.handle.as_ptr(), value.as_raw()) };
741    }
742
743    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture filter counterpart.
744    pub fn set_t_wrap_mode(&self, value: MaterialTextureWrapMode) {
745        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
746        unsafe { ffi::mdl_texture_filter_set_t_wrap_mode(self.handle.as_ptr(), value.as_raw()) };
747    }
748
749    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture filter counterpart.
750    pub fn set_r_wrap_mode(&self, value: MaterialTextureWrapMode) {
751        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
752        unsafe { ffi::mdl_texture_filter_set_r_wrap_mode(self.handle.as_ptr(), value.as_raw()) };
753    }
754
755    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture filter counterpart.
756    pub fn set_min_filter(&self, value: MaterialTextureFilterMode) {
757        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
758        unsafe { ffi::mdl_texture_filter_set_min_filter(self.handle.as_ptr(), value.as_raw()) };
759    }
760
761    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture filter counterpart.
762    pub fn set_mag_filter(&self, value: MaterialTextureFilterMode) {
763        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
764        unsafe { ffi::mdl_texture_filter_set_mag_filter(self.handle.as_ptr(), value.as_raw()) };
765    }
766
767    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture filter counterpart.
768    pub fn set_mip_filter(&self, value: MaterialMipMapFilterMode) {
769        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
770        unsafe { ffi::mdl_texture_filter_set_mip_filter(self.handle.as_ptr(), value.as_raw()) };
771    }
772}
773
774#[derive(Debug, Clone)]
775/// Wraps the corresponding Model I/O texture sampler counterpart.
776pub struct TextureSampler {
777    handle: ObjectHandle,
778}
779
780impl TextureSampler {
781    /// Builds this wrapper from the retained handle of the wrapped Model I/O texture sampler counterpart.
782    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
783        Self { handle }
784    }
785
786    /// Returns the opaque pointer used to call the wrapped Model I/O texture sampler counterpart.
787    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
788        self.handle.as_ptr()
789    }
790
791    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O texture sampler counterpart.
792    pub fn new() -> Result<Self> {
793        let mut out_sampler = ptr::null_mut();
794        let mut out_error = ptr::null_mut();
795        // SAFETY: Output pointers are initialized and managed; FFI function is called safely.
796        let status = unsafe { ffi::mdl_texture_sampler_new(&mut out_sampler, &mut out_error) };
797        crate::util::status_result(status, out_error)?;
798        Ok(Self::from_handle(required_handle(
799            out_sampler,
800            "MDLTextureSampler",
801        )?))
802    }
803
804    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture sampler counterpart.
805    pub fn info(&self) -> Result<TextureSamplerInfo> {
806        parse_json(
807            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
808            unsafe { ffi::mdl_texture_sampler_info_json(self.handle.as_ptr()) },
809            "MDLTextureSampler",
810        )
811    }
812
813    #[must_use]
814    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture sampler counterpart.
815    pub fn texture(&self) -> Option<Texture> {
816        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
817        let ptr = unsafe { ffi::mdl_texture_sampler_texture(self.handle.as_ptr()) };
818        // SAFETY: The unsafe operation is valid in this context.
819        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Texture::from_handle)
820    }
821
822    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture sampler counterpart.
823    pub fn set_texture(&self, texture: Option<&Texture>) {
824        // SAFETY: The unsafe operation is valid in this context.
825        unsafe {
826            ffi::mdl_texture_sampler_set_texture(
827                self.handle.as_ptr(),
828                texture.map_or(ptr::null_mut(), Texture::as_ptr),
829            );
830        };
831    }
832
833    #[must_use]
834    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture sampler counterpart.
835    pub fn hardware_filter(&self) -> Option<TextureFilter> {
836        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
837        let ptr = unsafe { ffi::mdl_texture_sampler_hardware_filter(self.handle.as_ptr()) };
838        // SAFETY: The unsafe operation is valid in this context.
839        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(TextureFilter::from_handle)
840    }
841
842    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture sampler counterpart.
843    pub fn set_hardware_filter(&self, filter: Option<&TextureFilter>) {
844        // SAFETY: The unsafe operation is valid in this context.
845        unsafe {
846            ffi::mdl_texture_sampler_set_hardware_filter(
847                self.handle.as_ptr(),
848                filter.map_or(ptr::null_mut(), TextureFilter::as_ptr),
849            );
850        };
851    }
852
853    #[must_use]
854    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture sampler counterpart.
855    pub fn transform(&self) -> Option<Transform> {
856        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
857        let ptr = unsafe { ffi::mdl_texture_sampler_transform(self.handle.as_ptr()) };
858        // SAFETY: The unsafe operation is valid in this context.
859        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Transform::from_handle)
860    }
861
862    /// Calls the corresponding Model I/O method on the wrapped Model I/O texture sampler counterpart.
863    pub fn set_transform(&self, transform: Option<&Transform>) {
864        // SAFETY: The unsafe operation is valid in this context.
865        unsafe {
866            ffi::mdl_texture_sampler_set_transform(
867                self.handle.as_ptr(),
868                transform.map_or(ptr::null_mut(), Transform::as_ptr),
869            );
870        };
871    }
872}
873
874#[derive(Debug, Clone)]
875/// Wraps the corresponding Model I/O material property connection counterpart.
876pub struct MaterialPropertyConnection {
877    handle: ObjectHandle,
878}
879
880impl Named for MaterialPropertyConnection {
881    fn name(&self) -> Option<String> {
882        self.name()
883    }
884
885    fn set_name(&self, name: &str) -> Result<()> {
886        self.set_name(name)
887    }
888}
889
890impl MaterialPropertyConnection {
891    /// Builds this wrapper from the retained handle of the wrapped Model I/O material property connection counterpart.
892    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
893        Self { handle }
894    }
895
896    /// Returns the opaque pointer used to call the wrapped Model I/O material property connection counterpart.
897    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
898        self.handle.as_ptr()
899    }
900
901    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O material property connection counterpart.
902    pub fn new(output: &MaterialProperty, input: &MaterialProperty) -> Result<Self> {
903        let mut out_connection = ptr::null_mut();
904        let mut out_error = ptr::null_mut();
905        // SAFETY: The unsafe operation is valid in this context.
906        let status = unsafe {
907            ffi::mdl_material_property_connection_new(
908                output.as_ptr(),
909                input.as_ptr(),
910                &mut out_connection,
911                &mut out_error,
912            )
913        };
914        crate::util::status_result(status, out_error)?;
915        Ok(Self::from_handle(required_handle(
916            out_connection,
917            "MDLMaterialPropertyConnection",
918        )?))
919    }
920
921    #[must_use]
922    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property connection counterpart.
923    pub fn name(&self) -> Option<String> {
924        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
925        take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
926    }
927
928    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property connection counterpart.
929    pub fn set_name(&self, name: &str) -> Result<()> {
930        let name = c_string(name)?;
931        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
932        unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
933        Ok(())
934    }
935
936    #[must_use]
937    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property connection counterpart.
938    pub fn output(&self) -> Option<MaterialProperty> {
939        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
940        let ptr = unsafe { ffi::mdl_material_property_connection_output(self.handle.as_ptr()) };
941        // SAFETY: The unsafe operation is valid in this context.
942        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
943    }
944
945    #[must_use]
946    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property connection counterpart.
947    pub fn input(&self) -> Option<MaterialProperty> {
948        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
949        let ptr = unsafe { ffi::mdl_material_property_connection_input(self.handle.as_ptr()) };
950        // SAFETY: The unsafe operation is valid in this context.
951        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
952    }
953}
954
955#[derive(Debug, Clone)]
956/// Wraps the corresponding Model I/O material property node counterpart.
957pub struct MaterialPropertyNode {
958    handle: ObjectHandle,
959}
960
961impl Named for MaterialPropertyNode {
962    fn name(&self) -> Option<String> {
963        self.name()
964    }
965
966    fn set_name(&self, name: &str) -> Result<()> {
967        self.set_name(name)
968    }
969}
970
971impl MaterialPropertyNode {
972    /// Builds this wrapper from the retained handle of the wrapped Model I/O material property node counterpart.
973    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
974        Self { handle }
975    }
976
977    /// Returns the opaque pointer used to call the wrapped Model I/O material property node counterpart.
978    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
979        self.handle.as_ptr()
980    }
981
982    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O material property node counterpart.
983    pub fn new(inputs: &[&MaterialProperty], outputs: &[&MaterialProperty]) -> Result<Self> {
984        let input_ptrs = inputs
985            .iter()
986            .map(|property| property.as_ptr())
987            .collect::<Vec<_>>();
988        let output_ptrs = outputs
989            .iter()
990            .map(|property| property.as_ptr())
991            .collect::<Vec<_>>();
992        let mut out_node = ptr::null_mut();
993        let mut out_error = ptr::null_mut();
994        // SAFETY: The unsafe operation is valid in this context.
995        let status = unsafe {
996            ffi::mdl_material_property_node_new(
997                input_ptrs.as_ptr(),
998                input_ptrs.len() as u64,
999                output_ptrs.as_ptr(),
1000                output_ptrs.len() as u64,
1001                &mut out_node,
1002                &mut out_error,
1003            )
1004        };
1005        crate::util::status_result(status, out_error)?;
1006        Ok(Self::from_handle(required_handle(
1007            out_node,
1008            "MDLMaterialPropertyNode",
1009        )?))
1010    }
1011
1012    #[must_use]
1013    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property node counterpart.
1014    pub fn name(&self) -> Option<String> {
1015        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
1016        take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
1017    }
1018
1019    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property node counterpart.
1020    pub fn set_name(&self, name: &str) -> Result<()> {
1021        let name = c_string(name)?;
1022        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
1023        unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
1024        Ok(())
1025    }
1026
1027    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property node counterpart.
1028    pub fn inputs(&self) -> Result<Vec<MaterialProperty>> {
1029        array_objects(
1030            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
1031            unsafe { ffi::mdl_material_property_node_inputs(self.handle.as_ptr()) },
1032            "MDLMaterialPropertyNode inputs",
1033            MaterialProperty::from_handle,
1034        )
1035    }
1036
1037    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property node counterpart.
1038    pub fn outputs(&self) -> Result<Vec<MaterialProperty>> {
1039        array_objects(
1040            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
1041            unsafe { ffi::mdl_material_property_node_outputs(self.handle.as_ptr()) },
1042            "MDLMaterialPropertyNode outputs",
1043            MaterialProperty::from_handle,
1044        )
1045    }
1046}
1047
1048#[derive(Debug, Clone)]
1049/// Wraps the corresponding Model I/O material property graph counterpart.
1050pub struct MaterialPropertyGraph {
1051    handle: ObjectHandle,
1052}
1053
1054impl Named for MaterialPropertyGraph {
1055    fn name(&self) -> Option<String> {
1056        self.name()
1057    }
1058
1059    fn set_name(&self, name: &str) -> Result<()> {
1060        self.set_name(name)
1061    }
1062}
1063
1064impl MaterialPropertyGraph {
1065    /// Builds this wrapper from the retained handle of the wrapped Model I/O material property graph counterpart.
1066    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
1067        Self { handle }
1068    }
1069
1070    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O material property graph counterpart.
1071    pub fn new(
1072        nodes: &[&MaterialPropertyNode],
1073        connections: &[&MaterialPropertyConnection],
1074    ) -> Result<Self> {
1075        let node_ptrs = nodes.iter().map(|node| node.as_ptr()).collect::<Vec<_>>();
1076        let connection_ptrs = connections
1077            .iter()
1078            .map(|connection| connection.as_ptr())
1079            .collect::<Vec<_>>();
1080        let mut out_graph = ptr::null_mut();
1081        let mut out_error = ptr::null_mut();
1082        // SAFETY: The unsafe operation is valid in this context.
1083        let status = unsafe {
1084            ffi::mdl_material_property_graph_new(
1085                node_ptrs.as_ptr(),
1086                node_ptrs.len() as u64,
1087                connection_ptrs.as_ptr(),
1088                connection_ptrs.len() as u64,
1089                &mut out_graph,
1090                &mut out_error,
1091            )
1092        };
1093        crate::util::status_result(status, out_error)?;
1094        Ok(Self::from_handle(required_handle(
1095            out_graph,
1096            "MDLMaterialPropertyGraph",
1097        )?))
1098    }
1099
1100    #[must_use]
1101    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property graph counterpart.
1102    pub fn name(&self) -> Option<String> {
1103        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
1104        take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
1105    }
1106
1107    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property graph counterpart.
1108    pub fn set_name(&self, name: &str) -> Result<()> {
1109        let name = c_string(name)?;
1110        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
1111        unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
1112        Ok(())
1113    }
1114
1115    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property graph counterpart.
1116    pub fn evaluate(&self) {
1117        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
1118        unsafe { ffi::mdl_material_property_graph_evaluate(self.handle.as_ptr()) };
1119    }
1120
1121    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property graph counterpart.
1122    pub fn nodes(&self) -> Result<Vec<MaterialPropertyNode>> {
1123        array_objects(
1124            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
1125            unsafe { ffi::mdl_material_property_graph_nodes(self.handle.as_ptr()) },
1126            "MDLMaterialPropertyGraph nodes",
1127            MaterialPropertyNode::from_handle,
1128        )
1129    }
1130
1131    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property graph counterpart.
1132    pub fn connections(&self) -> Result<Vec<MaterialPropertyConnection>> {
1133        array_objects(
1134            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
1135            unsafe { ffi::mdl_material_property_graph_connections(self.handle.as_ptr()) },
1136            "MDLMaterialPropertyGraph connections",
1137            MaterialPropertyConnection::from_handle,
1138        )
1139    }
1140
1141    #[must_use]
1142    /// Calls the corresponding Model I/O method on the wrapped Model I/O material property graph counterpart.
1143    pub fn as_node(&self) -> MaterialPropertyNode {
1144        MaterialPropertyNode::from_handle(self.handle.clone())
1145    }
1146}