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