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 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 let ptr = unsafe { ffi::mdl_array_object_at(array.as_ptr(), index as u64) };
32 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)]
41pub 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 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
59 Self { handle }
60 }
61
62 pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
64 self.handle.as_ptr()
65 }
66
67 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 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 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 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 pub fn info(&self) -> Result<MaterialInfo> {
114 parse_json(
115 unsafe { ffi::mdl_material_info_json(self.handle.as_ptr()) },
117 "MDLMaterial",
118 )
119 }
120
121 #[must_use]
122 pub fn count(&self) -> usize {
124 unsafe { ffi::mdl_material_count(self.handle.as_ptr()) as usize }
126 }
127
128 #[must_use]
129 pub fn name(&self) -> Option<String> {
131 take_string(unsafe { ffi::mdl_material_name_string(self.handle.as_ptr()) })
133 }
134
135 pub fn set_name(&self, name: &str) -> Result<()> {
137 let name = c_string(name)?;
138 unsafe { ffi::mdl_material_set_name(self.handle.as_ptr(), name.as_ptr()) };
140 Ok(())
141 }
142
143 #[must_use]
144 pub fn material_face(&self) -> Option<MaterialFace> {
146 MaterialFace::from_raw(unsafe { ffi::mdl_material_material_face(self.handle.as_ptr()) })
148 }
149
150 pub fn set_material_face(&self, face: MaterialFace) {
152 unsafe { ffi::mdl_material_set_material_face(self.handle.as_ptr(), face.as_raw()) };
154 }
155
156 pub fn remove_all_properties(&self) {
158 unsafe { ffi::mdl_material_remove_all_properties(self.handle.as_ptr()) };
160 }
161
162 pub fn load_textures_using_resolver(&self, resolver: &AssetResolver) {
164 unsafe {
166 ffi::mdl_material_load_textures_using_resolver(self.handle.as_ptr(), resolver.as_ptr());
167 };
168 }
169
170 #[must_use]
171 pub fn property(&self, index: usize) -> Option<MaterialProperty> {
173 let ptr = unsafe { ffi::mdl_material_property_at(self.handle.as_ptr(), index as u64) };
175 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
177 }
178
179 pub fn property_named(&self, name: &str) -> Result<Option<MaterialProperty>> {
181 let name = c_string(name)?;
182 let ptr = unsafe { ffi::mdl_material_property_named(self.handle.as_ptr(), name.as_ptr()) };
184 Ok(unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle))
186 }
187
188 #[must_use]
189 pub fn property_with_semantic(&self, semantic: MaterialSemantic) -> Option<MaterialProperty> {
191 let ptr = unsafe {
193 ffi::mdl_material_property_with_semantic(self.handle.as_ptr(), semantic as u32)
194 };
195 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
197 }
198
199 #[must_use]
200 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 pub fn scattering_function(&self) -> Option<ScatteringFunction> {
210 let ptr = unsafe { ffi::mdl_material_scattering_function(self.handle.as_ptr()) };
212 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)]
243pub 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 pub fn new() -> Result<Self> {
261 let mut out_scattering = ptr::null_mut();
262 let mut out_error = ptr::null_mut();
263 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 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
274 Self { handle }
275 }
276
277 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 let ptr = unsafe { ffi::mdl_scattering_function_property(self.handle.as_ptr(), code) };
285 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
287 }
288
289 #[must_use]
290 pub fn name(&self) -> Option<String> {
292 take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
294 }
295
296 pub fn set_name(&self, name: &str) -> Result<()> {
298 let name = c_string(name)?;
299 unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
301 Ok(())
302 }
303
304 #[must_use]
305 pub fn base_color(&self) -> Option<MaterialProperty> {
307 self.property(scattering_property_code::BASE_COLOR)
308 }
309
310 #[must_use]
311 pub fn emission(&self) -> Option<MaterialProperty> {
313 self.property(scattering_property_code::EMISSION)
314 }
315
316 #[must_use]
317 pub fn specular(&self) -> Option<MaterialProperty> {
319 self.property(scattering_property_code::SPECULAR)
320 }
321
322 #[must_use]
323 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 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 pub fn normal(&self) -> Option<MaterialProperty> {
337 self.property(scattering_property_code::NORMAL)
338 }
339
340 #[must_use]
341 pub fn ambient_occlusion(&self) -> Option<MaterialProperty> {
343 self.property(scattering_property_code::AMBIENT_OCCLUSION)
344 }
345
346 #[must_use]
347 pub fn ambient_occlusion_scale(&self) -> Option<MaterialProperty> {
349 self.property(scattering_property_code::AMBIENT_OCCLUSION_SCALE)
350 }
351
352 #[must_use]
353 pub fn as_physically_plausible_scattering_function(
355 &self,
356 ) -> Option<PhysicallyPlausibleScatteringFunction> {
357 (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)]
364pub 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 pub fn new() -> Result<Self> {
382 let mut out_scattering = ptr::null_mut();
383 let mut out_error = ptr::null_mut();
384 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 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
400 Self { handle }
401 }
402
403 fn property(&self, code: u32) -> Option<MaterialProperty> {
404 let ptr = unsafe {
406 ffi::mdl_physically_plausible_scattering_function_property(self.handle.as_ptr(), code)
407 };
408 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
410 }
411
412 #[must_use]
413 pub fn version(&self) -> i64 {
415 unsafe { ffi::mdl_physically_plausible_scattering_function_version(self.handle.as_ptr()) }
417 }
418
419 #[must_use]
420 pub fn name(&self) -> Option<String> {
422 self.as_scattering_function().name()
423 }
424
425 pub fn set_name(&self, name: &str) -> Result<()> {
427 self.as_scattering_function().set_name(name)
428 }
429
430 #[must_use]
431 pub fn subsurface(&self) -> Option<MaterialProperty> {
433 self.property(physically_plausible_scattering_property_code::SUBSURFACE)
434 }
435
436 #[must_use]
437 pub fn metallic(&self) -> Option<MaterialProperty> {
439 self.property(physically_plausible_scattering_property_code::METALLIC)
440 }
441
442 #[must_use]
443 pub fn specular_amount(&self) -> Option<MaterialProperty> {
445 self.property(physically_plausible_scattering_property_code::SPECULAR_AMOUNT)
446 }
447
448 #[must_use]
449 pub fn specular_tint(&self) -> Option<MaterialProperty> {
451 self.property(physically_plausible_scattering_property_code::SPECULAR_TINT)
452 }
453
454 #[must_use]
455 pub fn roughness(&self) -> Option<MaterialProperty> {
457 self.property(physically_plausible_scattering_property_code::ROUGHNESS)
458 }
459
460 #[must_use]
461 pub fn anisotropic(&self) -> Option<MaterialProperty> {
463 self.property(physically_plausible_scattering_property_code::ANISOTROPIC)
464 }
465
466 #[must_use]
467 pub fn anisotropic_rotation(&self) -> Option<MaterialProperty> {
469 self.property(physically_plausible_scattering_property_code::ANISOTROPIC_ROTATION)
470 }
471
472 #[must_use]
473 pub fn sheen(&self) -> Option<MaterialProperty> {
475 self.property(physically_plausible_scattering_property_code::SHEEN)
476 }
477
478 #[must_use]
479 pub fn sheen_tint(&self) -> Option<MaterialProperty> {
481 self.property(physically_plausible_scattering_property_code::SHEEN_TINT)
482 }
483
484 #[must_use]
485 pub fn clearcoat(&self) -> Option<MaterialProperty> {
487 self.property(physically_plausible_scattering_property_code::CLEARCOAT)
488 }
489
490 #[must_use]
491 pub fn clearcoat_gloss(&self) -> Option<MaterialProperty> {
493 self.property(physically_plausible_scattering_property_code::CLEARCOAT_GLOSS)
494 }
495
496 #[must_use]
497 pub fn as_scattering_function(&self) -> ScatteringFunction {
499 ScatteringFunction::from_handle(self.handle.clone())
500 }
501}
502
503#[derive(Debug, Clone)]
504pub 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 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
522 Self { handle }
523 }
524
525 pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
527 self.handle.as_ptr()
528 }
529
530 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 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 pub fn name(&self) -> Option<String> {
554 take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
556 }
557
558 pub fn set_name(&self, name: &str) -> Result<()> {
560 let name = c_string(name)?;
561 unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
563 Ok(())
564 }
565
566 pub fn info(&self) -> Result<MaterialPropertyInfo> {
568 parse_json(
569 unsafe { ffi::mdl_material_property_info_json(self.handle.as_ptr()) },
571 "MDLMaterialProperty",
572 )
573 }
574
575 #[must_use]
576 pub fn texture(&self) -> Option<Texture> {
578 let ptr = unsafe { ffi::mdl_material_property_texture(self.handle.as_ptr()) };
580 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Texture::from_handle)
582 }
583
584 #[must_use]
585 pub fn texture_sampler(&self) -> Option<TextureSampler> {
587 let ptr = unsafe { ffi::mdl_material_property_texture_sampler(self.handle.as_ptr()) };
589 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(TextureSampler::from_handle)
591 }
592
593 pub fn set_texture_sampler(&self, sampler: Option<&TextureSampler>) {
595 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 pub fn set_float(&self, value: f32) {
606 unsafe { ffi::mdl_material_property_set_float(self.handle.as_ptr(), value) };
608 }
609
610 pub fn set_float2(&self, value: [f32; 2]) {
612 unsafe { ffi::mdl_material_property_set_float2(self.handle.as_ptr(), value[0], value[1]) };
614 }
615
616 pub fn set_float3(&self, value: [f32; 3]) {
618 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 pub fn set_float4(&self, value: [f32; 4]) {
631 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 pub fn set_matrix4x4(&self, value: [f32; 16]) {
645 unsafe { ffi::mdl_material_property_set_matrix4x4(self.handle.as_ptr(), value.as_ptr()) };
647 }
648
649 pub fn set_string(&self, value: Option<&str>) -> Result<()> {
651 let value = value.map(c_string).transpose()?;
652 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 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 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 pub fn set_color(&self, color: [f32; 4]) {
679 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 pub fn set_luminance(&self, value: f32) {
693 unsafe { ffi::mdl_material_property_set_luminance(self.handle.as_ptr(), value) };
695 }
696}
697
698#[derive(Debug, Clone)]
699pub struct TextureFilter {
701 handle: ObjectHandle,
702}
703
704impl TextureFilter {
705 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
707 Self { handle }
708 }
709
710 pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
712 self.handle.as_ptr()
713 }
714
715 pub fn new() -> Result<Self> {
717 let mut out_filter = ptr::null_mut();
718 let mut out_error = ptr::null_mut();
719 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 pub fn info(&self) -> Result<TextureFilterInfo> {
730 parse_json(
731 unsafe { ffi::mdl_texture_filter_info_json(self.handle.as_ptr()) },
733 "MDLTextureFilter",
734 )
735 }
736
737 pub fn set_s_wrap_mode(&self, value: MaterialTextureWrapMode) {
739 unsafe { ffi::mdl_texture_filter_set_s_wrap_mode(self.handle.as_ptr(), value.as_raw()) };
741 }
742
743 pub fn set_t_wrap_mode(&self, value: MaterialTextureWrapMode) {
745 unsafe { ffi::mdl_texture_filter_set_t_wrap_mode(self.handle.as_ptr(), value.as_raw()) };
747 }
748
749 pub fn set_r_wrap_mode(&self, value: MaterialTextureWrapMode) {
751 unsafe { ffi::mdl_texture_filter_set_r_wrap_mode(self.handle.as_ptr(), value.as_raw()) };
753 }
754
755 pub fn set_min_filter(&self, value: MaterialTextureFilterMode) {
757 unsafe { ffi::mdl_texture_filter_set_min_filter(self.handle.as_ptr(), value.as_raw()) };
759 }
760
761 pub fn set_mag_filter(&self, value: MaterialTextureFilterMode) {
763 unsafe { ffi::mdl_texture_filter_set_mag_filter(self.handle.as_ptr(), value.as_raw()) };
765 }
766
767 pub fn set_mip_filter(&self, value: MaterialMipMapFilterMode) {
769 unsafe { ffi::mdl_texture_filter_set_mip_filter(self.handle.as_ptr(), value.as_raw()) };
771 }
772}
773
774#[derive(Debug, Clone)]
775pub struct TextureSampler {
777 handle: ObjectHandle,
778}
779
780impl TextureSampler {
781 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
783 Self { handle }
784 }
785
786 pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
788 self.handle.as_ptr()
789 }
790
791 pub fn new() -> Result<Self> {
793 let mut out_sampler = ptr::null_mut();
794 let mut out_error = ptr::null_mut();
795 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 pub fn info(&self) -> Result<TextureSamplerInfo> {
806 parse_json(
807 unsafe { ffi::mdl_texture_sampler_info_json(self.handle.as_ptr()) },
809 "MDLTextureSampler",
810 )
811 }
812
813 #[must_use]
814 pub fn texture(&self) -> Option<Texture> {
816 let ptr = unsafe { ffi::mdl_texture_sampler_texture(self.handle.as_ptr()) };
818 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Texture::from_handle)
820 }
821
822 pub fn set_texture(&self, texture: Option<&Texture>) {
824 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 pub fn hardware_filter(&self) -> Option<TextureFilter> {
836 let ptr = unsafe { ffi::mdl_texture_sampler_hardware_filter(self.handle.as_ptr()) };
838 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(TextureFilter::from_handle)
840 }
841
842 pub fn set_hardware_filter(&self, filter: Option<&TextureFilter>) {
844 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 pub fn transform(&self) -> Option<Transform> {
856 let ptr = unsafe { ffi::mdl_texture_sampler_transform(self.handle.as_ptr()) };
858 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Transform::from_handle)
860 }
861
862 pub fn set_transform(&self, transform: Option<&Transform>) {
864 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)]
875pub 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 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
893 Self { handle }
894 }
895
896 pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
898 self.handle.as_ptr()
899 }
900
901 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 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 pub fn name(&self) -> Option<String> {
924 take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
926 }
927
928 pub fn set_name(&self, name: &str) -> Result<()> {
930 let name = c_string(name)?;
931 unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
933 Ok(())
934 }
935
936 #[must_use]
937 pub fn output(&self) -> Option<MaterialProperty> {
939 let ptr = unsafe { ffi::mdl_material_property_connection_output(self.handle.as_ptr()) };
941 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
943 }
944
945 #[must_use]
946 pub fn input(&self) -> Option<MaterialProperty> {
948 let ptr = unsafe { ffi::mdl_material_property_connection_input(self.handle.as_ptr()) };
950 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
952 }
953}
954
955#[derive(Debug, Clone)]
956pub 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 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
974 Self { handle }
975 }
976
977 pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
979 self.handle.as_ptr()
980 }
981
982 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 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 pub fn name(&self) -> Option<String> {
1015 take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
1017 }
1018
1019 pub fn set_name(&self, name: &str) -> Result<()> {
1021 let name = c_string(name)?;
1022 unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
1024 Ok(())
1025 }
1026
1027 pub fn inputs(&self) -> Result<Vec<MaterialProperty>> {
1029 array_objects(
1030 unsafe { ffi::mdl_material_property_node_inputs(self.handle.as_ptr()) },
1032 "MDLMaterialPropertyNode inputs",
1033 MaterialProperty::from_handle,
1034 )
1035 }
1036
1037 pub fn outputs(&self) -> Result<Vec<MaterialProperty>> {
1039 array_objects(
1040 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)]
1049pub 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 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
1067 Self { handle }
1068 }
1069
1070 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 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 pub fn name(&self) -> Option<String> {
1103 take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
1105 }
1106
1107 pub fn set_name(&self, name: &str) -> Result<()> {
1109 let name = c_string(name)?;
1110 unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
1112 Ok(())
1113 }
1114
1115 pub fn evaluate(&self) {
1117 unsafe { ffi::mdl_material_property_graph_evaluate(self.handle.as_ptr()) };
1119 }
1120
1121 pub fn nodes(&self) -> Result<Vec<MaterialPropertyNode>> {
1123 array_objects(
1124 unsafe { ffi::mdl_material_property_graph_nodes(self.handle.as_ptr()) },
1126 "MDLMaterialPropertyGraph nodes",
1127 MaterialPropertyNode::from_handle,
1128 )
1129 }
1130
1131 pub fn connections(&self) -> Result<Vec<MaterialPropertyConnection>> {
1133 array_objects(
1134 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 pub fn as_node(&self) -> MaterialPropertyNode {
1144 MaterialPropertyNode::from_handle(self.handle.clone())
1145 }
1146}