Skip to main content

modelio/
material.rs

1use std::path::Path;
2use std::ptr;
3
4use crate::error::Result;
5use crate::ffi;
6use crate::handle::ObjectHandle;
7use crate::texture::Texture;
8use crate::types::{MaterialFace, MaterialInfo, MaterialPropertyInfo, MaterialSemantic};
9use crate::util::{c_string, parse_json, path_to_c_string, required_handle, take_string};
10
11#[derive(Debug, Clone)]
12pub struct Material {
13    handle: ObjectHandle,
14}
15
16impl Material {
17    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
18        Self { handle }
19    }
20
21    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
22        self.handle.as_ptr()
23    }
24
25    pub fn new(name: &str, physically_plausible: bool) -> Result<Self> {
26        let name = c_string(name)?;
27        let mut out_material = ptr::null_mut();
28        let mut out_error = ptr::null_mut();
29        let status = unsafe {
30            ffi::mdl_material_new(
31                name.as_ptr(),
32                i32::from(physically_plausible),
33                &mut out_material,
34                &mut out_error,
35            )
36        };
37        crate::util::status_result(status, out_error)?;
38        Ok(Self::from_handle(required_handle(
39            out_material,
40            "MDLMaterial",
41        )?))
42    }
43
44    pub fn info(&self) -> Result<MaterialInfo> {
45        parse_json(
46            unsafe { ffi::mdl_material_info_json(self.handle.as_ptr()) },
47            "MDLMaterial",
48        )
49    }
50
51    #[must_use]
52    pub fn count(&self) -> usize {
53        unsafe { ffi::mdl_material_count(self.handle.as_ptr()) as usize }
54    }
55
56    #[must_use]
57    pub fn name(&self) -> Option<String> {
58        take_string(unsafe { ffi::mdl_material_name_string(self.handle.as_ptr()) })
59    }
60
61    #[must_use]
62    pub fn material_face(&self) -> Option<MaterialFace> {
63        MaterialFace::from_raw(unsafe { ffi::mdl_material_material_face(self.handle.as_ptr()) })
64    }
65
66    pub fn set_material_face(&self, face: MaterialFace) {
67        unsafe { ffi::mdl_material_set_material_face(self.handle.as_ptr(), face.as_raw()) };
68    }
69
70    pub fn remove_all_properties(&self) {
71        unsafe { ffi::mdl_material_remove_all_properties(self.handle.as_ptr()) };
72    }
73
74    #[must_use]
75    pub fn property(&self, index: usize) -> Option<MaterialProperty> {
76        let ptr = unsafe { ffi::mdl_material_property_at(self.handle.as_ptr(), index as u64) };
77        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
78    }
79
80    pub fn property_named(&self, name: &str) -> Result<Option<MaterialProperty>> {
81        let name = c_string(name)?;
82        let ptr = unsafe { ffi::mdl_material_property_named(self.handle.as_ptr(), name.as_ptr()) };
83        Ok(unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle))
84    }
85
86    #[must_use]
87    pub fn property_with_semantic(&self, semantic: MaterialSemantic) -> Option<MaterialProperty> {
88        let ptr = unsafe {
89            ffi::mdl_material_property_with_semantic(self.handle.as_ptr(), semantic as u32)
90        };
91        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MaterialProperty::from_handle)
92    }
93
94    #[must_use]
95    pub fn properties(&self) -> Vec<MaterialProperty> {
96        (0..self.count())
97            .filter_map(|index| self.property(index))
98            .collect()
99    }
100}
101
102#[derive(Debug, Clone)]
103pub struct MaterialProperty {
104    handle: ObjectHandle,
105}
106
107impl MaterialProperty {
108    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
109        Self { handle }
110    }
111
112    pub fn info(&self) -> Result<MaterialPropertyInfo> {
113        parse_json(
114            unsafe { ffi::mdl_material_property_info_json(self.handle.as_ptr()) },
115            "MDLMaterialProperty",
116        )
117    }
118
119    #[must_use]
120    pub fn texture(&self) -> Option<Texture> {
121        let ptr = unsafe { ffi::mdl_material_property_texture(self.handle.as_ptr()) };
122        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Texture::from_handle)
123    }
124
125    pub fn set_float(&self, value: f32) {
126        unsafe { ffi::mdl_material_property_set_float(self.handle.as_ptr(), value) };
127    }
128
129    pub fn set_float2(&self, value: [f32; 2]) {
130        unsafe { ffi::mdl_material_property_set_float2(self.handle.as_ptr(), value[0], value[1]) };
131    }
132
133    pub fn set_float3(&self, value: [f32; 3]) {
134        unsafe {
135            ffi::mdl_material_property_set_float3(
136                self.handle.as_ptr(),
137                value[0],
138                value[1],
139                value[2],
140            );
141        };
142    }
143
144    pub fn set_float4(&self, value: [f32; 4]) {
145        unsafe {
146            ffi::mdl_material_property_set_float4(
147                self.handle.as_ptr(),
148                value[0],
149                value[1],
150                value[2],
151                value[3],
152            );
153        };
154    }
155
156    pub fn set_matrix4x4(&self, value: [f32; 16]) {
157        unsafe { ffi::mdl_material_property_set_matrix4x4(self.handle.as_ptr(), value.as_ptr()) };
158    }
159
160    pub fn set_string(&self, value: Option<&str>) -> Result<()> {
161        let value = value.map(c_string).transpose()?;
162        unsafe {
163            ffi::mdl_material_property_set_string(
164                self.handle.as_ptr(),
165                value.as_ref().map_or(ptr::null(), |value| value.as_ptr()),
166            );
167        };
168        Ok(())
169    }
170
171    pub fn set_url(&self, path: Option<impl AsRef<Path>>) -> Result<()> {
172        let path = path
173            .map(|path| path_to_c_string(path.as_ref()))
174            .transpose()?;
175        unsafe {
176            ffi::mdl_material_property_set_url(
177                self.handle.as_ptr(),
178                path.as_ref().map_or(ptr::null(), |path| path.as_ptr()),
179            );
180        };
181        Ok(())
182    }
183
184    pub fn set_color(&self, color: [f32; 4]) {
185        unsafe {
186            ffi::mdl_material_property_set_color(
187                self.handle.as_ptr(),
188                color[0],
189                color[1],
190                color[2],
191                color[3],
192            );
193        };
194    }
195
196    pub fn set_luminance(&self, value: f32) {
197        unsafe { ffi::mdl_material_property_set_luminance(self.handle.as_ptr(), value) };
198    }
199}