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