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 info(&self) -> Result<MaterialInfo> {
90 parse_json(
91 unsafe { ffi::mdl_material_info_json(self.handle.as_ptr()) },
93 "MDLMaterial",
94 )
95 }
96
97 #[must_use]
98 pub fn count(&self) -> usize {
100 unsafe { ffi::mdl_material_count(self.handle.as_ptr()) as usize }
102 }
103
104 #[must_use]
105 pub fn name(&self) -> Option<String> {
107 take_string(unsafe { ffi::mdl_material_name_string(self.handle.as_ptr()) })
109 }
110
111 pub fn set_name(&self, name: &str) -> Result<()> {
113 let name = c_string(name)?;
114 unsafe { ffi::mdl_material_set_name(self.handle.as_ptr(), name.as_ptr()) };
116 Ok(())
117 }
118
119 #[must_use]
120 pub fn material_face(&self) -> Option<MaterialFace> {
122 MaterialFace::from_raw(unsafe { ffi::mdl_material_material_face(self.handle.as_ptr()) })
124 }
125
126 pub fn set_material_face(&self, face: MaterialFace) {
128 unsafe { ffi::mdl_material_set_material_face(self.handle.as_ptr(), face.as_raw()) };
130 }
131
132 pub fn remove_all_properties(&self) {
134 unsafe { ffi::mdl_material_remove_all_properties(self.handle.as_ptr()) };
136 }
137
138 pub fn load_textures_using_resolver(&self, resolver: &AssetResolver) {
140 unsafe {
142 ffi::mdl_material_load_textures_using_resolver(self.handle.as_ptr(), resolver.as_ptr());
143 };
144 }
145
146 #[must_use]
147 pub fn property(&self, index: usize) -> Option<MaterialProperty> {
149 let ptr = unsafe { ffi::mdl_material_property_at(self.handle.as_ptr(), index as u64) };
151 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
153 }
154
155 pub fn property_named(&self, name: &str) -> Result<Option<MaterialProperty>> {
157 let name = c_string(name)?;
158 let ptr = unsafe { ffi::mdl_material_property_named(self.handle.as_ptr(), name.as_ptr()) };
160 Ok(unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle))
162 }
163
164 #[must_use]
165 pub fn property_with_semantic(&self, semantic: MaterialSemantic) -> Option<MaterialProperty> {
167 let ptr = unsafe {
169 ffi::mdl_material_property_with_semantic(self.handle.as_ptr(), semantic as u32)
170 };
171 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
173 }
174
175 #[must_use]
176 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)]
185pub 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 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
203 Self { handle }
204 }
205
206 pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
208 self.handle.as_ptr()
209 }
210
211 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 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 pub fn name(&self) -> Option<String> {
235 take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
237 }
238
239 pub fn set_name(&self, name: &str) -> Result<()> {
241 let name = c_string(name)?;
242 unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
244 Ok(())
245 }
246
247 pub fn info(&self) -> Result<MaterialPropertyInfo> {
249 parse_json(
250 unsafe { ffi::mdl_material_property_info_json(self.handle.as_ptr()) },
252 "MDLMaterialProperty",
253 )
254 }
255
256 #[must_use]
257 pub fn texture(&self) -> Option<Texture> {
259 let ptr = unsafe { ffi::mdl_material_property_texture(self.handle.as_ptr()) };
261 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Texture::from_handle)
263 }
264
265 #[must_use]
266 pub fn texture_sampler(&self) -> Option<TextureSampler> {
268 let ptr = unsafe { ffi::mdl_material_property_texture_sampler(self.handle.as_ptr()) };
270 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(TextureSampler::from_handle)
272 }
273
274 pub fn set_texture_sampler(&self, sampler: Option<&TextureSampler>) {
276 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 pub fn set_float(&self, value: f32) {
287 unsafe { ffi::mdl_material_property_set_float(self.handle.as_ptr(), value) };
289 }
290
291 pub fn set_float2(&self, value: [f32; 2]) {
293 unsafe { ffi::mdl_material_property_set_float2(self.handle.as_ptr(), value[0], value[1]) };
295 }
296
297 pub fn set_float3(&self, value: [f32; 3]) {
299 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 pub fn set_float4(&self, value: [f32; 4]) {
312 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 pub fn set_matrix4x4(&self, value: [f32; 16]) {
326 unsafe { ffi::mdl_material_property_set_matrix4x4(self.handle.as_ptr(), value.as_ptr()) };
328 }
329
330 pub fn set_string(&self, value: Option<&str>) -> Result<()> {
332 let value = value.map(c_string).transpose()?;
333 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 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 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 pub fn set_color(&self, color: [f32; 4]) {
360 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 pub fn set_luminance(&self, value: f32) {
374 unsafe { ffi::mdl_material_property_set_luminance(self.handle.as_ptr(), value) };
376 }
377}
378
379#[derive(Debug, Clone)]
380pub struct TextureFilter {
382 handle: ObjectHandle,
383}
384
385impl TextureFilter {
386 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
388 Self { handle }
389 }
390
391 pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
393 self.handle.as_ptr()
394 }
395
396 pub fn new() -> Result<Self> {
398 let mut out_filter = ptr::null_mut();
399 let mut out_error = ptr::null_mut();
400 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 pub fn info(&self) -> Result<TextureFilterInfo> {
411 parse_json(
412 unsafe { ffi::mdl_texture_filter_info_json(self.handle.as_ptr()) },
414 "MDLTextureFilter",
415 )
416 }
417
418 pub fn set_s_wrap_mode(&self, value: MaterialTextureWrapMode) {
420 unsafe { ffi::mdl_texture_filter_set_s_wrap_mode(self.handle.as_ptr(), value.as_raw()) };
422 }
423
424 pub fn set_t_wrap_mode(&self, value: MaterialTextureWrapMode) {
426 unsafe { ffi::mdl_texture_filter_set_t_wrap_mode(self.handle.as_ptr(), value.as_raw()) };
428 }
429
430 pub fn set_r_wrap_mode(&self, value: MaterialTextureWrapMode) {
432 unsafe { ffi::mdl_texture_filter_set_r_wrap_mode(self.handle.as_ptr(), value.as_raw()) };
434 }
435
436 pub fn set_min_filter(&self, value: MaterialTextureFilterMode) {
438 unsafe { ffi::mdl_texture_filter_set_min_filter(self.handle.as_ptr(), value.as_raw()) };
440 }
441
442 pub fn set_mag_filter(&self, value: MaterialTextureFilterMode) {
444 unsafe { ffi::mdl_texture_filter_set_mag_filter(self.handle.as_ptr(), value.as_raw()) };
446 }
447
448 pub fn set_mip_filter(&self, value: MaterialMipMapFilterMode) {
450 unsafe { ffi::mdl_texture_filter_set_mip_filter(self.handle.as_ptr(), value.as_raw()) };
452 }
453}
454
455#[derive(Debug, Clone)]
456pub struct TextureSampler {
458 handle: ObjectHandle,
459}
460
461impl TextureSampler {
462 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
464 Self { handle }
465 }
466
467 pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
469 self.handle.as_ptr()
470 }
471
472 pub fn new() -> Result<Self> {
474 let mut out_sampler = ptr::null_mut();
475 let mut out_error = ptr::null_mut();
476 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 pub fn info(&self) -> Result<TextureSamplerInfo> {
487 parse_json(
488 unsafe { ffi::mdl_texture_sampler_info_json(self.handle.as_ptr()) },
490 "MDLTextureSampler",
491 )
492 }
493
494 #[must_use]
495 pub fn texture(&self) -> Option<Texture> {
497 let ptr = unsafe { ffi::mdl_texture_sampler_texture(self.handle.as_ptr()) };
499 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Texture::from_handle)
501 }
502
503 pub fn set_texture(&self, texture: Option<&Texture>) {
505 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 pub fn hardware_filter(&self) -> Option<TextureFilter> {
517 let ptr = unsafe { ffi::mdl_texture_sampler_hardware_filter(self.handle.as_ptr()) };
519 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(TextureFilter::from_handle)
521 }
522
523 pub fn set_hardware_filter(&self, filter: Option<&TextureFilter>) {
525 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 pub fn transform(&self) -> Option<Transform> {
537 let ptr = unsafe { ffi::mdl_texture_sampler_transform(self.handle.as_ptr()) };
539 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Transform::from_handle)
541 }
542
543 pub fn set_transform(&self, transform: Option<&Transform>) {
545 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)]
556pub 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 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
574 Self { handle }
575 }
576
577 pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
579 self.handle.as_ptr()
580 }
581
582 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 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 pub fn name(&self) -> Option<String> {
605 take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
607 }
608
609 pub fn set_name(&self, name: &str) -> Result<()> {
611 let name = c_string(name)?;
612 unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
614 Ok(())
615 }
616
617 #[must_use]
618 pub fn output(&self) -> Option<MaterialProperty> {
620 let ptr = unsafe { ffi::mdl_material_property_connection_output(self.handle.as_ptr()) };
622 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
624 }
625
626 #[must_use]
627 pub fn input(&self) -> Option<MaterialProperty> {
629 let ptr = unsafe { ffi::mdl_material_property_connection_input(self.handle.as_ptr()) };
631 unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
633 }
634}
635
636#[derive(Debug, Clone)]
637pub 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 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
655 Self { handle }
656 }
657
658 pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
660 self.handle.as_ptr()
661 }
662
663 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 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 pub fn name(&self) -> Option<String> {
696 take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
698 }
699
700 pub fn set_name(&self, name: &str) -> Result<()> {
702 let name = c_string(name)?;
703 unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
705 Ok(())
706 }
707
708 pub fn inputs(&self) -> Result<Vec<MaterialProperty>> {
710 array_objects(
711 unsafe { ffi::mdl_material_property_node_inputs(self.handle.as_ptr()) },
713 "MDLMaterialPropertyNode inputs",
714 MaterialProperty::from_handle,
715 )
716 }
717
718 pub fn outputs(&self) -> Result<Vec<MaterialProperty>> {
720 array_objects(
721 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)]
730pub 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 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
748 Self { handle }
749 }
750
751 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 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 pub fn name(&self) -> Option<String> {
784 take_string(unsafe { ffi::mdl_named_name_string(self.handle.as_ptr()) })
786 }
787
788 pub fn set_name(&self, name: &str) -> Result<()> {
790 let name = c_string(name)?;
791 unsafe { ffi::mdl_named_set_name(self.handle.as_ptr(), name.as_ptr()) };
793 Ok(())
794 }
795
796 pub fn evaluate(&self) {
798 unsafe { ffi::mdl_material_property_graph_evaluate(self.handle.as_ptr()) };
800 }
801
802 pub fn nodes(&self) -> Result<Vec<MaterialPropertyNode>> {
804 array_objects(
805 unsafe { ffi::mdl_material_property_graph_nodes(self.handle.as_ptr()) },
807 "MDLMaterialPropertyGraph nodes",
808 MaterialPropertyNode::from_handle,
809 )
810 }
811
812 pub fn connections(&self) -> Result<Vec<MaterialPropertyConnection>> {
814 array_objects(
815 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 pub fn as_node(&self) -> MaterialPropertyNode {
825 MaterialPropertyNode::from_handle(self.handle.clone())
826 }
827}