1use std::path::Path;
2use std::ptr;
3
4use crate::error::Result;
5use crate::ffi;
6use crate::handle::ObjectHandle;
7use crate::types::{TextureChannelEncoding, TextureInfo};
8use crate::util::{c_string, parse_json, path_to_c_string, required_handle};
9
10#[derive(Debug, Clone)]
11pub struct Texture {
12 handle: ObjectHandle,
13}
14
15impl Texture {
16 pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
17 Self { handle }
18 }
19
20 pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
21 self.handle.as_ptr()
22 }
23
24 pub fn from_url(path: impl AsRef<Path>, name: Option<&str>) -> Result<Self> {
25 let path = path_to_c_string(path.as_ref())?;
26 let name = name.map(c_string).transpose()?;
27 let mut out_texture = ptr::null_mut();
28 let mut out_error = ptr::null_mut();
29 let status = unsafe {
30 ffi::mdl_url_texture_new(
31 path.as_ptr(),
32 name.as_ref().map_or(ptr::null(), |name| name.as_ptr()),
33 &mut out_texture,
34 &mut out_error,
35 )
36 };
37 crate::util::status_result(status, out_error)?;
38 Ok(Self::from_handle(required_handle(
39 out_texture,
40 "MDLURLTexture",
41 )?))
42 }
43
44 pub fn new_checkerboard(
45 divisions: f32,
46 name: Option<&str>,
47 dimensions: [i32; 2],
48 channel_count: usize,
49 channel_encoding: TextureChannelEncoding,
50 color1: [f32; 4],
51 color2: [f32; 4],
52 ) -> Result<Self> {
53 let name = name.map(c_string).transpose()?;
54 let mut out_texture = ptr::null_mut();
55 let mut out_error = ptr::null_mut();
56 let status = unsafe {
57 ffi::mdl_checkerboard_texture_new(
58 divisions,
59 name.as_ref().map_or(ptr::null(), |name| name.as_ptr()),
60 dimensions[0],
61 dimensions[1],
62 channel_count as u64,
63 channel_encoding as i32,
64 color1[0],
65 color1[1],
66 color1[2],
67 color1[3],
68 color2[0],
69 color2[1],
70 color2[2],
71 color2[3],
72 &mut out_texture,
73 &mut out_error,
74 )
75 };
76 crate::util::status_result(status, out_error)?;
77 Ok(Self::from_handle(required_handle(
78 out_texture,
79 "MDLCheckerboardTexture",
80 )?))
81 }
82
83 pub fn new_color_temperature_gradient(
84 color_temperature1: f32,
85 color_temperature2: f32,
86 name: Option<&str>,
87 dimensions: [i32; 2],
88 ) -> Result<Self> {
89 let name = name.map(c_string).transpose()?;
90 let mut out_texture = ptr::null_mut();
91 let mut out_error = ptr::null_mut();
92 let status = unsafe {
93 ffi::mdl_color_swatch_texture_new_temperature_gradient(
94 color_temperature1,
95 color_temperature2,
96 name.as_ref().map_or(ptr::null(), |name| name.as_ptr()),
97 dimensions[0],
98 dimensions[1],
99 &mut out_texture,
100 &mut out_error,
101 )
102 };
103 crate::util::status_result(status, out_error)?;
104 Ok(Self::from_handle(required_handle(
105 out_texture,
106 "MDLColorSwatchTexture",
107 )?))
108 }
109
110 pub fn new_color_gradient(
111 color1: [f32; 4],
112 color2: [f32; 4],
113 name: Option<&str>,
114 dimensions: [i32; 2],
115 ) -> Result<Self> {
116 let name = name.map(c_string).transpose()?;
117 let mut out_texture = ptr::null_mut();
118 let mut out_error = ptr::null_mut();
119 let status = unsafe {
120 ffi::mdl_color_swatch_texture_new_color_gradient(
121 color1[0],
122 color1[1],
123 color1[2],
124 color1[3],
125 color2[0],
126 color2[1],
127 color2[2],
128 color2[3],
129 name.as_ref().map_or(ptr::null(), |name| name.as_ptr()),
130 dimensions[0],
131 dimensions[1],
132 &mut out_texture,
133 &mut out_error,
134 )
135 };
136 crate::util::status_result(status, out_error)?;
137 Ok(Self::from_handle(required_handle(
138 out_texture,
139 "MDLColorSwatchTexture",
140 )?))
141 }
142
143 pub fn new_vector_noise(
144 smoothness: f32,
145 name: Option<&str>,
146 dimensions: [i32; 2],
147 channel_encoding: TextureChannelEncoding,
148 ) -> Result<Self> {
149 let name = name.map(c_string).transpose()?;
150 let mut out_texture = ptr::null_mut();
151 let mut out_error = ptr::null_mut();
152 let status = unsafe {
153 ffi::mdl_noise_texture_new_vector(
154 smoothness,
155 name.as_ref().map_or(ptr::null(), |name| name.as_ptr()),
156 dimensions[0],
157 dimensions[1],
158 channel_encoding.as_raw(),
159 &mut out_texture,
160 &mut out_error,
161 )
162 };
163 crate::util::status_result(status, out_error)?;
164 Ok(Self::from_handle(required_handle(out_texture, "MDLNoiseTexture")?))
165 }
166
167 pub fn new_scalar_noise(
168 smoothness: f32,
169 name: Option<&str>,
170 dimensions: [i32; 2],
171 channel_count: usize,
172 channel_encoding: TextureChannelEncoding,
173 grayscale: bool,
174 ) -> Result<Self> {
175 let name = name.map(c_string).transpose()?;
176 let mut out_texture = ptr::null_mut();
177 let mut out_error = ptr::null_mut();
178 let status = unsafe {
179 ffi::mdl_noise_texture_new_scalar(
180 smoothness,
181 name.as_ref().map_or(ptr::null(), |name| name.as_ptr()),
182 dimensions[0],
183 dimensions[1],
184 channel_count as u64,
185 channel_encoding.as_raw(),
186 i32::from(grayscale),
187 &mut out_texture,
188 &mut out_error,
189 )
190 };
191 crate::util::status_result(status, out_error)?;
192 Ok(Self::from_handle(required_handle(out_texture, "MDLNoiseTexture")?))
193 }
194
195 pub fn new_cellular_noise(
196 frequency: f32,
197 name: Option<&str>,
198 dimensions: [i32; 2],
199 channel_encoding: TextureChannelEncoding,
200 ) -> Result<Self> {
201 let name = name.map(c_string).transpose()?;
202 let mut out_texture = ptr::null_mut();
203 let mut out_error = ptr::null_mut();
204 let status = unsafe {
205 ffi::mdl_noise_texture_new_cellular(
206 frequency,
207 name.as_ref().map_or(ptr::null(), |name| name.as_ptr()),
208 dimensions[0],
209 dimensions[1],
210 channel_encoding.as_raw(),
211 &mut out_texture,
212 &mut out_error,
213 )
214 };
215 crate::util::status_result(status, out_error)?;
216 Ok(Self::from_handle(required_handle(out_texture, "MDLNoiseTexture")?))
217 }
218
219 pub fn new_normal_map(
220 source_texture: &Self,
221 name: Option<&str>,
222 smoothness: f32,
223 contrast: f32,
224 ) -> Result<Self> {
225 let name = name.map(c_string).transpose()?;
226 let mut out_texture = ptr::null_mut();
227 let mut out_error = ptr::null_mut();
228 let status = unsafe {
229 ffi::mdl_normal_map_texture_new(
230 source_texture.as_ptr(),
231 name.as_ref().map_or(ptr::null(), |name| name.as_ptr()),
232 smoothness,
233 contrast,
234 &mut out_texture,
235 &mut out_error,
236 )
237 };
238 crate::util::status_result(status, out_error)?;
239 Ok(Self::from_handle(required_handle(
240 out_texture,
241 "MDLNormalMapTexture",
242 )?))
243 }
244
245 pub fn new_sky_cube(
246 name: Option<&str>,
247 dimensions: [i32; 2],
248 channel_encoding: TextureChannelEncoding,
249 turbidity: f32,
250 sun_elevation: f32,
251 upper_atmosphere_scattering: f32,
252 ground_albedo: f32,
253 ) -> Result<Self> {
254 let name = name.map(c_string).transpose()?;
255 let mut out_texture = ptr::null_mut();
256 let mut out_error = ptr::null_mut();
257 let status = unsafe {
258 ffi::mdl_sky_cube_texture_new(
259 name.as_ref().map_or(ptr::null(), |name| name.as_ptr()),
260 channel_encoding.as_raw(),
261 dimensions[0],
262 dimensions[1],
263 turbidity,
264 sun_elevation,
265 upper_atmosphere_scattering,
266 ground_albedo,
267 &mut out_texture,
268 &mut out_error,
269 )
270 };
271 crate::util::status_result(status, out_error)?;
272 Ok(Self::from_handle(required_handle(out_texture, "MDLSkyCubeTexture")?))
273 }
274
275 #[allow(clippy::too_many_arguments)]
276 pub fn new_sky_cube_with_azimuth(
277 name: Option<&str>,
278 dimensions: [i32; 2],
279 channel_encoding: TextureChannelEncoding,
280 turbidity: f32,
281 sun_elevation: f32,
282 sun_azimuth: f32,
283 upper_atmosphere_scattering: f32,
284 ground_albedo: f32,
285 ) -> Result<Self> {
286 let name = name.map(c_string).transpose()?;
287 let mut out_texture = ptr::null_mut();
288 let mut out_error = ptr::null_mut();
289 let status = unsafe {
290 ffi::mdl_sky_cube_texture_new_with_azimuth(
291 name.as_ref().map_or(ptr::null(), |name| name.as_ptr()),
292 channel_encoding.as_raw(),
293 dimensions[0],
294 dimensions[1],
295 turbidity,
296 sun_elevation,
297 sun_azimuth,
298 upper_atmosphere_scattering,
299 ground_albedo,
300 &mut out_texture,
301 &mut out_error,
302 )
303 };
304 crate::util::status_result(status, out_error)?;
305 Ok(Self::from_handle(required_handle(out_texture, "MDLSkyCubeTexture")?))
306 }
307
308 pub fn update_sky_cube(&self) {
309 unsafe { ffi::mdl_sky_cube_texture_update(self.as_ptr()) };
310 }
311
312 pub fn info(&self) -> Result<TextureInfo> {
313 parse_json(
314 unsafe { ffi::mdl_texture_info_json(self.handle.as_ptr()) },
315 "MDLTexture",
316 )
317 }
318
319 pub fn write_to_url(&self, path: impl AsRef<Path>) -> Result<()> {
320 let path = path_to_c_string(path.as_ref())?;
321 let mut out_error = ptr::null_mut();
322 let status = unsafe {
323 ffi::mdl_texture_write_to_url(self.handle.as_ptr(), path.as_ptr(), &mut out_error)
324 };
325 crate::util::status_result(status, out_error)
326 }
327
328 fn texel_data(&self, top_left_origin: bool) -> Vec<u8> {
329 let length = unsafe {
330 ffi::mdl_texture_texel_data_length(self.handle.as_ptr(), i32::from(top_left_origin))
331 } as usize;
332 let mut bytes = vec![0_u8; length];
333 if length == 0 {
334 return bytes;
335 }
336 let written = unsafe {
337 ffi::mdl_texture_copy_texel_data(
338 self.handle.as_ptr(),
339 i32::from(top_left_origin),
340 bytes.as_mut_ptr(),
341 bytes.len() as u64,
342 )
343 } as usize;
344 bytes.truncate(written);
345 bytes
346 }
347
348 #[must_use]
349 pub fn texel_data_top_left(&self) -> Vec<u8> {
350 self.texel_data(true)
351 }
352
353 #[must_use]
354 pub fn texel_data_bottom_left(&self) -> Vec<u8> {
355 self.texel_data(false)
356 }
357}