stereokit_rust/util.rs
1use crate::{
2 StereoKitError,
3 maths::{Bool32T, Vec3, lerp},
4 sk::DisplayBlend,
5 system::TextContext,
6 ui::IdHashT,
7};
8use std::{
9 ffi::{CStr, CString, c_char, c_void},
10 fmt::Display,
11 ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
12 path::Path,
13 ptr::NonNull,
14};
15
16/// A color value stored as 4 floats with values that are generally between 0 and 1! Note that there’s also a Color32
17/// structure, and that 4 floats is generally a lot more than you need. So, use this for calculating individual
18/// colors at quality, but maybe store them en-masse with Color32!
19///
20/// Also note that RGB is often a terrible color format for picking colors, but it’s how our displays work and we’re
21/// stuck with it. If you want to create a color via code, try out the static Color.HSV method instead!
22///
23/// A note on gamma space vs. linear space colors! Color is not inherently one or the other, but most color values we
24/// work with tend to be gamma space colors, so most functions in StereoKit are gamma space. There are occasional
25/// functions that will ask for linear space colors instead, primarily in performance critical places, or places where
26/// a color may not always be a color! However, performing math on gamma space colors is bad, and will result in
27/// incorrect colors. We do our best to indicate what color space a function uses, but it’s not enforced through syntax!
28/// <https://stereokit.net/Pages/StereoKit/Color.html>
29///
30/// see also [`Color32`] [`named_colors`]
31/// ### Examples
32/// ```
33/// use stereokit_rust::{util::{Color128, named_colors}, maths::Vec3};
34///
35/// // Cyan from different sources:
36/// let color_cyan1: Color128 = named_colors::CYAN.into();
37/// let color_cyan2: Color128 = [0.0, 1.0, 1.0, 1.0].into();
38/// let color_cyan3 = Color128 {r: 0.0, g: 1.0, b: 1.0, a: 1.0};
39/// let color_cyan4 = Color128::new (0.0, 1.0, 1.0, 1.0);
40/// let color_cyan5 = Color128::rgba(0.0, 1.0, 1.0, 1.0);
41/// let color_cyan6 = Color128::rgb (0.0, 1.0, 1.0 );
42/// let color_cyan7 = Color128::hsv (0.5, 1.0, 1.0, 1.0);
43/// let color_cyan8 = Color128::hsv_vec3 (Vec3::new(0.5, 1.0, 1.0), 1.0);
44/// let color_cyan9 = Color128::lab (0.911, -0.493, 0.042, 1.0);
45/// let color_cyanA = Color128::lab_vec3 (Vec3::new(0.911, -0.493, 0.042), 1.0);
46/// let color_cyanB = Color128::hex (0x00FFFFFF);
47///
48/// # assert_eq!(color_cyan1, color_cyan2);
49/// # assert_eq!(color_cyan1, color_cyan3);
50/// # assert_eq!(color_cyan1, color_cyan4);
51/// # assert_eq!(color_cyan1, color_cyan5);
52/// # assert_eq!(color_cyan1, color_cyan6);
53/// # assert_eq!(color_cyan1, color_cyan7);
54/// # assert_eq!(color_cyan1, color_cyan8);
55/// # assert_eq!(color_cyan1, color_cyan9);
56/// # assert_eq!(color_cyan1, color_cyanA);
57/// assert_eq!(color_cyan1, color_cyanB);
58/// ```
59#[repr(C)]
60#[derive(Debug, Copy, Clone, Default)]
61pub struct Color128 {
62 pub r: f32,
63 pub g: f32,
64 pub b: f32,
65 pub a: f32,
66}
67
68impl PartialEq for Color128 {
69 fn eq(&self, other: &Self) -> bool {
70 (self.r - other.r).abs() < 0.000001
71 && (self.g - other.g).abs() < 0.000001
72 && (self.b - other.b).abs() < 0.000001
73 && (self.a - other.a).abs() < 0.000001
74 }
75}
76
77impl From<[f32; 4]> for Color128 {
78 fn from(s: [f32; 4]) -> Self {
79 Self::new(s[0], s[1], s[2], s[3])
80 }
81}
82
83impl From<Color32> for Color128 {
84 fn from(a: Color32) -> Self {
85 Self::new(a.r as f32 / 255.0, a.g as f32 / 255.0, a.b as f32 / 255.0, a.a as f32 / 255.0)
86 }
87}
88
89unsafe extern "C" {
90 pub fn color_hsv(hue: f32, saturation: f32, value: f32, transparency: f32) -> Color128;
91 pub fn color_to_hsv(color: *const Color128) -> Vec3;
92 pub fn color_lab(l: f32, a: f32, b: f32, transparency: f32) -> Color128;
93 pub fn color_to_lab(color: *const Color128) -> Vec3;
94 pub fn color_to_linear(srgb_gamma_correct: Color128) -> Color128;
95 pub fn color_to_gamma(srgb_linear: Color128) -> Color128;
96}
97
98impl Color128 {
99 /// Opaque black color
100 pub const BLACK: Color128 = Color128 { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
101
102 /// Transparent black color
103 pub const BLACK_TRANSPARENT: Color128 = Color128 { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
104
105 /// Opaque white color
106 pub const WHITE: Color128 = Color128 { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
107
108 /// Try hsv instead! But if you really need to create a color from RGB values, I suppose you’re in the right
109 /// place. All parameter values are generally in the range of 0-1.
110 /// <https://stereokit.net/Pages/StereoKit/Color/Color.html>
111 pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
112 Self { r, g, b, a }
113 }
114
115 /// Try hsv instead! But if you really need to create a color from RGB values, I suppose you’re in the right
116 /// place. All parameter values are generally in the range of 0-1.
117 /// <https://stereokit.net/Pages/StereoKit/Color/Color.html>
118 pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
119 Self { r, g, b, a }
120 }
121
122 /// Try hsv instead! But if you really need to create a color from RGB values, I suppose you’re in the right
123 /// place. All parameter values are generally in the range of 0-1. Alpha will be set to 1.0
124 /// <https://stereokit.net/Pages/StereoKit/Color/Color.html>
125 #[deprecated = "Use Color128::rgb instead"]
126 pub const fn new_rgb(r: f32, g: f32, b: f32) -> Self {
127 Self::new(r, g, b, 1.0)
128 }
129
130 /// Try hsv instead! But if you really need to create a color from RGB values, I suppose you’re in the right
131 /// place. All parameter values are generally in the range of 0-1. Alpha will be set to 1.0
132 /// <https://stereokit.net/Pages/StereoKit/Color/Color.html>
133 pub const fn rgb(r: f32, g: f32, b: f32) -> Self {
134 Self::new(r, g, b, 1.0)
135 }
136
137 /// Creates a Red/Green/Blue gamma space color from Hue/Saturation/Value information.
138 /// <https://stereokit.net/Pages/StereoKit/Color/HSV.html>
139 pub fn hsv(hue: f32, saturation: f32, value: f32, transparency: f32) -> Color128 {
140 unsafe { color_hsv(hue, saturation, value, transparency) }
141 }
142
143 /// Creates a Red/Green/Blue gamma space color from Hue/Saturation/Value information.
144 /// <https://stereokit.net/Pages/StereoKit/Color/HSV.html>
145 ///
146 /// see also [`color_hsv`]
147 pub fn hsv_vec3(vec: impl Into<Vec3>, transparency: f32) -> Self {
148 let vec = vec.into();
149 unsafe { color_hsv(vec.x, vec.y, vec.z, transparency) }
150 }
151
152 /// Creates a gamma space RGB color from a CIE-Lab color space. CIE-Lab is a color space that models human
153 /// perception, and has significantly more accurate to perception lightness values, so this is an excellent color
154 /// space for color operations that wish to preserve color brightness properly.
155 ///
156 /// Traditionally, values are L `[0,100], a,b [-200,+200]` but here we normalize them all to the 0-1 range. If you
157 /// hate it, let me know why!
158 /// <https://stereokit.net/Pages/StereoKit/Color/LAB.html>
159 ///
160 /// see also [`color_lab`]
161 pub fn lab(l: f32, a: f32, b: f32, transparency: f32) -> Self {
162 unsafe { color_lab(l, a, b, transparency) }
163 }
164
165 /// Creates a gamma space RGB color from a CIE-Lab color space. CIE-Lab is a color space that models human
166 /// perception, and has significantly more accurate to perception lightness values, so this is an excellent color
167 /// space for color operations that wish to preserve color brightness properly.
168 ///
169 /// Traditionally, values are L `[0,100], a,b [-200,+200]` but here we normalize them all to the 0-1 range. If you
170 /// hate it, let me know why!
171 /// <https://stereokit.net/Pages/StereoKit/Color/LAB.html>
172 ///
173 /// see also [`color_lab`]
174 pub fn lab_vec3(vec: Vec3, transparency: f32) -> Self {
175 unsafe { color_lab(vec.x, vec.y, vec.z, transparency) }
176 }
177
178 /// Create a color from an integer based hex value! This can make it easier to copy over colors from the web. This
179 /// isn’t a string function though, so you’ll need to fill it out the whole way. Ex: Color32::Hex(0x0000FFFF) would
180 /// be RGBA(0,0,1,1).
181 /// <https://stereokit.net/Pages/StereoKit/Color/Hex.html>
182 pub fn hex(hex_value: u32) -> Self {
183 Self {
184 r: (hex_value >> 24) as f32 / 255.0,
185 g: ((hex_value >> 16) & 0x000000FF) as f32 / 255.0,
186 b: ((hex_value >> 8) & 0x000000FF) as f32 / 255.0,
187 a: (hex_value & 0x000000FF) as f32 / 255.0,
188 }
189 }
190
191 /// This will linearly blend between two different colors! Best done on linear colors, rather than gamma corrected
192 /// colors, but will work either way. This will not clamp the percentage to the 0-1 range.
193 /// <https://stereokit.net/Pages/StereoKit/Color/Lerp.html>
194 /// * `a` - The first color, this will be the result if t is 0.
195 /// * `b` - The second color, this will be the result if t is 1.
196 /// * `t` - A percentage representing the blend between a and b. This is not clamped to the 0-1 range, and will
197 /// result in extrapolation outside this range.
198 ///
199 /// see also [`crate::maths::lerp`]
200 /// ### Examples
201 /// ```
202 /// use stereokit_rust::{util::{Color128, named_colors}};
203 ///
204 /// let color_red: Color128 = named_colors::RED.into();
205 /// let color_blue: Color128 = named_colors::BLUE.into();
206 /// let color_mix = Color128::lerp(color_red, color_blue, 0.25);
207 /// assert_eq!(color_mix, Color128::rgb(0.75, 0.0, 0.25));
208 /// ```
209 pub fn lerp(a: Color128, b: Color128, t: f32) -> Self {
210 Self { r: lerp(a.r, b.r, t), g: lerp(a.g, b.g, t), b: lerp(a.b, b.b, t), a: lerp(a.a, b.a, t) }
211 }
212
213 /// Converts this from a gamma space color, into a linear space color! If this is not a gamma space color, this
214 /// will just make your color wacky!
215 /// <https://stereokit.net/Pages/StereoKit/Color/ToLinear.html>
216 ///
217 /// see also [`color_to_linear`]
218 /// ### Examples
219 /// ```
220 /// use stereokit_rust::{util::{Color128, named_colors}};
221 ///
222 /// let color = Color128::rgb(0.75, 0.0, 0.25);
223 /// let color_linear = color.to_linear();
224 /// assert_eq!(color_linear, Color128 { r: 0.5310492, g: 0.0, b: 0.04736614, a: 1.0 });
225 /// ```
226 pub fn to_linear(&self) -> Self {
227 unsafe { color_to_linear(*self) }
228 }
229
230 /// Converts this from a linear space color, into a gamma space color! If this is not a linear space color, this
231 /// will just make your color wacky!
232 /// <https://stereokit.net/Pages/StereoKit/Color/ToGamma.html>
233 ///
234 /// see also [`color_to_gamma`]
235 /// ### Examples
236 /// ```
237 /// use stereokit_rust::{util::{Color128, named_colors}};
238 ///
239 /// let color = Color128 { r: 0.5310492, g: 0.0, b: 0.04736614, a: 1.0 };
240 /// let color_gamma = color.to_gamma();
241 /// assert_eq!(color_gamma, Color128::rgb(0.75, 0.0, 0.25));
242 /// ```
243 pub fn to_gamma(&self) -> Self {
244 unsafe { color_to_gamma(*self) }
245 }
246
247 /// Converts the gamma space color to a Hue/Saturation/Value format! Does not consider transparency when
248 /// calculating the result.
249 /// <https://stereokit.net/Pages/StereoKit/Color/ToHSV.html>
250 ///
251 /// Returns a Vec3 containing Hue, Saturation, and Value, stored in x, y, and z respectively. All values are
252 /// between 0-1.
253 /// see also [`color_to_hsv`]
254 /// ### Examples
255 /// ```
256 /// use stereokit_rust::{util::{Color128, named_colors}, maths::Vec3};
257 ///
258 /// let color = Color128::rgb(0.75, 0.0, 0.25);
259 /// let color_hsv = color.to_hsv();
260 /// assert_eq!(color_hsv, Vec3::new(0.9444444, 1.0, 0.75));
261 /// ```
262 pub fn to_hsv(&self) -> Vec3 {
263 unsafe { color_to_hsv(self) }
264 }
265
266 /// Converts the gamma space RGB color to a CIE LAB color space value! Conversion back and forth from LAB space
267 /// could be somewhat lossy.
268 /// <https://stereokit.net/Pages/StereoKit/Color/ToLAB.html>
269 ///
270 /// Returns a LAB Vec3 where x=L, y=A, z=B.
271 /// see also [`color_to_lab`]
272 /// ### Examples
273 /// ```
274 /// use stereokit_rust::{util::{Color128, named_colors}, maths::Vec3};
275 ///
276 /// let color = Color128::rgb(0.75, 0.0, 0.25);
277 /// let color_lab = color.to_lab();
278 /// assert_eq!(color_lab, Vec3{ x: 0.403711, y: 0.6654343, z: 0.55437124 });
279 /// ```
280 pub fn to_lab(&self) -> Vec3 {
281 unsafe { color_to_lab(self) }
282 }
283}
284
285impl Display for Color128 {
286 /// Mostly for debug purposes, this is a decent way to log or inspect the color in debug mode. Looks like
287 /// “Color128 {r: 1.0, g: 1.0, b: 1.0, a: 1.0}”
288 /// <https://stereokit.net/Pages/StereoKit/Color/ToString.html>
289 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290 write!(f, "r:{}, g:{}, b:{}, a:{}", self.r, self.g, self.b, self.a)
291 }
292}
293
294/// This will add a color component-wise with another color, including alpha. Best done on colors in linear space.
295/// No clamping is applied.
296/// <https://stereokit.net/Pages/StereoKit/Color/op_Addition.html>
297impl Add for Color128 {
298 type Output = Self;
299
300 #[inline]
301 fn add(self, rhs: Self) -> Self {
302 Color128 { r: self.r + rhs.r, g: self.g + rhs.g, b: self.b + rhs.b, a: self.a + rhs.a }
303 }
304}
305
306/// This will add a color component-wise with another color, including alpha. Best done on colors in linear space.
307/// No clamping is applied.
308/// <https://stereokit.net/Pages/StereoKit/Color/op_Addition.html>
309impl AddAssign for Color128 {
310 #[inline]
311 fn add_assign(&mut self, rhs: Self) {
312 self.r += rhs.r;
313 self.g += rhs.g;
314 self.b += rhs.b;
315 self.a += rhs.a;
316 }
317}
318
319/// This will subtract color b component-wise from color a, including alpha. Best done on colors in linear space. No
320/// clamping is applied.
321/// <https://stereokit.net/Pages/StereoKit/Color/op_Subtraction.html>
322impl Sub for Color128 {
323 type Output = Self;
324
325 #[inline]
326 fn sub(self, rhs: Self) -> Self {
327 Color128 { r: self.r - rhs.r, g: self.g - rhs.g, b: self.b - rhs.b, a: self.a - rhs.a }
328 }
329}
330
331/// This will subtract a color component-wise from another color, including alpha. Best done on colors in linear space.
332/// No clamping is applied.
333/// <https://stereokit.net/Pages/StereoKit/Color/op_Subtraction.html>
334impl SubAssign for Color128 {
335 #[inline]
336 fn sub_assign(&mut self, rhs: Self) {
337 self.r -= rhs.r;
338 self.g -= rhs.g;
339 self.b -= rhs.b;
340 self.a -= rhs.a;
341 }
342}
343
344/// This will divide a color component-wise against another color, including alpha. Best done on colors in linear space.
345/// No clamping is applied.
346/// <https://stereokit.net/Pages/StereoKit/Color/op_Division.html>
347impl Div<Color128> for Color128 {
348 type Output = Self;
349 #[inline]
350 fn div(self, rhs: Self) -> Self::Output {
351 Self { r: self.r.div(rhs.r), g: self.g.div(rhs.g), b: self.b.div(rhs.b), a: self.a.div(rhs.a) }
352 }
353}
354
355/// This will divide a color component-wise against another color, including alpha. Best done on colors in linear space.
356/// No clamping is applied.
357/// <https://stereokit.net/Pages/StereoKit/Color/op_Division.html>
358impl DivAssign<Color128> for Color128 {
359 #[inline]
360 fn div_assign(&mut self, rhs: Self) {
361 self.r.div_assign(rhs.r);
362 self.g.div_assign(rhs.g);
363 self.b.div_assign(rhs.b);
364 self.a.div_assign(rhs.a);
365 }
366}
367
368/// This will divide a color linearly, including alpha. Best done on a color in linear space. No clamping is applied.
369/// <https://stereokit.net/Pages/StereoKit/Color/op_Division.html>
370impl Div<f32> for Color128 {
371 type Output = Self;
372 #[inline]
373 fn div(self, rhs: f32) -> Self::Output {
374 Self { r: self.r.div(rhs), g: self.g.div(rhs), b: self.b.div(rhs), a: self.a.div(rhs) }
375 }
376}
377
378/// This will divide a color linearly, including alpha. Best done on a color in linear space. No clamping is applied.
379/// <https://stereokit.net/Pages/StereoKit/Color/op_Division.html>
380impl DivAssign<f32> for Color128 {
381 #[inline]
382 fn div_assign(&mut self, rhs: f32) {
383 self.r.div_assign(rhs);
384 self.g.div_assign(rhs);
385 self.b.div_assign(rhs);
386 self.a.div_assign(rhs);
387 }
388}
389
390/// This will divide a color linearly, including alpha. Best done on a color in linear space. No clamping is applied.
391/// <https://stereokit.net/Pages/StereoKit/Color/op_Division.html>
392impl Div<Color128> for f32 {
393 type Output = Color128;
394 #[inline]
395 fn div(self, rhs: Color128) -> Self::Output {
396 Self::Output { r: self.div(rhs.r), g: self.div(rhs.g), b: self.div(rhs.b), a: self.div(rhs.a) }
397 }
398}
399
400/// This will multiply a color component-wise against another color, including alpha. Best done on colors in linear
401/// space. No clamping is applied.
402/// <https://stereokit.net/Pages/StereoKit/Color/op_Multiply.html>
403impl Mul<Color128> for Color128 {
404 type Output = Self;
405 #[inline]
406 fn mul(self, rhs: Self) -> Self::Output {
407 Self { r: self.r.mul(rhs.r), g: self.g.mul(rhs.g), b: self.b.mul(rhs.b), a: self.a.mul(rhs.a) }
408 }
409}
410
411/// This will multiply a color component-wise against another color, including alpha. Best done on colors in linear
412/// space. No clamping is applied.
413/// <https://stereokit.net/Pages/StereoKit/Color/op_Multiply.html>
414impl MulAssign<Color128> for Color128 {
415 #[inline]
416 fn mul_assign(&mut self, rhs: Self) {
417 self.r.mul_assign(rhs.r);
418 self.g.mul_assign(rhs.g);
419 self.b.mul_assign(rhs.b);
420 self.a.mul_assign(rhs.a);
421 }
422}
423
424/// This will multiply a color linearly, including alpha. Best done on a color in linear space. No clamping is applied.
425/// <https://stereokit.net/Pages/StereoKit/Color/op_Multiply.html>
426impl Mul<f32> for Color128 {
427 type Output = Self;
428 #[inline]
429 fn mul(self, rhs: f32) -> Self::Output {
430 Self { r: self.r.mul(rhs), g: self.g.mul(rhs), b: self.b.mul(rhs), a: self.a.mul(rhs) }
431 }
432}
433
434/// This will multiply a color linearly, including alpha. Best done on a color in linear space. No clamping is applied.
435/// <https://stereokit.net/Pages/StereoKit/Color/op_Multiply.html>
436impl MulAssign<f32> for Color128 {
437 #[inline]
438 fn mul_assign(&mut self, rhs: f32) {
439 self.r.mul_assign(rhs);
440 self.g.mul_assign(rhs);
441 self.b.mul_assign(rhs);
442 self.a.mul_assign(rhs);
443 }
444}
445
446/// This will multiply a color linearly, including alpha. Best done on a color in linear space. No clamping is applied.
447/// <https://stereokit.net/Pages/StereoKit/Color/op_Multiply.html>
448impl Mul<Color128> for f32 {
449 type Output = Color128;
450 #[inline]
451 fn mul(self, rhs: Color128) -> Self::Output {
452 Self::Output { r: self.mul(rhs.r), g: self.mul(rhs.g), b: self.mul(rhs.b), a: self.mul(rhs.a) }
453 }
454}
455
456/// A 32 bit color struct! This is often directly used by StereoKit data structures, and so is often necessary for
457/// setting texture data, or mesh data. Note that the Color type implicitly converts to Color32, so you can use the
458/// static methods there to create Color32 values!
459///
460/// It’s generally best to avoid doing math on 32-bit color values, as they lack the precision necessary to create
461/// results. It’s best to think of a Color32 as an optimized end stage format of a color.
462/// <https://stereokit.net/Pages/StereoKit/Color32.html>
463///
464/// see also [`Color128`]
465/// ### Examples
466/// ```
467/// use stereokit_rust::util::{Color32, named_colors, Color128};
468///
469/// // Cyan from different sources:
470/// let color_cyan1 = named_colors::CYAN;
471/// let color_cyan2: Color32 = [0, 255, 255, 255].into();
472/// let color_cyan3: Color32 = Color128 { r: 0.0, g: 1.0, b: 1.0, a: 1.0 }.into();
473/// let color_cyan4 = Color32 {r: 0, g: 255, b: 255, a: 255};
474/// let color_cyan5 = Color32::new (0, 255, 255, 255);
475/// let color_cyan6 = Color32::rgba(0, 255, 255, 255);
476/// let color_cyan7 = Color32::rgb (0, 255, 255 );
477/// let color_cyan8 = Color32::hex (0x00FFFFFF);
478///
479/// # assert_eq!(color_cyan1, color_cyan2);
480/// # assert_eq!(color_cyan1, color_cyan3);
481/// # assert_eq!(color_cyan1, color_cyan4);
482/// # assert_eq!(color_cyan1, color_cyan5);
483/// # assert_eq!(color_cyan1, color_cyan6);
484/// # assert_eq!(color_cyan1, color_cyan7);
485/// assert_eq!(color_cyan1, color_cyan8);
486/// ```
487#[repr(C)]
488#[derive(Debug, Copy, Clone, Default, PartialEq)]
489pub struct Color32 {
490 pub r: u8,
491 pub g: u8,
492 pub b: u8,
493 pub a: u8,
494}
495
496impl From<Color128> for Color32 {
497 fn from(a: Color128) -> Self {
498 Self::new((a.r * 255.0) as u8, (a.g * 255.0) as u8, (a.b * 255.0) as u8, (a.a * 255.0) as u8)
499 }
500}
501impl From<[u8; 4]> for Color32 {
502 fn from(s: [u8; 4]) -> Self {
503 Self::new(s[0], s[1], s[2], s[3])
504 }
505}
506
507impl Color32 {
508 /// Opaque black color.
509 pub const BLACK: Color32 = Color32 { r: 0, g: 0, b: 0, a: 255 };
510
511 /// transparent black color.
512 pub const BLACK_TRANSPARENT: Color32 = Color32 { r: 0, g: 0, b: 0, a: 0 };
513
514 /// Opaque white color.
515 pub const WHITE: Color32 = Color32 { r: 255, g: 255, b: 255, a: 255 };
516
517 /// Constructs a 32-bit color from bytes! You may also be interested in Color32::hex.
518 /// <https://stereokit.net/Pages/StereoKit/Color32/Color32.html>
519 pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
520 Self { r, g, b, a }
521 }
522
523 /// Constructs a 32-bit color from bytes! You may also be interested in Color32::hex.
524 /// <https://stereokit.net/Pages/StereoKit/Color32/Color32.html>
525 pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
526 Self { r, g, b, a }
527 }
528
529 /// Constructs a 32-bit color from bytes! You may also be interested in Color32::hex.
530 /// <https://stereokit.net/Pages/StereoKit/Color32/Color32.html>
531 pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
532 Self { r, g, b, a: 255 }
533 }
534
535 /// Constructs a 32-bit color from bytes! You may also be interested in Color32::hex.
536 /// a is set to 255 !
537 /// <https://stereokit.net/Pages/StereoKit/Color32/Color32.html>
538 #[deprecated = "Use Color32::rgb instead"]
539 pub const fn new_rgb(r: u8, g: u8, b: u8) -> Self {
540 Self { r, g, b, a: 255 }
541 }
542
543 /// Create a color from an integer based hex value! This can make it easier to copy over colors from the web. This
544 /// isn’t a string function though, so you’ll need to fill it out the whole way. Ex: Color.Hex(0x0000FFFF) would be
545 /// RGBA(0,0,255,255).
546 /// <https://stereokit.net/Pages/StereoKit/Color32/Hex.html>
547 pub fn hex(hex_value: u32) -> Self {
548 Self {
549 r: (hex_value >> 24) as u8,
550 g: ((hex_value >> 16) & 0x000000FF) as u8,
551 b: ((hex_value >> 8) & 0x000000FF) as u8,
552 a: (hex_value & 0x000000FF) as u8,
553 }
554 }
555}
556
557impl Display for Color32 {
558 /// Mostly for debug purposes, this is a decent way to log or inspect the color in debug mode. Looks like
559 /// “Color32{r: 255, g: 255, b: 255, a: 255}”
560 /// <https://stereokit.net/Pages/StereoKit/Color/ToString.html>
561 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
562 write!(f, "r:{}, g:{}, b:{}, a:{}", self.r, self.g, self.b, self.a)
563 }
564}
565
566/// Named colors <https://www.w3.org/wiki/CSS/Properties/color/keywords>
567pub mod named_colors {
568 use super::Color32;
569
570 // Basic :
571 /// Black color rgb(0, 0, 0)
572 pub const BLACK: Color32 = Color32::rgb(0, 0, 0);
573 /// silver color rgb(192, 192, 192)
574 pub const SILVER: Color32 = Color32::rgb(192, 192, 192);
575 /// gray color rgb(128, 128, 128)
576 pub const GRAY: Color32 = Color32::rgb(128, 128, 128);
577 /// white color rgb(255, 255, 255)
578 pub const WHITE: Color32 = Color32::rgb(255, 255, 255);
579 /// maroon color rgb(128, 0, 0)
580 pub const MAROON: Color32 = Color32::rgb(128, 0, 0);
581 /// red color rgb(255, 0, 0)
582 pub const RED: Color32 = Color32::rgb(255, 0, 0);
583 /// purple color rgb(128, 0, 128)
584 pub const PURPLE: Color32 = Color32::rgb(128, 0, 128);
585 /// fuchsia color rgb(255, 0, 255)
586 pub const FUCHSIA: Color32 = Color32::rgb(255, 0, 255);
587 /// green color rgb(0, 128, 0)
588 pub const GREEN: Color32 = Color32::rgb(0, 128, 0);
589 /// lime color rgb(0, 255, 0)
590 pub const LIME: Color32 = Color32::rgb(0, 255, 0);
591 /// olive color rgb(128, 128, 0)
592 pub const OLIVE: Color32 = Color32::rgb(128, 128, 0);
593 /// yellow color rgb(255, 255, 0)
594 pub const YELLOW: Color32 = Color32::rgb(255, 255, 0);
595 /// navy color rgb(0, 0, 128)
596 pub const NAVY: Color32 = Color32::rgb(0, 0, 128);
597 /// blue color rgb(0, 0, 255)
598 pub const BLUE: Color32 = Color32::rgb(0, 0, 255);
599 /// teal color rgb(0, 128, 128)
600 pub const TEAL: Color32 = Color32::rgb(0, 128, 128);
601 /// aqua color rgb(0, 255, 255)
602 pub const AQUA: Color32 = Color32::rgb(0, 255, 255);
603
604 //Extended
605 /// alice blue color rgb(240, 248, 255)
606 pub const ALICE_BLUE: Color32 = Color32::rgb(240, 248, 255);
607 /// antique white color rgb(250, 235, 215)
608 pub const ANTIQUE_WHITE: Color32 = Color32::rgb(250, 235, 215);
609 /// aquamarine color rgb(127, 255, 212)
610 pub const AQUAMARINE: Color32 = Color32::rgb(127, 255, 212);
611 /// azure color rgb(240, 255, 255)
612 pub const AZURE: Color32 = Color32::rgb(240, 255, 255);
613 /// beige color rgb(255, 228, 196)
614 pub const BEIGE: Color32 = Color32::rgb(255, 228, 196);
615 /// bisque color rgb(255, 228, 196)
616 pub const BISQUE: Color32 = Color32::rgb(255, 228, 196);
617 /// blanched almond color rgb(255, 235, 205)
618 pub const BLANCHED_ALMOND: Color32 = Color32::rgb(255, 235, 205);
619 /// blue violet color rgb(138, 43, 226)
620 pub const BLUE_VIOLET: Color32 = Color32::rgb(138, 43, 226);
621 /// brown color rgb(165, 42, 42)
622 pub const BROWN: Color32 = Color32::rgb(165, 42, 42);
623 /// burlywood color rgb(222, 184, 135)
624 pub const BURLY_WOOD: Color32 = Color32::rgb(222, 184, 135);
625 /// cadet blue color rgb(95, 158, 160)
626 pub const CADET_BLUE: Color32 = Color32::rgb(95, 158, 160);
627 /// chartreuse color rgb(127, 255, 0)
628 pub const CHARTREUSE: Color32 = Color32::rgb(127, 255, 0);
629 /// chocolate color rgb(210, 105, 30)
630 pub const CHOCOLATE: Color32 = Color32::rgb(210, 105, 30);
631 /// coral color rgb(255, 127, 80)
632 pub const CORAL: Color32 = Color32::rgb(255, 127, 80);
633 /// cornflower blue color rgb(100, 149, 237)
634 pub const CORNFLOWER_BLUE: Color32 = Color32::rgb(100, 149, 237);
635 /// cornsilk color rgb(255, 248, 220)
636 pub const CORN_SILK: Color32 = Color32::rgb(255, 248, 220);
637 /// crimson color rgb(220, 20, 60)
638 pub const CRIMSON: Color32 = Color32::rgb(220, 20, 60);
639 /// cyan color rgb(0, 255, 255)
640 pub const CYAN: Color32 = Color32::rgb(0, 255, 255);
641 /// dark blue color rgb(0, 0, 139)
642 pub const DARK_BLUE: Color32 = Color32::rgb(0, 0, 139);
643 /// dark cyan color rgb(0, 139, 139)
644 pub const DARK_CYAN: Color32 = Color32::rgb(0, 139, 139);
645 /// dark goldenrod color rgb(184, 134, 11)
646 pub const DARK_GOLDEN_ROD: Color32 = Color32::rgb(184, 134, 11);
647 /// dark gray color rgb(169, 169, 169)
648 pub const DARK_GRAY: Color32 = Color32::rgb(169, 169, 169);
649 /// dark green color rgb(0, 100, 0)
650 pub const DARK_GREEN: Color32 = Color32::rgb(0, 100, 0);
651 /// dark grey color rgb(169, 169, 169)
652 pub const DARK_GREY: Color32 = Color32::rgb(169, 169, 169);
653 /// dark khaki color rgb(189, 183, 107)
654 pub const DARK_KHAKI: Color32 = Color32::rgb(189, 183, 107);
655 /// dark magenta color rgb(139, 0, 139)
656 pub const DARK_MAGENTA: Color32 = Color32::rgb(139, 0, 139);
657 /// dark olive green color rgb(85, 107, 47)
658 pub const DARK_OLIVE_GREEN: Color32 = Color32::rgb(85, 107, 47);
659 /// dark orange color rgb(255, 140, 0)
660 pub const DARK_ORANGE: Color32 = Color32::rgb(255, 140, 0);
661 /// dark orchid color rgb(153, 50, 204)
662 pub const DARK_ORCHID: Color32 = Color32::rgb(153, 50, 204);
663 /// dark red color rgb(139, 0, 0)
664 pub const DARK_RED: Color32 = Color32::rgb(139, 0, 0);
665 /// dark salmon color rgb(233, 150, 122)
666 pub const DARK_SALMON: Color32 = Color32::rgb(233, 150, 122);
667 /// dark sea green color rgb(143, 188, 143)
668 pub const DARK_SEA_GREEN: Color32 = Color32::rgb(143, 188, 143);
669 /// dark slate blue color rgb(72, 61, 139)
670 pub const DARK_SLATE_BLUE: Color32 = Color32::rgb(72, 61, 139);
671 /// dark slate gray color rgb(47, 79, 79)
672 pub const DARK_SLATE_GRAY: Color32 = Color32::rgb(47, 79, 79);
673 /// dark slate grey color rgb(47, 79, 79)
674 pub const DARK_SLATE_GREY: Color32 = Color32::rgb(47, 79, 79);
675 /// dark turquoise color rgb(0, 206, 209)
676 pub const DARK_TURQUOISE: Color32 = Color32::rgb(0, 206, 209);
677 /// dark violet color rgb(148, 0, 211)
678 pub const DARK_VIOLET: Color32 = Color32::rgb(148, 0, 211);
679 /// deep pink color rgb(255, 20, 147)
680 pub const DEEP_PINK: Color32 = Color32::rgb(255, 20, 147);
681 /// deep sky blue color rgb(0, 191, 255)
682 pub const DEEP_SKY_BLUE: Color32 = Color32::rgb(0, 191, 255);
683 /// dim gray color rgb(105, 105, 105)
684 pub const DIM_GRAY: Color32 = Color32::rgb(105, 105, 105);
685 /// dim grey color rgb(105, 105, 105)
686 pub const DIM_GREY: Color32 = Color32::rgb(105, 105, 105);
687 /// dodger blue color rgb(30, 144, 255)
688 pub const DOGER_BLUE: Color32 = Color32::rgb(30, 144, 255);
689 /// fire brick color rgb(178, 34, 34)
690 pub const FIRE_BRICK: Color32 = Color32::rgb(178, 34, 34);
691 /// floral white color rgb(255, 250, 240)
692 pub const FLORAL_WHITE: Color32 = Color32::rgb(255, 250, 240);
693 /// forest green color rgb(34, 139, 34)
694 pub const FOREST_GREEN: Color32 = Color32::rgb(34, 139, 34);
695 /// gainsboro color rgb(220, 220, 220)
696 pub const GAINSBORO: Color32 = Color32::rgb(220, 220, 220);
697 /// ghost white color rgb(248, 248, 255)
698 pub const GHOST_WHITE: Color32 = Color32::rgb(248, 248, 255);
699 /// gold color rgb(255, 215, 0)
700 pub const GOLD: Color32 = Color32::rgb(255, 215, 0);
701 /// goldenrod color rgb(218, 165, 32)
702 pub const GOLDENROD: Color32 = Color32::rgb(218, 165, 32);
703 /// green yellow color rgb(173, 255, 47)
704 pub const GREEN_YELLOW: Color32 = Color32::rgb(173, 255, 47);
705 /// grey color rgb(128, 128, 128)
706 pub const GREY: Color32 = Color32::rgb(128, 128, 128);
707 /// honeydew color rgb(240, 255, 240)
708 pub const HONEYDEW: Color32 = Color32::rgb(240, 255, 240);
709 /// hot pink color rgb(255, 105, 180)
710 pub const HOT_PINK: Color32 = Color32::rgb(255, 105, 180);
711 /// indian red color rgb(205, 92, 92)
712 pub const INDIAN_RED: Color32 = Color32::rgb(205, 92, 92);
713 /// indigo color rgb(75, 0, 130)
714 pub const INDIGO: Color32 = Color32::rgb(75, 0, 130);
715 /// ivory color rgb(255, 255, 240)
716 pub const IVORY: Color32 = Color32::rgb(255, 255, 240);
717 /// khaki color rgb(240, 230, 140)
718 pub const KHAKI: Color32 = Color32::rgb(240, 230, 140);
719 /// lavender color rgb(230, 230, 250)
720 pub const LAVENDER: Color32 = Color32::rgb(230, 230, 250);
721 /// lavender blush color rgb(255, 240, 245)
722 pub const LAVENDER_BLUSH: Color32 = Color32::rgb(255, 240, 245);
723 /// lawn green color rgb(124, 242, 0)
724 pub const LAWN_GREEN: Color32 = Color32::rgb(124, 242, 0);
725 /// lemon chiffon color rgb(255, 250, 205)
726 pub const LEMON_CHIFFON: Color32 = Color32::rgb(255, 250, 205);
727 /// light blue color rgb(173, 216, 230)
728 pub const LIGHT_BLUE: Color32 = Color32::rgb(173, 216, 230);
729 /// light coral color rgb(240, 128, 128)
730 pub const LIGHT_CORAL: Color32 = Color32::rgb(240, 128, 128);
731 /// light cyan color rgb(224, 255, 255)
732 pub const LIGHT_CYAN: Color32 = Color32::rgb(224, 255, 255);
733 /// light goldenrod yellow color rgb(250, 250, 210)
734 pub const LIGHT_GOLDENROD_YELLOW: Color32 = Color32::rgb(250, 250, 210);
735 /// light gray color rgb(211, 211, 211)
736 pub const LIGHT_GRAY: Color32 = Color32::rgb(211, 211, 211);
737 /// light green color rgb(144, 238, 144)
738 pub const LIGHT_GREEN: Color32 = Color32::rgb(144, 238, 144);
739 /// light grey color rgb(211, 211, 211)
740 pub const LIGHT_GREY: Color32 = Color32::rgb(211, 211, 211);
741 /// light pink color rgb(255, 182, 193)
742 pub const LIGHT_PINK: Color32 = Color32::rgb(255, 182, 193);
743 /// light salmon color rgb(255, 160, 122)
744 pub const LIGHT_SALMON: Color32 = Color32::rgb(255, 160, 122);
745 /// light sea green color rgb(32, 178, 170)
746 pub const LIGHT_SEA_GREEN: Color32 = Color32::rgb(32, 178, 170);
747 /// light sky blue color rgb(135, 206, 250)
748 pub const LIGHT_SKY_BLUE: Color32 = Color32::rgb(135, 206, 250);
749 /// light slate gray color rgb(119, 136, 153)
750 pub const LIGHT_SLATE_GRAY: Color32 = Color32::rgb(119, 136, 153);
751 /// light steel blue color rgb(176, 196, 222)
752 pub const LIGHT_STEEL_BLUE: Color32 = Color32::rgb(176, 196, 222);
753 /// light yellow color rgb(255, 255, 224)
754 pub const LIGHT_YELLOW: Color32 = Color32::rgb(255, 255, 224);
755 /// lime green color rgb(50, 205, 50)
756 pub const LIME_GREEN: Color32 = Color32::rgb(50, 205, 50);
757 /// linen color rgb(250, 240, 230)
758 pub const LINEN: Color32 = Color32::rgb(250, 240, 230);
759 /// magenta color rgb(255, 0, 255)
760 pub const MAGENTA: Color32 = Color32::rgb(255, 0, 255);
761 /// medium aquamarine color rgb(102, 205, 170)
762 pub const MEDIUM_AQUAMARINE: Color32 = Color32::rgb(102, 205, 170);
763 /// medium blue color rgb(0, 0, 205)
764 pub const MEDIUM_BLUE: Color32 = Color32::rgb(0, 0, 205);
765 /// medium orchid color rgb(186, 85, 211)
766 pub const MEDIUM_ORCHID: Color32 = Color32::rgb(186, 85, 211);
767 /// medium purple color rgb(147, 112, 219)
768 pub const MEDIUM_PURPLE: Color32 = Color32::rgb(147, 112, 219);
769 /// medium sea green color rgb(60, 179, 113)
770 pub const MEDIUM_SEA_GREEN: Color32 = Color32::rgb(60, 179, 113);
771 /// medium slate blue color rgb(123, 104, 238)
772 pub const MEDIUM_SLATE_BLUE: Color32 = Color32::rgb(123, 104, 238);
773 /// medium spring green color rgb(0, 250, 154)
774 pub const MEDIUM_SPRING_GREEN: Color32 = Color32::rgb(0, 250, 154);
775 /// medium turquoise color rgb(72, 209, 204)
776 pub const MEDIUM_TURQUOISE: Color32 = Color32::rgb(72, 209, 204);
777 /// medium violet red color rgb(199, 21, 133)
778 pub const MEDIUM_VIOLET_RED: Color32 = Color32::rgb(199, 21, 133);
779 /// midnight blue color rgb(25, 25, 112)
780 pub const MIDNIGHT_BLUE: Color32 = Color32::rgb(25, 25, 112);
781 /// mint cream color rgb(245, 255, 250)
782 pub const MINT_CREAM: Color32 = Color32::rgb(245, 255, 250);
783 /// misty rose color rgb(255, 228, 225)
784 pub const MISTY_ROSE: Color32 = Color32::rgb(255, 228, 225);
785 /// moccasin color rgb(255, 228, 181)
786 pub const MOCCASIN: Color32 = Color32::rgb(255, 228, 181);
787 /// navajo white color rgb(255, 222, 173)
788 pub const NAVAJO_WHITE: Color32 = Color32::rgb(255, 222, 173);
789 /// old lace color rgb(253, 245, 230)
790 pub const OLD_LACE: Color32 = Color32::rgb(253, 245, 230);
791 /// olive drab color rgb(107, 142, 35)
792 pub const OLIVE_DRAB: Color32 = Color32::rgb(107, 142, 35);
793 /// orange color rgb(255, 165, 0)
794 pub const ORANGE: Color32 = Color32::rgb(255, 165, 0);
795 /// orange red color rgb(255, 69, 0)
796 pub const ORANGE_RED: Color32 = Color32::rgb(255, 69, 0);
797 /// orchid color rgb(218, 112, 214)
798 pub const ORCHID: Color32 = Color32::rgb(218, 112, 214);
799 /// pale golden rod color rgb(238, 232, 170)
800 pub const PALE_GOLDEN_ROD: Color32 = Color32::rgb(238, 232, 170);
801 /// pale green color rgb(152, 251, 152)
802 pub const PALE_GREEN: Color32 = Color32::rgb(152, 251, 152);
803 /// pale turquoise color rgb(175, 238, 238)
804 pub const PALE_TURQUOISE: Color32 = Color32::rgb(175, 238, 238);
805 /// pale violet red color rgb(219, 112, 147)
806 pub const PALE_VIOLET_RED: Color32 = Color32::rgb(219, 112, 147);
807 /// papaya whip color rgb(255, 239, 213)
808 pub const PAPAYAWHIP: Color32 = Color32::rgb(255, 239, 213);
809 /// peach puff color rgb(255, 218, 185)
810 pub const PEACH_PUFF: Color32 = Color32::rgb(255, 218, 185);
811 /// peru color rgb(205, 133, 63)
812 pub const PERU: Color32 = Color32::rgb(205, 133, 63);
813 /// pink color rgb(255, 192, 203)
814 pub const PINK: Color32 = Color32::rgb(255, 192, 203);
815 /// plum color rgb(221, 160, 221)
816 pub const PLUM: Color32 = Color32::rgb(221, 160, 221);
817 /// powder blue color rgb(176, 224, 230)
818 pub const POWDER_BLUE: Color32 = Color32::rgb(176, 224, 230);
819 /// rosy brown color rgb(188, 143, 143)
820 pub const ROSY_BROWN: Color32 = Color32::rgb(188, 143, 143);
821 /// royal blue color rgb(65, 105, 225)
822 pub const ROYAL_BLUE: Color32 = Color32::rgb(65, 105, 225);
823 /// saddle brown color rgb(139, 69, 19)
824 pub const SADDLE_BROWN: Color32 = Color32::rgb(139, 69, 19);
825 /// salmon color rgb(250, 128, 114)
826 pub const SALMON: Color32 = Color32::rgb(250, 128, 114);
827 /// sandy brown color rgb(244, 164, 96)
828 pub const SANDY_BROWN: Color32 = Color32::rgb(244, 164, 96);
829 /// sea green color rgb(46, 139, 87)
830 pub const SEA_GREEN: Color32 = Color32::rgb(46, 139, 87);
831 /// sea shell color rgb(255, 245, 238)
832 pub const SEA_SHELL: Color32 = Color32::rgb(255, 245, 238);
833 /// sienna color rgb(160, 82, 45)
834 pub const SIENNA: Color32 = Color32::rgb(160, 82, 45);
835 /// sky blue color rgb(135, 206, 235)
836 pub const SKY_BLUE: Color32 = Color32::rgb(135, 206, 235);
837 /// slate blue color rgb(106, 90, 205)
838 pub const SLATE_BLUE: Color32 = Color32::rgb(106, 90, 205);
839 /// slate gray color rgb(112, 128, 144)
840 pub const SLATE_GRAY: Color32 = Color32::rgb(112, 128, 144);
841 /// slate grey color rgb(112, 128, 144)
842 pub const SLATE_GREY: Color32 = Color32::rgb(112, 128, 144);
843 /// snow color rgb(255, 250, 250)
844 pub const SNOW: Color32 = Color32::rgb(255, 250, 250);
845 /// spring green color rgb(0, 255, 127)
846 pub const SPRING_GREEN: Color32 = Color32::rgb(0, 255, 127);
847 /// steel blue color rgb(70, 130, 180)
848 pub const STEEL_BLUE: Color32 = Color32::rgb(70, 130, 180);
849 /// tan color rgb(210, 180, 140)
850 pub const TAN: Color32 = Color32::rgb(210, 180, 140);
851 /// thistle color rgb(216, 191, 216)
852 pub const THISTLE: Color32 = Color32::rgb(216, 191, 216);
853 /// tomato color rgb(255, 99, 71)
854 pub const TOMATO: Color32 = Color32::rgb(255, 99, 71);
855 /// turquoise color rgb(64, 224, 208)
856 pub const TURQUOISE: Color32 = Color32::rgb(64, 224, 208);
857 /// violet color rgb(238, 130, 238)
858 pub const VIOLET: Color32 = Color32::rgb(238, 130, 238);
859 /// wheat color rgb(245, 222, 179)
860 pub const WHEAT: Color32 = Color32::rgb(245, 222, 179);
861 /// white smoke color rgb(245, 245, 245)
862 pub const WHITE_SMOKE: Color32 = Color32::rgb(245, 245, 245);
863 /// yellow green color rgb(154, 205, 50)
864 pub const YELLOW_GREEN: Color32 = Color32::rgb(154, 205, 50);
865}
866
867/// What type of user motion is the device capable of tracking? For the normal fully capable XR headset, this should be
868/// 6dof (rotation and translation), but more limited headsets may be restricted to 3dof (rotation) and flatscreen
869/// computers with the simulator off would be none.
870/// <https://stereokit.net/Pages/StereoKit/DeviceTracking.html>
871///
872/// see [`Device::get_tracking`]
873#[derive(Debug, Copy, Clone, PartialEq, Eq)]
874#[repr(u32)]
875pub enum DeviceTracking {
876 /// No tracking is available! This is likely a flatscreen application, not an XR applicaion.
877 None = 0,
878 /// This tracks rotation only, this may be a limited device without tracking cameras, or could be a more capable
879 /// headset in a 3dof mode. DoF stands for Degrees of Freedom.
880 Dof3 = 1,
881 /// This is capable of tracking both the position and rotation of the device, most fully featured XR headsets
882 /// (such as a HoloLens 2) will have this. DoF stands for Degrees of Freedom.
883 Dof6 = 2,
884}
885
886/// This describes a type of display hardware!
887/// <https://stereokit.net/Pages/StereoKit/DisplayType.html>
888///
889/// see [`Device::get_display_type`]
890#[derive(Debug, Copy, Clone, PartialEq, Eq)]
891#[repr(u32)]
892pub enum DisplayType {
893 /// Not a display at all, or the variable hasn’t been initialized properly yet.
894 None = 0,
895 /// This is a stereo display! It has 2 screens, or two sections that display content in stereo, one for each eye.
896 /// This could be a VR headset, or like a 3D tv.
897 Stereo = 1,
898 /// This is a single flat screen, with no stereo depth. This could be something like either a computer monitor, or a
899 /// phone with passthrough AR.
900 Flatscreen = 2,
901}
902
903/// TODO: waiting for C# implementation
904/// <https://stereokit.net/Pages/StereoKit/FovInfo.html>
905#[derive(Debug, Copy, Clone)]
906#[repr(C)]
907pub struct FovInfo {
908 pub left: f32,
909 pub right: f32,
910 pub top: f32,
911 pub bottom: f32,
912}
913
914/// This class describes the device that is running this application! It contains identifiers, capabilities, and a few
915/// adjustable settings here and there.
916/// <https://stereokit.net/Pages/StereoKit/Device.html>
917///
918/// ### Examples
919/// ```
920/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
921/// use stereokit_rust::{util::{Device, DisplayType, DeviceTracking}, sk::DisplayBlend};
922///
923/// let display_type = Device::get_display_type();
924/// let display_blend = Device::get_display_blend();
925/// let device_tracking = Device::get_tracking();
926/// let device_name = Device::get_name().unwrap();
927/// let device_runtime = Device::get_runtime().unwrap();
928/// let device_gpu = Device::get_gpu().unwrap();
929/// let has_eye_gaze = Device::has_eye_gaze();
930/// let has_hand_tracking = Device::has_hand_tracking();
931/// let valid_blend_none = Device::valid_blend(DisplayBlend::None);
932/// let display_blend_none= Device::display_blend(DisplayBlend::None);
933///
934/// xr_mode_stop_here!();
935/// // These are the expected results for offscreen tests on a PC:
936/// assert_eq!(display_type, DisplayType::Flatscreen);
937/// assert_eq!(display_blend, DisplayBlend::Opaque);
938/// assert_eq!(device_tracking, DeviceTracking::None);
939/// assert_eq!(device_name, "Offscreen");
940/// assert_eq!(device_runtime, "None");
941/// assert_ne!(device_gpu, "Name of your GPU");
942/// assert_eq!(has_eye_gaze, false);
943/// assert_eq!(has_hand_tracking, false);
944/// assert_eq!(valid_blend_none, false);
945/// assert_eq!(display_blend_none, false);
946/// ```
947pub struct Device;
948
949unsafe extern "C" {
950 pub fn device_display_get_type() -> DisplayType;
951 pub fn device_display_get_blend() -> DisplayBlend;
952 pub fn device_display_set_blend(blend: DisplayBlend) -> Bool32T;
953 pub fn device_display_valid_blend(blend: DisplayBlend) -> Bool32T;
954 pub fn device_display_get_refresh_rate() -> f32;
955 pub fn device_display_get_width() -> i32;
956 pub fn device_display_get_height() -> i32;
957 pub fn device_display_get_fov() -> FovInfo;
958 pub fn device_get_tracking() -> DeviceTracking;
959 pub fn device_get_name() -> *const c_char;
960 pub fn device_get_runtime() -> *const c_char;
961 pub fn device_get_gpu() -> *const c_char;
962 pub fn device_has_eye_gaze() -> Bool32T;
963 pub fn device_has_hand_tracking() -> Bool32T;
964}
965
966impl Device {
967 /// Allows you to set and get the current blend mode of the device! Setting this may not succeed if the blend mode
968 /// is not valid.
969 /// <https://stereokit.net/Pages/StereoKit/Device/DisplayBlend.html>
970 ///
971 /// see also [`device_display_set_blend`]
972 /// ### Examples
973 /// ```
974 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
975 /// use stereokit_rust::{util::Device, sk::DisplayBlend};
976 ///
977 /// // These are the expected results for tests on a PC:
978 /// assert_eq!(Device::get_display_blend(), DisplayBlend::Opaque);
979 ///
980 /// xr_mode_stop_here!();
981 /// assert_eq!(Device::display_blend(DisplayBlend::AnyTransparent), false);
982 /// assert_eq!(Device::display_blend(DisplayBlend::None), false);
983 /// assert_eq!(Device::display_blend(DisplayBlend::Additive), false);
984 /// ```
985 pub fn display_blend(blend: DisplayBlend) -> bool {
986 unsafe { device_display_set_blend(blend) != 0 }
987 }
988
989 /// Allows you to set and get the current blend mode of the device! Setting this may not succeed if the blend mode
990 /// is not valid.
991 /// <https://stereokit.net/Pages/StereoKit/Device/DisplayBlend.html>
992 ///
993 /// see also [`device_display_get_blend`]
994 /// see example in [`Device::display_blend`]
995 pub fn get_display_blend() -> DisplayBlend {
996 unsafe { device_display_get_blend() }
997 }
998
999 /// What type of display is this? Most XR headsets will report stereo, but the Simulator will report flatscreen.
1000 /// <https://stereokit.net/Pages/StereoKit/Device/DisplayType.html>
1001 ///
1002 /// see also [`device_display_get_type`]
1003 /// ### Examples
1004 /// ```
1005 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1006 /// use stereokit_rust::util::{Device, DisplayType};
1007 ///
1008 /// xr_mode_stop_here!();
1009 /// // These are the expected results for offscreen tests on a PC:
1010 /// assert_eq!(Device::get_display_type(), DisplayType::Flatscreen);
1011 /// ```
1012 pub fn get_display_type() -> DisplayType {
1013 unsafe { device_display_get_type() }
1014 }
1015
1016 /// This is the name of the OpenXR runtime that powers the current device! This can help you determine which
1017 /// implementation quirks to expect based on the codebase used. On the simulator, this will be "Simulator", and in
1018 /// other non-XR modes this will be "None".
1019 /// <https://stereokit.net/Pages/StereoKit/Device/Runtime.html>
1020 ///
1021 /// see also [`device_get_runtime`]
1022 /// ### Examples
1023 /// ```
1024 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1025 /// use stereokit_rust::util::Device;
1026 ///
1027 /// assert!(Device::get_runtime().is_ok());
1028 ///
1029 /// xr_mode_stop_here!();
1030 /// // These are the expected results for offscreen tests on a PC:
1031 /// assert_eq!(Device::get_runtime().unwrap(), "None");
1032 /// ```
1033 pub fn get_runtime<'a>() -> Result<&'a str, StereoKitError> {
1034 unsafe { CStr::from_ptr(device_get_runtime()) }
1035 .to_str()
1036 .map_err(|e| StereoKitError::CStrError(e.to_string()))
1037 }
1038
1039 /// The reported name of the GPU, this will differ between D3D and GL.
1040 /// <https://stereokit.net/Pages/StereoKit/Device/GPU.html>
1041 ///
1042 /// see also [`device_get_gpu`]
1043 pub fn get_gpu<'a>() -> Result<&'a str, StereoKitError> {
1044 unsafe { CStr::from_ptr(device_get_gpu()) }
1045 .to_str()
1046 .map_err(|e| StereoKitError::CStrError(e.to_string()))
1047 }
1048
1049 /// Does the device we’re on have eye tracking support present for input purposes? This is not an indicator that the
1050 /// user has given the application permission to access this information. See Input.Gaze for how to use this data.
1051 /// <https://stereokit.net/Pages/StereoKit/Device/HasEyeGaze.html>
1052 ///
1053 /// see also [`device_has_eye_gaze`]
1054 /// ### Examples
1055 /// ```
1056 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1057 /// use stereokit_rust::util::Device;
1058 ///
1059 /// let has_eye_gaze = Device::has_eye_gaze();
1060 ///
1061 /// xr_mode_stop_here!();
1062 /// // These are the expected results for offscreen tests on a PC:
1063 /// assert_eq!(has_eye_gaze, false);
1064 /// ```
1065 pub fn has_eye_gaze() -> bool {
1066 unsafe { device_has_eye_gaze() != 0 }
1067 }
1068
1069 /// Tells if the device is capable of tracking hands. This does not tell if the user is actually using their hands
1070 /// for input, merely that it’s possible to!
1071 /// <https://stereokit.net/Pages/StereoKit/Device/HasHandTracking.html>
1072 ///
1073 /// see also [`device_has_hand_tracking`]
1074 /// ### Examples
1075 /// ```
1076 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1077 /// use stereokit_rust::util::Device;
1078 ///
1079 /// let has_hand_tracking = Device::has_hand_tracking();
1080 ///
1081 /// xr_mode_stop_here!();
1082 /// // These are the expected results for offscreen tests on a PC:
1083 /// assert_eq!(has_hand_tracking, false);
1084 /// ```
1085 pub fn has_hand_tracking() -> bool {
1086 unsafe { device_has_hand_tracking() != 0 }
1087 }
1088
1089 /// This is the name of the active device! From OpenXR, this is the same as systemName from XrSystemProperties. The
1090 /// simulator will say “Simulator”.
1091 /// <https://stereokit.net/Pages/StereoKit/Device/Name.html>
1092 ///
1093 /// see also [`device_get_name`]
1094 /// ### Examples
1095 /// ```
1096 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1097 /// use stereokit_rust::util::Device;
1098 ///
1099 /// let name = Device::get_name();
1100 /// assert!(name.is_ok());
1101 ///
1102 /// xr_mode_stop_here!();
1103 /// // These are the expected results for offscreen tests on a PC:
1104 /// assert_eq!(Device::get_name().unwrap(), "Offscreen");
1105 /// ```
1106 pub fn get_name<'a>() -> Result<&'a str, StereoKitError> {
1107 unsafe { CStr::from_ptr(device_get_name()) }
1108 .to_str()
1109 .map_err(|e| StereoKitError::CStrError(e.to_string()))
1110 }
1111
1112 /// The tracking capabilities of this device! Is it 3DoF, rotation only? Or is it 6DoF, with positional tracking as
1113 /// well? Maybe it can’t track at all!
1114 /// <https://stereokit.net/Pages/StereoKit/Device/Tracking.html>
1115 ///
1116 /// see also [`device_get_tracking`]
1117 /// ### Examples
1118 /// ```
1119 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1120 /// use stereokit_rust::util::{Device, DeviceTracking};
1121 ///
1122 /// let tracking = Device::get_tracking();
1123 ///
1124 /// xr_mode_stop_here!();
1125 /// // These are the expected results for offscreen tests on a PC:
1126 /// assert_eq!(tracking, DeviceTracking::None);
1127 /// ```
1128 pub fn get_tracking() -> DeviceTracking {
1129 unsafe { device_get_tracking() }
1130 }
1131
1132 /// Tells if a particular blend mode is valid on this device. Some devices may be capable of more than one blend
1133 /// mode.
1134 /// <https://stereokit.net/Pages/StereoKit/Device/ValidBlend.html>
1135 ///
1136 /// see also [`device_display_valid_blend`]
1137 /// ### Examples
1138 /// ```
1139 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1140 /// use stereokit_rust::{util::Device, sk::DisplayBlend};
1141 ///
1142 /// assert_eq!(Device::valid_blend(DisplayBlend::Opaque), true);
1143 ///
1144 /// xr_mode_stop_here!();
1145 /// // These are the expected results for offscreen tests on a PC:
1146 /// assert_eq!(Device::valid_blend(DisplayBlend::None), false);
1147 /// assert_eq!(Device::valid_blend(DisplayBlend::Additive), false);
1148 /// assert_eq!(Device::valid_blend(DisplayBlend::Blend), false);
1149 /// assert_eq!(Device::valid_blend(DisplayBlend::AnyTransparent), false);
1150 /// ```
1151 pub fn valid_blend(blend: DisplayBlend) -> bool {
1152 unsafe { device_display_valid_blend(blend) != 0 }
1153 }
1154}
1155
1156/// A color/position pair for Gradient values!
1157/// <https://stereokit.net/Pages/StereoKit/GradientKey.html>
1158///
1159/// see [`Gradient`]
1160/// ### Examples
1161/// ```
1162/// use stereokit_rust::util::{GradientKey, Color128, named_colors};
1163///
1164/// let key0 = GradientKey::new(named_colors::GOLD, 0.75);
1165/// let key1 = GradientKey::new( Color128::new(1.0, 0.84313726, 0.0, 1.0), 0.75);
1166/// let key2 = GradientKey{ color: Color128::new(1.0, 0.84313726, 0.0, 1.0), position: 0.75 };
1167///
1168/// assert_eq!(key0, key1);
1169/// assert_eq!(key1, key2);
1170/// ```
1171#[derive(Debug, Copy, Clone, PartialEq)]
1172#[repr(C)]
1173pub struct GradientKey {
1174 /// The color for this item, preferably in some form of linear color space. Gamma corrected colors will definitely
1175 /// not math correctly.
1176 pub color: Color128,
1177 /// Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the gradient.
1178 pub position: f32,
1179}
1180
1181impl GradientKey {
1182 /// A basic copy constructor for GradientKey.
1183 /// <https://stereokit.net/Pages/StereoKit/GradientKey/GradientKey.html>
1184 /// * `color_linear` - The color for this item, preferably in some form of linear color space. Gamma corrected
1185 /// colors will definitely not math correctly.
1186 /// * `position` - Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the
1187 /// gradient.
1188 pub fn new(color_linear: impl Into<Color128>, position: f32) -> Self {
1189 Self { color: color_linear.into(), position }
1190 }
1191}
1192
1193/// A Gradient is a sparse collection of color keys that are used to represent a ramp of colors! This class is largely
1194/// just storing colors and allowing you to sample between them.
1195///
1196/// Since the Gradient is just interpolating values, you can use whatever color space you want here, as long as it's
1197/// linear and not gamma! Gamma space RGB can't math properly at all. It can be RGB(linear), HSV, LAB, just remember
1198/// which one you have, and be sure to convert it appropriately later. Data is stored as float colors, so this'll be a
1199/// high accuracy blend!
1200/// <https://stereokit.net/Pages/StereoKit/Gradient.html>
1201///
1202/// see also [GradientKey] [`crate::tex::Tex::gen_particle`] [`crate::tex::SHCubemap::gen_cubemap_gradient`]
1203/// ### Examples
1204/// ```
1205/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1206/// use stereokit_rust::{maths::Vec3, system::AssetState, tex::{Tex, SHCubemap},
1207/// util::{named_colors, Gradient, GradientKey, Color128}};
1208///
1209/// let keys = [
1210/// GradientKey::new(Color128::BLACK_TRANSPARENT, 0.0),
1211/// GradientKey::new(named_colors::RED, 0.1),
1212/// GradientKey::new(named_colors::CYAN, 0.4),
1213/// GradientKey::new(named_colors::BLUE, 0.5),
1214/// GradientKey::new(Color128::BLACK, 0.7)];
1215///
1216/// let sh_cubemap = SHCubemap::gen_cubemap_gradient(Gradient::new(Some(&keys)),
1217/// Vec3::UP, 128);
1218/// sh_cubemap.render_as_sky();
1219///
1220/// let mut gradient = Gradient::new(None);
1221/// gradient
1222/// .add(Color128::BLACK_TRANSPARENT, 0.0)
1223/// .add(named_colors::YELLOW, 0.1)
1224/// .add(named_colors::LIGHT_BLUE, 0.4)
1225/// .add(named_colors::BLUE, 0.5)
1226/// .add(Color128::BLACK, 0.7);
1227/// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1228/// ```
1229pub struct Gradient(pub NonNull<_GradientT>);
1230
1231impl Drop for Gradient {
1232 fn drop(&mut self) {
1233 unsafe { gradient_destroy(self.0.as_ptr()) };
1234 }
1235}
1236
1237impl AsRef<Gradient> for Gradient {
1238 fn as_ref(&self) -> &Gradient {
1239 self
1240 }
1241}
1242
1243/// StereoKit internal type.
1244#[repr(C)]
1245#[derive(Debug)]
1246pub struct _GradientT {
1247 _unused: [u8; 0],
1248}
1249
1250/// StereoKit ffi type.
1251pub type GradientT = *mut _GradientT;
1252
1253unsafe extern "C" {
1254 pub fn gradient_create() -> GradientT;
1255 pub fn gradient_create_keys(in_arr_keys: *const GradientKey, count: i32) -> GradientT;
1256 pub fn gradient_add(gradient: GradientT, color_linear: Color128, position: f32);
1257 pub fn gradient_set(gradient: GradientT, index: i32, color_linear: Color128, position: f32);
1258 pub fn gradient_remove(gradient: GradientT, index: i32);
1259 pub fn gradient_count(gradient: GradientT) -> i32;
1260 pub fn gradient_get(gradient: GradientT, at: f32) -> Color128;
1261 pub fn gradient_get32(gradient: GradientT, at: f32) -> Color32;
1262 pub fn gradient_destroy(gradient: GradientT);
1263}
1264impl Gradient {
1265 /// Creates a new, completely empty gradient.
1266 /// <https://stereokit.net/Pages/StereoKit/Gradient/Gradient.html>
1267 /// * `keys` - These can be in any order that you like, they’ll be sorted by their GradientKey.position value
1268 /// regardless!
1269 ///
1270 /// see also [`gradient_create`] [`gradient_create_keys`]
1271 /// ### Examples
1272 /// ```
1273 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1274 /// use stereokit_rust::{maths::Vec3, tex::{Tex, SHCubemap},
1275 /// util::{named_colors, Gradient, GradientKey, Color128}};
1276 ///
1277 /// let keys = [
1278 /// GradientKey::new(Color128::BLACK_TRANSPARENT, 0.0),
1279 /// GradientKey::new(named_colors::RED, 0.5),
1280 /// GradientKey::new(Color128::BLACK, 0.7)];
1281 ///
1282 /// let gradient1 = Gradient::new(Some(&keys));
1283 /// assert_eq!(gradient1.get_count(), 3);
1284 /// let sh_cubemap = SHCubemap::gen_cubemap_gradient(gradient1, Vec3::UP, 16);
1285 /// sh_cubemap.render_as_sky();
1286 ///
1287 /// let mut gradient2 = Gradient::new(Some(&keys));
1288 /// gradient2.add(named_colors::CYAN, 0.4);
1289 /// assert_eq!(gradient2.get_count(), 4);
1290 /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient2));
1291 /// ```
1292 pub fn new(keys: Option<&[GradientKey]>) -> Self {
1293 match keys {
1294 Some(keys) => {
1295 Gradient(NonNull::new(unsafe { gradient_create_keys(keys.as_ptr(), keys.len() as i32) }).unwrap())
1296 }
1297 None => Gradient(NonNull::new(unsafe { gradient_create() }).unwrap()),
1298 }
1299 }
1300
1301 /// This adds a color key into the list. It’ll get inserted to the right slot based on its position.
1302 /// <https://stereokit.net/Pages/StereoKit/Gradient/Add.html>
1303 /// * `color_linear` - Any linear space color you like!
1304 /// * `position` - Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the
1305 /// gradient.
1306 ///
1307 /// see also [`gradient_add`]
1308 /// ### Examples
1309 /// ```
1310 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1311 /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1312 ///
1313 /// let mut gradient = Gradient::new(None);
1314 /// gradient
1315 /// .add(Color128::BLACK_TRANSPARENT, 0.0)
1316 /// .add(named_colors::YELLOW, 0.1)
1317 /// .add(named_colors::LIGHT_BLUE, 0.4)
1318 /// .add(named_colors::BLUE, 0.5)
1319 /// .add(Color128::BLACK, 0.7);
1320 /// assert_eq!(gradient.get_count(), 5);
1321 /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1322 /// ```
1323 pub fn add(&mut self, color_linear: impl Into<Color128>, position: f32) -> &mut Self {
1324 unsafe { gradient_add(self.0.as_ptr(), color_linear.into(), position) };
1325 self
1326 }
1327
1328 /// Updates the color key at the given index! This will NOT re-order color keys if they are moved past another
1329 /// key’s position, which could lead to strange behavior.
1330 /// <https://stereokit.net/Pages/StereoKit/Gradient/Set.html>
1331 /// * `index` - Index of the color key to change.
1332 /// * `color_linear` - Any linear space color you like!
1333 /// * `position` - Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the
1334 /// gradient.
1335 ///
1336 /// see also [`gradient_set`]
1337 /// ### Examples
1338 /// ```
1339 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1340 /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1341 ///
1342 /// let mut gradient = Gradient::new(None);
1343 /// gradient
1344 /// .add(Color128::BLACK_TRANSPARENT, 0.0)
1345 /// .add(named_colors::YELLOW, 0.1)
1346 /// .add(named_colors::LIGHT_BLUE, 0.4)
1347 /// .add(named_colors::BLUE, 0.5)
1348 /// .add(Color128::BLACK, 0.7);
1349 /// assert_eq!(gradient.get_count(), 5);
1350 /// assert_eq!(gradient.get(0.3), Color128 { r: 0.7856209, g: 0.8980392, b: 0.6013072, a: 1.0 });
1351 ///
1352 /// gradient.set(2, named_colors::RED, 0.3);
1353 /// gradient.set(-20, named_colors::RED, -10.3); // out of bounds, should do nothing
1354 /// assert_eq!(gradient.get_count(), 5);
1355 /// assert_eq!(gradient.get(0.3), named_colors::RED.into());
1356 ///
1357 /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1358 /// ```
1359 pub fn set(&mut self, index: i32, color_linear: impl Into<Color128>, position: f32) -> &mut Self {
1360 if index < 0 || index >= self.get_count() {
1361 return self;
1362 }
1363 unsafe { gradient_set(self.0.as_ptr(), index, color_linear.into(), position) };
1364 self
1365 }
1366
1367 /// Removes the color key at the given index! This won't reindex the gradient so get_count will still return the
1368 /// same value.
1369 /// <https://stereokit.net/Pages/StereoKit/Gradient/Remove.html>
1370 /// * `index` - The index of the color key to remove.
1371 ///
1372 /// see also [`gradient_remove`]
1373 /// ### Examples
1374 /// ```
1375 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1376 /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1377 ///
1378 /// let mut gradient = Gradient::new(None);
1379 /// gradient
1380 /// .add(Color128::BLACK_TRANSPARENT, 0.0)
1381 /// .add(named_colors::YELLOW, 0.1)
1382 /// .add(named_colors::LIGHT_BLUE, 0.4)
1383 /// .add(named_colors::BLUE, 0.5)
1384 /// .add(Color128::BLACK, 0.7);
1385 /// assert_eq!(gradient.get_count(), 5);
1386 /// assert_eq!(gradient.get(0.4), named_colors::LIGHT_BLUE.into());
1387 ///
1388 /// gradient.remove(2);
1389 /// gradient.remove(19).remove(-189);
1390 /// assert_eq!(gradient.get_count(), 5);
1391 /// assert_eq!(gradient.get(0.4), Color128 { r: 0.25, g: 0.25, b: 0.75, a: 1.0 });
1392 ///
1393 /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1394 /// ```
1395 pub fn remove(&mut self, index: i32) -> &mut Self {
1396 if index < 0 || index >= self.get_count() {
1397 return self;
1398 }
1399 unsafe { gradient_remove(self.0.as_ptr(), index) };
1400 self
1401 }
1402
1403 /// The number of color keys present in this gradient.
1404 /// <https://stereokit.net/Pages/StereoKit/Gradient/Count.html>
1405 ///
1406 /// see also [`gradient_count`]
1407 /// ### Examples
1408 /// ```
1409 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1410 /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1411 ///
1412 /// let mut gradient = Gradient::new(None);
1413 /// assert_eq!(gradient.get_count(), 0);
1414 ///
1415 /// gradient
1416 /// .add(Color128::BLACK_TRANSPARENT, 0.0)
1417 /// .add(named_colors::YELLOW, 0.1)
1418 /// .add(Color128::BLACK, 0.7);
1419 /// assert_eq!(gradient.get_count(), 3);
1420 ///
1421 /// gradient.remove(1);
1422 /// assert_eq!(gradient.get_count(), 3);
1423 ///
1424 /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1425 ///```
1426 pub fn get_count(&self) -> i32 {
1427 unsafe { gradient_count(self.0.as_ptr()) }
1428 }
1429
1430 /// Samples the gradient’s color at the given position!
1431 /// <https://stereokit.net/Pages/StereoKit/Gradient/Get.html>
1432 /// * `at` - Typically a value between 0-1, but if you used larger or smaller values for your color key’s positions,
1433 /// it’ll be in that range!
1434 ///
1435 /// Returns the interpolated color at the given position. If ‘at’ is smaller or larger than the gradient’s position
1436 /// range, then the color will be clamped to the color at the beginning or end of the gradient!
1437 /// see also [`gradient_get`]
1438 /// ### Examples
1439 /// ```
1440 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1441 /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1442 ///
1443 /// let mut gradient = Gradient::new(None);
1444 /// gradient
1445 /// .add(Color128::BLACK_TRANSPARENT, 0.0)
1446 /// .add(named_colors::YELLOW, 0.1)
1447 /// .add(named_colors::LIGHT_BLUE, 0.4)
1448 /// .add(named_colors::BLUE, 0.5)
1449 /// .add(Color128::BLACK, 0.7);
1450 ///
1451 /// assert_eq!(gradient.get(0.3), Color128 { r: 0.7856209, g: 0.8980392, b: 0.6013072, a: 1.0 });
1452 /// assert_eq!(gradient.get(0.4), named_colors::LIGHT_BLUE.into());
1453 /// assert_eq!(gradient.get(0.5), named_colors::BLUE.into());
1454 ///
1455 /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1456 /// ```
1457 pub fn get(&self, at: f32) -> Color128 {
1458 unsafe { gradient_get(self.0.as_ptr(), at) }
1459 }
1460
1461 /// Samples the gradient’s color at the given position, and converts it to a 32 bit color. If your RGBA color
1462 /// values are outside of the 0-1 range, then you’ll get some issues as they’re converted to 0-255 range bytes!
1463 /// <https://stereokit.net/Pages/StereoKit/Gradient/Remove.html>
1464 /// * `at` - Typically a value between 0-1, but if you used larger or smaller values for your color key’s positions,
1465 /// it’ll be in that range!
1466 ///
1467 /// Returns the interpolated 32 bit color at the given position. If ‘at’ is smaller or larger than the gradient’s
1468 /// position range, then the color will be clamped to the color at the beginning or end of the gradient!
1469 /// see also [`gradient_get32`]
1470 /// ### Examples
1471 /// ```
1472 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1473 /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128, Color32}};
1474 ///
1475 /// let mut gradient = Gradient::new(None);
1476 /// gradient
1477 /// .add(Color128::BLACK_TRANSPARENT, 0.0)
1478 /// .add(named_colors::YELLOW, 0.1)
1479 /// .add(named_colors::LIGHT_BLUE, 0.4)
1480 /// .add(named_colors::BLUE, 0.5)
1481 /// .add(Color128::BLACK, 0.7);
1482 ///
1483 /// assert_eq!(gradient.get32(0.3), Color32 { r: 200, g: 229, b: 153, a: 255 });
1484 /// assert_eq!(gradient.get32(0.4), named_colors::LIGHT_BLUE);
1485 /// assert_eq!(gradient.get32(0.5), named_colors::BLUE);
1486 ///
1487 /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1488 /// ```
1489 pub fn get32(&self, at: f32) -> Color32 {
1490 unsafe { gradient_get32(self.0.as_ptr(), at) }
1491 }
1492}
1493
1494/// file extension to filter ie ".txt" ".gltf"
1495/// <https://stereokit.net/Pages/StereoKit/Platform.html>
1496///
1497/// see also [`crate::system::Assets::MODEL_FORMATS`] [`crate::system::Assets::TEXTURE_FORMATS`]
1498#[derive(Debug, Default, Copy, Clone)]
1499#[repr(C)]
1500pub struct FileFilter {
1501 ext: [c_char; 32usize],
1502}
1503
1504impl FileFilter {
1505 pub fn new(str: impl AsRef<str>) -> Self {
1506 let mut value: [c_char; 32usize] = [0; 32usize];
1507 let c_array = CString::new(str.as_ref()).unwrap();
1508 let c_array = c_array.as_bytes_with_nul();
1509
1510 let mut c_array_iter = c_array.iter().map(|v| *v as c_char);
1511 value[0..c_array.len()].fill_with(|| c_array_iter.next().unwrap());
1512
1513 Self { ext: value }
1514 }
1515}
1516
1517/// This class contains some tools for hashing data within StereoKit! Certain systems in StereoKit use string hashes
1518/// instead of full strings for faster search and compare, like UI and Materials, so this class gives access to the code
1519/// SK uses for hashing. StereoKit currently internally uses a 64 bit FNV hash, though this detail should be pretty
1520/// transparent to developers
1521pub struct Hash;
1522
1523unsafe extern "C" {
1524 pub fn hash_string(str_utf8: *const c_char) -> IdHashT;
1525 pub fn hash_string_with(str_utf8: *const c_char, root: IdHashT) -> IdHashT;
1526 pub fn hash_int(val: i32) -> IdHashT;
1527 pub fn hash_int_with(val: i32, root: IdHashT) -> IdHashT;
1528}
1529
1530impl Hash {
1531 /// This will hash the UTF8 representation of the given string into a hash value that StereoKit can use.
1532 /// <https://stereokit.net/Pages/StereoKit/Hash/String.html>
1533 /// * `str` - A string (UTF8),that will be hashed.
1534 ///
1535 /// Returns a StereoKit hash representing the provided string.
1536 /// see also [`hash_string`] [`Hash::string_with`]
1537 /// ### Examples
1538 /// ```
1539 /// use stereokit_rust::util::Hash;
1540 ///
1541 /// let hash = Hash::string("Hello World");
1542 /// assert_eq!(hash, 4420528118743043111);
1543 /// ```
1544 pub fn string(str: impl AsRef<str>) -> IdHashT {
1545 let c_str = CString::new(str.as_ref()).unwrap();
1546 unsafe { hash_string(c_str.as_ptr()) }
1547 }
1548
1549 /// This will hash the UTF8 representation of the given string into a hash value that StereoKit can use. This
1550 /// overload allows you to combine your hash with an existing hash.
1551 /// <https://stereokit.net/Pages/StereoKit/Hash/String.html>
1552 /// * `str` - A string (UTF8),that will be hashed.
1553 /// * `root` - The hash value this new hash will start from.
1554 ///
1555 /// Returns a StereoKit hash representing a combination of the provided string and the root hash.
1556 /// see also [`hash_string_with`] [`Hash::string`]
1557 /// ### Examples
1558 /// ```
1559 /// use stereokit_rust::util::Hash;
1560 ///
1561 /// let hash1 = Hash::string("Hello");
1562 /// assert_eq!(hash1, 7201466553693376363);
1563 ///
1564 /// let hash2 = Hash::string_with(" World", hash1);
1565 /// assert_eq!(hash2, 4420528118743043111);
1566 /// ```
1567 pub fn string_with(str: impl AsRef<str>, root: IdHashT) -> IdHashT {
1568 let c_str = CString::new(str.as_ref()).unwrap();
1569 unsafe { hash_string_with(c_str.as_ptr(), root) }
1570 }
1571
1572 /// This will hash an integer into a hash value that StereoKit can use. This is helpful for adding in some
1573 /// uniqueness using something like a for loop index. This may be best when combined with additional hashes.
1574 /// <https://stereokit.net/Pages/StereoKit/Hash/Int.html>
1575 /// * `val` - An integer that will be hashed.
1576 ///
1577 /// Returns a StereoKit hash representing the provided integer.
1578 /// see also [`hash_int`] [`Hash::int_with`]
1579 /// ### Examples
1580 /// ```
1581 /// use stereokit_rust::util::Hash;
1582 ///
1583 /// let hash = Hash::int(123456789);
1584 /// assert_eq!(hash, 8379007418144316681);
1585 /// ```
1586 pub fn int(val: i32) -> IdHashT {
1587 unsafe { hash_int(val) }
1588 }
1589
1590 /// This will hash an integer into a hash value that StereoKit can use. This is helpful for adding in some
1591 /// uniqueness using something like a for loop index. This overload allows you to combine your hash with an existing
1592 /// hash.
1593 /// <https://stereokit.net/Pages/StereoKit/Hash/Int.html>
1594 /// * `val` - An integer that will be hashed.
1595 /// * `root` - The hash value this new hash will start from.
1596 ///
1597 /// Returns a StereoKit hash representing a combination of the provided string and the root hash.
1598 /// see also [`hash_int_with`] [`Hash::int`]
1599 /// ### Examples
1600 /// ```
1601 /// use stereokit_rust::util::Hash;
1602 ///
1603 /// let hash1 = Hash::int(123456789);
1604 /// assert_eq!(hash1, 8379007418144316681);
1605 ///
1606 /// let hash2 = Hash::int_with(123456789, hash1);
1607 /// assert_eq!(hash2, 13864748593440029765);
1608 /// ```
1609 pub fn int_with(int: i32, root: IdHashT) -> IdHashT {
1610 unsafe { hash_int_with(int, root) }
1611 }
1612}
1613
1614/// TODO: UNSTABLE: When opening the Platform.FilePicker, this enum describes how the picker should look and behave.
1615/// <https://stereokit.net/Pages/StereoKit/PickerMode.html>
1616#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1617#[repr(u32)]
1618pub enum PickerMode {
1619 /// Allow opening a single file.
1620 Open = 0,
1621 /// Allow the user to enter or select the name of the destination file.
1622 Save = 1,
1623}
1624
1625/// This class provides some platform related code that runs cross-platform. You might be able to do many of these
1626/// things with rust or C#, but you might not be able to do them in as a portable manner as these methods do!
1627/// <https://stereokit.net/Pages/StereoKit/Platform.html>
1628///
1629/// ### Examples
1630/// ```
1631/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1632/// use stereokit_rust::{util::{Platform}, system::TextContext};
1633///
1634/// Platform::keyboard_show(true, TextContext::Text);
1635///
1636/// let mut path = std::env::current_dir().expect("Current directory should be readable");
1637/// path.push("assets/textures/");
1638/// assert!(path.is_dir());
1639/// path.push("readme.md");
1640/// assert!(path.is_file());
1641///
1642/// let file_content = Platform::read_file_text(&path)
1643/// .expect("File should be readable");
1644/// assert!(file_content.starts_with("# Images "));
1645///
1646/// assert!(Platform::write_file_text(path, file_content).is_ok());
1647/// ```
1648pub struct Platform;
1649
1650unsafe extern "C" {
1651 pub fn platform_file_picker(
1652 mode: PickerMode,
1653 callback_data: *mut c_void,
1654 picker_callback: ::std::option::Option<
1655 unsafe extern "C" fn(callback_data: *mut c_void, confirmed: Bool32T, filename: *const c_char),
1656 >,
1657 filters: *const FileFilter,
1658 filter_count: i32,
1659 );
1660 pub fn platform_file_picker_sz(
1661 mode: PickerMode,
1662 callback_data: *mut c_void,
1663 picker_callback_sz: ::std::option::Option<
1664 unsafe extern "C" fn(
1665 callback_data: *mut c_void,
1666 confirmed: Bool32T,
1667 filename_ptr: *const c_char,
1668 filename_length: i32,
1669 ),
1670 >,
1671 in_arr_filters: *const FileFilter,
1672 filter_count: i32,
1673 );
1674 pub fn platform_file_picker_close();
1675 pub fn platform_file_picker_visible() -> Bool32T;
1676 pub fn platform_read_file(
1677 filename_utf8: *const c_char,
1678 out_data: *mut *mut c_void,
1679 out_size: *mut usize,
1680 ) -> Bool32T;
1681 pub fn platform_write_file(filename_utf8: *const c_char, data: *mut c_void, size: usize) -> Bool32T;
1682 pub fn platform_write_file_text(filename_utf8: *const c_char, text_utf8: *const c_char) -> Bool32T;
1683 pub fn platform_keyboard_get_force_fallback() -> Bool32T;
1684 pub fn platform_keyboard_set_force_fallback(force_fallback: Bool32T);
1685 pub fn platform_keyboard_show(visible: Bool32T, type_: TextContext);
1686 pub fn platform_keyboard_visible() -> Bool32T;
1687 pub fn platform_keyboard_set_layout(
1688 type_: TextContext,
1689 keyboard_layout: *const *const c_char,
1690 layouts_num: i32,
1691 ) -> Bool32T;
1692}
1693
1694/// File_picker trampoline
1695///
1696/// see also [`Plaform::file_picker`]
1697unsafe extern "C" fn fp_trampoline<FS: FnMut(&str), FC: FnMut()>(
1698 user_data: *mut c_void,
1699 confirmed: Bool32T,
1700 filename: *const c_char,
1701) {
1702 let data = unsafe { &mut *(user_data as *mut (&mut FS, &mut FC)) };
1703 let (update, cancel) = data;
1704 if confirmed != 0 {
1705 let c_str = unsafe { CStr::from_ptr(filename).to_str().unwrap() };
1706 update(c_str)
1707 } else {
1708 cancel()
1709 }
1710}
1711
1712/// File_picker_sz trampoline
1713///
1714/// see also [`Plaform::file_picker`]
1715unsafe extern "C" fn fp_sz_trampoline<F: FnMut(bool, &str)>(
1716 user_data: *mut c_void,
1717 confirmed: Bool32T,
1718 filename: *const c_char,
1719 filename_length: i32,
1720) {
1721 let closure = unsafe { &mut *(user_data as *mut &mut F) };
1722 if confirmed != 0 && filename_length > 0 {
1723 let c_str = unsafe { CStr::from_ptr(filename).to_str().unwrap() };
1724 closure(true, c_str)
1725 } else {
1726 let c_str = "";
1727 closure(false, c_str)
1728 }
1729}
1730
1731impl Platform {
1732 /// Force the use of StereoKit’s built-in fallback keyboard instead of the system keyboard. This may be great for
1733 /// testing or look and feel matching, but the system keyboard should generally be preferred for accessibility
1734 /// reasons.
1735 /// <https://stereokit.net/Pages/StereoKit/Platform/ForceFallbackKeyboard.html>
1736 ///
1737 /// see also [`platform_keyboard_set_force_fallback`]
1738 /// ### Examples
1739 /// ```
1740 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1741 /// use stereokit_rust::util::Platform;
1742 ///
1743 /// assert_eq!(Platform::get_force_fallback_keyboard(), false);
1744 ///
1745 /// Platform::force_fallback_keyboard(true);
1746 /// assert_eq!(Platform::get_force_fallback_keyboard(), true);
1747 ///
1748 /// Platform::force_fallback_keyboard(false);
1749 /// assert_eq!(Platform::get_force_fallback_keyboard(), false);
1750 /// ```
1751 pub fn force_fallback_keyboard(force_fallback: bool) {
1752 unsafe { platform_keyboard_set_force_fallback(force_fallback as Bool32T) }
1753 }
1754
1755 /// TODO: UNSTABLE: Starts a file picker window! This will create a native file picker window if one is available in the current
1756 /// setup, and if it is not, it’ll create a fallback filepicker build using StereoKit’s UI.
1757 ///
1758 /// Flatscreen apps will show traditional file pickers, and UWP has an OS provided file picker that works in MR. All
1759 /// others currently use the fallback system.
1760 ///
1761 /// A note for UWP apps, UWP generally does not have permission to access random files, unless the user has chosen
1762 /// them with the picker! This picker properly handles permissions for individual files on UWP, but may have issues
1763 /// with files that reference other files, such as .gltf files with external textures. See [`Platform::write_file`]
1764 /// and [`Platform.read_file`] for manually reading and writing files in a cross-platfom manner.
1765 /// <https://stereokit.net/Pages/StereoKit/Platform/FilePicker.html>
1766 /// * `mode` - Are we trying to Open a file, or Save a file? This changes the appearance and behavior of the picker
1767 /// to support the specified action.
1768 /// * `on_select_file` - This Action will be called with the proper filename when the picker has successfully
1769 /// completed! On a cancel or close event, this Action is not called.
1770 /// * `on_cancel` - If the user cancels the file picker, or the picker is closed via FilePickerClose, this Action is
1771 /// called.
1772 /// * `filters` - A list of file extensions that the picker should filter for. This is in the format of “.glb” and
1773 /// is case insensitive.
1774 ///
1775 /// see also [`platform_file_picker`]
1776 pub fn file_picker<FS: FnMut(&str), FC: FnMut()>(
1777 mode: PickerMode,
1778 mut on_select_file: FS,
1779 mut on_cancel: FC,
1780 filters: &[impl AsRef<str>],
1781 ) {
1782 let mut c_filters = Vec::new();
1783 for filter in filters {
1784 c_filters.push(FileFilter::new(filter));
1785 }
1786
1787 let mut closure = (&mut on_select_file, &mut on_cancel);
1788 unsafe {
1789 platform_file_picker(
1790 mode,
1791 &mut closure as *mut _ as *mut c_void,
1792 Some(fp_trampoline::<FS, FC>),
1793 c_filters.as_slice().as_ptr(),
1794 c_filters.len() as i32,
1795 )
1796 }
1797 }
1798
1799 /// TODO: UNSTABLE: Starts a file picker window! This will create a native file picker window if one is available in the current
1800 /// setup, and if it is not, it’ll create a fallback filepicker build using StereoKit’s UI.
1801 ///
1802 /// Flatscreen apps will show traditional file pickers, and UWP has an OS provided file picker that works in MR. All
1803 /// others currently use the fallback system. Some pickers will block the system and return right away, but others
1804 /// will stick around and let users continue to interact with the app.
1805 ///
1806 /// A note for UWP apps, UWP generally does not have permission to access random files, unless the user has chosen
1807 /// them with the picker! This picker properly handles permissions for individual files on UWP, but may have issues
1808 /// with files that reference other files, such as .gltf files with external textures. See [`Platform::write_file`]
1809 /// and [`Platform.read_file`] for manually reading and writing files in a cross-platfom manner.
1810 /// <https://stereokit.net/Pages/StereoKit/Platform/FilePicker.html>
1811 /// * `mode` - Are we trying to Open a file, or Save a file? This changes the appearance and behavior of the picker
1812 /// to support the specified action.
1813 /// * on_complete - This action will be called when the file picker has finished, either via a cancel event, or from
1814 /// a confirm event. First parameter is a bool, where true indicates the presence of a valid filename, and false
1815 /// indicates a failure or cancel event.
1816 /// * `filters` - A list of file extensions that the picker should filter for. This is in the format of “.glb” and
1817 /// is case insensitive.
1818 ///
1819 /// see also [`platform_file_picker_sz`]
1820 pub fn file_picker_sz<F: FnMut(bool, &str)>(mode: PickerMode, mut on_complete: F, filters: &[impl AsRef<str>]) {
1821 let mut c_filters = Vec::new();
1822 for filter in filters {
1823 c_filters.push(FileFilter::new(filter));
1824 }
1825
1826 let mut closure = &mut on_complete;
1827 unsafe {
1828 platform_file_picker_sz(
1829 mode,
1830 &mut closure as *mut _ as *mut c_void,
1831 Some(fp_sz_trampoline::<F>),
1832 c_filters.as_slice().as_ptr(),
1833 c_filters.len() as i32,
1834 )
1835 }
1836 }
1837
1838 /// TODO: UNSTABLE: If the picker is visible, this will close it and immediately trigger a cancel event for the active picker.
1839 /// <https://stereokit.net/Pages/StereoKit/Platform/FilePickerClose.html>
1840 ///
1841 /// see also [`platform_file_picker_close`]
1842 pub fn file_picker_close() {
1843 unsafe { platform_file_picker_close() }
1844 }
1845
1846 /// Request or hide a soft keyboard for the user to type on. StereoKit will surface OS provided soft keyboards where
1847 /// available, and use a fallback keyboard when not. On systems with physical keyboards, soft keyboards generally
1848 /// will not be shown if the user has interacted with their physical keyboard recently.
1849 /// <https://stereokit.net/Pages/StereoKit/Platform/KeyboardShow.html>
1850 /// * `show` - Tells whether or not to open or close the soft keyboard.
1851 /// * `input_type` - Soft keyboards can change layout to optimize for the type of text that’s required. StereoKit
1852 /// will request the soft keyboard layout that most closely represents the TextContext provided.
1853 ///
1854 /// see also [`platform_keyboard_show`]
1855 /// ### Examples
1856 /// ```
1857 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1858 /// use stereokit_rust::{util::Platform, system::TextContext};
1859 ///
1860 /// xr_mode_stop_here!();
1861 /// // On non XR mode, this will not show the keyboard, as it is assumed that the user has a physical keyboard.
1862 /// Platform::keyboard_show(true, TextContext::Text);
1863 /// assert_eq!(Platform::is_keyboard_visible(), false);
1864 ///
1865 /// Platform::keyboard_show(false, TextContext::Text);
1866 /// assert_eq!(Platform::is_keyboard_visible(), false);
1867 /// ```
1868 pub fn keyboard_show(show: bool, input_type: TextContext) {
1869 unsafe { platform_keyboard_show(show as Bool32T, input_type) }
1870 }
1871
1872 /// Replace the default keyboard type with a custom layout.
1873 /// <https://stereokit.net/Pages/StereoKit/Platform/KeyboardSetLayout.html>
1874 /// * `keyboard_type` - The type of keyboard to replace.
1875 /// * `keyboard_layouts` - Custom keyboard layout to replace the defualt layout.
1876 ///
1877 /// Returns `true` if keyboard type was swapped with the provided layout.
1878 /// see also [`platform_keyboard_set_layout`]
1879 /// ### Examples
1880 /// ```
1881 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1882 /// use stereokit_rust::{util::Platform, system::TextContext};
1883 ///
1884 /// pub const FR_KEY_TEXT: &str = r#"²|&|é|"|'|(|\-|è|_|ç|à|)|=|{|}|spr:sk/ui/backspace-\b-8-3|spr:sk/ui/close----close
1885 /// Tab-\t-9-3|a|z|e|r|t|y|u|i|o|p|^|$|[|]|\|
1886 /// Entrée-\n-13-4|q|s|d|f|g|h|j|k|l|m|ù|*|#|Entrée-\n-13-3
1887 /// spr:sk/ui/shift--16-3-go_1|<|w|x|c|v|b|n|,|;|:|!|`|@|spr:sk/ui/shift--16-2-go_1|spr:sk/ui/arrow_up--38
1888 /// Ctrl--17-4-mod|Cmd--91-3|Alt--18-3-go_2| - -32-13|Alt--18-3-go_2|Ctrl--17-3-mod|spr:sk/ui/arrow_left--37|spr:sk/ui/arrow_down--40|spr:sk/ui/arrow_right--39|"#;
1889 ///
1890 /// pub const FR_KEY_TEXT_SHIFT: &str = r#"@|1|2|3|4|5|6|7|8|9|0|°|+|Æ|Œ|spr:sk/ui/backspace-\b-8-3|spr:sk/ui/close----close
1891 /// Tab-\t-9-3|A|Z|E|R|T|Y|U|I|O|P|¨|£|Ê|É|È
1892 /// Entrée-\n-13-4|Q|S|D|F|G|H|J|K|L|M|%|µ|Ç|Entrée-\n-13-3
1893 /// spr:sk/ui/shift--16-3-go_0|>|W|X|C|V|B|N|?|.|/|§|À|Ô|spr:sk/ui/shift--16-2-go_0|spr:sk/ui/arrow_up--38
1894 /// Ctrl--17-4-mod|Cmd--91-3|Alt--18-3-go_2| - -32-13|Alt--18-3-go_2|Ctrl--17-3-mod|spr:sk/ui/arrow_left--37|spr:sk/ui/arrow_down--40|spr:sk/ui/arrow_right--39|"#;
1895 ///
1896 /// pub const FR_KEY_TEXT_ALT: &str = r#"*|/|~|#|{|[|\||`|\\|^|@|]|}|æ|œ|spr:sk/ui/backspace-\b-8-3|spr:sk/ui/close----close
1897 /// Tab-\t-9-3|à|â|ä|ç|é|è|ê|ë|î|ï|ô|ö|«|»|¤
1898 /// Entrée-\n-13-4|ù|û|ü|ÿ|À|Â|Ä|Ç|É|È|Ê|Ë|%|Entrée-\n-13-3
1899 /// spr:sk/ui/shift--16-3-go_1|Î|Ï|Ô|Ö|Ù|Û|Ü|Ÿ|$|£|€|¥|✋|spr:sk/ui/shift--16-2-go_1|spr:sk/ui/arrow_up--38
1900 /// Ctrl--17-4-mod|Cmd--91-3|Alt--18-3-go_0| - -32-13|Alt--18-3-go_0|Ctrl--17-3-mod|spr:sk/ui/arrow_left--37|spr:sk/ui/arrow_down--40|spr:sk/ui/arrow_right--39|"#;
1901 ///
1902 /// let keyboard_layouts = vec![FR_KEY_TEXT, FR_KEY_TEXT_SHIFT, FR_KEY_TEXT_ALT];
1903 ///
1904 /// assert_eq!(Platform::keyboard_set_layout(TextContext::Text, &keyboard_layouts), true);
1905 /// ```
1906 pub fn keyboard_set_layout(keyboard_type: TextContext, keyboard_layouts: &Vec<&str>) -> bool {
1907 let mut keyboard_layouts_c = vec![];
1908 for str in keyboard_layouts {
1909 let c_str = CString::new(*str).unwrap().into_raw() as *const c_char;
1910 keyboard_layouts_c.push(c_str);
1911 }
1912 unsafe {
1913 platform_keyboard_set_layout(
1914 keyboard_type,
1915 keyboard_layouts_c.as_slice().as_ptr(),
1916 keyboard_layouts_c.len() as i32,
1917 ) != 0
1918 }
1919 }
1920
1921 /// Reads the entire contents of the file as a UTF-8 string, taking advantage of any permissions that may have been
1922 /// granted by Platform::file_picker(_sz?). Returns Err on failure.
1923 /// <https://stereokit.net/Pages/StereoKit/Platform/ReadFileText.html>
1924 /// * `filename` - The path to the file to read.
1925 ///
1926 /// see also [`platform_read_file`]
1927 /// ### Examples
1928 /// ```
1929 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1930 /// use stereokit_rust::{util::{Platform}, system::TextContext};
1931 ///
1932 /// let mut path = std::env::current_dir().expect("Current directory should be readable");
1933 /// path.push("config.toml");
1934 /// assert!(path.is_file());
1935 ///
1936 /// let file_content = Platform::read_file_text(&path)
1937 /// .expect("File should be readable");
1938 /// assert!(file_content.starts_with("[env]"));
1939 /// ```
1940 pub fn read_file_text<'a>(filename: impl AsRef<Path>) -> Result<&'a str, StereoKitError> {
1941 let path_buf = filename.as_ref().to_path_buf();
1942 let c_str = CString::new(
1943 path_buf
1944 .clone()
1945 .to_str() //
1946 .ok_or(StereoKitError::ReadFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
1947 )?;
1948 let out_data = CString::new("H")?.into_raw() as *mut *mut c_void;
1949 let mut len = 0usize;
1950 let len_ptr: *mut usize = &mut len;
1951 if unsafe { platform_read_file(c_str.as_ptr(), out_data, len_ptr) != 0 } {
1952 unsafe { CStr::from_ptr(*out_data as *const c_char) }
1953 .to_str()
1954 .map_err(|e| StereoKitError::ReadFileError(path_buf.clone(), e.to_string()))
1955 } else {
1956 Err(StereoKitError::ReadFileError(path_buf, "Failed to read file".into()))
1957 }
1958 }
1959
1960 /// Reads the entire contents of the file as a byte array, taking advantage of any permissions that may have been
1961 /// granted by Platform.FilePicker.
1962 /// <https://stereokit.net/Pages/StereoKit/Platform/ReadFile.html>
1963 /// * `filename` - The path to the file to read.
1964 ///
1965 /// see also [`platform_read_file`]
1966 /// ### Examples
1967 /// ```
1968 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1969 /// use stereokit_rust::{util::{Platform}, system::TextContext};
1970 ///
1971 /// let mut path = std::env::current_dir().expect("Current directory should be readable");
1972 /// path.push("assets/textures/");
1973 /// assert!(path.is_dir());
1974 /// path.push("readme.md");
1975 /// assert!(path.is_file());
1976 ///
1977 /// let file_content = Platform::read_file(&path)
1978 /// .expect("File should be readable");
1979 /// assert!(file_content.starts_with(b"# Images "));
1980 /// ```
1981 pub fn read_file<'a>(filename: impl AsRef<Path>) -> Result<&'a [u8], StereoKitError> {
1982 let path_buf = filename.as_ref().to_path_buf();
1983 let c_str = CString::new(
1984 path_buf
1985 .clone()
1986 .to_str() //
1987 .ok_or(StereoKitError::ReadFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
1988 )?;
1989 let out_data = CString::new("H")?.into_raw() as *mut *mut c_void;
1990 let mut len = 0usize;
1991 let len_ptr: *mut usize = &mut len;
1992 if unsafe { platform_read_file(c_str.as_ptr(), out_data, len_ptr) != 0 } {
1993 Ok(unsafe { std::slice::from_raw_parts(*out_data as *const u8, len) })
1994 } else {
1995 Err(StereoKitError::ReadFileError(path_buf, "Failed to read file".into()))
1996 }
1997 }
1998
1999 /// Writes a UTF-8 text file to the filesystem, taking advantage of any permissions that may have been granted by
2000 /// Platform::file_picker.
2001 /// <https://stereokit.net/Pages/StereoKit/Platform/WriteFile.html>
2002 /// * `filename` - The path to the file to write.
2003 /// * `text` - A string to write to the file. This gets converted to a UTF-8 encoding.
2004 ///
2005 /// see also [`platform_write_file_text`]
2006 /// ### Examples
2007 /// ```
2008 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2009 /// use stereokit_rust::{util::{Platform}, system::TextContext};
2010 ///
2011 /// let mut path = std::env::current_dir().expect("Current directory should be readable");
2012 /// path.push("assets/icons/");
2013 /// assert!(path.is_dir());
2014 /// path.push("readme.md");
2015 /// assert!(path.is_file());
2016 ///
2017 /// let file_content = Platform::read_file_text(&path)
2018 /// .expect("File should be readable");
2019 /// assert!(file_content.starts_with("# Images "));
2020 ///
2021 /// assert!(Platform::write_file_text(path, file_content).is_ok());
2022 /// ```
2023 pub fn write_file_text<S: AsRef<str>>(filename: impl AsRef<Path>, text: S) -> Result<bool, StereoKitError> {
2024 let path_buf = filename.as_ref().to_path_buf();
2025 let c_str = CString::new(
2026 path_buf
2027 .clone()
2028 .to_str() //
2029 .ok_or(StereoKitError::WriteFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
2030 )?;
2031 let in_data = CString::new(text.as_ref())?.into_raw() as *const c_char;
2032 if unsafe { platform_write_file_text(c_str.as_ptr(), in_data) != 0 } {
2033 Ok(true)
2034 } else {
2035 Err(StereoKitError::WriteFileError(path_buf, "Failed to write file".into()))
2036 }
2037 }
2038
2039 /// Writes an array of bytes to the filesystem, taking advantage of any permissions that may have been granted by
2040 /// Platform::file_picker.
2041 /// <https://stereokit.net/Pages/StereoKit/Platform/WriteFile.html>
2042 /// * `filename` - The path to the file to write.
2043 /// * `data` - The data to write to the file.
2044 ///
2045 /// see also [`platform_write_file`]
2046 /// ### Examples
2047 /// ```
2048 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2049 /// use stereokit_rust::{util::{Platform}, system::TextContext};
2050 ///
2051 /// let mut path = std::env::current_dir().expect("Current directory should be readable");
2052 /// path.push("assets/icons/");
2053 /// assert!(path.is_dir());
2054 /// path.push("readme.md");
2055 /// assert!(path.is_file());
2056 ///
2057 /// let file_content = Platform::read_file(&path)
2058 /// .expect("File should be readable");
2059 /// assert!(file_content.starts_with(b"# Images "));
2060 ///
2061 /// assert!(Platform::write_file(path, file_content).is_ok());
2062 /// ```
2063 pub fn write_file(filename: impl AsRef<Path>, data: &[u8]) -> Result<bool, StereoKitError> {
2064 let path_buf = filename.as_ref().to_path_buf();
2065 let c_str = CString::new(
2066 path_buf
2067 .clone()
2068 .to_str() //
2069 .ok_or(StereoKitError::WriteFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
2070 )?;
2071 if unsafe { platform_write_file(c_str.as_ptr(), data.as_ptr() as *mut c_void, data.len()) != 0 } {
2072 Ok(true)
2073 } else {
2074 Err(StereoKitError::WriteFileError(path_buf, "Failed to write file".into()))
2075 }
2076 }
2077
2078 /// TODO: UNSTABLE: This will check if the file picker interface is currently visible. Some pickers will never show this, as they
2079 /// block the application until the picker has completed.
2080 /// <https://stereokit.net/Pages/StereoKit/Platform/FilePickerVisible.html>
2081 ///
2082 /// see also [`platform_file_picker_visible`]
2083 /// ### Examples
2084 /// ```
2085 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2086 /// use stereokit_rust::util::Platform;
2087 ///
2088 /// assert_eq!(Platform::get_file_picker_visible(), false);
2089 /// ```
2090 pub fn get_file_picker_visible() -> bool {
2091 unsafe { platform_file_picker_visible() != 0 }
2092 }
2093
2094 /// Force the use of StereoKit’s built-in fallback keyboard instead of the system keyboard. This may be great for
2095 /// testing or look and feel matching, but the system keyboard should generally be preferred for accessibility
2096 /// reasons.
2097 /// <https://stereokit.net/Pages/StereoKit/Platform/ForceFallbackKeyboard.html>
2098 ///
2099 /// see also [`platform_keyboard_get_force_fallback`]
2100 /// see example [`Platform::force_fallback_keyboard`]
2101 pub fn get_force_fallback_keyboard() -> bool {
2102 unsafe { platform_keyboard_get_force_fallback() != 0 }
2103 }
2104
2105 /// Check if a soft keyboard is currently visible. This may be an OS provided keyboard or StereoKit’s fallback
2106 /// keyboard, but will not indicate the presence of a physical keyboard.
2107 /// <https://stereokit.net/Pages/StereoKit/Platform/KeyboardVisible.html>
2108 ///
2109 /// see also [`platform_keyboard_visible`]
2110 /// see example [`Platform::keyboard_show`]
2111 pub fn is_keyboard_visible() -> bool {
2112 unsafe { platform_keyboard_visible() != 0 }
2113 }
2114}
2115
2116/// A light source used for creating SphericalHarmonics data.
2117/// <https://stereokit.net/Pages/StereoKit/SHLight.html>
2118///
2119/// see [`SphericalHarmonics`] see also [`crate::tex::SHCubemap`]
2120/// ### Examples
2121/// ```
2122/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2123/// use stereokit_rust::{maths::Vec3, tex::SHCubemap, util::{SHLight, named_colors, Color128}};
2124///
2125/// let light0 = SHLight::new([1.0, 0.2, 0.3], named_colors::RED);
2126/// let light1 = SHLight::new(Vec3::new(1.0, 0.2, 0.3), Color128::new(1.0, 0.0, 0.0, 1.0));
2127/// let light2 = SHLight{ dir_to: Vec3::new(1.0, 0.2, 0.3), color: named_colors::RED.into()};
2128///
2129/// assert_eq!(light0, light1);
2130/// assert_eq!(light1, light2);
2131/// ```
2132#[derive(Debug, Copy, Clone, PartialEq)]
2133#[repr(C)]
2134pub struct SHLight {
2135 /// Direction to the light source.
2136 pub dir_to: Vec3,
2137 /// Color of the light in linear space! Values here can exceed 1.
2138 pub color: Color128,
2139}
2140impl SHLight {
2141 /// A basic constructor for SHLight.
2142 /// <https://stereokit.net/Pages/StereoKit/SHLight.html>
2143 /// * `dir_to` - Direction to the light source.
2144 /// * `color` - Color of the light in linear space! Values here can exceed 1.
2145 pub fn new(dir_to: impl Into<Vec3>, color: impl Into<Color128>) -> Self {
2146 Self { dir_to: dir_to.into(), color: color.into() }
2147 }
2148}
2149/// Spherical Harmonics are kinda like Fourier, but on a sphere. That doesn’t mean terribly much to me, and could be
2150/// wrong, but check out here for more details about how Spherical Harmonics work in this context!
2151///
2152/// However, the more prctical thing is, SH can be a function that describes a value over the surface of a sphere! This
2153/// is particularly useful for lighting, since you can basically store the lighting information for a space in this
2154/// value! This is often used for lightmap data, or a light probe grid, but StereoKit just uses a single SH for the
2155/// entire scene. It’s a gross oversimplification, but looks quite good, and is really fast! That’s extremely great
2156/// when you’re trying to hit 60fps, or even 144fps.
2157/// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics.html>
2158///
2159/// see also: [`crate::tex::SHCubemap`]
2160/// ### Examples
2161/// ```
2162/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2163/// use stereokit_rust::{maths::Vec3,
2164/// util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2165///
2166/// let light0 = SHLight::new([1.0, 0.0, 0.0], named_colors::RED);
2167/// let light1 = SHLight::new([0.0, 1.0, 0.0], named_colors::GREEN);
2168/// let light2 = SHLight::new([0.0, 0.0, 1.0], named_colors::BLUE);
2169///
2170/// let mut sh = SphericalHarmonics::from_lights(&[light0, light1, light2]);
2171/// sh.brightness(1.0)
2172/// .add(Vec3::NEG_Y, named_colors::GREEN)
2173/// .add(Vec3::NEG_Z, named_colors::BLUE)
2174/// .add(Vec3::NEG_X, named_colors::RED);
2175///
2176/// assert_eq!(sh.get_sample(Vec3::UP), Color128 { r: 0.5813507, g: 0.8046322, b: 0.5813487, a: 1.0 });
2177/// assert_eq!(sh.get_dominent_light_direction(), Vec3 { x: 0.27644092, y: 0.2728996, z: 0.9214696 });
2178/// ```
2179#[derive(Debug, Default, Copy, Clone, PartialEq)]
2180#[repr(C)]
2181pub struct SphericalHarmonics {
2182 pub coefficients: [Vec3; 9usize],
2183}
2184
2185unsafe extern "C" {
2186 pub fn sh_create(in_arr_lights: *const SHLight, light_count: i32) -> SphericalHarmonics;
2187 pub fn sh_brightness(ref_harmonics: *mut SphericalHarmonics, scale: f32);
2188 pub fn sh_add(ref_harmonics: *mut SphericalHarmonics, light_dir: Vec3, light_color: Vec3);
2189 pub fn sh_lookup(harmonics: *const SphericalHarmonics, normal: Vec3) -> Color128;
2190 pub fn sh_dominant_dir(harmonics: *const SphericalHarmonics) -> Vec3;
2191}
2192impl SphericalHarmonics {
2193 /// Creates a SphericalHarmonics approximation of the irradiance given from a set of directional lights!
2194 /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/FromLights.html>
2195 /// * `lights` - A list of directional lights!
2196 ///
2197 /// Returns a SphericalHarmonics approximation of the irradiance given from a set of directional lights!
2198 /// see also [`SHLight`]
2199 /// ### Examples
2200 /// ```
2201 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2202 /// use stereokit_rust::{maths::Vec3,
2203 /// util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2204 ///
2205 /// let light0 = SHLight::new([1.0, 1.0, 1.0], named_colors::RED);
2206 /// let light1 = SHLight::new([0.5, 0.5, 0.5], named_colors::RED);
2207 /// let light2 = SHLight::new([0.25, 0.25, 0.25], named_colors::RED);
2208 ///
2209 /// let sh = SphericalHarmonics::from_lights(&[light0, light1, light2]);
2210 ///
2211 /// assert_eq!(sh.get_sample(Vec3::UP), Color128 { r: 2.2098913, g: 0.0, b: 0.0, a: 1.0 });
2212 /// assert_eq!(sh.get_dominent_light_direction(), -Vec3::ONE.get_normalized());
2213 /// ```
2214 pub fn from_lights(lights: &[SHLight]) -> Self {
2215 unsafe { sh_create(lights.as_ptr(), lights.len() as i32) }
2216 }
2217
2218 /// Creates a SphericalHarmonic from an array of coefficients. Useful for loading stored data!
2219 /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/SphericalHarmonics.html>
2220 /// * `coefficients` - Must be an array with a length of 9!
2221 ///
2222 /// see also [`sh_create`]
2223 /// ### Examples
2224 /// ```
2225 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2226 /// use stereokit_rust::{maths::Vec3,
2227 /// util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2228 ///
2229 /// let mut sh0 = SphericalHarmonics::default();
2230 /// sh0.add([1.0, 0.0, 1.0], named_colors::RED);
2231 /// let coefficient = sh0.coefficients;
2232 ///
2233 /// let sh = SphericalHarmonics::new(coefficient);
2234 ///
2235 /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 11.453729, g: 0.0, b: 0.0, a: 1.0 });
2236 /// assert_eq!(sh.get_dominent_light_direction(), Vec3::new(-1.0, 0.0, -1.0).get_normalized());
2237 /// ```
2238 pub fn new(coefficients: [Vec3; 9]) -> Self {
2239 SphericalHarmonics { coefficients }
2240 }
2241
2242 /// Adds a ‘directional light’ to the lighting approximation. This can be used to bake a multiple light setup, or
2243 /// accumulate light
2244 /// from a field of points.
2245 /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/Add.html>
2246 /// * `light_dir` - The direction of the light source.
2247 /// * `light_color` - Color of the light, in linear color space.
2248 ///
2249 /// see also [`sh_add`]
2250 /// ### Examples
2251 /// ```
2252 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2253 /// use stereokit_rust::{maths::Vec3,
2254 /// util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2255 ///
2256 /// let mut sh = SphericalHarmonics::default();
2257 /// sh.add([1.0, 0.0, 1.0], named_colors::RED)
2258 /// .add([0.0, 1.0, 0.0], named_colors::GREEN)
2259 /// .add([0.0, 0.0, 1.0], named_colors::BLUE);
2260 ///
2261 /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 11.453729, g: -0.2956792, b: 4.4505944, a: 1.0 });
2262 /// assert_eq!(sh.get_dominent_light_direction(), Vec3 { x: -0.21951628, y: -0.21670417, z: -0.95123714 });
2263 ///```
2264 pub fn add(&mut self, light_dir: impl Into<Vec3>, light_color: impl Into<Color128>) -> &mut Self {
2265 let light_dir = light_dir.into();
2266 let color = light_color.into();
2267 unsafe { sh_add(self, light_dir, Vec3 { x: color.r, y: color.g, z: color.b }) };
2268 self
2269 }
2270
2271 /// Scales all the SphericalHarmonic’s coefficients! This behaves as if you’re modifying the brightness of the
2272 /// lighting this object represents.
2273 /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/Brightness.html>
2274 /// * `scale` - A multiplier for the coefficients! A value of 1 will leave everything the same, 0.5 will cut the
2275 /// brightness in half, and a 2 will double the brightness.
2276 ///
2277 /// see also [`sh_brightness`]
2278 /// ### Examples
2279 /// ```
2280 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2281 /// use stereokit_rust::{maths::Vec3,
2282 /// util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2283 ///
2284 /// let mut sh = SphericalHarmonics::default();
2285 /// sh.add([1.0, 0.0, 1.0], named_colors::RED)
2286 /// .brightness(0.5);
2287 ///
2288 /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 5.726864, g: 0.0, b: 0.0, a: 1.0 });
2289 /// assert_eq!(sh.get_dominent_light_direction(), Vec3::new(-1.0, 0.0, -1.0).get_normalized());
2290 ///
2291 /// sh.brightness(2.0);
2292 /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 11.453729, g: 0.0, b: 0.0, a: 1.0 });
2293 /// assert_eq!(sh.get_dominent_light_direction(), Vec3::new(-1.0, 0.0, -1.0).get_normalized());
2294 ///
2295 ///
2296 /// sh.brightness(0.0);
2297 /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128::BLACK);
2298 /// assert_eq!(sh.get_dominent_light_direction().x.is_nan(), true);
2299 /// ```
2300 pub fn brightness(&mut self, scale: f32) -> &mut Self {
2301 unsafe { sh_brightness(self, scale) };
2302 self
2303 }
2304
2305 /// Look up the color information in a particular direction!
2306 /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/Sample.html>
2307 /// * `normal` - The direction to look in. Should be normalized.
2308 ///
2309 /// Returns the color represented by the SH in the given direction.
2310 /// see also [`sh_brightness`]
2311 pub fn get_sample(&self, normal: impl Into<Vec3>) -> Color128 {
2312 unsafe { sh_lookup(self, normal.into()) }
2313 }
2314
2315 /// Returns the dominant direction of the light represented by this spherical harmonics data. The direction value is
2316 /// normalized.
2317 /// You can get the color of the light in this direction by using the struct’s Sample method:
2318 /// light.get_sample(-light.get_dominent_light_direction()).
2319 /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/DominantLightDirection.html>
2320 ///
2321 /// see also [`sh_brightness`]
2322 /// ### Examples
2323 /// ```
2324 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2325 /// use stereokit_rust::{maths::Vec3,
2326 /// util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2327 ///
2328 /// let mut sh = SphericalHarmonics::default();
2329 /// sh.add([1.0, 0.0, 1.0], named_colors::RED)
2330 /// .add([1.0, 1.0, 0.0], named_colors::GREEN)
2331 /// .add([0.0, 1.0, 1.0], named_colors::BLUE);
2332 ///
2333 /// assert_eq!(sh.get_dominent_light_direction(), Vec3 { x: -0.3088678, y: -0.6715365, z: -0.6735276 });
2334 /// ```
2335 pub fn get_dominent_light_direction(&self) -> Vec3 {
2336 unsafe { sh_dominant_dir(self) }
2337 }
2338
2339 /// Converts the SphericalHarmonic into a vector of coefficients 9 long. Useful for storing calculated data!
2340 /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/ToArray.html>
2341 ///
2342 /// Returns an array of coefficients 9 long.
2343 /// see also [`sh_brightness`]
2344 pub fn to_array(&self) -> [Vec3; 9] {
2345 self.coefficients
2346 }
2347}
2348
2349/// This class contains time information for the current session and frame!
2350/// <https://stereokit.net/Pages/StereoKit/Time.html>
2351///
2352/// ### Examples
2353/// ```
2354/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2355/// use stereokit_rust::util::Time;
2356///
2357/// // These are the expected results for tests on a PC:
2358/// assert_eq!(Time::get_totalf(), 0.0);
2359/// assert_eq!(Time::get_total(), 0.0);
2360/// assert_eq!(Time::get_stepf(), 0.0);
2361/// assert_eq!(Time::get_step(), 0.0);
2362///
2363/// // Time passes slowly:
2364/// Time::scale(0.5);
2365///
2366/// let mut total = 0.0f64;
2367/// let mut totalf = 0.0f32;
2368/// number_of_steps = 100;
2369/// test_steps!( // !!!! Get a proper main loop !!!!
2370/// if iter < number_of_steps + 2 {
2371/// assert_eq!(Time::get_frame(), iter + 1);
2372///
2373/// assert_eq!(Time::get_step_unscaled(), Time::get_step() * 2.0);
2374///
2375/// assert_eq!(Time::get_total(), total + Time::get_step());
2376/// assert_eq!(Time::get_total_unscaled(), total * 2.0 + Time::get_step() * 2.0);
2377///
2378/// // precision is worse for f32
2379/// assert!((Time::get_totalf()
2380/// - totalf - Time::get_stepf()).abs() < 0.000001);
2381/// assert!((Time::get_total_unscaledf()
2382/// - totalf * 2.0 - Time::get_stepf() * 2.0).abs() < 0.000001);
2383/// }
2384/// totalf = Time::get_totalf();
2385/// total = Time::get_total();
2386/// );
2387/// ```
2388pub struct Time;
2389
2390unsafe extern "C" {
2391 // Deprecated: pub fn time_get_raw() -> f64;
2392 // Deprecated: pub fn time_getf_unscaled() -> f32;
2393 // Deprecated: pub fn time_get_unscaled() -> f64;
2394 // Deprecated: pub fn time_getf() -> f32;
2395 // Deprecated: pub fn time_get() -> f64;
2396 // Deprecated: pub fn time_elapsedf_unscaled() -> f32;
2397 // Deprecated: pub fn time_elapsed_unscaled() -> f64;
2398 // Deprecated: pub fn time_elapsedf() -> f32;
2399 // Deprecated: pub fn time_elapsed() -> f64;
2400 pub fn time_total_raw() -> f64;
2401 pub fn time_totalf_unscaled() -> f32;
2402 pub fn time_total_unscaled() -> f64;
2403 pub fn time_totalf() -> f32;
2404 pub fn time_total() -> f64;
2405 pub fn time_stepf_unscaled() -> f32;
2406 pub fn time_step_unscaled() -> f64;
2407 pub fn time_stepf() -> f32;
2408 pub fn time_step() -> f64;
2409 pub fn time_scale(scale: f64);
2410 pub fn time_set_time(total_seconds: f64, frame_elapsed_seconds: f64);
2411 pub fn time_frame() -> u64;
2412}
2413
2414impl Time {
2415 /// Time is scaled by this value! Want time to pass slower? Set it to 0.5! Faster? Try 2!
2416 /// <https://stereokit.net/Pages/StereoKit/Time/Scale.html>
2417 ///
2418 /// see also [`time_scale`]
2419 /// ### Examples
2420 /// ```
2421 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2422 /// use stereokit_rust::util::Time;
2423 ///
2424 /// // Time passes faster:
2425 /// Time::scale(2.0);
2426 ///
2427 /// let mut total = 0.0f64;
2428 /// let mut totalf = 0.0f32;
2429 /// number_of_steps = 100;
2430 /// test_steps!( // !!!! Get a proper main loop !!!!
2431 /// assert_eq!(Time::get_step_unscaled(), Time::get_step() / 2.0);
2432 /// );
2433 /// ```
2434 pub fn scale(factor: f64) {
2435 unsafe { time_scale(factor) }
2436 }
2437
2438 /// This allows you to override the application time! The application will progress from this time using the current
2439 /// timescale.
2440 /// <https://stereokit.net/Pages/StereoKit/Time/SetTime.html>
2441 /// * `total_seconds` - What time should it now be? The app will progress from this point in time.
2442 /// * `frame_elapsed_seconds` - How long was the previous frame? This is a number often used in motion calculations.
2443 /// If left to zero, it’ll use the previous frame’s time, and if the previous frame’s time was also zero, it’ll
2444 /// use 1/90.
2445 ///
2446 /// see also [`time_set_time`]
2447 /// ### Examples
2448 /// ```
2449 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2450 /// use stereokit_rust::util::Time;
2451 ///
2452 /// // Time passes faster:
2453 /// Time::set_time(10.0, 0.01);
2454 ///
2455 /// assert_eq!(Time::get_total(), 10.0);
2456 /// assert_eq!(Time::get_step(), 0.01);
2457 /// ```
2458 pub fn set_time(total_seconds: f64, frame_elapsed_seconds: f64) {
2459 unsafe { time_set_time(total_seconds, frame_elapsed_seconds) }
2460 }
2461
2462 /// The number of frames/steps since the app started.
2463 /// <https://stereokit.net/Pages/StereoKit/Time/Frame.html>
2464 ///
2465 /// see also [`time_frame`]
2466 /// ### Examples
2467 /// ```
2468 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2469 /// use stereokit_rust::util::Time;
2470 ///
2471 /// assert_eq!(Time::get_frame(), 0);
2472 ///
2473 /// test_steps!( // !!!! Get a proper main loop !!!!
2474 /// if iter < number_of_steps + 2 {
2475 /// assert_eq!(Time::get_frame(), iter + 1);
2476 /// }
2477 /// );
2478 /// ```
2479 pub fn get_frame() -> u64 {
2480 unsafe { time_frame() }
2481 }
2482
2483 /// How many seconds have elapsed since the last frame? 64 bit time precision, calculated at the start of the frame.
2484 /// <https://stereokit.net/Pages/StereoKit/Time/Step.html>
2485 ///
2486 /// see also [`time_step`]
2487 /// ### Examples
2488 /// ```
2489 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2490 /// use stereokit_rust::util::Time;
2491 ///
2492 /// assert_eq!(Time::get_step(), 0.0);
2493 ///
2494 /// let mut total = 0.0f64;
2495 /// number_of_steps = 100;
2496 /// test_steps!( // !!!! Get a proper main loop !!!!
2497 /// if iter < number_of_steps + 2 {
2498 /// assert_eq!(Time::get_total(), total + Time::get_step());
2499 /// }
2500 /// total = Time::get_total();
2501 /// );
2502 /// ```
2503 pub fn get_step() -> f64 {
2504 unsafe { time_step() }
2505 }
2506
2507 /// How many seconds have elapsed since the last frame? 32 bit time precision, calculated at the start of the frame.
2508 /// <https://stereokit.net/Pages/StereoKit/Time/Stepf.html>
2509 ///
2510 /// see also [`time_stepf`]
2511 /// ### Examples
2512 /// ```
2513 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2514 /// use stereokit_rust::util::Time;
2515 ///
2516 /// assert_eq!(Time::get_stepf(), 0.0);
2517 ///
2518 /// let mut totalf = 0.0f32;
2519 /// number_of_steps = 100;
2520 /// test_steps!( // !!!! Get a proper main loop !!!!
2521 /// if iter < number_of_steps + 2 {
2522 /// assert!((Time::get_totalf() - totalf - Time::get_stepf()).abs() < 0.0001);
2523 /// }
2524 /// totalf = Time::get_totalf();
2525 /// );
2526 /// ```
2527 pub fn get_stepf() -> f32 {
2528 unsafe { time_stepf() }
2529 }
2530
2531 /// How many seconds have elapsed since the last frame? 64 bit time precision, calculated at the start of the frame.
2532 /// This version is unaffected by the Time::scale value!
2533 /// <https://stereokit.net/Pages/StereoKit/Time/StepUnscaled.html>
2534 ///
2535 /// see also [`time_step_unscaled`]
2536 /// ### Examples
2537 /// ```
2538 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2539 /// use stereokit_rust::util::Time;
2540 ///
2541 /// assert_eq!(Time::get_step_unscaled(), 0.0);
2542 ///
2543 /// let mut total = 0.0f64;
2544 /// number_of_steps = 100;
2545 /// test_steps!( // !!!! Get a proper main loop !!!!
2546 /// if iter < number_of_steps + 2 {
2547 /// assert_eq!(Time::get_total_unscaled(), total + Time::get_step_unscaled());
2548 /// }
2549 /// total = Time::get_total_unscaled();
2550 /// );
2551 /// ```
2552 pub fn get_step_unscaled() -> f64 {
2553 unsafe { time_step_unscaled() }
2554 }
2555
2556 /// How many seconds have elapsed since the last frame? 32 bit time precision, calculated at the start of the frame.
2557 /// This version is unaffected by the Time.Scale value!
2558 /// <https://stereokit.net/Pages/StereoKit/Time/StepUnscaledf.html>
2559 ///
2560 /// see also [`time_stepf_unscaled`]
2561 /// ### Examples
2562 /// ```
2563 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2564 /// use stereokit_rust::util::Time;
2565 ///
2566 /// assert_eq!(Time::get_step_unscaledf(), 0.0);
2567 ///
2568 /// let mut totalf = 0.0f32;
2569 /// number_of_steps = 100;
2570 /// test_steps!( // !!!! Get a proper main loop !!!!
2571 /// if iter < number_of_steps + 2 {
2572 /// assert!((Time::get_total_unscaledf() - totalf - Time::get_step_unscaledf().abs() < 0.0001));
2573 /// }
2574 /// totalf = Time::get_total_unscaledf();
2575 /// );
2576 /// ```
2577 pub fn get_step_unscaledf() -> f32 {
2578 unsafe { time_stepf_unscaled() }
2579 }
2580
2581 /// How many seconds have elapsed since StereoKit was initialized? 64 bit time precision, calculated at the start of
2582 /// the frame.
2583 /// <https://stereokit.net/Pages/StereoKit/Time/Total.html>
2584 ///
2585 /// see also [`time_total`]
2586 /// ### Examples
2587 /// ```
2588 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2589 /// use stereokit_rust::util::Time;
2590 ///
2591 /// // Time passes faster:
2592 /// Time::scale(2.0);
2593 ///
2594 /// assert_eq!(Time::get_total(), 0.0);
2595 ///
2596 /// number_of_steps = 100;
2597 /// test_steps!( // !!!! Get a proper main loop !!!!
2598 /// if iter < number_of_steps + 2 {
2599 /// assert_eq!(Time::get_total(), Time::get_total_unscaled() * 2.0);
2600 /// }
2601 /// );
2602 /// ```
2603 pub fn get_total() -> f64 {
2604 unsafe { time_total() }
2605 }
2606
2607 /// How many seconds have elapsed since StereoKit was initialized? 32 bit time precision, calculated at the start of
2608 /// the frame.
2609 /// <https://stereokit.net/Pages/StereoKit/Time/Totalf.html>
2610 ///
2611 /// see also [`time_totalf`]
2612 /// ### Examples
2613 /// ```
2614 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2615 /// use stereokit_rust::util::Time;
2616 ///
2617 /// // Time passes faster:
2618 /// Time::scale(0.25);
2619 ///
2620 /// assert_eq!(Time::get_totalf(), 0.0);
2621 ///
2622 /// number_of_steps = 100;
2623 /// test_steps!( // !!!! Get a proper main loop !!!!
2624 /// if iter < number_of_steps + 2 {
2625 /// assert_eq!(Time::get_totalf(), Time::get_total_unscaledf() / 4.0);
2626 /// }
2627 /// );
2628 /// ```
2629 pub fn get_totalf() -> f32 {
2630 unsafe { time_totalf() }
2631 }
2632
2633 /// How many seconds have elapsed since StereoKit was initialized? 64 bit time precision, calculated at the start of
2634 /// the frame. This version is unaffected by the Time::scale value!
2635 /// <https://stereokit.net/Pages/StereoKit/Time/TotalUnscaled.html>
2636 ///
2637 /// see also [`time_total_unscaled`]
2638 /// see example in [`Time::get_total`]
2639 pub fn get_total_unscaled() -> f64 {
2640 unsafe { time_total_unscaled() }
2641 }
2642
2643 /// How many seconds have elapsed since StereoKit was initialized? 32 bit time precision, calculated at the start of
2644 /// the frame. This version is unaffected by the Time::scale value!
2645 /// <https://stereokit.net/Pages/StereoKit/Time/TotalUnscaledf.html>
2646 ///
2647 /// see also [`time_totalf_unscaled`]
2648 /// see example in [`Time::get_total_unscaled`]
2649 pub fn get_total_unscaledf() -> f32 {
2650 unsafe { time_totalf_unscaled() }
2651 }
2652}