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}