stereokit_rust/tex.rs
1use crate::{
2 StereoKitError,
3 maths::{Bool32T, Vec3},
4 system::{AssetState, IAsset, Log, render_get_skylight, render_get_skytex, render_set_skylight, render_set_skytex},
5 util::{Color32, Color128, Gradient, GradientKey, GradientT, SphericalHarmonics},
6};
7use std::{
8 ffi::{CStr, CString, c_char, c_void},
9 mem::size_of,
10 path::{Path, PathBuf},
11 ptr::{NonNull, null_mut},
12};
13
14bitflags::bitflags! {
15 /// Textures come in various types and flavors! These are bit-flags
16 /// that tell StereoKit what type of texture we want; and how the application
17 /// might use it!
18 /// <https://stereokit.net/Pages/StereoKit/TexType.html>
19 ///
20 /// see also [`Tex`]
21 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
22 #[repr(C)]
23 pub struct TexType: u32 {
24 /// A standard color image; without any generated mip-maps.
25 const ImageNomips = 1 << 0;
26 /// A size sided texture that's used for things like skyboxes;
27 /// environment maps; and reflection probes. It behaves like a texture
28 /// array with 6 textures.
29 const Cubemap = 1 << 1;
30 /// This texture can be rendered to! This is great for textures
31 /// that might be passed in as a target to Renderer.Blit; or other
32 /// such situations.
33 const Rendertarget = 1 << 2;
34 /// This texture contains depth data; not color data!
35 const Depth = 1 << 3;
36 /// This texture will generate mip-maps any time the contents
37 /// change. Mip-maps are a list of textures that are each half the
38 /// size of the one before them! This is used to prevent textures from
39 /// 'sparkling' or aliasing in the distance.
40 const Mips = 1 << 4;
41 /// This texture's data will be updated frequently from the
42 /// CPU (not renders)! This ensures the graphics card stores it
43 /// someplace where writes are easy to do quickly.
44 const Dynamic = 1 << 5;
45 /// A standard color image that also generates mip-maps
46 /// automatically.
47 const Image = Self::ImageNomips.bits() | Self::Mips.bits();
48 }
49}
50impl TexType {
51 pub fn as_u32(&self) -> u32 {
52 self.bits()
53 }
54}
55/// What type of color information will the texture contain? A
56/// good default here is Rgba32.
57/// <https://stereokit.net/Pages/StereoKit/TexFormat.html>
58///
59/// see also [`Tex`] [`crate::system::Renderer`]
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61#[repr(u32)]
62pub enum TexFormat {
63 /// A default zero value for TexFormat! Uninitialized formats will get this value and **** **** up so you know to
64 /// assign it properly :)
65 None = 0,
66 /// Red/Green/Blue/Transparency data channels, at 8 bits per-channel in sRGB color space. This is what you'll
67 /// want most of the time you're dealing with color images! Matches well with the Color32 struct! If you're
68 /// storing normals, rough/metal, or anything else, use Rgba32Linear.
69 RGBA32 = 1,
70 /// Red/Green/Blue/Transparency data channels, at 8 bits per-channel in linear color space. This is what you'll
71 /// want most of the time you're dealing with color data! Matches well with the Color32 struct.
72 RGBA32Linear = 2,
73 /// Blue/Green/Red/Transparency data channels, at 8 bits per-channel in sRGB color space. This is a common swapchain
74 /// format on Windows.
75 BGRA32 = 3,
76 /// Blue/Green/Red/Transparency data channels, at 8 bits per-channel in linear color space. This is a common
77 /// swapchain format on Windows.
78 BGRA32Linear = 4,
79 /// Red/Green/Blue data channels, with 11 bits for R and G, and 10 bits for blue. This is a great presentation format
80 /// for high bit depth displays that still fits in 32 bits! This format has no alpha channel.
81 RG11B10 = 5,
82 /// Red/Green/Blue/Transparency data channels, with 10 bits for R, G, and B, and 2 for alpha. This is a great
83 /// presentation format for high bit depth displays that still fits in 32 bits, and also includes at least a bit of
84 /// transparency!
85 RGB10A2 = 6,
86 /// Red/Green/Blue/Transparency data channels, at 16 bits per-channel! This is not common, but you might encounter
87 /// it with raw photos, or HDR images. The u postfix indicates that the raw color data is stored as an unsigned
88 /// 16 bit integer, which is then normalized into the 0, 1 floating point range on the GPU.
89 RGBA64U = 7,
90 /// Red/Green/Blue/Transparency data channels, at 16 bits per-channel! This is not common, but you might encounter
91 /// it with raw photos, or HDR images. The s postfix indicates that the raw color data is stored as a signed 16 bit
92 /// integer, which is then normalized into the -1, +1 floating point range on the GPU.
93 RGBA64S = 8,
94 /// Red/Green/Blue/Transparency data channels, at 16 bits per-channel! This is not common, but you might encounter
95 /// it with raw photos, or HDR images. The f postfix indicates that the raw color data is stored as 16 bit floats,
96 /// which may be tricky to work with in most languages.
97 RGBA64F = 9,
98 /// Red/Green/Blue/Transparency data channels at 32 bits per-channel! Basically 4 floats per color, which is bonkers
99 /// expensive. Don't use this unless you know -exactly- what you're doing.
100 RGBA128 = 10,
101 /// A single channel of data, with 8 bits per-pixel! This can be great when you're only using one channel, and want
102 /// to reduce memory usage. Values in the shader are always 0.0-1.0.
103 R8 = 11,
104 /// A single channel of data, with 16 bits per-pixel! This is a good format for height maps, since it stores a fair
105 /// bit of information in it. Values in the shader are always 0.0-1.0.
106 /// TODO: remove during major version update, prefer s, f, or u postfixed versions of this format, this item is the
107 /// same as r16u.
108 //R16 = 12,
109 /// A single channel of data, with 16 bits per-pixel! This is a good format for height maps, since it stores a fair
110 /// bit of information in it. The u postfix indicates that the raw color data is stored as an unsigned 16 bit
111 /// integer, which is then normalized into the 0, 1 floating point range on the GPU.
112 R16u = 12,
113 /// A single channel of data, with 16 bits per-pixel! This is a good format for height maps, since it stores a fair
114 /// bit of information in it. The s postfix indicates that the raw color data is stored as a signed 16 bit integer,
115 /// which is then normalized into the -1, +1 floating point range on the GPU.
116 R16s = 13,
117 /// A single channel of data, with 16 bits per-pixel! This is a good format for height maps, since it stores a fair
118 /// bit of information in it. The f postfix indicates that the raw color data is stored as 16 bit floats, which may
119 /// be tricky to work with in most languages.
120 R16f = 14,
121 /// A single channel of data, with 32 bits per-pixel! This basically treats each pixel as a generic float, so you
122 /// can do all sorts of strange and interesting things with this.
123 R32 = 15,
124 /// A depth data format, 24 bits for depth data, and 8 bits to store stencil information! Stencil data can be used
125 /// for things like clipping effects, deferred rendering, or shadow effects.
126 DepthStencil = 16,
127 /// 32 bits of data per depth value! This is pretty detailed, and is excellent for experiences that have a very far
128 /// view distance.
129 Depth32 = 17,
130 /// 16 bits of depth is not a lot, but it can be enough if your far clipping plane is pretty close. If you're seeing
131 /// lots of flickering where two objects overlap, you either need to bring your far clip in, or switch to 32/24 bit
132 /// depth.
133 Depth16 = 18,
134 /// A double channel of data that supports 8 bits for the red channel and 8 bits for the green channel.
135 R8G8 = 19,
136}
137
138/// How does the shader grab pixels from the texture? Or more
139/// specifically, how does the shader grab colors between the provided
140/// pixels? If you'd like an in-depth explanation of these topics, check
141/// out [this exploration of texture filtering]
142/// <https://medium.com/@bgolus/sharper-mipmapping-using-shader-based-supersampling-ed7aadb47bec>
143/// by graphics wizard Ben Golus.
144/// <https://stereokit.net/Pages/StereoKit/TexSample.html>
145///
146/// see also [`Tex`]
147#[derive(Debug, Clone, Copy, PartialEq, Eq)]
148#[repr(u32)]
149pub enum TexSample {
150 /// Use a linear blend between adjacent pixels, this creates a smooth, blurry look when texture resolution is too
151 /// low.
152 Linear = 0,
153 /// Choose the nearest pixel's color! This makes your texture look like pixel art if you're too close.
154 Point = 1,
155 /// This helps reduce texture blurriness when a surface is viewed at an extreme angle!
156 Anisotropic = 2,
157}
158
159/// What happens when the shader asks for a texture coordinate
160/// that's outside the texture?? Believe it or not, this happens plenty
161/// often!
162///<https://stereokit.net/Pages/StereoKit/TexAddress.html>
163///
164/// see also [`Tex`]
165#[derive(Debug, Clone, Copy, PartialEq, Eq)]
166#[repr(u32)]
167pub enum TexAddress {
168 /// Wrap the UV coordinate around to the other side of the texture! This is basically like a looping texture, and
169 /// is an excellent default. If you can see weird bits of color at the edges of your texture, this may be due to
170 /// Wrap blending the color with the other side of the texture, Clamp may be better in such cases.
171 Wrap = 0,
172 /// Clamp the UV coordinates to the edge of the texture! This'll create color streaks that continue to forever. This
173 /// is actually really great for non-looping textures that you know will always be accessed on the 0-1 range.
174 Clamp = 1,
175 /// Like Wrap, but it reflects the image each time! Who needs this? I'm not sure!! But the graphics card can do it,
176 /// so now you can too!
177 Mirror = 2,
178}
179
180/// This is the texture asset class! This encapsulates 2D images, texture arrays, cubemaps, and rendertargets! It can
181/// load any image format that stb_image can, (jpg, png, tga, bmp, psd, gif, hdr, pic, ktx2) plus more later on, and you
182/// can also create textures procedurally.
183/// <https://stereokit.net/Pages/StereoKit/Tex.html>
184///
185/// ### Examples
186/// ```
187/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
188/// use stereokit_rust::{maths::{Vec3, Matrix, Quat}, util::{named_colors,Color32},
189/// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
190///
191/// let tex_left = Tex::from_file("textures/open_gltf.jpeg", true, None)
192/// .expect("tex_left should be created");
193///
194/// let mut tex_right = Tex::gen_color(named_colors::RED, 1, 1, TexType::Image, TexFormat::RGBA32);
195///
196/// let mut tex_back = Tex::gen_particle(128, 128, 0.2, None);
197///
198/// let mut tex_floor = Tex::new(TexType::Image, TexFormat::RGBA32, None);
199///
200/// let plane_mesh = Mesh::generate_plane_up([1.0,1.0], None, true);
201/// let material_left = Material::pbr().tex_copy(tex_left);
202/// let material_right = Material::pbr().tex_copy(tex_right);
203/// let material_back = Material::unlit_clip().tex_copy(tex_back);
204/// let material_floor = Material::pbr().tex_copy(tex_floor);
205///
206/// let transform_left = Matrix::t_r([-0.5, 0.0, 0.0], [0.0, 0.0, 90.0]);
207/// let transform_right = Matrix::t_r([ 0.5, 0.0, 0.0], [0.0, 0.0,-90.0]);
208/// let transform_back = Matrix::t_r([ 0.0, 0.0,-0.5], [90.0, 0.0, 0.0]);
209/// let transform_floor = Matrix::t( [0.0, -0.5, 0.0]);
210///
211/// filename_scr = "screenshots/tex.jpeg";
212/// test_screenshot!( // !!!! Get a proper main loop !!!!
213/// plane_mesh.draw(token, &material_left, transform_left, None, None);
214/// plane_mesh.draw(token, &material_right, transform_right, None, None);
215/// plane_mesh.draw(token, &material_back, transform_back, None, None);
216/// plane_mesh.draw(token, &material_floor, transform_floor, None, None);
217/// );
218/// ```
219/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/tex.jpeg" alt="screenshot" width="200">
220#[repr(C)]
221#[derive(Debug, PartialEq)]
222pub struct Tex(pub NonNull<_TexT>);
223
224impl Drop for Tex {
225 fn drop(&mut self) {
226 unsafe { tex_release(self.0.as_ptr()) };
227 }
228}
229
230impl AsRef<Tex> for Tex {
231 fn as_ref(&self) -> &Tex {
232 self
233 }
234}
235
236/// StereoKit internal type.
237#[repr(C)]
238#[derive(Debug)]
239pub struct _TexT {
240 _unused: [u8; 0],
241}
242
243/// StereoKit ffi type.
244pub type TexT = *mut _TexT;
245
246unsafe impl Send for Tex {}
247unsafe impl Sync for Tex {}
248
249unsafe extern "C" {
250 pub fn tex_find(id: *const c_char) -> TexT;
251 pub fn tex_create(type_: TexType, format: TexFormat) -> TexT;
252 pub fn tex_create_rendertarget(
253 width: i32,
254 height: i32,
255 msaa: i32,
256 color_format: TexFormat,
257 depth_format: TexFormat,
258 ) -> TexT;
259 pub fn tex_create_color32(in_arr_data: *mut Color32, width: i32, height: i32, srgb_data: Bool32T) -> TexT;
260 pub fn tex_create_color128(in_arr_data: *mut Color128, width: i32, height: i32, srgb_data: Bool32T) -> TexT;
261 pub fn tex_create_mem(data: *mut c_void, data_size: usize, srgb_data: Bool32T, load_priority: i32) -> TexT;
262 pub fn tex_create_file(file_utf8: *const c_char, srgb_data: Bool32T, load_priority: i32) -> TexT;
263 pub fn tex_create_file_arr(
264 in_arr_files: *mut *const c_char,
265 file_count: i32,
266 srgb_data: Bool32T,
267 load_priority: i32,
268 ) -> TexT;
269 pub fn tex_create_cubemap_file(cubemap_file: *const c_char, srgb_data: Bool32T, load_priority: i32) -> TexT;
270 pub fn tex_create_cubemap_files(
271 in_arr_cube_face_file_xxyyzz: *mut *const c_char,
272 srgb_data: Bool32T,
273 load_priority: i32,
274 ) -> TexT;
275 pub fn tex_copy(texture: TexT, type_: TexType, format: TexFormat) -> TexT;
276 pub fn tex_gen_mips(texture: TexT) -> Bool32T;
277 pub fn tex_set_id(texture: TexT, id: *const c_char);
278 pub fn tex_get_id(texture: TexT) -> *const c_char;
279 pub fn tex_set_fallback(texture: TexT, fallback: TexT);
280 pub fn tex_set_surface(
281 texture: TexT,
282 native_surface: *mut c_void,
283 type_: TexType,
284 native_fmt: i64,
285 width: i32,
286 height: i32,
287 surface_count: i32,
288 multisample: i32,
289 framebuffer_multisample: i32,
290 owned: Bool32T,
291 );
292 pub fn tex_get_surface(texture: TexT) -> *mut c_void;
293 pub fn tex_addref(texture: TexT);
294 pub fn tex_release(texture: TexT);
295 pub fn tex_asset_state(texture: TexT) -> AssetState;
296 pub fn tex_on_load(
297 texture: TexT,
298 asset_on_load_callback: ::std::option::Option<unsafe extern "C" fn(texture: TexT, context: *mut c_void)>,
299 context: *mut c_void,
300 );
301 pub fn tex_on_load_remove(
302 texture: TexT,
303 asset_on_load_callback: ::std::option::Option<unsafe extern "C" fn(texture: TexT, context: *mut c_void)>,
304 );
305 pub fn tex_set_colors(texture: TexT, width: i32, height: i32, data: *mut c_void);
306 pub fn tex_set_color_arr(
307 texture: TexT,
308 width: i32,
309 height: i32,
310 data: *mut *mut c_void,
311 data_count: i32,
312 out_sh_lighting_info: *mut SphericalHarmonics,
313 multisample: i32,
314 );
315 pub fn tex_set_mem(
316 texture: TexT,
317 data: *mut c_void,
318 data_size: usize,
319 srgb_data: Bool32T,
320 blocking: Bool32T,
321 priority: i32,
322 );
323 pub fn tex_add_zbuffer(texture: TexT, format: TexFormat);
324 pub fn tex_set_zbuffer(texture: TexT, depth_texture: TexT);
325 pub fn tex_get_zbuffer(texture: TexT) -> TexT;
326 pub fn tex_get_data(texture: TexT, out_data: *mut c_void, out_data_size: usize, mip_level: i32);
327 pub fn tex_gen_color(color: Color128, width: i32, height: i32, type_: TexType, format: TexFormat) -> TexT;
328 pub fn tex_gen_particle(width: i32, height: i32, roundness: f32, gradient_linear: GradientT) -> TexT;
329 pub fn tex_gen_cubemap(
330 gradient: GradientT,
331 gradient_dir: Vec3,
332 resolution: i32,
333 out_sh_lighting_info: *mut SphericalHarmonics,
334 ) -> TexT;
335 pub fn tex_gen_cubemap_sh(
336 lookup: *const SphericalHarmonics,
337 face_size: i32,
338 light_spot_size_pct: f32,
339 light_spot_intensity: f32,
340 ) -> TexT;
341 pub fn tex_get_format(texture: TexT) -> TexFormat;
342 pub fn tex_get_width(texture: TexT) -> i32;
343 pub fn tex_get_height(texture: TexT) -> i32;
344 pub fn tex_set_sample(texture: TexT, sample: TexSample);
345 pub fn tex_get_sample(texture: TexT) -> TexSample;
346 pub fn tex_set_address(texture: TexT, address_mode: TexAddress);
347 pub fn tex_get_address(texture: TexT) -> TexAddress;
348 pub fn tex_set_anisotropy(texture: TexT, anisotropy_level: i32);
349 pub fn tex_get_anisotropy(texture: TexT) -> i32;
350 pub fn tex_get_mips(texture: TexT) -> i32;
351 pub fn tex_set_loading_fallback(loading_texture: TexT);
352 pub fn tex_set_error_fallback(error_texture: TexT);
353 pub fn tex_get_cubemap_lighting(cubemap_texture: TexT) -> SphericalHarmonics;
354}
355
356impl IAsset for Tex {
357 // fn id(&mut self, id: impl AsRef<str>) {
358 // self.id(id);
359 // }
360
361 fn get_id(&self) -> &str {
362 self.get_id()
363 }
364}
365
366impl Default for Tex {
367 /// A Default texture may be asked when a Tex creation or find returned an error. [`Tex::error()`] is a good default
368 /// value.
369 fn default() -> Self {
370 Self::error()
371 }
372}
373
374impl Tex {
375 /// Sets up an empty texture container! Fill it with data using SetColors next! Creates a default unique asset Id.
376 /// <https://stereokit.net/Pages/StereoKit/Tex/Tex.html>
377 /// * `texture_type` - What type of texture is it? Just a 2D Image? A Cubemap? Should it have mip-maps?
378 /// * `format` - What information is the texture composed of? 32 bit colors, 64 bit colors, etc.
379 /// * `id` - A unique asset Id for this texture, this is used to find the texture later on, and to reference it.
380 /// if
381 ///
382 /// see also [`tex_create`]
383 /// ### Examples
384 /// ```
385 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
386 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color32, Color128},
387 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
388 ///
389 /// let plane_mesh = Mesh::generate_plane_up([1.0,1.0], None, true);
390 ///
391 /// let mut color_dots = [named_colors::CYAN; 128 * 128];
392 /// let mut tex_left = Tex::new(TexType::Image, TexFormat::RGBA32, Some("tex_left_ID"));
393 /// tex_left.set_colors32(128, 128, &color_dots);
394 ///
395 /// let mut color_dots = [Color128::new(0.5, 0.75, 0.25, 1.0); 128 * 128];
396 /// let mut tex_right = Tex::new(TexType::Image, TexFormat::RGBA128, None);
397 /// tex_right.set_colors128(128, 128, &color_dots);
398 ///
399 /// let material_left = Material::pbr().tex_copy(tex_left);
400 /// let material_right = Material::pbr().tex_copy(tex_right);
401 ///
402 /// let transform_left = Matrix::t_r([-0.5, 0.0, 0.0], [0.0,-45.0, 90.0]);
403 /// let transform_right = Matrix::t_r([ 0.5, 0.0, 0.0], [0.0, 45.0,-90.0]);
404 ///
405 /// test_steps!( // !!!! Get a proper main loop !!!!
406 /// plane_mesh.draw(token, &material_left, transform_left, None, None);
407 /// plane_mesh.draw(token, &material_right, transform_right, None, None);
408 /// );
409 /// ```
410 pub fn new(texture_type: TexType, format: TexFormat, id: Option<&str>) -> Tex {
411 let tex = Tex(NonNull::new(unsafe { tex_create(texture_type, format) }).unwrap());
412 if let Some(id) = id {
413 let c_str = CString::new(id).unwrap();
414 unsafe { tex_set_id(tex.0.as_ptr(), c_str.as_ptr()) };
415 }
416 tex
417 }
418
419 /// Loads an image file stored in memory directly into a texture! Supported formats are: jpg, png, tga, bmp, psd,
420 /// gif, hdr, pic, ktx2.
421 /// Asset Id will be the same as the filename.
422 /// <https://stereokit.net/Pages/StereoKit/Tex/FromMemory.html>
423 /// * `data` - The binary data of an image file, this is NOT a raw RGB color array!
424 /// * `srgb_data` - Is this image color data in sRGB format, or is it normal/metal/rough/data that’s not for direct
425 /// display? sRGB colors get converted to linear color space on the graphics card, so getting this right can have
426 /// a big impact on visuals.
427 /// * `priority` - The priority sort order for this asset in the async loading system. Lower values mean loading
428 /// sooner. If None will be set to 10
429 ///
430 /// see also [`tex_create_mem`]
431 /// ### Examples
432 /// ```
433 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
434 /// use stereokit_rust::{maths::{Vec3, Matrix},
435 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
436 ///
437 /// let plane_mesh = Mesh::generate_plane_up([1.0,1.0], None, true);
438 ///
439 /// let left_data = std::include_bytes!("../assets/textures/open_gltf.jpeg");
440 /// let right_data = std::include_bytes!("../assets/textures/log_viewer.jpeg");
441 ///
442 /// let tex_left = Tex::from_memory(left_data, true, None)
443 /// .expect("open_gltf.jpeg should be loaded");
444 /// let tex_right = Tex::from_memory(right_data, true, None)
445 /// .expect("open_gltf.jpeg should be loaded");
446 ///
447 /// let material_left = Material::pbr().tex_copy(tex_left);
448 /// let material_right = Material::pbr().tex_copy(tex_right);
449 ///
450 /// let transform_left = Matrix::t_r([-0.5, 0.0, 0.0], [0.0,-45.0, 90.0]);
451 /// let transform_right = Matrix::t_r([ 0.5, 0.0, 0.0], [0.0, 45.0,-90.0]);
452 ///
453 /// test_steps!( // !!!! Get a proper main loop !!!!
454 /// plane_mesh.draw(token, &material_left, transform_left, None, None);
455 /// plane_mesh.draw(token, &material_right, transform_right, None, None);
456 /// );
457 /// ```
458 pub fn from_memory(data: &[u8], srgb_data: bool, priority: Option<i32>) -> Result<Tex, StereoKitError> {
459 let priority = priority.unwrap_or(10);
460 Ok(Tex(NonNull::new(unsafe {
461 tex_create_mem(data.as_ptr() as *mut c_void, data.len(), srgb_data as Bool32T, priority)
462 })
463 .ok_or(StereoKitError::TexMemory)?))
464 }
465
466 /// Loads an image file directly into a texture! Supported formats are: jpg, png, tga, bmp, psd, gif, hdr, pic, ktx2.
467 /// Asset Id will be the same as the filename.
468 /// <https://stereokit.net/Pages/StereoKit/Tex/FromFile.html>
469 /// * `file_utf8` - An absolute filename, or a filename relative to the assets folder. Supports jpg, png, tga, bmp,
470 /// psd, gif, hdr, pic, ktx2.
471 /// * `srgb_data` - Is this image color data in sRGB format, or is it normal/metal/rough/data that’s not for direct
472 /// display? sRGB colors get converted to linear color space on the graphics card, so getting this right can have
473 /// a big impact on visuals.
474 /// * `priority` - The priority sort order for this asset in the async loading system. Lower values mean loading
475 /// sooner. If None will be set to 10
476 ///
477 /// see also [`tex_create_file`] [`Tex::get_asset_state`] [`crate::material::Material::tex_file_copy`]
478 /// ### Examples
479 /// ```
480 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
481 /// use stereokit_rust::{maths::{Vec3, Matrix}, system::AssetState,
482 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
483 ///
484 /// let plane_mesh = Mesh::generate_plane_up([1.0,1.0], None, true);
485 ///
486 /// let tex_left = Tex::from_file("textures/open_gltf.jpeg", true, Some(9999))
487 /// .expect("tex_left should be created");
488 /// let tex_right = Tex::from_file("textures/log_viewer.jpeg", true, Some(9999))
489 /// .expect("tex_right should be created");
490 /// let tex_floor = Tex::from_file("not a file so we'll have error tex", true, Some(9999))
491 /// .expect("tex_error should be loaded");
492 ///
493 /// let material_left = Material::pbr().tex_copy(&tex_left);
494 /// let material_right = Material::pbr().tex_copy(&tex_right);
495 /// let material_floor = Material::pbr().tex_copy(&tex_floor);
496 ///
497 /// let transform_left = Matrix::t_r([-0.5, 0.0, 0.0], [0.0,-45.0, 90.0]);
498 /// let transform_right = Matrix::t_r([ 0.5, 0.0, 0.0], [0.0, 45.0,-90.0]);
499 /// let transform_floor = Matrix::t( [0.0, -0.5, 0.0]);
500 ///
501 /// filename_scr = "screenshots/tex_from_file.jpeg";
502 /// test_screenshot!( // !!!! Get a proper main loop !!!!
503 ///
504 /// // We ensure to have the Tex loaded for the screenshot.
505 /// if tex_left.get_asset_state() != AssetState::Loaded
506 /// || tex_right.get_asset_state() != AssetState::Loaded { iter -= 1; }
507 ///
508 /// plane_mesh.draw(token, &material_left, transform_left, None, None);
509 /// plane_mesh.draw(token, &material_right, transform_right, None, None);
510 /// plane_mesh.draw(token, &material_floor, transform_floor, None, None);
511 /// );
512 /// assert_eq!(tex_left.get_asset_state(), AssetState::Loaded);
513 /// assert_eq!(tex_right.get_asset_state(), AssetState::Loaded);
514 /// assert_eq!(tex_floor.get_asset_state(), AssetState::NotFound);
515 /// ```
516 /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/tex_from_file.jpeg" alt="screenshot" width="200">
517 pub fn from_file(
518 file_utf8: impl AsRef<Path>,
519 srgb_data: bool,
520 priority: Option<i32>,
521 ) -> Result<Tex, StereoKitError> {
522 let priority = priority.unwrap_or(10);
523 let path_buf = file_utf8.as_ref().to_path_buf();
524 let c_str = CString::new(
525 path_buf
526 .clone()
527 .to_str()
528 .ok_or(StereoKitError::TexFile(path_buf.clone(), "CString conversion".to_string()))?,
529 )?;
530 Ok(Tex(NonNull::new(unsafe { tex_create_file(c_str.as_ptr(), srgb_data as Bool32T, priority) })
531 .ok_or(StereoKitError::TexFile(path_buf, "tex_create failed".to_string()))?))
532 }
533
534 /// Loads an array of image files directly into a single array texture! Array textures are often useful for shader
535 /// effects, layering, material merging, weird stuff, and will generally need a specific shader to support it.
536 /// Supported formats are: jpg, png, tga, bmp, psd, gif, hdr, pic, ktx2. Asset Id will be the hash of all the
537 /// filenames merged consecutively.
538 /// <https://stereokit.net/Pages/StereoKit/Tex/FromFiles.html>
539 /// * `files_utf8` - An absolute filenames, or filenames relative to the assets folder. Supports jpg, png, tga, bmp,
540 /// psd, gif, hdr, pic, ktx2.
541 /// * `srgb_data` - Is this image color data in sRGB format, or is it normal/metal/rough/data that’s not for direct
542 /// display? sRGB colors get converted to linear color space on the graphics card, so getting this right can have
543 /// a big impact on visuals.
544 /// * `priority` - The priority sort order for this asset in the async loading system. Lower values mean loading
545 /// sooner. If None will be set to 10
546 ///
547 /// see also [`tex_create_file`]
548 /// ### Examples
549 /// ```
550 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
551 /// use stereokit_rust::{maths::{Vec3, Matrix},
552 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
553 ///
554 /// let plane_mesh = Mesh::generate_plane_up([1.0,1.0], None, true);
555 ///
556 /// let tex = Tex::from_files(&["textures/open_gltf.jpeg",
557 /// "textures/log_viewer.jpeg"], true, Some(100))
558 /// .expect("tex should be created");
559 ///
560 /// let material = Material::pbr().tex_copy(tex);
561 ///
562 /// let transform = Matrix::t_r([-0.5, 0.0, 0.0], [0.0, -45.0, 90.0]);
563 ///
564 /// test_steps!( // !!!! Get a proper main loop !!!!
565 /// plane_mesh.draw(token, &material, transform, None, None);
566 /// );
567 /// ```
568 pub fn from_files<P: AsRef<Path>>(
569 files_utf8: &[P],
570 srgb_data: bool,
571 priority: Option<i32>,
572 ) -> Result<Tex, StereoKitError> {
573 let priority = priority.unwrap_or(10);
574 let mut c_files = Vec::new();
575 for path in files_utf8 {
576 let path = path.as_ref();
577 let path_buf = path.to_path_buf();
578 let c_str =
579 CString::new(path.to_str().ok_or(StereoKitError::TexCString(path_buf.to_str().unwrap().to_owned()))?)?;
580 c_files.push(c_str);
581 }
582 let mut c_files_ptr = Vec::new();
583 for str in c_files.iter() {
584 c_files_ptr.push(str.as_ptr());
585 }
586 let in_arr_files_cstr = c_files_ptr.as_mut_slice().as_mut_ptr();
587 let tex = Tex(NonNull::new(unsafe {
588 tex_create_file_arr(in_arr_files_cstr, files_utf8.len() as i32, srgb_data as Bool32T, priority)
589 })
590 .ok_or(StereoKitError::TexFile(
591 PathBuf::from(r"one_of_many_files"),
592 "tex_create_file_arr failed".to_string(),
593 ))?);
594 Ok(tex)
595 }
596
597 /// Creates a texture and sets the texture’s pixels using a color array! This will be an image of type TexType.Image,
598 /// and a format of TexFormat.Rgba32 or TexFormat.Rgba32Linear depending on the value of the sRGBData parameter.
599 /// <https://stereokit.net/Pages/StereoKit/Tex/FromColors.html>
600 /// * `colors` - An array of 32 bit colors, should be a length of width*height.
601 /// * `width` - Width in pixels of the texture. Powers of two are generally best!
602 /// * `height` - Height in pixels of the texture. Powers of two are generally best!
603 /// * `srgb_data` - s this image color data in sRGB format, or is it normal/metal/rough/data that’s not for direct
604 /// display? sRGB colors get converted to linear color space on the graphics card, so getting this right can have
605 /// a big impact on visuals.
606 ///
607 /// see also [`tex_create_color32`] [`Tex::gen_color`]
608 /// ### Examples
609 /// ```
610 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
611 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color32},
612 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
613 ///
614 /// let plane_mesh = Mesh::generate_plane_up([1.0,1.0], None, true);
615 ///
616 /// let color_dots = [named_colors::RED; 128 * 128];
617 /// let tex = Tex::from_color32(&color_dots, 128, 128, true)
618 /// .expect("Tex should be created");
619 ///
620 /// let material = Material::pbr().tex_copy(tex);
621 ///
622 /// let transform = Matrix::t_r([-0.5, 0.0, 0.0], [0.0, -45.0, 90.0]);
623 ///
624 /// test_steps!( // !!!! Get a proper main loop !!!!
625 /// plane_mesh.draw(token, &material, transform, None, None);
626 /// );
627 /// ```
628 pub fn from_color32(
629 colors: &[Color32],
630 width: usize,
631 height: usize,
632 srgb_data: bool,
633 ) -> Result<Tex, StereoKitError> {
634 if width * height != { colors }.len() {
635 return Err(StereoKitError::TexColor(
636 format!("{}x{} differ from {}", height, width, { colors }.len()),
637 "tex_create_color32 failed".to_string(),
638 ));
639 }
640 Ok(Tex(NonNull::new(unsafe {
641 tex_create_color32(colors.as_ptr() as *mut Color32, width as i32, height as i32, srgb_data as i32)
642 })
643 .ok_or(StereoKitError::TexColor(
644 format!("{height}x{width}"),
645 "tex_create_color32 failed".to_string(),
646 ))?))
647 }
648
649 /// Creates a texture and sets the texture’s pixels using a color array! Color values are converted to 32 bit colors,
650 /// so this means a memory allocation and conversion. Prefer the Color32 overload for performance, or create an empty
651 /// Texture and use SetColors for more flexibility. This will be an image of type TexType.Image, and a format of
652 /// TexFormat. Rgba32 or TexFormat.Rgba32Linear depending on the value of the sRGBData parameter.
653 /// <https://stereokit.net/Pages/StereoKit/Tex/FromColors.html>
654 /// * `colors` - An array of 128 bit colors, should be a length of width*height.
655 /// * `width` - Width in pixels of the texture. Powers of two are generally best!
656 /// * `height` - Height in pixels of the texture. Powers of two are generally best!
657 /// * `srgb_data` - s this image color data in sRGB format, or is it normal/metal/rough/data that’s not for direct
658 /// display? sRGB colors get converted to linear color space on the graphics card, so getting this right can have
659 /// a big impact on visuals.
660 ///
661 /// Important: The color conversion from 128 to 32 may crash if the data do not contains color128.
662 ///
663 /// see also [`tex_create_color128`] [`Tex::gen_color()`]
664 /// ### Examples
665 /// ```
666 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
667 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color128},
668 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
669 ///
670 /// let plane_mesh = Mesh::generate_plane_up([1.0,1.0], None, true);
671 ///
672 /// let color_dots = [Color128::new(0.1, 0.2, 0.5, 1.0); 128 * 128];
673 /// let tex = Tex::from_color128(&color_dots, 128, 128, true)
674 /// .expect("Tex should be created");
675 ///
676 /// let material = Material::pbr().tex_copy(tex);
677 ///
678 /// let transform = Matrix::t_r([-0.5, 0.0, 0.0], [0.0, -45.0, 90.0]);
679 ///
680 /// test_steps!( // !!!! Get a proper main loop !!!!
681 /// plane_mesh.draw(token, &material, transform, None, None);
682 /// );
683 /// ```
684 pub fn from_color128(
685 colors: &[Color128],
686 width: usize,
687 height: usize,
688 srgb_data: bool,
689 ) -> Result<Tex, StereoKitError> {
690 if width * height != { colors }.len() {
691 return Err(StereoKitError::TexColor(
692 format!("{}x{} differ from {}", height, width, { colors }.len()),
693 "tex_create_color128 failed".to_string(),
694 ));
695 }
696 Ok(Tex(NonNull::new(unsafe {
697 tex_create_color128(colors.as_ptr() as *mut Color128, width as i32, height as i32, srgb_data as i32)
698 })
699 .ok_or(StereoKitError::TexColor(
700 format!("{height}x{width}"),
701 "tex_create_color128 failed".to_string(),
702 ))?))
703 }
704
705 /// This will assemble a texture ready for rendering to! It creates a render target texture with no mip maps and a
706 /// depth buffer attached.
707 /// <https://stereokit.net/Pages/StereoKit/Tex/RenderTarget.html>
708 /// * `width` - in pixels
709 /// * `height` - in pixels
710 /// * `multisample` - Multisample level, or MSAA. This should be 1, 2, 4, 8, or 16. The results will have moother
711 /// edges with higher values, but will cost more RAM and time to render. Note that GL platforms cannot trivially
712 /// draw a multisample > 1 texture in a shader. If this is None, the default is 1.
713 /// * `color_format` - The format of the color surface. If this is None, the default is RGBA32.
714 /// * `depth_format` - The format of the depth buffer. If this is TexFormat::None, no depth buffer will be attached
715 /// to this. If this is None, the default is Depth16.
716 /// rendertarget.
717 ///
718 /// see also [`tex_create_rendertarget`]
719 ///
720 /// see also [`tex_get_data`]
721 /// ### Examples
722 /// ```
723 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
724 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color32},
725 /// system::Renderer,
726 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
727 ///
728 /// let plane_mesh = Mesh::generate_plane_up([1.0,1.0], None, true);
729 ///
730 /// let tex = Tex::render_target(128, 128, Some(2), Some(TexFormat::RGBA32), None)
731 /// .expect("Tex should be created");
732 ///
733 /// let material = Material::pbr().tex_copy(&tex);
734 ///
735 /// let transform = Matrix::t_r([-0.5, 0.0, 0.0], [0.0, -45.0, 90.0]);
736 ///
737 /// Renderer::blit(&tex, &material);
738 /// ```
739 pub fn render_target(
740 width: usize,
741 height: usize,
742 multisample: Option<i32>,
743 color_format: Option<TexFormat>,
744 depth_format: Option<TexFormat>,
745 ) -> Result<Tex, StereoKitError> {
746 let multisample = multisample.unwrap_or(1);
747 let color_format = color_format.unwrap_or(TexFormat::RGBA32);
748 let depth_format = depth_format.unwrap_or(TexFormat::Depth16);
749 Ok(Tex(NonNull::new(unsafe {
750 tex_create_rendertarget(width as i32, height as i32, multisample, color_format, depth_format)
751 })
752 .ok_or(StereoKitError::TexRenderTarget(
753 format!("{height}x{width}"),
754 "tex_create_rendertarget failed".to_string(),
755 ))?))
756 }
757
758 /// This generates a solid color texture of the given dimensions. Can be quite nice for creating placeholder textures!
759 /// Make sure to match linear/gamma colors with the correct format.
760 /// <https://stereokit.net/Pages/StereoKit/Tex/GenColor.html>
761 /// * `color` - The color to use for the texture. This is interpreted slightly differently based on what TexFormat
762 /// gets used.
763 /// * `width` - Width of the final texture, in pixels.
764 /// * `height` - Height of the final texture, in pixels.
765 /// * `tex_type` - Not all types here are applicable, but TexType.Image or TexType::ImageNomips are good options here.
766 /// * `format` - Not all formats are supported, but this does support a decent range. The provided color is
767 /// interpreted slightly different depending on this format.
768 ///
769 /// see also [`tex_gen_color`]
770 /// ### Examples
771 /// ```
772 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
773 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color128},
774 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
775 ///
776 /// let plane_mesh = Mesh::generate_plane_up([1.0,1.0], None, true);
777 ///
778 /// let tex_err = Tex::gen_color(named_colors::RED, 128, 128, TexType::Image, TexFormat::RGBA32);
779 /// Tex::set_error_fallback(&tex_err);
780 ///
781 /// let tex = Tex::gen_color(Color128::new(0.1, 0.2, 0.5, 1.0), 128, 128, TexType::Image, TexFormat::RGBA128);
782 ///
783 /// let material = Material::pbr().tex_copy(tex);
784 ///
785 /// let transform = Matrix::t_r([-0.5, 0.0, 0.0], [0.0, -45.0, 90.0]);
786 ///
787 /// test_steps!( // !!!! Get a proper main loop !!!!
788 /// plane_mesh.draw(token, &material, transform, None, None);
789 /// );
790 /// ```
791 pub fn gen_color(color: impl Into<Color128>, width: i32, height: i32, tex_type: TexType, format: TexFormat) -> Tex {
792 Tex(NonNull::new(unsafe { tex_gen_color(color.into(), width, height, tex_type, format) }).unwrap())
793 }
794
795 /// Generates a ‘radial’ gradient that works well for particles, blob shadows, glows, or various other things.
796 /// The roundness can be used to change the shape from round, ‘1’, to star-like, ‘0’. Default color is transparent white to opaque white,
797 /// but this can be configured by providing a Gradient of your own.
798 /// <https://stereokit.net/Pages/StereoKit/Tex/GenParticle.html>
799 /// * `width` - Width of the final texture, in pixels.
800 /// * `height` - Height of the final texture, in pixels.
801 /// * `gradient_linear` : A color gradient that starts with the background/outside at 0, and progresses to the center
802 /// at 1. If None, will use a white gradient.
803 ///
804 /// see also [`tex_gen_particle`]
805 /// ### Examples
806 /// ```
807 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
808 /// use stereokit_rust::{maths::{Vec3, Matrix, Quat},
809 /// util::{named_colors, Gradient, GradientKey, Color128},
810 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
811 ///
812 /// let mut keys = [
813 /// GradientKey::new(Color128::BLACK_TRANSPARENT, 0.0),
814 /// GradientKey::new(named_colors::RED, 0.1),
815 /// GradientKey::new(named_colors::CYAN, 0.4),
816 /// GradientKey::new(named_colors::YELLOW, 0.5),
817 /// GradientKey::new(Color128::BLACK, 0.7)];
818 ///
819 /// let tex_back = Tex::gen_particle(128, 128, 0.15, Some(Gradient::new(Some(&keys))));
820 /// let tex_floor = Tex::gen_particle(128, 128, 0.3, Some(Gradient::new(Some(&keys))));
821 /// let tex_right = Tex::gen_particle(128, 128, 0.6, Some(Gradient::new(Some(&keys))));
822 /// let tex_left = Tex::gen_particle(128, 128, 0.9, Some(Gradient::new(Some(&keys))));
823 ///
824 /// let plane_mesh = Mesh::generate_plane_up([1.0,1.0], None, true);
825 /// let material_left = Material::unlit_clip().tex_copy(tex_left);
826 /// let material_right = Material::unlit_clip().tex_copy(tex_right);
827 /// let material_back = Material::unlit_clip().tex_copy(tex_back);
828 /// let material_floor = Material::unlit_clip().tex_copy(tex_floor);
829 ///
830 /// let transform_left = Matrix::t_r([-0.5, 0.0, 0.0], [0.0, 0.0, 90.0]);
831 /// let transform_right = Matrix::t_r([ 0.5, 0.0, 0.0], [0.0, 0.0, -90.0]);
832 /// let transform_back = Matrix::t_r([ 0.0, 0.0,-0.5], [90.0, 0.0, 0.0]);
833 /// let transform_floor = Matrix::t( [0.0, -0.5, 0.0]);
834 ///
835 /// filename_scr = "screenshots/tex_gen_particle.jpeg";
836 /// test_screenshot!( // !!!! Get a proper main loop !!!!
837 /// plane_mesh.draw(token, &material_left, transform_left, None, None);
838 /// plane_mesh.draw(token, &material_right, transform_right, None, None);
839 /// plane_mesh.draw(token, &material_back, transform_back, None, None);
840 /// plane_mesh.draw(token, &material_floor, transform_floor, None, None);
841 /// );
842 /// ```
843 /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/tex_gen_particle.jpeg" alt="screenshot" width="200">
844 pub fn gen_particle(width: i32, height: i32, roundness: f32, gradient_linear: Option<Gradient>) -> Tex {
845 let gradient_linear = match gradient_linear {
846 Some(gl) => gl,
847 None => {
848 let keys: [GradientKey; 2] = [
849 GradientKey { color: [1.0, 1.0, 1.0, 0.0].into(), position: 0.0 },
850 GradientKey { color: Color128::WHITE, position: 1.0 },
851 ];
852 Gradient::new(Some(&keys))
853 }
854 };
855 Tex(NonNull::new(unsafe { tex_gen_particle(width, height, roundness, gradient_linear.0.as_ptr()) }).unwrap())
856 }
857
858 /// This is the texture that all Tex objects will fall back to by default if they are still loading. Assigning a
859 /// texture here that isn’t fully loaded will cause the app to block until it is loaded.
860 /// <https://stereokit.net/Pages/StereoKit/Tex/SetLoadingFallback.html>
861 ///
862 /// see also [`tex_set_loading_fallback`]
863 /// ### Examples
864 /// ```
865 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
866 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color128},
867 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
868 ///
869 /// let tex_loading = Tex::gen_color(named_colors::GREEN, 128, 128, TexType::Image, TexFormat::RGBA32);
870 /// Tex::set_loading_fallback(&tex_loading);
871 ///
872 /// let tex = Tex::new(TexType::Image, TexFormat::RGBA32, None);
873 /// let material = Material::pbr().tex_copy(tex);
874 /// let plane_mesh = Mesh::generate_plane_up([1.0,1.0], None, true);
875 /// let transform_floor = Matrix::t( [0.0, -0.5, 0.0]);
876 ///
877 /// test_steps!( // !!!! Get a proper main loop !!!!
878 /// plane_mesh.draw(token, &material, transform_floor, None, None);
879 /// );
880 /// ```
881 pub fn set_loading_fallback<T: AsRef<Tex>>(fallback: T) {
882 unsafe { tex_set_loading_fallback(fallback.as_ref().0.as_ptr()) };
883 }
884
885 /// This is the texture that all Tex objects with errors will fall back to. Assigning a texture here that isn’t
886 /// fully loaded will cause the app to block until it is loaded.
887 /// <https://stereokit.net/Pages/StereoKit/Tex/SetErrorFallback.html>
888 ///
889 /// see also [`tex_set_error_fallback`]
890 /// ### Examples
891 /// ```
892 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
893 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color128},
894 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
895 ///
896 /// let tex_err = Tex::gen_color(named_colors::RED, 128, 128, TexType::Image, TexFormat::RGBA32);
897 /// Tex::set_error_fallback(&tex_err);
898 ///
899 /// let tex = Tex::from_file("file that doesn't exist", true, None)
900 /// .expect("tex should be created");
901 /// let material = Material::pbr().tex_copy(tex);
902 /// let plane_mesh = Mesh::generate_plane_up([1.0,1.0], None, true);
903 /// let transform_floor = Matrix::t( [0.0, -0.5, 0.0]);
904 ///
905 /// test_steps!( // !!!! Get a proper main loop !!!!
906 /// plane_mesh.draw(token, &material, transform_floor, None, None);
907 /// );
908 /// ```
909 pub fn set_error_fallback<T: AsRef<Tex>>(fallback: T) {
910 unsafe { tex_set_error_fallback(fallback.as_ref().0.as_ptr()) };
911 }
912
913 /// Looks for a Material asset that’s already loaded, matching the given id!
914 /// <https://stereokit.net/Pages/StereoKit/Tex/Find.html>
915 /// * `id` - The id of the texture to find.
916 ///
917 /// see also [`tex_find`]
918 /// ### Examples
919 /// ```
920 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
921 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color128},
922 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
923 ///
924 /// let mut tex_blue = Tex::gen_color(named_colors::BLUE, 1, 1, TexType::Image, TexFormat::RGBA32);
925 /// assert!(tex_blue.get_id().starts_with("auto/tex_"));
926 /// tex_blue.id("my_tex_blue");
927 /// let same_tex_blue = Tex::find("my_tex_blue").expect("my_tex_blue should be found");
928 /// assert_eq!(tex_blue, same_tex_blue);
929 ///
930 /// let tex = Tex::from_file("textures/open_gltf.jpeg", true, None)
931 /// .expect("tex should be created");
932 /// assert_eq!(tex.get_id(), "textures/open_gltf.jpeg");
933 /// let same_tex = Tex::find("textures/open_gltf.jpeg")
934 /// .expect("same_tex should be found");
935 /// assert_eq!(tex, same_tex);
936 /// ```
937 pub fn find<S: AsRef<str>>(id: S) -> Result<Tex, StereoKitError> {
938 let c_str = CString::new(id.as_ref()).map_err(|_| StereoKitError::TexCString(id.as_ref().into()))?;
939 Ok(Tex(
940 NonNull::new(unsafe { tex_find(c_str.as_ptr()) }).ok_or(StereoKitError::TexFind(id.as_ref().into()))?
941 ))
942 }
943
944 /// Get a copy of the texture
945 /// <https://stereokit.net/Pages/StereoKit/Tex.html>
946 /// * `tex_type` - Type of the copy. If None has default value of TexType::Image.
947 /// * `tex_format` - Format of the copy - If None has default value of TexFormat::None.
948 ///
949 /// see also [`tex_copy`]
950 /// ### Examples
951 /// ```
952 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
953 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{Color32, Color128},
954 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
955 ///
956 ///
957 /// let tex_blue = Tex::gen_color(Color32::new(64, 32, 255, 255), 1, 1,
958 /// TexType::Image, TexFormat::RGBA32Linear);
959 ///
960 /// let tex_copy = tex_blue.copy(None, Some(TexFormat::RGBA32))
961 /// .expect("copy should be done");
962 /// let mut color_data = [Color32::WHITE; 1];
963 /// assert!(tex_copy.get_color_data::<Color32>(&mut color_data, 0));
964 /// //TODO: windows assert_eq!(color_data[0], Color32 { r: 64, g: 32, b: 255, a: 255 });
965 /// //TODO: linux assert_eq!(color_data[0], Color32 { r: 137, g: 99, b: 255, a: 255 });
966 ///
967 /// let tex_copy = tex_blue.copy(Some(TexType::Image), Some(TexFormat::RGBA128))
968 /// .expect("copy should be done");
969 /// let mut color_data = [Color128::WHITE; 1];
970 /// assert!(tex_copy.get_color_data::<Color128>(&mut color_data, 0));
971 /// //TODO: windows assert_eq!(color_data[0], Color128 { r: 0.0, g: 0.0, b: 0.0, a: 0.0 });
972 /// //TODO: linux assert_eq!(color_data[0], Color128 { r: 0.2509804, g: 0.1254902, b: 1.0, a:1.0 });
973 /// ```
974 pub fn copy(&self, tex_type: Option<TexType>, tex_format: Option<TexFormat>) -> Result<Tex, StereoKitError> {
975 let type_ = tex_type.unwrap_or(TexType::Image);
976 let format = tex_format.unwrap_or(TexFormat::None);
977 Ok(Tex(NonNull::new(unsafe { tex_copy(self.0.as_ptr(), type_, format) })
978 .ok_or(StereoKitError::TexCopy(self.get_id().into()))?))
979 }
980
981 /// Creates a clone of the same reference. Basically, the new variable is the same asset. This is what you get by
982 /// calling find() method.
983 /// <https://stereokit.net/Pages/StereoKit/Tex/Find.html>
984 ///
985 /// see also [`tex_find()`]
986 /// ### Examples
987 /// ```
988 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
989 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color128},
990 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
991 ///
992 /// let mut tex_blue = Tex::gen_color(named_colors::BLUE, 1, 1, TexType::Image, TexFormat::RGBA32);
993 /// assert!(tex_blue.get_id().starts_with("auto/tex_"));
994 /// let same_tex_blue = tex_blue.clone_ref();
995 /// assert_eq!(tex_blue, same_tex_blue);
996 ///
997 /// let tex = Tex::from_file("textures/open_gltf.jpeg", true, None)
998 /// .expect("tex should be created");
999 /// assert_eq!(tex.get_id(), "textures/open_gltf.jpeg");
1000 /// let same_tex = tex.clone_ref();
1001 /// assert_eq!(tex, same_tex);
1002 /// ```
1003 pub fn clone_ref(&self) -> Tex {
1004 Tex(NonNull::new(unsafe { tex_find(tex_get_id(self.0.as_ptr())) }).expect("<asset>::clone_ref failed!"))
1005 }
1006
1007 /// Set a new id to the texture.
1008 /// <https://stereokit.net/Pages/StereoKit/Tex/Id.html>
1009 ///
1010 /// see also [`tex_set_id`]
1011 /// ### Examples
1012 /// ```
1013 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1014 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color128},
1015 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
1016 ///
1017 /// let mut tex_blue = Tex::gen_color(named_colors::BLUE, 1, 1, TexType::Image, TexFormat::RGBA32);
1018 /// assert!(tex_blue.get_id().starts_with("auto/tex_"));
1019 /// tex_blue.id("my_tex_blue");
1020 /// assert_eq!(tex_blue.get_id(), "my_tex_blue");
1021 ///
1022 /// let mut tex = Tex::from_file("textures/open_gltf.jpeg", true, None)
1023 /// .expect("tex should be created");
1024 /// assert_eq!(tex.get_id(), "textures/open_gltf.jpeg");
1025 /// tex_blue.id("my_tex_image");
1026 /// assert_eq!(tex_blue.get_id(), "my_tex_image");
1027 /// ```
1028 pub fn id<S: AsRef<str>>(&mut self, id: S) -> &mut Self {
1029 let c_str = CString::new(id.as_ref()).unwrap();
1030 unsafe { tex_set_id(self.0.as_ptr(), c_str.as_ptr()) };
1031 self
1032 }
1033
1034 /// Only applicable if this texture is a rendertarget! This creates and attaches a zbuffer surface to the texture
1035 /// for use when rendering to it.
1036 /// <https://stereokit.net/Pages/StereoKit/Tex/AddZBuffer.html>
1037 /// * `depth_format` - The format of the depth texture, must be a depth format type!
1038 ///
1039 /// see also [`tex_add_zbuffer`] [`Tex::set_zbuffer`]
1040 /// ### Examples
1041 /// ```
1042 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1043 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color32},
1044 /// system::Renderer,
1045 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
1046 ///
1047 ///
1048 /// let mut tex = Tex::render_target(128, 128, Some(2), Some(TexFormat::RGBA32),
1049 /// Some(TexFormat::None))
1050 /// .expect("Tex should be created");
1051 /// assert_eq!(tex.get_zbuffer(), None);
1052 ///
1053 /// tex.add_zbuffer(TexFormat::Depth16);
1054 /// assert_ne!(tex.get_zbuffer(), None);
1055 ///
1056 /// let plane_mesh = Mesh::generate_plane_up([1.0,1.0], None, true);
1057 /// let material = Material::pbr().tex_copy(&tex);
1058 /// let transform = Matrix::t_r([-0.5, 0.0, 0.0], [0.0, -45.0, 90.0]);
1059 ///
1060 /// Renderer::blit(&tex, &material);
1061 /// ```
1062 pub fn add_zbuffer(&mut self, depth_format: TexFormat) -> &mut Self {
1063 unsafe { tex_add_zbuffer(self.0.as_ptr(), depth_format) };
1064 self
1065 }
1066
1067 /// Loads an image file stored in memory directly into the created texture! Supported formats are: jpg, png, tga,
1068 /// bmp, psd, gif, hdr, pic, ktx2. This method introduces a blocking boolean parameter, which allows you to specify
1069 /// whether this method blocks until the image fully loads! The default case is to have it as part of the
1070 /// asynchronous asset pipeline, in which the Asset Id will
1071 /// be the same as the filename.
1072 /// <https://stereokit.net/Pages/StereoKit/Tex/SetMemory.html>
1073 /// * `data` - The binary data of an image file, this is NOT a raw RGB color array!
1074 /// * `srgb_data` - Is this image color data in sRGB format, or is it normal/metal/rough/data that’s not for direct
1075 /// display? sRGB colors get converted to linear color space on the graphics card, so getting this right can have
1076 /// a big impact on visuals.
1077 /// * `blocking` - Will this method wait for the image to load. By default, we try to load it asynchronously.
1078 /// * `priority` - The priority sort order for this asset in the async loading system. Lower values mean loading
1079 /// sooner. If None will be set to 10
1080 ///
1081 /// see also [`tex_set_mem`] [`Tex::from_memory`]
1082 /// ### Examples
1083 /// ```
1084 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1085 /// use stereokit_rust::{maths::{Vec3, Matrix},
1086 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
1087 ///
1088 /// let image_data = std::include_bytes!("../assets/textures/open_gltf.jpeg");
1089 /// let mut tex = Tex::new(TexType::Image, TexFormat::RGBA32, None);
1090 ///
1091 /// tex.set_memory(image_data, true, false, Some(0));
1092 ///
1093 /// let plane_mesh = Mesh::generate_plane_up([1.0,1.0], None, true);
1094 /// let material = Material::pbr().tex_copy(tex);
1095 /// let transform_floor = Matrix::t([0.0, -0.5, 0.0]);
1096 ///
1097 /// test_steps!( // !!!! Get a proper main loop !!!!
1098 /// plane_mesh.draw(token, &material, transform_floor, None, None);
1099 /// );
1100 /// ```
1101 pub fn set_memory(&mut self, data: &[u8], srgb_data: bool, blocking: bool, priority: Option<i32>) -> &mut Self {
1102 let priority = priority.unwrap_or(10);
1103 unsafe {
1104 tex_set_mem(
1105 self.0.as_ptr(),
1106 data.as_ptr() as *mut c_void,
1107 data.len(),
1108 srgb_data as Bool32T,
1109 blocking as Bool32T,
1110 priority,
1111 )
1112 };
1113 self
1114 }
1115
1116 /// Set the texture’s pixels using a pointer to a chunk of memory! This is great if you’re pulling in some color
1117 /// data from native code, and don’t want to pay the cost of trying to marshal that data around.
1118 /// The data should contains width*height*(TextureFormat size) bytes.
1119 /// Warning: The check width*height*(TextureFormat size) upon the size of the data values must be done before
1120 /// calling this function.
1121 /// Warning: The color data type must be compliant with the format of the texture.
1122 /// <https://stereokit.net/Pages/StereoKit/Tex/SetColors.html>
1123 /// * `width` - Width in pixels of the texture. Powers of two are generally best!
1124 /// * `height` - Height in pixels of the texture. Powers of two are generally best!
1125 /// * `data` - A pointer to a chunk of memory containing color data! Should be widthheightsize_of_texture_format
1126 /// bytes large. Color data should definitely match the format provided when constructing the texture!
1127 ///
1128 /// # Safety
1129 /// The data pointer must be a valid array for the size of the texture.
1130 ///
1131 /// see also [`tex_set_colors`]
1132 /// ### Examples
1133 /// ```
1134 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1135 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color32},
1136 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
1137 ///
1138 /// let mut color_dots = [named_colors::CYAN; 16 * 16];
1139 /// let mut tex = Tex::new(TexType::Image, TexFormat::RGBA32, None);
1140 ///
1141 /// unsafe { tex.set_colors(16, 16, color_dots.as_mut_ptr() as *mut std::os::raw::c_void); }
1142 ///
1143 /// let check_dots = [Color32::WHITE; 16 * 16];
1144 /// assert!(tex.get_color_data::<Color32>(&check_dots, 0));
1145 /// assert_eq!(check_dots, color_dots);
1146 /// ```
1147 pub unsafe fn set_colors(&mut self, width: usize, height: usize, data: *mut std::os::raw::c_void) -> &mut Self {
1148 unsafe { tex_set_colors(self.0.as_ptr(), width as i32, height as i32, data) };
1149 self
1150 }
1151
1152 /// Set the texture’s pixels using a color array! This function should only be called on textures with a format of
1153 /// Rgba32 or Rgba32Linear. You can call this as many times as you’d like, even with different widths and heights.
1154 /// Calling this multiple times will mark it as dynamic on the graphics card. Calling this function can also result
1155 /// in building mip-maps, which has a non-zero cost: use TexType.ImageNomips when creating the Tex to avoid this.
1156 /// <https://stereokit.net/Pages/StereoKit/Tex/SetColors.html>
1157 /// * `width` - Width in pixels of the texture. Powers of two are generally best!
1158 /// * `height` - Height in pixels of the texture. Powers of two are generally best!
1159 /// * `data` - An array of 32 bit colors, should be a length of width*height.
1160 ///
1161 /// Warning, instead of [`Tex::set_colors`], this call may not be done if the asset is not loaded
1162 /// (see [`Tex::get_asset_state`]) or the size is inconsistent or the format is incompatible.
1163 ///
1164 /// see also [`tex_set_colors`]
1165 /// ### Examples
1166 /// ```
1167 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1168 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color32},
1169 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
1170 ///
1171 /// let mut color_dots = [named_colors::CYAN; 16 * 16];
1172 /// let mut tex = Tex::new(TexType::Image, TexFormat::RGBA32, None);
1173 ///
1174 /// tex.set_colors32(16, 16, &color_dots);
1175 ///
1176 /// let check_dots = [Color32::WHITE; 16 * 16];
1177 /// assert!(tex.get_color_data::<Color32>(&check_dots, 0));
1178 /// assert_eq!(check_dots, color_dots);
1179 /// ```
1180 pub fn set_colors32(&mut self, width: usize, height: usize, data: &[Color32]) -> &mut Self {
1181 match self.get_format() {
1182 Some(TexFormat::RGBA32) => (),
1183 Some(TexFormat::RGBA32Linear) => (),
1184 Some(_) => {
1185 Log::err(format!(
1186 "The format of the texture {} is not compatible with Tex::set_colors32",
1187 self.get_id()
1188 ));
1189 return self;
1190 }
1191 None => {
1192 Log::err(format!("The texture {} is not loaded during Tex::set_colors32", self.get_id()));
1193 return self;
1194 }
1195 }
1196 if width * height != data.len() {
1197 Log::err(format!(
1198 "{}x{} differ from {} in Tex::set_color32 for texture {}",
1199 height,
1200 width,
1201 data.len(),
1202 self.get_id()
1203 ));
1204 return self;
1205 }
1206 unsafe {
1207 tex_set_colors(self.0.as_ptr(), width as i32, height as i32, data.as_ptr() as *mut std::os::raw::c_void)
1208 };
1209 self
1210 }
1211
1212 /// Set the texture’s pixels using a color array! This function should only be called on textures with a format of
1213 /// Rgba128. You can call this as many times as you’d like, even with different widths and heights. Calling this
1214 /// multiple times will mark it as dynamic on the graphics card.
1215 /// Calling this function can also result in building mip-maps, which has a non-zero cost: use TexType.ImageNomips
1216 /// when creating the Tex to avoid this.
1217 /// <https://stereokit.net/Pages/StereoKit/Tex/SetColors.html>
1218 /// * `width` - Width in pixels of the texture. Powers of two are generally best!
1219 /// * `height` - Height in pixels of the texture. Powers of two are generally best!
1220 /// * `data` - An array of 128 bit colors, should be a length of width*height.
1221 ///
1222 /// Warning, instead of [`Tex::set_colors`], this call may not be done if the asset is not loaded
1223 /// (see [`Tex::get_asset_state`]) or the size is inconsistent or the format is incompatible.
1224 ///
1225 /// see also [`tex_set_colors`]
1226 /// ### Examples
1227 /// ```
1228 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1229 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color128},
1230 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
1231 ///
1232 /// let mut color_dots = [Color128{r: 0.25, g: 0.125, b: 1.0, a: 1.0}; 16 * 16];
1233 /// let mut tex = Tex::new(TexType::Image, TexFormat::RGBA128, None);
1234 ///
1235 /// tex.set_colors128(16, 16, &color_dots);
1236 ///
1237 /// let check_dots = [Color128::BLACK; 16 * 16];
1238 /// assert!(tex.get_color_data::<Color128>(&check_dots, 0));
1239 /// assert_eq!(check_dots, color_dots);
1240 /// ```
1241 pub fn set_colors128(&mut self, width: usize, height: usize, data: &[Color128]) -> &mut Self {
1242 match self.get_format() {
1243 Some(TexFormat::RGBA128) => (),
1244 Some(_) => {
1245 Log::err(format!(
1246 "The format of the texture {} is not compatible with Tex::set_colors128",
1247 self.get_id()
1248 ));
1249 return self;
1250 }
1251 None => {
1252 Log::err(format!("The texture {} is not loaded during Tex::set_colors128", self.get_id()));
1253 return self;
1254 }
1255 }
1256 if width * height != data.len() {
1257 Log::err(format!(
1258 "{}x{} differ from {} for Tex::set_color128 for texture {}",
1259 height,
1260 width,
1261 data.len(),
1262 self.get_id()
1263 ));
1264 return self;
1265 }
1266 unsafe {
1267 tex_set_colors(self.0.as_ptr(), width as i32, height as i32, data.as_ptr() as *mut std::os::raw::c_void)
1268 };
1269 self
1270 }
1271
1272 /// Set the texture’s pixels using a scalar array for channel R ! This function should only be called on textures
1273 /// with a format of R8. You can call this as many times as you’d like, even with different widths and heights.
1274 /// Calling this multiple times will mark it as dynamic on the graphics card. Calling this function can also result
1275 /// in building mip-maps, which has a non-zero cost: use TexType.ImageNomips when creating the Tex to avoid this.
1276 /// <https://stereokit.net/Pages/StereoKit/Tex/SetColors.html>
1277 /// * `width` - Width in pixels of the texture. Powers of two are generally best!
1278 /// * `height` - Height in pixels of the texture. Powers of two are generally best!
1279 /// * `data` - An array of 8 bit values, should be a length of width*height.
1280 ///
1281 /// Warning, instead of [`Tex::set_colors`], this call may not be done if the asset is not loaded
1282 /// (see [`Tex::get_asset_state`]) or the size is inconsistent or the format is incompatible.
1283 ///
1284 /// see also [`tex_set_colors`]
1285 /// ### Examples
1286 /// ```
1287 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1288 /// use stereokit_rust::{maths::{Vec3, Matrix},
1289 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
1290 ///
1291 /// let mut color_dots = [125u8; 16 * 16];
1292 /// let mut tex = Tex::new(TexType::Image, TexFormat::R8, None);
1293 ///
1294 /// tex.set_colors_r8(16, 16, &color_dots);
1295 ///
1296 /// let check_dots = [0u8; 16 * 16];
1297 /// assert!(tex.get_color_data::<u8>(&check_dots, 0));
1298 /// assert_eq!(check_dots, color_dots);
1299 /// ```
1300 pub fn set_colors_r8(&mut self, width: usize, height: usize, data: &[u8]) -> &mut Self {
1301 match self.get_format() {
1302 Some(TexFormat::R8) => (),
1303 Some(_) => {
1304 Log::err(format!(
1305 "The format of the texture {} is not compatible with Tex::set_colors_r8",
1306 self.get_id()
1307 ));
1308 return self;
1309 }
1310 None => {
1311 Log::err(format!("The texture {} is not loaded during Tex::set_colors_r8", self.get_id()));
1312 return self;
1313 }
1314 }
1315 if width * height != data.len() {
1316 Log::err(format!(
1317 "{}x{} differ from {} for Tex::set_color_r8 for texture {}",
1318 height,
1319 width,
1320 data.len(),
1321 self.get_id()
1322 ));
1323 return self;
1324 }
1325 unsafe {
1326 tex_set_colors(self.0.as_ptr(), width as i32, height as i32, data.as_ptr() as *mut std::os::raw::c_void)
1327 };
1328 self
1329 }
1330
1331 /// Non canonical function !!
1332 /// Set the texture’s pixels using an u8 array ! This function should only be called for all textures format
1333 /// with a format of R8. You can call this as many times as you’d like, even with different widths and heights.
1334 /// Calling this multiple times will mark it as dynamic on the graphics card. Calling this function can also result
1335 /// in building mip-maps, which has a non-zero cost: use TexType.ImageNomips when creating the Tex to avoid this.
1336 /// <https://stereokit.net/Pages/StereoKit/Tex/SetColors.html>
1337 /// * `width` - Width in pixels of the texture. Powers of two are generally best!
1338 /// * `height` - Height in pixels of the texture. Powers of two are generally best!
1339 /// * `data` - An array of 8 bit values, should be a length of width*height.
1340 /// * `color_size` - number of byte for a pixel used by the format of this texture
1341 ///
1342 /// Warning, instead of [`Tex::set_colors`], this call may not be done if the asset is not loaded
1343 /// (see [`Tex::get_asset_state`]) or the size is inconsistent or the format is incompatible.
1344 ///
1345 /// see also [`tex_set_colors`]
1346 /// ### Examples
1347 /// ```
1348 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1349 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color32},
1350 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
1351 ///
1352 /// let mut color_dots = [127u8; 16 * 16 * 4];
1353 /// let mut tex = Tex::new(TexType::Image, TexFormat::RGBA32, None);
1354 ///
1355 /// tex.set_colors_u8(16, 16, &color_dots, 4);
1356 ///
1357 /// let check_dots = [Color32::BLACK; 16 * 16];
1358 /// assert!(tex.get_color_data::<Color32>(&check_dots, 0));
1359 /// assert_eq!(check_dots[0],Color32{r:127,g:127,b:127,a:127});
1360 /// ```
1361 pub fn set_colors_u8(&mut self, width: usize, height: usize, data: &[u8], color_size: usize) -> &mut Self {
1362 if width * height * color_size != data.len() {
1363 Log::err(format!(
1364 "{}x{}x{} differ from {} for Tex::set_color_u8 for texture {}",
1365 height,
1366 width,
1367 color_size,
1368 data.len(),
1369 self.get_id()
1370 ));
1371 return self;
1372 }
1373 unsafe {
1374 tex_set_colors(self.0.as_ptr(), width as i32, height as i32, data.as_ptr() as *mut std::os::raw::c_void)
1375 };
1376 self
1377 }
1378
1379 /// Set the texture’s pixels using a scalar array for channel R ! This function should only be called on textures
1380 /// with a format of R16u. You can call this as many times as you’d like, even with different widths and heights.
1381 /// Calling this multiple times will mark it as dynamic on the graphics card. Calling this function can also result
1382 /// in building mip-maps, which has a non-zero cost: use TexType.ImageNomips when creating the Tex to avoid this.
1383 /// <https://stereokit.net/Pages/StereoKit/Tex/SetColors.html>
1384 /// * `width` - Width in pixels of the texture. Powers of two are generally best!
1385 /// * `height` - Height in pixels of the texture. Powers of two are generally best!
1386 /// * `data` - An array of 16 bit values, should be a length of width*height.
1387 ///
1388 /// Warning, instead of [`Tex::set_colors`], this call may not be done if the asset is not loaded
1389 /// (see [`Tex::get_asset_state`]) or the size is inconsistent or the format is incompatible.
1390 ///
1391 /// see also [`tex_set_colors`]
1392 /// ### Examples
1393 /// ```
1394 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1395 /// use stereokit_rust::{maths::{Vec3, Matrix},
1396 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
1397 ///
1398 /// let mut color_dots = [256u16; 16 * 16];
1399 /// let mut tex = Tex::new(TexType::Image, TexFormat::R16u, None);
1400 ///
1401 /// tex.set_colors_r16(16, 16, &color_dots);
1402 ///
1403 /// let check_dots = [0u16; 16 * 16];
1404 /// assert!(tex.get_color_data::<u16>(&check_dots, 0));
1405 /// assert_eq!(check_dots, color_dots);
1406 /// ```
1407 pub fn set_colors_r16(&mut self, width: usize, height: usize, data: &[u16]) -> &mut Self {
1408 match self.get_format() {
1409 Some(TexFormat::R16u) => (),
1410 Some(_) => {
1411 Log::err(format!(
1412 "The format of the texture {} is not compatible with Tex::set_colors_r16",
1413 self.get_id()
1414 ));
1415 return self;
1416 }
1417 None => {
1418 Log::err(format!("The texture {} is not loaded during Tex::set_colors_r16", self.get_id()));
1419 return self;
1420 }
1421 }
1422 if width * height != data.len() {
1423 Log::err(format!(
1424 "{}x{} differ from {} for Tex::set_color_r16 for texture {}",
1425 height,
1426 width,
1427 data.len(),
1428 self.get_id()
1429 ));
1430 return self;
1431 }
1432 unsafe {
1433 tex_set_colors(self.0.as_ptr(), width as i32, height as i32, data.as_ptr() as *mut std::os::raw::c_void)
1434 };
1435 self
1436 }
1437
1438 /// Set the texture’s pixels using a scalar array! This function should only be called on textures with a format of
1439 /// R32. You can call this as many times as you’d like, even with different widths and heights. Calling this
1440 /// multiple times will mark it as dynamic on the graphics card. Calling this function can also result in building
1441 /// mip-maps, which has a non-zero cost: use TexType.ImageNomips when creating the Tex to avoid this.
1442 /// <https://stereokit.net/Pages/StereoKit/Tex/SetColors.html>
1443 /// * `width` - Width in pixels of the texture. Powers of two are generally best!
1444 /// * `height` - Height in pixels of the texture. Powers of two are generally best!
1445 /// * `data` - An array of 32 bit values, should be a length of width*height.
1446 ///
1447 /// Warning, instead of [`Tex::set_colors`], this call may not be done if the asset is not loaded
1448 /// (see [`Tex::get_asset_state`]) or the size is inconsistent or the format is incompatible.
1449 ///
1450 /// see also [`tex_set_colors`]
1451 /// ### Examples
1452 /// ```
1453 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1454 /// use stereokit_rust::{maths::{Vec3, Matrix},
1455 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
1456 ///
1457 /// let mut color_dots = [0.13f32; 16 * 16];
1458 /// let mut tex = Tex::new(TexType::Image, TexFormat::R32, None);
1459 ///
1460 /// tex.set_colors_r32(16, 16, &color_dots);
1461 ///
1462 /// let check_dots = [0.0f32; 16 * 16];
1463 /// assert!(tex.get_color_data::<f32>(&check_dots, 0));
1464 /// assert_eq!(check_dots, color_dots);
1465 /// ```
1466 pub fn set_colors_r32(&mut self, width: usize, height: usize, data: &[f32]) -> &mut Self {
1467 match self.get_format() {
1468 Some(TexFormat::R32) => (),
1469 Some(_) => {
1470 Log::err(format!(
1471 "The format of the texture {} is not compatible with Tex::set_colors_r32",
1472 self.get_id()
1473 ));
1474 return self;
1475 }
1476 None => {
1477 Log::err(format!("The texture {} is not loaded during Tex::set_colors_r32", self.get_id()));
1478 return self;
1479 }
1480 }
1481 if width * height != data.len() {
1482 Log::err(format!(
1483 "{}x{} differ from {} for Tex::set_color_r32 for texture {}",
1484 height,
1485 width,
1486 data.len(),
1487 self.get_id()
1488 ));
1489 return self;
1490 }
1491 unsafe {
1492 tex_set_colors(self.0.as_ptr(), width as i32, height as i32, data.as_ptr() as *mut std::os::raw::c_void)
1493 };
1494 self
1495 }
1496
1497 /// This allows you to attach a z/depth buffer from a rendertarget texture. This texture _must_ be a
1498 /// rendertarget to set this, and the zbuffer texture _must_ be a depth format (or null). For no-rendertarget
1499 /// textures, this will always be None.
1500 /// <https://stereokit.net/Pages/StereoKit/Tex/SetZBuffer.html>
1501 /// * `tex` - TODO: None may crash the program
1502 ///
1503 /// see also [`tex_set_zbuffer`] [`Tex::add_zbuffer`]
1504 /// ### Examples
1505 /// ```
1506 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1507 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color32},
1508 /// system::Renderer,
1509 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
1510 ///
1511 ///
1512 /// let mut tex = Tex::render_target(128, 128, Some(2), Some(TexFormat::RGBA32),
1513 /// Some(TexFormat::Depth16))
1514 /// .expect("Tex should be created");
1515 ///
1516 /// let zbuffer = tex.get_zbuffer().expect("Tex should have a zbuffer");
1517 ///
1518 /// let mut tex2 = Tex::render_target(128, 128, Some(2), Some(TexFormat::RGBA32),
1519 /// Some(TexFormat::None))
1520 /// .expect("Tex2 should be created");
1521 /// tex2.set_zbuffer(Some(zbuffer));
1522 /// assert_ne!(tex2.get_zbuffer(), None);
1523 ///
1524 /// //tex2.set_zbuffer(None);
1525 /// //assert_eq!(tex2.get_zbuffer(), None);
1526 /// ```
1527 pub fn set_zbuffer(&mut self, tex: Option<Tex>) -> &mut Self {
1528 if let Some(tex) = tex {
1529 unsafe { tex_set_zbuffer(self.0.as_ptr(), tex.0.as_ptr()) }
1530 } else {
1531 unsafe { tex_set_zbuffer(self.0.as_ptr(), null_mut()) }
1532 }
1533 self
1534 }
1535
1536 /// This function is dependent on the graphics backend! It will take a texture resource for the current graphics
1537 /// backend (D3D or GL) and wrap it in a StereoKit texture for use within StereoKit. This is a bit of an advanced
1538 /// feature.
1539 /// # Safety
1540 /// native_surface must be a valid pointer to a texture resource for the current graphics backend.
1541 /// <https://stereokit.net/Pages/StereoKit/Tex/SetNativeSurface.html>
1542 /// * `native_surface` - For D3D, this should be an ID3D11Texture2D*, and for GL, this should be a uint32_t from a
1543 /// glGenTexture call, coerced into the IntPtr.
1544 /// * `tex_type` - The image flags that tell SK how to treat the texture, this should match up with the settings the
1545 /// texture was originally created with. If SK can figure the appropriate settings, it may override the value
1546 /// provided here.
1547 /// * `native_fmt` - The texture’s format using the graphics backend’s value, not SK’s. This should match up with
1548 /// the settings the texture was originally created with. If SK can figure the appropriate settings, it may
1549 /// override the value provided here. 0 is a valide default value.
1550 /// * `width` - Width of the texture. This should match up with the settings the texture was originally created
1551 /// with. If SK can figure the appropriate settings, it may override the value provided here. 0 is a valide default
1552 /// value.
1553 /// * `height` - Height of the texture. This should match up with the settings the texture was originally created
1554 /// with. If SK can figure the appropriate settings, it may override the value provided here. 0 is a valide default
1555 /// value.
1556 /// * `surface_count` - Texture array surface count. This should match up with the settings the texture was
1557 /// originally created with. If SK can figure the appropriate settings, it may override the value provided here.
1558 /// 1 is a valide default value.
1559 /// * `owned` - Should ownership of this texture resource be passed on to StereoKit? If so, StereoKit may delete
1560 /// it when it’s finished with it. True is a valide default value, if this is not desired, pass in false.
1561 ///
1562 /// see also [`tex_set_surface`]
1563 /// ### Examples
1564 /// ```
1565 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1566 /// # use stereokit_rust::{tex::{Tex, TexFormat, TexType}};
1567 /// # use std::ptr::null_mut;
1568 ///
1569 /// let mut tex = Tex::new(TexType::Image, TexFormat::RGBA32, None);
1570 /// let native_surface = tex.get_native_surface();
1571 /// unsafe { tex.set_native_surface(native_surface, TexType::Image, 0, 1, 1, 1, false); }
1572 /// ```
1573 #[allow(clippy::too_many_arguments)]
1574 pub unsafe fn set_native_surface(
1575 &mut self,
1576 native_surface: *mut std::os::raw::c_void,
1577 tex_type: TexType,
1578 native_fmt: i64,
1579 width: i32,
1580 height: i32,
1581 surface_count: i32,
1582 owned: bool,
1583 ) -> &mut Self {
1584 unsafe {
1585 tex_set_surface(
1586 self.0.as_ptr(),
1587 native_surface,
1588 tex_type,
1589 native_fmt,
1590 width,
1591 height,
1592 surface_count,
1593 1,
1594 1,
1595 owned as Bool32T,
1596 )
1597 };
1598 self
1599 }
1600
1601 /// Set the texture’s size without providing any color data. In most cases, you should probably just call SetColors
1602 /// instead, but this can be useful if you’re adding color data some other way, such as when blitting or rendering
1603 /// to it.
1604 /// <https://stereokit.net/Pages/StereoKit/Tex/SetSize.html>
1605 ///
1606 /// see also [`tex_set_colors`]
1607 /// ### Examples
1608 /// ```
1609 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1610 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color32},
1611 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
1612 ///
1613 /// let mut tex = Tex::new(TexType::Image, TexFormat::RGBA32, None);
1614 ///
1615 /// assert_eq!(tex.get_width(), Some(0));
1616 /// assert_eq!(tex.get_height(), Some(0));
1617 ///
1618 /// tex.set_size(16, 16);
1619 /// assert_eq!(tex.get_width(), Some(16));
1620 /// assert_eq!(tex.get_height(), Some(16));
1621 ///
1622 /// let check_dots = [Color32::BLACK; 16 * 16];
1623 /// assert!(tex.get_color_data::<Color32>(&check_dots, 0));
1624 /// ```
1625 pub fn set_size(&mut self, width: usize, height: usize) -> &mut Self {
1626 unsafe { tex_set_colors(self.0.as_ptr(), width as i32, height as i32, null_mut()) };
1627 self
1628 }
1629
1630 /// This will override the default fallback texture that gets used before the Tex has finished loading. This is
1631 /// useful for textures with a specific purpose where the normal fallback texture would appear strange, such as a
1632 /// metal/rough map.
1633 /// <https://stereokit.net/Pages/StereoKit/Tex/FallbackOverride.html>
1634 ///
1635 /// see also [`tex_set_fallback`] [`Tex::set_loading_fallback`]
1636 /// ### Examples
1637 /// ```
1638 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1639 /// use stereokit_rust::{maths::{Vec3, Matrix}, util::{named_colors, Color128},
1640 /// tex::{Tex, TexFormat, TexType}, mesh::Mesh, material::Material};
1641 ///
1642 /// let tex_fallback = Tex::gen_color(named_colors::VIOLET, 128, 128, TexType::Image, TexFormat::RGBA32);
1643 /// let mut tex = Tex::new(TexType::Image, TexFormat::RGBA32, None);
1644 /// tex.fallback_override(&tex_fallback);
1645 ///
1646 /// let tex = Tex::new(TexType::Image, TexFormat::RGBA32, Some("tex_left_ID"));
1647 /// let tex_metal = Tex::from_file("textures/parquet2/parquet2metal.ktx2", true, Some(9999))
1648 /// .expect("Metal tex should be created");
1649 /// let mut material = Material::pbr().tex_copy(tex);
1650 /// material.metal_tex(&tex_metal);
1651 /// let plane_mesh = Mesh::generate_plane_up([1.0,1.0], None, true);
1652 /// let transform_floor = Matrix::t( [0.0, -0.5, 0.0]);
1653 ///
1654 /// test_steps!( // !!!! Get a proper main loop !!!!
1655 /// plane_mesh.draw(token, &material, transform_floor, None, None);
1656 /// );
1657 /// ```
1658 pub fn fallback_override<T: AsRef<Tex>>(&mut self, fallback: T) -> &mut Self {
1659 unsafe { tex_set_fallback(self.0.as_ptr(), fallback.as_ref().0.as_ptr()) };
1660 self
1661 }
1662
1663 /// When sampling a texture that’s stretched, or shrunk beyond its screen size, how do we handle figuring out which
1664 /// color to grab from the texture? Default is Linear.
1665 /// <https://stereokit.net/Pages/StereoKit/Tex/SampleMode.html>
1666 ///
1667 /// see also [`tex_set_sample`]
1668 /// ### Examples
1669 /// ```
1670 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1671 /// use stereokit_rust::{util::named_colors,
1672 /// tex::{Tex, TexFormat, TexType, TexSample}};
1673 ///
1674 /// let mut tex = Tex::gen_color(named_colors::VIOLET, 128, 128, TexType::Image, TexFormat::RGBA32);
1675 /// assert_eq!(tex.get_sample_mode(), TexSample::Linear);
1676 /// tex.sample_mode(TexSample::Anisotropic);
1677 /// assert_eq!(tex.get_sample_mode(), TexSample::Anisotropic);
1678 /// ```
1679 pub fn sample_mode(&mut self, sample: TexSample) -> &mut Self {
1680 unsafe { tex_set_sample(self.0.as_ptr(), sample) };
1681 self
1682 }
1683
1684 //// When looking at a UV texture coordinate on this texture, how do we handle values larger than 1, or less than zero?
1685 /// Do we Wrap to the other side? Clamp it between 0-1, or just keep Mirroring back and forth? Wrap is the default.
1686 /// <https://stereokit.net/Pages/StereoKit/Tex/AddressMode.html>
1687 ///
1688 /// see also [`tex_set_address`]
1689 /// ### Examples
1690 /// ```
1691 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1692 /// use stereokit_rust::{util::named_colors,
1693 /// tex::{Tex, TexFormat, TexType, TexAddress}};
1694 ///
1695 /// let mut tex = Tex::gen_color(named_colors::VIOLET, 128, 128, TexType::Image, TexFormat::RGBA32);
1696 /// assert_eq!(tex.get_address_mode(), TexAddress::Wrap);
1697 /// tex.address_mode(TexAddress::Mirror);
1698 /// assert_eq!(tex.get_address_mode(), TexAddress::Mirror);
1699 /// ```
1700 pub fn address_mode(&mut self, address_mode: TexAddress) -> &mut Self {
1701 unsafe { tex_set_address(self.0.as_ptr(), address_mode) };
1702 self
1703 }
1704
1705 /// When SampleMode is set to Anisotropic, this is the number of samples the GPU takes to figure out the correct color.
1706 /// Default is 4, and 16 is pretty high.
1707 /// <https://stereokit.net/Pages/StereoKit/Tex/Anisoptropy.html>
1708 /// <https://stereokit.net/Pages/StereoKit/Tex/Anisotropy.html>
1709 ///
1710 /// see also [`tex_set_anisotropy`]
1711 /// ### Examples
1712 /// ```
1713 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1714 /// use stereokit_rust::{util::named_colors,
1715 /// tex::{Tex, TexFormat, TexType, TexSample}};
1716 ///
1717 /// let mut tex = Tex::gen_color(named_colors::VIOLET, 128, 128, TexType::Image, TexFormat::RGBA32);
1718 /// assert_eq!(tex.get_sample_mode(), TexSample::Linear);
1719 /// assert_eq!(tex.get_anisotropy(), 4);
1720 ///
1721 /// tex.sample_mode(TexSample::Anisotropic).anisotropy(10);
1722 ///
1723 /// assert_eq!(tex.get_anisotropy(), 10);
1724 /// ```
1725 pub fn anisotropy(&mut self, anisotropy_level: i32) -> &mut Self {
1726 unsafe { tex_set_anisotropy(self.0.as_ptr(), anisotropy_level) };
1727 self
1728 }
1729
1730 /// Gets the unique identifier of this asset resource! This can be helpful for debugging, managing your assets, or
1731 /// finding them later on!
1732 /// <https://stereokit.net/Pages/StereoKit/Tex/Id.html>
1733 ///
1734 /// see also [`tex_get_id`]
1735 /// see example in [`Tex::id`]
1736 pub fn get_id(&self) -> &str {
1737 unsafe { CStr::from_ptr(tex_get_id(self.0.as_ptr())) }.to_str().unwrap()
1738 }
1739
1740 /// Textures are loaded asyncronously, so this tells you the current state of this texture! This also can tell if
1741 /// an error occured, and what type of error it may have been.
1742 /// <https://stereokit.net/Pages/StereoKit/Tex/AssetState.html>
1743 ///
1744 /// see also [`tex_asset_state`]
1745 /// ### Examples
1746 /// ```
1747 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1748 /// use stereokit_rust::{util::named_colors, system::AssetState,
1749 /// tex::{Tex, TexFormat, TexType}};
1750 ///
1751 /// let tex = Tex::gen_color(named_colors::VIOLET, 128, 128,
1752 /// TexType::Image, TexFormat::RGBA32);
1753 /// assert_eq!(tex.get_asset_state(), AssetState::Loaded);
1754 ///
1755 /// let tex_icon = Tex::from_file("icons/checked.png", true, None)
1756 /// .expect("Tex_icon should be created");
1757 /// assert_ne!(tex_icon.get_asset_state(), AssetState::NotFound);
1758 ///
1759 /// let tex_not_icon = Tex::from_file("icccons/checddked.png", true, None)
1760 /// .expect("Tex_not_icon should be created");
1761 /// assert_ne!(tex_not_icon.get_asset_state(), AssetState::Loaded);
1762 ///
1763 /// test_steps!( // !!!! Get a proper main loop !!!!
1764 /// // We ensure to have the Tex loaded.
1765 /// if tex_icon.get_asset_state() != AssetState::Loaded
1766 /// || tex_not_icon.get_asset_state() == AssetState::Loading { iter -= 1; }
1767 /// );
1768 /// assert_eq!(tex_icon.get_asset_state(), AssetState::Loaded);
1769 /// assert_eq!(tex_not_icon.get_asset_state(), AssetState::NotFound);
1770 /// assert_eq!(tex_not_icon.get_width(), None);
1771 /// assert_eq!(tex_not_icon.get_height(), None);
1772 /// ```
1773 pub fn get_asset_state(&self) -> AssetState {
1774 unsafe { tex_asset_state(self.0.as_ptr()) }
1775 }
1776
1777 /// The StereoKit format this texture was initialized with. This will be a blocking call if AssetState is less than
1778 /// LoadedMeta so None will be return instead
1779 /// <https://stereokit.net/Pages/StereoKit/Tex/Format.html>
1780 ///
1781 /// see also [`tex_get_format`]
1782 /// ### Examples
1783 /// ```
1784 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1785 /// use stereokit_rust::{util::named_colors, system::AssetState,
1786 /// tex::{Tex, TexFormat, TexType}};
1787 ///
1788 /// let tex = Tex::gen_color(named_colors::VIOLET, 128, 128,
1789 /// TexType::Image, TexFormat::RGBA128);
1790 /// assert_eq!(tex.get_format(), Some(TexFormat::RGBA128));
1791 ///
1792 /// let tex_icon = Tex::from_file("icons/checked.png", true, None)
1793 /// .expect("Tex_icon should be created");
1794 ///
1795 /// let tex_not_icon = Tex::from_file("icccons/checddked.png", true, None)
1796 /// .expect("Tex_not_icon should be created");
1797 ///
1798 /// test_steps!( // !!!! Get a proper main loop !!!!
1799 /// // We ensure to have the Tex loaded.
1800 /// if tex_icon.get_asset_state() != AssetState::Loaded
1801 /// || tex_not_icon.get_asset_state() == AssetState::Loading { iter -= 1; }
1802 /// );
1803 /// assert_eq!(tex_icon.get_format(), Some(TexFormat::RGBA32));
1804 /// assert_eq!(tex_not_icon.get_format(), None);
1805 /// ```
1806 pub fn get_format(&self) -> Option<TexFormat> {
1807 match self.get_asset_state() {
1808 AssetState::Loaded => (),
1809 AssetState::LoadedMeta => (),
1810 AssetState::None => (),
1811 _ => return None,
1812 }
1813 Some(unsafe { tex_get_format(self.0.as_ptr()) })
1814 }
1815
1816 /// This allows you to retreive a z/depth buffer from a rendertarget texture. This texture _must_ be a
1817 /// rendertarget to set this, and the zbuffer texture _must_ be a depth format (or null). For no-rendertarget
1818 /// textures, this will always be null.
1819 /// <https://stereokit.net/Pages/StereoKit/Tex/GetZBuffer.html>
1820 ///
1821 /// see also [`tex_get_zbuffer`]
1822 /// see example in [`Tex::set_zbuffer`]
1823 pub fn get_zbuffer(&self) -> Option<Tex> {
1824 NonNull::new(unsafe { tex_get_zbuffer(self.0.as_ptr()) }).map(Tex)
1825 }
1826
1827 /// This will return the texture’s native resource for use with external libraries. For D3D, this will be an
1828 /// ID3D11Texture2D*, and for GL, this will be a uint32_t from a glGenTexture call, coerced into the IntPtr. This
1829 /// call will block execution until the texture is loaded, if it is not already.
1830 /// <https://stereokit.net/Pages/StereoKit/Tex/GetNativeSurface.html>
1831 ///
1832 /// see also [`tex_get_surface`]
1833 /// see example in [`Tex::set_native_surface`]
1834 pub fn get_native_surface(&self) -> *mut c_void {
1835 unsafe { tex_get_surface(self.0.as_ptr()) }
1836 }
1837
1838 /// The width of the texture, in pixels. This will be a blocking call if AssetState is less than LoadedMeta so None
1839 /// will be return instead
1840 /// <https://stereokit.net/Pages/StereoKit/Tex/Width.html>
1841 ///
1842 /// see also [`tex_get_width`]
1843 /// see example in [`Tex::set_size`] [`Tex::get_asset_state`]
1844 pub fn get_width(&self) -> Option<usize> {
1845 match self.get_asset_state() {
1846 AssetState::Loaded => (),
1847 AssetState::LoadedMeta => (),
1848 AssetState::None => (),
1849 _ => return None,
1850 }
1851 Some(unsafe { tex_get_width(self.0.as_ptr()) } as usize)
1852 }
1853
1854 /// The height of the texture, in pixels. This will be a blocking call if AssetState is less than LoadedMeta so None
1855 /// will be return instead
1856 /// <https://stereokit.net/Pages/StereoKit/Tex/Height.html>
1857 ///
1858 /// see also [`tex_get_height`]
1859 /// see example in [`Tex::set_size`] [`Tex::get_asset_state`]
1860 pub fn get_height(&self) -> Option<usize> {
1861 match self.get_asset_state() {
1862 AssetState::Loaded => (),
1863 AssetState::LoadedMeta => (),
1864 AssetState::None => (),
1865 _ => return None,
1866 }
1867 Some(unsafe { tex_get_height(self.0.as_ptr()) } as usize)
1868 }
1869
1870 /// Non-canon function which returns a tuple made of (width, heigh, size) of the corresponding texture.
1871 ///
1872 /// use `mip` < 0 for textures using [`TexType::ImageNomips`]
1873 ///
1874 /// use `mip` >=0 to retrieve the info about one MIP of the texture
1875 ///
1876 /// the size corresponding to the mip texture and the width and height of this mip texture
1877 /// This will be a blocking call if AssetState is less than LoadedMeta so None will be return instead
1878 ///
1879 /// ### Examples
1880 /// ```
1881 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1882 /// use stereokit_rust::{util::{named_colors, Color32}, system::AssetState,
1883 /// tex::{Tex, TexFormat, TexType}};
1884 ///
1885 /// let mut color_dots = [named_colors::CYAN; 16 * 16];
1886 /// let mut tex = Tex::new(TexType::Image, TexFormat::RGBA32, None);
1887 /// tex.set_colors32(16, 16, &color_dots);
1888 ///
1889 /// let check_dots = [Color32::WHITE; 16 * 16];
1890 /// assert!(tex.get_color_data::<Color32>(&check_dots, 0));
1891 /// assert_eq!(check_dots, color_dots);
1892 ///
1893 /// let (width, height, size) = tex.get_data_infos(0).expect("tex should be loaded");
1894 /// assert_eq!(width, 16);
1895 /// assert_eq!(height, 16);
1896 /// assert_eq!(size, 256);
1897 ///
1898 /// let (width, height, size) = tex.get_data_infos(1).expect("tex should be loaded");
1899 /// assert_eq!(width, 8);
1900 /// assert_eq!(height, 8);
1901 /// assert_eq!(size, 64);
1902 ///
1903 /// let tex_icon = Tex::from_file("icons/checked.png", true, None)
1904 /// .expect("Tex_icon should be created");
1905 /// test_steps!( // !!!! Get a proper main loop !!!!
1906 /// // We ensure to have the Tex loaded.
1907 /// if tex_icon.get_asset_state() != AssetState::Loaded { iter -= 1; }
1908 /// );
1909 /// assert_eq!(tex_icon.get_data_infos(0), Some((128, 128, 16384)));
1910 /// ```
1911 pub fn get_data_infos(&self, mip: i8) -> Option<(usize, usize, usize)> {
1912 match self.get_asset_state() {
1913 AssetState::Loaded => (),
1914 AssetState::LoadedMeta => (),
1915 AssetState::None => (),
1916 _ => {
1917 Log::err(format!("Texture {} not loaded. Function tex_get_data_info failed!", self.get_id()));
1918 return None;
1919 }
1920 }
1921 let mut width = unsafe { tex_get_width(self.0.as_ptr()) } as usize;
1922 let mut height = unsafe { tex_get_height(self.0.as_ptr()) } as usize;
1923 let size_test;
1924 let mut mips_test = unsafe { tex_get_mips(self.0.as_ptr()) } as usize;
1925
1926 if mip >= mips_test as i8 {
1927 Log::err(format!(
1928 "Texture {} has only {} mips. Index {} is too high. Function tex_get_data_info failed!",
1929 self.get_id(),
1930 mips_test,
1931 mip
1932 ));
1933 return None;
1934 }
1935
1936 let deux: usize = 2;
1937 if mip <= 0 {
1938 size_test = width * height;
1939 } else {
1940 mips_test = deux.pow(mip as u32);
1941 width /= mips_test;
1942 height /= mips_test;
1943
1944 size_test = width * height;
1945 }
1946 Some((width, height, size_test))
1947 }
1948
1949 /// Retrieve the color data of the texture from the GPU. This can be a very slow operation,
1950 /// so use it cautiously. The out_data pointer must correspond to an array with the correct size.
1951 /// <https://stereokit.net/Pages/StereoKit/Tex/GetColorData.html>
1952 /// * mip_level - Retrieves the color data for a specific mip-mapping level. This function will log a fail and
1953 /// return a black array if an invalid mip-level is provided.
1954 ///
1955 /// The function [`Tex::get_data_infos`] may help you to shape the right receiver.
1956 ///
1957 /// see also [`tex_get_data`]
1958 /// ### Examples
1959 /// ```
1960 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1961 /// use stereokit_rust::{util::{named_colors, Color32, Color128},
1962 /// tex::{Tex, TexFormat, TexType}};
1963 ///
1964 /// let mut tex = Tex::gen_color(named_colors::CYAN, 8 , 8, TexType::Image, TexFormat::RGBA32);
1965 ///
1966 /// let check_dots = [Color32::WHITE; 8 * 8];
1967 /// assert!(tex.get_color_data::<Color32>(&check_dots, 0));
1968 /// assert_eq!(check_dots[5], named_colors::CYAN);
1969 ///
1970 /// let mut tex = Tex::gen_color(named_colors::MAGENTA, 8 , 8, TexType::Image, TexFormat::RGBA128);
1971 ///
1972 /// let check_dots = [Color128::WHITE; 8 * 8];
1973 /// assert!(tex.get_color_data::<Color128>(&check_dots, 0));
1974 /// assert_eq!(check_dots[5], named_colors::MAGENTA.into());
1975 /// ```
1976 pub fn get_color_data<T>(&self, color_data: &[T], mut mip_level: i8) -> bool {
1977 let size_of_color = std::mem::size_of_val(color_data);
1978 let (width, height, size_test) = match self.get_data_infos(mip_level) {
1979 Some(value) => value,
1980 None => return false,
1981 };
1982 if size_test * size_of::<T>() != size_of_color {
1983 Log::err(format!(
1984 "Size of the Tex {} is {}x{}/mip={} when size of the given buffer is {} instead of {}. Function Tex::get_color failed!",
1985 self.get_id(),
1986 height,
1987 width,
1988 mip_level,
1989 size_of_color,
1990 size_test * size_of::<T>(),
1991 ));
1992 return false;
1993 }
1994
1995 if mip_level < 0 {
1996 mip_level = 0
1997 }
1998 unsafe {
1999 tex_get_data(
2000 self.0.as_ptr(),
2001 color_data.as_ptr() as *mut std::os::raw::c_void,
2002 size_of_color,
2003 mip_level as i32,
2004 )
2005 };
2006
2007 true
2008 }
2009
2010 /// Non canonical function!
2011 /// Retrieve the color data of the texture from the GPU. This can be a very slow operation,
2012 /// so use it cautiously. The out_data pointer must correspond to an u8 array with the correct size.
2013 /// <https://stereokit.net/Pages/StereoKit/Tex/GetColorData.html>
2014 /// * `color_size`: number of bytes of the color (Color32: 4, Color128: 16 ...)
2015 /// * `mip_level` - Retrieves the color data for a specific mip-mapping level. This function will log a fail and
2016 /// return a black array if an invalid mip-level is provided.
2017 ///
2018 /// The function [`Tex::get_data_infos`] may help you to shape the right receiver.
2019 ///
2020 /// see also [`tex_get_data`]
2021 /// ### Examples
2022 /// ```
2023 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2024 /// use stereokit_rust::{util::{named_colors, Color32},
2025 /// tex::{Tex, TexFormat, TexType}};
2026 ///
2027 /// let mut tex = Tex::gen_color(named_colors::CYAN, 8 , 8, TexType::Image, TexFormat::RGBA32);
2028 ///
2029 /// let mut check_dots = [0u8; 8 * 8 * 4];
2030 /// assert!(tex.get_color_data_u8(&mut check_dots, 4, 0));
2031 /// assert_eq!(check_dots[5*4], named_colors::CYAN.r);
2032 /// assert_eq!(check_dots[5*4+1], named_colors::CYAN.g);
2033 /// assert_eq!(check_dots[5*4+2], named_colors::CYAN.b);
2034 /// assert_eq!(check_dots[5*4+3], named_colors::CYAN.a);
2035 /// ```
2036 pub fn get_color_data_u8(&self, color_data: &[u8], color_size: usize, mut mip_level: i8) -> bool {
2037 let size_of_color = std::mem::size_of_val(color_data);
2038 let (width, height, size_test) = match self.get_data_infos(mip_level) {
2039 Some(value) => value,
2040 None => return false,
2041 };
2042
2043 if size_test * color_size != size_of_color {
2044 Log::err(format!(
2045 "Size of the Tex {} is {}x{}/mip={} when size of the given buffer is {} instead of {}. Function Tex::get_color_data_u8 failed!",
2046 self.get_id(),
2047 height,
2048 width,
2049 mip_level,
2050 size_of_color,
2051 size_test * color_size,
2052 ));
2053 return false;
2054 }
2055
2056 if mip_level < 0 {
2057 mip_level = 0
2058 }
2059 unsafe {
2060 tex_get_data(
2061 self.0.as_ptr(),
2062 color_data.as_ptr() as *mut std::os::raw::c_void,
2063 size_of_color,
2064 mip_level as i32,
2065 )
2066 };
2067
2068 true
2069 }
2070
2071 /// When sampling a texture that’s stretched, or shrunk beyond its screen size, how do we handle figuring out which
2072 /// color to grab from the texture? Default is Linear.
2073 /// <https://stereokit.net/Pages/StereoKit/Tex/SampleMode.html>
2074 ///
2075 /// see also [`tex_get_sample`]
2076 /// see example in [`Tex::sample_mode`]
2077 pub fn get_sample_mode(&self) -> TexSample {
2078 unsafe { tex_get_sample(self.0.as_ptr()) }
2079 }
2080
2081 /// When looking at a UV texture coordinate on this texture, how do we handle values larger than 1, or less than
2082 /// zero? Do we Wrap to the other side? Clamp it between 0-1, or just keep Mirroring back and forth? Wrap is the
2083 /// default.
2084 /// <https://stereokit.net/Pages/StereoKit/Tex/AddressMode.html>
2085 ///
2086 /// see also [`tex_get_address`]
2087 /// see example in [`Tex::address_mode`]
2088 pub fn get_address_mode(&self) -> TexAddress {
2089 unsafe { tex_get_address(self.0.as_ptr()) }
2090 }
2091
2092 /// When SampleMode is set to Anisotropic, this is the number of samples the GPU takes to figure out the correct
2093 /// color. Default is 4, and 16 is pretty high.
2094 /// <https://stereokit.net/Pages/StereoKit/Tex/Anisoptropy.html>
2095 /// <https://stereokit.net/Pages/StereoKit/Tex/Anisotropy.html>
2096 ///
2097 /// see also [`tex_get_anisotropy`]
2098 /// see example in [`Tex::anisotropy`]
2099 pub fn get_anisotropy(&self) -> i32 {
2100 unsafe { tex_get_anisotropy(self.0.as_ptr()) }
2101 }
2102
2103 /// The number of mip-map levels this texture has. This will be 1 if the texture doesn’t have mip mapping enabled.
2104 /// This will be a blocking call if AssetState is less than LoadedMeta so None will be return instead.
2105 /// <https://stereokit.net/Pages/StereoKit/Tex/Mips.html>
2106 ///
2107 /// see also [`tex_get_mips`]
2108 /// ### Examples
2109 /// ```
2110 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2111 /// use stereokit_rust::{util::named_colors, system::AssetState,
2112 /// tex::{Tex, TexFormat, TexType}};
2113 ///
2114 /// let tex_nomips = Tex::gen_color(named_colors::VIOLET, 128, 128,
2115 /// TexType::ImageNomips, TexFormat::RGBA32);
2116 ///
2117 /// let tex = Tex::gen_color(named_colors::VIOLET, 128, 128,
2118 /// TexType::Image, TexFormat::RGBA32);
2119 ///
2120 /// let tex_icon = Tex::from_file("icons/checked.png", true, None)
2121 /// .expect("Tex_icon should be created");
2122 /// // TODO: assert_eq!(tex_icon.get_mips(), None);
2123 ///
2124 /// let tex_not_icon = Tex::from_file("Not an icon file", true, None)
2125 /// .expect("Tex_not_icon should be created");
2126 /// assert_eq!(tex_not_icon.get_mips(), None);
2127 ///
2128 /// test_steps!( // !!!! Get a proper main loop !!!!
2129 /// // We ensure to have the Tex loaded.
2130 /// if tex_icon.get_asset_state() != AssetState::Loaded
2131 /// || tex_not_icon.get_asset_state() == AssetState::Loading { iter -= 1; }
2132 /// );
2133 /// assert_eq!(tex_nomips.get_mips(), Some(1));
2134 /// // TODO: assert_eq!(tex.get_mips(), Some(8));
2135 /// // TODO: assert_eq!(tex_icon.get_mips(), Some(8));
2136 /// assert_eq!(tex_not_icon.get_mips(), None);
2137 /// ```
2138 pub fn get_mips(&self) -> Option<i32> {
2139 match self.get_asset_state() {
2140 AssetState::Loaded => (),
2141 AssetState::LoadedMeta => (),
2142 AssetState::None => (),
2143 _ => return None,
2144 }
2145 Some(unsafe { tex_get_mips(self.0.as_ptr()) })
2146 }
2147
2148 /// ONLY valid for cubemap textures! This will calculate a spherical harmonics representation of the cubemap for use
2149 /// with StereoKit’s lighting. First call may take a frame or two of time, but subsequent calls will pull from a
2150 /// cached value.
2151 /// <https://stereokit.net/Pages/StereoKit/Tex/CubemapLighting.html>
2152 ///
2153 /// see also [`tex_get_cubemap_lighting`] use instead [`SHCubemap`]
2154 /// ### Examples
2155 /// ```
2156 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2157 /// use stereokit_rust::{util::named_colors, maths::Vec3,
2158 /// tex::{Tex, TexFormat, TexType}};
2159 ///
2160 /// let tex = Tex::gen_color(named_colors::VIOLET, 128, 128,
2161 /// TexType::Cubemap, TexFormat::RGBA32);
2162 ///
2163 /// // Cubemap must be created with SHCubemap static methods.
2164 /// let sh_cubemap = tex.get_cubemap_lighting();
2165 /// assert_eq!(sh_cubemap.sh.coefficients[2], Vec3::ZERO);
2166 /// assert_eq!(sh_cubemap.sh.coefficients[5], Vec3::ZERO);
2167 /// ```
2168 pub fn get_cubemap_lighting(&self) -> SHCubemap {
2169 SHCubemap {
2170 sh: unsafe { tex_get_cubemap_lighting(self.0.as_ptr()) },
2171 tex: Tex(NonNull::new(unsafe { tex_find(tex_get_id(self.0.as_ptr())) }).unwrap()),
2172 }
2173 }
2174
2175 /// Default 2x2 black opaque texture, this is the texture referred to as ‘black’ in the shader texture defaults.
2176 /// <https://stereokit.net/Pages/StereoKit/Tex/Black.html>
2177 ///
2178 /// ### Examples
2179 /// ```
2180 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2181 /// use stereokit_rust::tex::Tex;
2182 ///
2183 /// let tex= Tex::black();
2184 /// assert_eq!(tex.get_id(), "default/tex_black");
2185 /// ```
2186 pub fn black() -> Self {
2187 Self::find("default/tex_black").unwrap()
2188 }
2189
2190 /// This is a white checkered grid texture used to easily add visual features to materials. By default, this is used
2191 /// for the loading fallback texture for all Tex objects.
2192 /// <https://stereokit.net/Pages/StereoKit/Tex/DevTex.html>
2193 ///
2194 /// ### Examples
2195 /// ```
2196 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2197 /// use stereokit_rust::tex::Tex;
2198 ///
2199 /// let tex = Tex::dev_tex();
2200 /// assert_eq!(tex.get_id(), "default/tex_devtex");
2201 /// ```
2202 pub fn dev_tex() -> Self {
2203 Self::find("default/tex_devtex").unwrap()
2204 }
2205
2206 /// This is a red checkered grid texture used to indicate some sort of error has occurred. By default, this is used
2207 /// for the error fallback texture for all Tex objects.
2208 /// <https://stereokit.net/Pages/StereoKit/Tex/Error.html>
2209 ///
2210 /// ### Examples
2211 /// ```
2212 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2213 /// use stereokit_rust::tex::Tex;
2214 ///
2215 /// let tex = Tex::error();
2216 /// assert_eq!(tex.get_id(), "default/tex_error");
2217 /// ```
2218 pub fn error() -> Self {
2219 Self::find("default/tex_error").unwrap()
2220 }
2221
2222 /// Default 2x2 flat normal texture, this is a normal that faces out from the, face, and has a color value of
2223 /// (0.5,0.5,1). This is the texture referred to as ‘flat’ in the shader texture defaults.
2224 /// <https://stereokit.net/Pages/StereoKit/Tex/Flat.html>
2225 ///
2226 /// ### Examples
2227 /// ```
2228 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2229 /// use stereokit_rust::tex::Tex;
2230 ///
2231 /// let tex = Tex::flat();
2232 /// assert_eq!(tex.get_id(), "default/tex_flat");
2233 /// ```
2234 pub fn flat() -> Self {
2235 Self::find("default/tex_flat").unwrap()
2236 }
2237
2238 /// Default 2x2 middle gray (0.5,0.5,0.5) opaque texture, this is the texture referred to as ‘gray’ in the shader
2239 /// texture defaults.
2240 /// <https://stereokit.net/Pages/StereoKit/Tex/Gray.html>
2241 ///
2242 /// ### Examples
2243 /// ```
2244 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2245 /// use stereokit_rust::tex::Tex;
2246 ///
2247 /// let tex = Tex::gray();
2248 /// assert_eq!(tex.get_id(), "default/tex_gray");
2249 /// ```
2250 pub fn gray() -> Self {
2251 Self::find("default/tex_gray").unwrap()
2252 }
2253
2254 /// Default 2x2 roughness color (1,1,0,1) texture, this is the texture referred to as ‘rough’ in the shader texture
2255 /// defaults.
2256 /// <https://stereokit.net/Pages/StereoKit/Tex/Rough.html>
2257 ///
2258 /// ### Examples
2259 /// ```
2260 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2261 /// use stereokit_rust::tex::Tex;
2262 ///
2263 /// let tex = Tex::rough();
2264 /// assert_eq!(tex.get_id(), "default/tex_rough");
2265 /// ```
2266 pub fn rough() -> Self {
2267 Self::find("default/tex_rough").unwrap()
2268 }
2269
2270 /// Default 2x2 white opaque texture, this is the texture referred to as ‘white’ in the shader texture defaults.
2271 /// <https://stereokit.net/Pages/StereoKit/Tex/White.html>
2272 ///
2273 /// ### Examples
2274 /// ```
2275 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2276 /// use stereokit_rust::tex::Tex;
2277 ///
2278 /// let tex = Tex::white();
2279 /// assert_eq!(tex.get_id(), "default/tex");
2280 /// ```
2281 pub fn white() -> Self {
2282 Self::find("default/tex").unwrap()
2283 }
2284
2285 // /// The equirectangular texture used for the default dome
2286 // /// <https://stereokit.net/Pages/StereoKit/Tex.html>
2287 // pub fn cubemap() -> Self {
2288 // Self::find("default/tex_cubemap").unwrap()
2289 // }
2290}
2291
2292/// fluent syntax for Texture cubemap
2293/// <https://stereokit.net/Pages/StereoKit/Tex.html>
2294///
2295/// see also [`Tex`] [`crate::util::SphericalHarmonics`]
2296/// ### Examples
2297/// ```
2298/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2299/// use stereokit_rust::{maths::Vec3, tex::SHCubemap, system::AssetState};
2300///
2301/// let sh_cubemap = SHCubemap::from_cubemap("hdri/sky_dawn.hdr", true, 9999)
2302/// .expect("Cubemap should be created");
2303///
2304/// sh_cubemap.render_as_sky();
2305/// assert_eq!(sh_cubemap.tex.get_asset_state(), AssetState::Loaded);
2306///
2307/// let tex = sh_cubemap.tex;
2308///
2309/// filename_scr = "screenshots/sh_cubemap.jpeg";
2310/// test_screenshot!( // !!!! Get a proper main loop !!!!
2311/// if tex.get_asset_state() != AssetState::Loaded {iter -= 1}
2312/// );
2313/// ```
2314/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/sh_cubemap.jpeg" alt="screenshot" width="200">
2315#[derive(Debug)]
2316pub struct SHCubemap {
2317 pub sh: SphericalHarmonics,
2318 pub tex: Tex,
2319}
2320
2321impl SHCubemap {
2322 /// Creates a cubemap texture from a single equirectangular image! You know, the ones that look like an unwrapped
2323 /// globe with the poles all stretched out. It uses some fancy shaders and texture blitting to create 6 faces from
2324 /// the equirectangular image.
2325 /// <https://stereokit.net/Pages/StereoKit/Tex/FromCubemapEquirectangular.html>
2326 ///
2327 /// see also [`tex_create_cubemap_file`]
2328 #[deprecated(since = "0.40.0", note = "please use `from_cubemap` instead")]
2329 pub fn from_cubemap_equirectangular(
2330 equirectangular_file_utf8: impl AsRef<Path>,
2331 srgb_data: bool,
2332 priority: i32,
2333 ) -> Result<SHCubemap, StereoKitError> {
2334 Self::from_cubemap(equirectangular_file_utf8, srgb_data, priority)
2335 }
2336
2337 /// Creates a cubemap texture from a single file! This will load KTX2 files with 6 surfaces, or convert
2338 /// equirectangular images into cubemap images. KTX2 files are the _fastest_ way to load a cubemap, but
2339 /// equirectangular images can be acquired quite easily!
2340 ///
2341 /// Equirectangular images look like an unwrapped globe with the poles all stretched out, and are sometimes referred
2342 /// to as HDRIs.
2343 /// <https://stereokit.net/Pages/StereoKit/Tex/FromCubemap.html>
2344 /// * `cubemap_file` - Filename of the cubemap image.
2345 /// * `srgb_data` - Is this image color data in sRGB format, or is it normal/metal/rough/data that's not for direct
2346 /// display? sRGB colors get converted to linear color space on the graphics card, so getting this right can have
2347 /// a big impact on visuals.
2348 /// * `load_priority` - The priority sort order for this asset in the async loading system. Lower values mean loading
2349 /// sooner.
2350 ///
2351 /// see also [`tex_create_cubemap_file`]
2352 /// ### Examples
2353 /// ```
2354 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2355 /// use stereokit_rust::{maths::Vec3, tex::SHCubemap, system::AssetState};
2356 ///
2357 /// let sh_cubemap = SHCubemap::from_cubemap("hdri/sky_dawn.hdr", true, 9999)
2358 /// .expect("Cubemap should be created");
2359 /// sh_cubemap.render_as_sky();
2360 ///
2361 /// assert_ne!(sh_cubemap.sh.coefficients[0], Vec3::ZERO);
2362 /// assert_ne!(sh_cubemap.sh.coefficients[8], Vec3::ZERO);
2363 ///
2364 /// let tex = sh_cubemap.tex;
2365 ///
2366 /// test_steps!( // !!!! Get a proper main loop !!!!
2367 /// if tex.get_asset_state() != AssetState::Loaded {iter -= 1}
2368 /// );
2369 /// assert_eq!(tex.get_asset_state(), AssetState::Loaded);
2370 /// ```
2371 pub fn from_cubemap(
2372 cubemap_file: impl AsRef<Path>,
2373 srgb_data: bool,
2374 load_priority: i32,
2375 ) -> Result<SHCubemap, StereoKitError> {
2376 let path = cubemap_file.as_ref();
2377 let path_buf = path.to_path_buf();
2378 let c_str = CString::new(path.to_str().ok_or(StereoKitError::TexCString(path.to_str().unwrap().to_owned()))?)?;
2379 let tex =
2380 Tex(
2381 NonNull::new(unsafe { tex_create_cubemap_file(c_str.as_ptr(), srgb_data as Bool32T, load_priority) })
2382 .ok_or(StereoKitError::TexFile(path_buf.clone(), "tex_create_cubemap_file failed".to_string()))?,
2383 );
2384
2385 Ok(Tex::get_cubemap_lighting(&tex))
2386 }
2387
2388 /// Creates a cubemap texture from 6 different image files! If you have a single equirectangular image, use
2389 /// Tex.FromEquirectangular instead. Asset Id will be the first filename.
2390 /// order of the file names is +X -X +Y -Y +Z -Z
2391 /// <https://stereokit.net/Pages/StereoKit/Tex/FromCubemapFile.html>
2392 /// * `files_utf8` - 6 image filenames, in order of/ +X, -X, +Y, -Y, +Z, -Z.
2393 /// * `srgb_data` - Is this image color data in sRGB format, or is it normal/metal/rough/data that's not for direct
2394 /// display? sRGB colors get converted to linear color space on the graphics card, so getting this right can have a
2395 /// big impact on visuals.
2396 /// * `load_priority` - The priority sort order for this asset in the async loading system. Lower values mean loading
2397 /// sooner.
2398 ///
2399 /// see also [`tex_create_cubemap_files`]
2400 /// ### Examples
2401 /// ```
2402 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2403 /// use stereokit_rust::{system::AssetState, tex::SHCubemap};
2404 ///
2405 /// let cubemap_files = [
2406 /// "hdri/giza/right.png",
2407 /// "hdri/giza/left.png",
2408 /// "hdri/giza/top.png",
2409 /// "hdri/giza/bottom.png",
2410 /// "hdri/giza/front.png",
2411 /// "hdri/giza/back.png",
2412 /// ];
2413 /// let sh_cubemap = SHCubemap::from_cubemap_files(&cubemap_files, true, 9999)
2414 /// .expect("Cubemap should be created");
2415 /// sh_cubemap.render_as_sky();
2416 ///
2417 /// let tex = sh_cubemap.tex;
2418 ///
2419 /// test_steps!( // !!!! Get a proper main loop !!!!
2420 /// if tex.get_asset_state() != AssetState::Loaded {iter -= 1}
2421 /// );
2422 /// assert_eq!(tex.get_asset_state(), AssetState::Loaded);
2423 /// ```
2424 pub fn from_cubemap_files<P: AsRef<Path>>(
2425 files_utf8: &[P; 6],
2426 srgb_data: bool,
2427 load_priority: i32,
2428 ) -> Result<SHCubemap, StereoKitError> {
2429 let mut c_files = Vec::new();
2430 for path in files_utf8 {
2431 let path = path.as_ref();
2432 let path_buf = path.to_path_buf();
2433 let c_str =
2434 CString::new(path.to_str().ok_or(StereoKitError::TexCString(path_buf.to_str().unwrap().to_owned()))?)?;
2435 c_files.push(c_str);
2436 }
2437 let mut c_files_ptr = Vec::new();
2438 for str in c_files.iter() {
2439 c_files_ptr.push(str.as_ptr());
2440 }
2441 let in_arr_cube_face_file_xxyyzz = c_files_ptr.as_mut_slice().as_mut_ptr();
2442 let tex = Tex(NonNull::new(unsafe {
2443 tex_create_cubemap_files(in_arr_cube_face_file_xxyyzz, srgb_data as Bool32T, load_priority)
2444 })
2445 .ok_or(StereoKitError::TexFiles(
2446 PathBuf::from(r"one_of_6_files"),
2447 "tex_create_cubemap_files failed".to_string(),
2448 ))?);
2449
2450 //Ok(Tex::get_cubemap_lighting(&tex))
2451 Ok(SHCubemap { sh: SphericalHarmonics::default(), tex })
2452 }
2453
2454 /// Generates a cubemap texture from a gradient and a direction! These are entirely suitable for skyboxes, which
2455 /// you can set via Renderer.SkyTex.
2456 /// <https://stereokit.net/Pages/StereoKit/Tex/GenCubemap.html>
2457 /// * `gradient` - A color gradient the generator will sample from! This looks at the 0-1 range of the gradient.
2458 /// * `gradient_dir` - This vector points to where the ‘top’ of the color gradient will go. Conversely, the ‘bottom’
2459 /// of the gradient will be opposite, and it’ll blend along that axis.
2460 /// * `resolution` - The square size in pixels of each cubemap face! This generally doesn’t need to be large, unless
2461 /// you have a really complicated gradient. 16 is a good default value.
2462 ///
2463 /// see also [`tex_gen_cubemap`]
2464 /// ### Examples
2465 /// ```
2466 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2467 /// use stereokit_rust::{maths::Vec3, tex::SHCubemap, system::AssetState,
2468 /// util::{named_colors, Gradient, GradientKey, Color128}};
2469 ///
2470 /// let mut keys = [
2471 /// GradientKey::new(Color128::BLACK_TRANSPARENT, 0.0),
2472 /// GradientKey::new(named_colors::RED, 0.1),
2473 /// GradientKey::new(named_colors::CYAN, 0.4),
2474 /// GradientKey::new(named_colors::YELLOW, 0.5),
2475 /// GradientKey::new(Color128::BLACK, 0.7)];
2476 ///
2477 /// let sh_cubemap = SHCubemap::gen_cubemap_gradient(Gradient::new(Some(&keys)),
2478 /// Vec3::UP, 128);
2479 /// sh_cubemap.render_as_sky();
2480 ///
2481 /// let tex = sh_cubemap.tex;
2482 /// assert_eq!(tex.get_asset_state(), AssetState::Loaded);
2483 /// assert_ne!(sh_cubemap.sh.coefficients[0], Vec3::ZERO);
2484 /// assert_ne!(sh_cubemap.sh.coefficients[8], Vec3::ZERO);
2485 /// test_steps!( // !!!! Get a proper main loop !!!!
2486 /// );
2487 /// ```
2488 pub fn gen_cubemap_gradient(
2489 gradient: impl AsRef<Gradient>,
2490 gradient_dir: impl Into<Vec3>,
2491 resolution: i32,
2492 ) -> SHCubemap {
2493 let mut sh = SphericalHarmonics::default();
2494 let tex = Tex(NonNull::new(unsafe {
2495 tex_gen_cubemap(gradient.as_ref().0.as_ptr(), gradient_dir.into(), resolution, &mut sh)
2496 })
2497 .unwrap());
2498 //unsafe { sk.tex_addref(&cubemap.1) }
2499 SHCubemap { sh, tex }
2500 }
2501
2502 /// Create the associated cubemap texture with the light spot.
2503 /// warning ! The SphericalHarmonics is moved to the result struct.
2504 /// <https://stereokit.net/Pages/StereoKit/Tex/GenCubemap.html>
2505 /// * `lighting` - Lighting information stored in a SphericalHarmonics.
2506 /// * `resolution` - The square size in pixels of each cubemap face! This generally doesn’t need to be large, as
2507 /// SphericalHarmonics typically contain pretty low frequency information.
2508 /// * `light_spot_size_pct` - The size of the glowing spot added in the primary light direction. You can kinda think
2509 /// of the unit as a percentage of the cubemap face’s size, but it’s technically a Chebyshev distance from the
2510 /// light’s point on a 2m cube.
2511 /// * `light_spot_intensity` - The glowing spot’s color is the primary light direction’s color, but multiplied by
2512 /// this value. Since this method generates a 128bpp texture, this is not clamped between 0-1, so feel free to go
2513 /// nuts here! Remember that reflections will often cut down some reflection intensity.
2514 ///
2515 ///
2516 /// see also [`tex_gen_cubemap_sh`]
2517 /// ### Examples
2518 /// ```
2519 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2520 /// use stereokit_rust::{maths::Vec3, tex::SHCubemap, system::AssetState,
2521 /// util::{named_colors, SHLight, SphericalHarmonics}};
2522 ///
2523 /// let lights: [SHLight; 1] = [SHLight::new(Vec3::ONE, named_colors::WHITE); 1];
2524 /// let sh = SphericalHarmonics::from_lights(&lights);
2525 /// let sh_cubemap = SHCubemap::gen_cubemap_sh(sh, 128, 0.5, 1.0);
2526 /// sh_cubemap.render_as_sky();
2527 ///
2528 /// let tex = sh_cubemap.tex;
2529 /// assert_eq!(tex.get_asset_state(), AssetState::Loaded);
2530 /// assert_eq!(sh_cubemap.sh.get_dominent_light_direction(), -Vec3::ONE.get_normalized());
2531 /// assert_ne!(sh_cubemap.sh.coefficients[0], Vec3::ZERO);
2532 /// assert_ne!(sh_cubemap.sh.coefficients[1], Vec3::ZERO);
2533 /// assert_eq!(sh_cubemap.sh.coefficients[8], Vec3::ZERO);
2534 /// test_steps!( // !!!! Get a proper main loop !!!!
2535 /// );
2536 /// ```
2537 pub fn gen_cubemap_sh(
2538 lighting: SphericalHarmonics,
2539 resolution: i32,
2540 light_spot_size_pct: f32,
2541 light_spot_intensity: f32,
2542 ) -> SHCubemap {
2543 let tex = Tex(NonNull::new(unsafe {
2544 tex_gen_cubemap_sh(&lighting, resolution, light_spot_size_pct, light_spot_intensity)
2545 })
2546 .unwrap());
2547 SHCubemap { sh: lighting, tex }
2548 }
2549
2550 /// Get the associated lighting extracted from the cubemap.
2551 /// <https://stereokit.net/Pages/StereoKit/Tex/CubemapLighting.html>
2552 ///
2553 /// see also [`tex_gen_cubemap_sh`]
2554 /// ### Examples
2555 /// ```
2556 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2557 /// use stereokit_rust::{maths::Vec3, tex::SHCubemap, system::AssetState,
2558 /// util::{named_colors, SHLight, SphericalHarmonics}};
2559 ///
2560 /// let lights: [SHLight; 1] = [SHLight::new(Vec3::ONE, named_colors::WHITE); 1];
2561 /// let sh = SphericalHarmonics::from_lights(&lights);
2562 /// let sh_cubemap = SHCubemap::gen_cubemap_sh(sh, 128, 0.5, 1.0);
2563 /// let tex = sh_cubemap.tex;
2564 ///
2565 /// let sh_cubemap2 = SHCubemap::get_cubemap_lighting(tex);
2566 /// let tex2 = sh_cubemap2.tex;
2567 /// assert_eq!(tex2.get_asset_state(), AssetState::Loaded);
2568 /// assert_eq!(sh_cubemap2.sh.get_dominent_light_direction(), -Vec3::ONE.get_normalized());
2569 /// assert_ne!(sh_cubemap2.sh.coefficients[0], Vec3::ZERO);
2570 /// assert_ne!(sh_cubemap2.sh.coefficients[1], Vec3::ZERO);
2571 /// assert_eq!(sh_cubemap2.sh.coefficients[8], Vec3::ZERO);
2572 /// ```
2573 pub fn get_cubemap_lighting(cubemap_texture: impl AsRef<Tex>) -> SHCubemap {
2574 SHCubemap {
2575 sh: unsafe { tex_get_cubemap_lighting(cubemap_texture.as_ref().0.as_ptr()) },
2576 tex: Tex(NonNull::new(unsafe { tex_find(tex_get_id(cubemap_texture.as_ref().0.as_ptr())) }).unwrap()),
2577 }
2578 }
2579
2580 /// Get the cubemap texture and SH light of the the current skylight
2581 /// <https://stereokit.net/Pages/StereoKit/Renderer/SkyLight.html>
2582 /// <https://stereokit.net/Pages/StereoKit/Renderer/SkyTex.html>
2583 ///
2584 /// see also [`crate::system::Renderer`]
2585 /// ### Examples
2586 /// ```
2587 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2588 /// use stereokit_rust::tex::SHCubemap;
2589 ///
2590 /// let sh_cubemap = SHCubemap::get_rendered_sky();
2591 ///
2592 /// let tex = sh_cubemap.tex;
2593 /// assert_eq!(tex.get_id(), "default/cubemap");
2594 /// ```
2595 pub fn get_rendered_sky() -> SHCubemap {
2596 SHCubemap {
2597 sh: unsafe { render_get_skylight() },
2598 tex: Tex(NonNull::new(unsafe { render_get_skytex() }).unwrap()),
2599 }
2600 }
2601
2602 /// set the spherical harmonics as skylight and the the cubemap texture as skytex
2603 /// <https://stereokit.net/Pages/StereoKit/Renderer/SkyLight.html>
2604 /// <https://stereokit.net/Pages/StereoKit/Renderer/SkyTex.html>
2605 ///
2606 /// see also see also [`crate::system::Renderer`]
2607 /// ### Examples
2608 /// ```
2609 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2610 /// use stereokit_rust::{maths::Vec3, tex::SHCubemap, system::{AssetState, Renderer}};
2611 ///
2612 /// let mut sh_cubemap = SHCubemap::from_cubemap("hdri/sky_dawn.hdr", true, 9999)
2613 /// .expect("Cubemap should be created");
2614 /// assert_eq!(Renderer::get_enable_sky(), true);
2615 ///
2616 /// sh_cubemap.render_as_sky();
2617 ///
2618 /// Renderer::enable_sky(false);
2619 /// assert_eq!(Renderer::get_enable_sky(), false);
2620 /// ```
2621 pub fn render_as_sky(&self) {
2622 unsafe {
2623 render_set_skylight(&self.sh);
2624 render_set_skytex(self.tex.0.as_ptr());
2625 }
2626 }
2627
2628 /// Get the cubemap tuple
2629 ///
2630 /// see also [`Tex`] [`crate::util::SphericalHarmonics`]
2631 /// ### Examples
2632 /// ```
2633 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2634 /// use stereokit_rust::{tex::SHCubemap, maths::Vec3};
2635 ///
2636 /// let sh_cubemap = SHCubemap::get_rendered_sky();
2637 ///
2638 /// let (sh, tex) = sh_cubemap.get();
2639 /// assert_eq!(tex.get_id(), "default/cubemap");
2640 /// assert_eq!(sh.get_dominent_light_direction(), Vec3 { x: -0.20119436, y: -0.92318374, z: -0.32749438 });
2641 /// ```
2642 pub fn get(&self) -> (SphericalHarmonics, Tex) {
2643 (self.sh, Tex(NonNull::new(unsafe { tex_find(tex_get_id(self.tex.0.as_ptr())) }).unwrap()))
2644 }
2645}