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/// // These are the expected results for tests on a PC:
924/// let display_type = Device::get_display_type();
925/// assert_eq!(display_type, DisplayType::Flatscreen);
926///
927/// let display_blend = Device::get_display_blend();
928/// assert_eq!(display_blend, DisplayBlend::Opaque);
929///
930/// let device_tracking = Device::get_tracking();
931/// assert_eq!(device_tracking, DeviceTracking::None);
932///
933/// let device_name = Device::get_name().unwrap();
934/// assert_eq!(device_name, "Offscreen");
935///
936/// let device_runtime = Device::get_runtime().unwrap();
937/// assert_eq!(device_runtime, "None");
938///
939/// let device_gpu = Device::get_gpu().unwrap();
940/// assert_ne!(device_gpu, "Name of your GPU");
941///
942/// assert_eq!(Device::has_eye_gaze(), false);
943/// assert_eq!(Device::has_hand_tracking(), false);
944///
945/// assert_eq!(Device::valid_blend(DisplayBlend::None), false);
946/// assert_eq!(Device::display_blend(DisplayBlend::None), false);
947/// ```
948pub struct Device;
949
950unsafe extern "C" {
951 pub fn device_display_get_type() -> DisplayType;
952 pub fn device_display_get_blend() -> DisplayBlend;
953 pub fn device_display_set_blend(blend: DisplayBlend) -> Bool32T;
954 pub fn device_display_valid_blend(blend: DisplayBlend) -> Bool32T;
955 pub fn device_display_get_refresh_rate() -> f32;
956 pub fn device_display_get_width() -> i32;
957 pub fn device_display_get_height() -> i32;
958 pub fn device_display_get_fov() -> FovInfo;
959 pub fn device_get_tracking() -> DeviceTracking;
960 pub fn device_get_name() -> *const c_char;
961 pub fn device_get_runtime() -> *const c_char;
962 pub fn device_get_gpu() -> *const c_char;
963 pub fn device_has_eye_gaze() -> Bool32T;
964 pub fn device_has_hand_tracking() -> Bool32T;
965}
966
967impl Device {
968 /// Allows you to set and get the current blend mode of the device! Setting this may not succeed if the blend mode
969 /// is not valid.
970 /// <https://stereokit.net/Pages/StereoKit/Device/DisplayBlend.html>
971 ///
972 /// see also [`device_display_set_blend`]
973 /// ### Examples
974 /// ```
975 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
976 /// use stereokit_rust::{util::Device, sk::DisplayBlend};
977 ///
978 /// // These are the expected results for tests on a PC:
979 /// assert_eq!(Device::get_display_blend(), DisplayBlend::Opaque);
980 ///
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 /// // These are the expected results for tests on a PC:
1009 /// assert_eq!(Device::get_display_type(), DisplayType::Flatscreen);
1010 /// ```
1011 pub fn get_display_type() -> DisplayType {
1012 unsafe { device_display_get_type() }
1013 }
1014
1015 /// This is the name of the OpenXR runtime that powers the current device! This can help you determine which
1016 /// implementation quirks to expect based on the codebase used. On the simulator, this will be "Simulator", and in
1017 /// other non-XR modes this will be "None".
1018 /// <https://stereokit.net/Pages/StereoKit/Device/Runtime.html>
1019 ///
1020 /// see also [`device_get_runtime`]
1021 /// ### Examples
1022 /// ```
1023 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1024 /// use stereokit_rust::util::Device;
1025 ///
1026 /// // These are the expected results for tests on a PC:
1027 /// assert_eq!(Device::get_runtime().unwrap(), "None");
1028 /// ```
1029 pub fn get_runtime<'a>() -> Result<&'a str, StereoKitError> {
1030 unsafe { CStr::from_ptr(device_get_runtime()) }
1031 .to_str()
1032 .map_err(|e| StereoKitError::CStrError(e.to_string()))
1033 }
1034
1035 /// The reported name of the GPU, this will differ between D3D and GL.
1036 /// <https://stereokit.net/Pages/StereoKit/Device/GPU.html>
1037 ///
1038 /// see also [`device_get_gpu`]
1039 pub fn get_gpu<'a>() -> Result<&'a str, StereoKitError> {
1040 unsafe { CStr::from_ptr(device_get_gpu()) }
1041 .to_str()
1042 .map_err(|e| StereoKitError::CStrError(e.to_string()))
1043 }
1044
1045 /// Does the device we’re on have eye tracking support present for input purposes? This is not an indicator that the
1046 /// user has given the application permission to access this information. See Input.Gaze for how to use this data.
1047 /// <https://stereokit.net/Pages/StereoKit/Device/HasEyeGaze.html>
1048 ///
1049 /// see also [`device_has_eye_gaze`]
1050 /// ### Examples
1051 /// ```
1052 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1053 /// use stereokit_rust::util::Device;
1054 ///
1055 /// // These are the expected results for tests on a PC:
1056 /// assert_eq!(Device::has_eye_gaze(), false);
1057 /// ```
1058 pub fn has_eye_gaze() -> bool {
1059 unsafe { device_has_eye_gaze() != 0 }
1060 }
1061
1062 /// Tells if the device is capable of tracking hands. This does not tell if the user is actually using their hands
1063 /// for input, merely that it’s possible to!
1064 /// <https://stereokit.net/Pages/StereoKit/Device/HasHandTracking.html>
1065 ///
1066 /// see also [`device_has_hand_tracking`]
1067 /// ### Examples
1068 /// ```
1069 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1070 /// use stereokit_rust::util::Device;
1071 ///
1072 /// // These are the expected results for tests on a PC:
1073 /// assert_eq!(Device::has_hand_tracking(), false);
1074 /// ```
1075 pub fn has_hand_tracking() -> bool {
1076 unsafe { device_has_hand_tracking() != 0 }
1077 }
1078
1079 /// This is the name of the active device! From OpenXR, this is the same as systemName from XrSystemProperties. The
1080 /// simulator will say “Simulator”.
1081 /// <https://stereokit.net/Pages/StereoKit/Device/Name.html>
1082 ///
1083 /// see also [`device_get_name`]
1084 /// ### Examples
1085 /// ```
1086 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1087 /// use stereokit_rust::util::Device;
1088 ///
1089 /// // These are the expected results for tests on a PC:
1090 /// assert_eq!(Device::get_name().unwrap(), "Offscreen");
1091 /// ```
1092 pub fn get_name<'a>() -> Result<&'a str, StereoKitError> {
1093 unsafe { CStr::from_ptr(device_get_name()) }
1094 .to_str()
1095 .map_err(|e| StereoKitError::CStrError(e.to_string()))
1096 }
1097
1098 /// The tracking capabilities of this device! Is it 3DoF, rotation only? Or is it 6DoF, with positional tracking as
1099 /// well? Maybe it can’t track at all!
1100 /// <https://stereokit.net/Pages/StereoKit/Device/Tracking.html>
1101 ///
1102 /// see also [`device_get_tracking`]
1103 /// ### Examples
1104 /// ```
1105 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1106 /// use stereokit_rust::util::{Device, DeviceTracking};
1107 ///
1108 /// // These are the expected results for tests on a PC:
1109 /// assert_eq!(Device::get_tracking(), DeviceTracking::None);
1110 /// ```
1111 pub fn get_tracking() -> DeviceTracking {
1112 unsafe { device_get_tracking() }
1113 }
1114
1115 /// Tells if a particular blend mode is valid on this device. Some devices may be capable of more than one blend
1116 /// mode.
1117 /// <https://stereokit.net/Pages/StereoKit/Device/ValidBlend.html>
1118 ///
1119 /// see also [`device_display_valid_blend`]
1120 /// ### Examples
1121 /// ```
1122 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1123 /// use stereokit_rust::{util::Device, sk::DisplayBlend};
1124 ///
1125 /// // These are the expected results for tests on a PC:
1126 /// assert_eq!(Device::valid_blend(DisplayBlend::Opaque), true);
1127 /// assert_eq!(Device::valid_blend(DisplayBlend::None), false);
1128 /// assert_eq!(Device::valid_blend(DisplayBlend::Additive), false);
1129 /// assert_eq!(Device::valid_blend(DisplayBlend::Blend), false);
1130 /// assert_eq!(Device::valid_blend(DisplayBlend::AnyTransparent), false);
1131 /// ```
1132 pub fn valid_blend(blend: DisplayBlend) -> bool {
1133 unsafe { device_display_valid_blend(blend) != 0 }
1134 }
1135}
1136
1137/// A color/position pair for Gradient values!
1138/// <https://stereokit.net/Pages/StereoKit/GradientKey.html>
1139///
1140/// see [`Gradient`]
1141/// ### Examples
1142/// ```
1143/// use stereokit_rust::util::{GradientKey, Color128, named_colors};
1144///
1145/// let key0 = GradientKey::new(named_colors::GOLD, 0.75);
1146/// let key1 = GradientKey::new( Color128::new(1.0, 0.84313726, 0.0, 1.0), 0.75);
1147/// let key2 = GradientKey{ color: Color128::new(1.0, 0.84313726, 0.0, 1.0), position: 0.75 };
1148///
1149/// assert_eq!(key0, key1);
1150/// assert_eq!(key1, key2);
1151/// ```
1152#[derive(Debug, Copy, Clone, PartialEq)]
1153#[repr(C)]
1154pub struct GradientKey {
1155 /// The color for this item, preferably in some form of linear color space. Gamma corrected colors will definitely
1156 /// not math correctly.
1157 pub color: Color128,
1158 /// Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the gradient.
1159 pub position: f32,
1160}
1161
1162impl GradientKey {
1163 /// A basic copy constructor for GradientKey.
1164 /// <https://stereokit.net/Pages/StereoKit/GradientKey/GradientKey.html>
1165 /// * `color_linear` - The color for this item, preferably in some form of linear color space. Gamma corrected
1166 /// colors will definitely not math correctly.
1167 /// * `position` - Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the
1168 /// gradient.
1169 pub fn new(color_linear: impl Into<Color128>, position: f32) -> Self {
1170 Self { color: color_linear.into(), position }
1171 }
1172}
1173
1174/// A Gradient is a sparse collection of color keys that are used to represent a ramp of colors! This class is largely
1175/// just storing colors and allowing you to sample between them.
1176///
1177/// Since the Gradient is just interpolating values, you can use whatever color space you want here, as long as it's
1178/// linear and not gamma! Gamma space RGB can't math properly at all. It can be RGB(linear), HSV, LAB, just remember
1179/// which one you have, and be sure to convert it appropriately later. Data is stored as float colors, so this'll be a
1180/// high accuracy blend!
1181/// <https://stereokit.net/Pages/StereoKit/Gradient.html>
1182///
1183/// see also [GradientKey] [`crate::tex::Tex::gen_particle`] [`crate::tex::SHCubemap::gen_cubemap_gradient`]
1184/// ### Examples
1185/// ```
1186/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1187/// use stereokit_rust::{maths::Vec3, system::AssetState, tex::{Tex, SHCubemap},
1188/// util::{named_colors, Gradient, GradientKey, Color128}};
1189///
1190/// let keys = [
1191/// GradientKey::new(Color128::BLACK_TRANSPARENT, 0.0),
1192/// GradientKey::new(named_colors::RED, 0.1),
1193/// GradientKey::new(named_colors::CYAN, 0.4),
1194/// GradientKey::new(named_colors::BLUE, 0.5),
1195/// GradientKey::new(Color128::BLACK, 0.7)];
1196///
1197/// let sh_cubemap = SHCubemap::gen_cubemap_gradient(Gradient::new(Some(&keys)),
1198/// Vec3::UP, 128);
1199/// sh_cubemap.render_as_sky();
1200///
1201/// let mut gradient = Gradient::new(None);
1202/// gradient
1203/// .add(Color128::BLACK_TRANSPARENT, 0.0)
1204/// .add(named_colors::YELLOW, 0.1)
1205/// .add(named_colors::LIGHT_BLUE, 0.4)
1206/// .add(named_colors::BLUE, 0.5)
1207/// .add(Color128::BLACK, 0.7);
1208/// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1209/// ```
1210pub struct Gradient(pub NonNull<_GradientT>);
1211
1212impl Drop for Gradient {
1213 fn drop(&mut self) {
1214 unsafe { gradient_destroy(self.0.as_ptr()) };
1215 }
1216}
1217
1218impl AsRef<Gradient> for Gradient {
1219 fn as_ref(&self) -> &Gradient {
1220 self
1221 }
1222}
1223
1224/// StereoKit internal type.
1225#[repr(C)]
1226#[derive(Debug)]
1227pub struct _GradientT {
1228 _unused: [u8; 0],
1229}
1230
1231/// StereoKit ffi type.
1232pub type GradientT = *mut _GradientT;
1233
1234unsafe extern "C" {
1235 pub fn gradient_create() -> GradientT;
1236 pub fn gradient_create_keys(in_arr_keys: *const GradientKey, count: i32) -> GradientT;
1237 pub fn gradient_add(gradient: GradientT, color_linear: Color128, position: f32);
1238 pub fn gradient_set(gradient: GradientT, index: i32, color_linear: Color128, position: f32);
1239 pub fn gradient_remove(gradient: GradientT, index: i32);
1240 pub fn gradient_count(gradient: GradientT) -> i32;
1241 pub fn gradient_get(gradient: GradientT, at: f32) -> Color128;
1242 pub fn gradient_get32(gradient: GradientT, at: f32) -> Color32;
1243 pub fn gradient_destroy(gradient: GradientT);
1244}
1245impl Gradient {
1246 /// Creates a new, completely empty gradient.
1247 /// <https://stereokit.net/Pages/StereoKit/Gradient/Gradient.html>
1248 /// * `keys` - These can be in any order that you like, they’ll be sorted by their GradientKey.position value
1249 /// regardless!
1250 ///
1251 /// see also [`gradient_create`] [`gradient_create_keys`]
1252 /// ### Examples
1253 /// ```
1254 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1255 /// use stereokit_rust::{maths::Vec3, tex::{Tex, SHCubemap},
1256 /// util::{named_colors, Gradient, GradientKey, Color128}};
1257 ///
1258 /// let keys = [
1259 /// GradientKey::new(Color128::BLACK_TRANSPARENT, 0.0),
1260 /// GradientKey::new(named_colors::RED, 0.5),
1261 /// GradientKey::new(Color128::BLACK, 0.7)];
1262 ///
1263 /// let gradient1 = Gradient::new(Some(&keys));
1264 /// assert_eq!(gradient1.get_count(), 3);
1265 /// let sh_cubemap = SHCubemap::gen_cubemap_gradient(gradient1, Vec3::UP, 16);
1266 /// sh_cubemap.render_as_sky();
1267 ///
1268 /// let mut gradient2 = Gradient::new(Some(&keys));
1269 /// gradient2.add(named_colors::CYAN, 0.4);
1270 /// assert_eq!(gradient2.get_count(), 4);
1271 /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient2));
1272 /// ```
1273 pub fn new(keys: Option<&[GradientKey]>) -> Self {
1274 match keys {
1275 Some(keys) => {
1276 Gradient(NonNull::new(unsafe { gradient_create_keys(keys.as_ptr(), keys.len() as i32) }).unwrap())
1277 }
1278 None => Gradient(NonNull::new(unsafe { gradient_create() }).unwrap()),
1279 }
1280 }
1281
1282 /// This adds a color key into the list. It’ll get inserted to the right slot based on its position.
1283 /// <https://stereokit.net/Pages/StereoKit/Gradient/Add.html>
1284 /// * `color_linear` - Any linear space color you like!
1285 /// * `position` - Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the
1286 /// gradient.
1287 ///
1288 /// see also [`gradient_add`]
1289 /// ### Examples
1290 /// ```
1291 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1292 /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1293 ///
1294 /// let mut gradient = Gradient::new(None);
1295 /// gradient
1296 /// .add(Color128::BLACK_TRANSPARENT, 0.0)
1297 /// .add(named_colors::YELLOW, 0.1)
1298 /// .add(named_colors::LIGHT_BLUE, 0.4)
1299 /// .add(named_colors::BLUE, 0.5)
1300 /// .add(Color128::BLACK, 0.7);
1301 /// assert_eq!(gradient.get_count(), 5);
1302 /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1303 /// ```
1304 pub fn add(&mut self, color_linear: impl Into<Color128>, position: f32) -> &mut Self {
1305 unsafe { gradient_add(self.0.as_ptr(), color_linear.into(), position) };
1306 self
1307 }
1308
1309 /// Updates the color key at the given index! This will NOT re-order color keys if they are moved past another
1310 /// key’s position, which could lead to strange behavior.
1311 /// <https://stereokit.net/Pages/StereoKit/Gradient/Set.html>
1312 /// * `index` - Index of the color key to change.
1313 /// * `color_linear` - Any linear space color you like!
1314 /// * `position` - Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the
1315 /// gradient.
1316 ///
1317 /// see also [`gradient_set`]
1318 /// ### Examples
1319 /// ```
1320 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1321 /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1322 ///
1323 /// let mut gradient = Gradient::new(None);
1324 /// gradient
1325 /// .add(Color128::BLACK_TRANSPARENT, 0.0)
1326 /// .add(named_colors::YELLOW, 0.1)
1327 /// .add(named_colors::LIGHT_BLUE, 0.4)
1328 /// .add(named_colors::BLUE, 0.5)
1329 /// .add(Color128::BLACK, 0.7);
1330 /// assert_eq!(gradient.get_count(), 5);
1331 /// assert_eq!(gradient.get(0.3), Color128 { r: 0.7856209, g: 0.8980392, b: 0.6013072, a: 1.0 });
1332 ///
1333 /// gradient.set(2, named_colors::RED, 0.3);
1334 /// gradient.set(-20, named_colors::RED, -10.3); // out of bounds, should do nothing
1335 /// assert_eq!(gradient.get_count(), 5);
1336 /// assert_eq!(gradient.get(0.3), named_colors::RED.into());
1337 ///
1338 /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1339 /// ```
1340 pub fn set(&mut self, index: i32, color_linear: impl Into<Color128>, position: f32) -> &mut Self {
1341 if index < 0 || index >= self.get_count() {
1342 return self;
1343 }
1344 unsafe { gradient_set(self.0.as_ptr(), index, color_linear.into(), position) };
1345 self
1346 }
1347
1348 /// Removes the color key at the given index! This won't reindex the gradient so get_count will still return the
1349 /// same value.
1350 /// <https://stereokit.net/Pages/StereoKit/Gradient/Remove.html>
1351 /// * `index` - The index of the color key to remove.
1352 ///
1353 /// see also [`gradient_remove`]
1354 /// ### Examples
1355 /// ```
1356 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1357 /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1358 ///
1359 /// let mut gradient = Gradient::new(None);
1360 /// gradient
1361 /// .add(Color128::BLACK_TRANSPARENT, 0.0)
1362 /// .add(named_colors::YELLOW, 0.1)
1363 /// .add(named_colors::LIGHT_BLUE, 0.4)
1364 /// .add(named_colors::BLUE, 0.5)
1365 /// .add(Color128::BLACK, 0.7);
1366 /// assert_eq!(gradient.get_count(), 5);
1367 /// assert_eq!(gradient.get(0.4), named_colors::LIGHT_BLUE.into());
1368 ///
1369 /// gradient.remove(2);
1370 /// gradient.remove(19).remove(-189);
1371 /// assert_eq!(gradient.get_count(), 5);
1372 /// assert_eq!(gradient.get(0.4), Color128 { r: 0.25, g: 0.25, b: 0.75, a: 1.0 });
1373 ///
1374 /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1375 /// ```
1376 pub fn remove(&mut self, index: i32) -> &mut Self {
1377 if index < 0 || index >= self.get_count() {
1378 return self;
1379 }
1380 unsafe { gradient_remove(self.0.as_ptr(), index) };
1381 self
1382 }
1383
1384 /// The number of color keys present in this gradient.
1385 /// <https://stereokit.net/Pages/StereoKit/Gradient/Count.html>
1386 ///
1387 /// see also [`gradient_count`]
1388 /// ### Examples
1389 /// ```
1390 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1391 /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1392 ///
1393 /// let mut gradient = Gradient::new(None);
1394 /// assert_eq!(gradient.get_count(), 0);
1395 ///
1396 /// gradient
1397 /// .add(Color128::BLACK_TRANSPARENT, 0.0)
1398 /// .add(named_colors::YELLOW, 0.1)
1399 /// .add(Color128::BLACK, 0.7);
1400 /// assert_eq!(gradient.get_count(), 3);
1401 ///
1402 /// gradient.remove(1);
1403 /// assert_eq!(gradient.get_count(), 3);
1404 ///
1405 /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1406 ///```
1407 pub fn get_count(&self) -> i32 {
1408 unsafe { gradient_count(self.0.as_ptr()) }
1409 }
1410
1411 /// Samples the gradient’s color at the given position!
1412 /// <https://stereokit.net/Pages/StereoKit/Gradient/Get.html>
1413 /// * `at` - Typically a value between 0-1, but if you used larger or smaller values for your color key’s positions,
1414 /// it’ll be in that range!
1415 ///
1416 /// Returns the interpolated color at the given position. If ‘at’ is smaller or larger than the gradient’s position
1417 /// range, then the color will be clamped to the color at the beginning or end of the gradient!
1418 /// see also [`gradient_get`]
1419 /// ### Examples
1420 /// ```
1421 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1422 /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1423 ///
1424 /// let mut gradient = Gradient::new(None);
1425 /// gradient
1426 /// .add(Color128::BLACK_TRANSPARENT, 0.0)
1427 /// .add(named_colors::YELLOW, 0.1)
1428 /// .add(named_colors::LIGHT_BLUE, 0.4)
1429 /// .add(named_colors::BLUE, 0.5)
1430 /// .add(Color128::BLACK, 0.7);
1431 ///
1432 /// assert_eq!(gradient.get(0.3), Color128 { r: 0.7856209, g: 0.8980392, b: 0.6013072, a: 1.0 });
1433 /// assert_eq!(gradient.get(0.4), named_colors::LIGHT_BLUE.into());
1434 /// assert_eq!(gradient.get(0.5), named_colors::BLUE.into());
1435 ///
1436 /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1437 /// ```
1438 pub fn get(&self, at: f32) -> Color128 {
1439 unsafe { gradient_get(self.0.as_ptr(), at) }
1440 }
1441
1442 /// Samples the gradient’s color at the given position, and converts it to a 32 bit color. If your RGBA color
1443 /// values are outside of the 0-1 range, then you’ll get some issues as they’re converted to 0-255 range bytes!
1444 /// <https://stereokit.net/Pages/StereoKit/Gradient/Remove.html>
1445 /// * `at` - Typically a value between 0-1, but if you used larger or smaller values for your color key’s positions,
1446 /// it’ll be in that range!
1447 ///
1448 /// Returns the interpolated 32 bit color at the given position. If ‘at’ is smaller or larger than the gradient’s
1449 /// position range, then the color will be clamped to the color at the beginning or end of the gradient!
1450 /// see also [`gradient_get32`]
1451 /// ### Examples
1452 /// ```
1453 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1454 /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128, Color32}};
1455 ///
1456 /// let mut gradient = Gradient::new(None);
1457 /// gradient
1458 /// .add(Color128::BLACK_TRANSPARENT, 0.0)
1459 /// .add(named_colors::YELLOW, 0.1)
1460 /// .add(named_colors::LIGHT_BLUE, 0.4)
1461 /// .add(named_colors::BLUE, 0.5)
1462 /// .add(Color128::BLACK, 0.7);
1463 ///
1464 /// assert_eq!(gradient.get32(0.3), Color32 { r: 200, g: 229, b: 153, a: 255 });
1465 /// assert_eq!(gradient.get32(0.4), named_colors::LIGHT_BLUE);
1466 /// assert_eq!(gradient.get32(0.5), named_colors::BLUE);
1467 ///
1468 /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1469 /// ```
1470 pub fn get32(&self, at: f32) -> Color32 {
1471 unsafe { gradient_get32(self.0.as_ptr(), at) }
1472 }
1473}
1474
1475/// file extension to filter ie ".txt" ".gltf"
1476/// <https://stereokit.net/Pages/StereoKit/Platform.html>
1477///
1478/// see also [`crate::system::Assets::MODEL_FORMATS`] [`crate::system::Assets::TEXTURE_FORMATS`]
1479#[derive(Debug, Default, Copy, Clone)]
1480#[repr(C)]
1481pub struct FileFilter {
1482 ext: [c_char; 32usize],
1483}
1484
1485impl FileFilter {
1486 pub fn new(str: impl AsRef<str>) -> Self {
1487 let mut value: [c_char; 32usize] = [0; 32usize];
1488 let c_array = CString::new(str.as_ref()).unwrap();
1489 let c_array = c_array.as_bytes_with_nul();
1490
1491 let mut c_array_iter = c_array.iter().map(|v| *v as c_char);
1492 value[0..c_array.len()].fill_with(|| c_array_iter.next().unwrap());
1493
1494 Self { ext: value }
1495 }
1496}
1497
1498/// This class contains some tools for hashing data within StereoKit! Certain systems in StereoKit use string hashes
1499/// instead of full strings for faster search and compare, like UI and Materials, so this class gives access to the code
1500/// SK uses for hashing. StereoKit currently internally uses a 64 bit FNV hash, though this detail should be pretty
1501/// transparent to developers
1502pub struct Hash;
1503
1504unsafe extern "C" {
1505 pub fn hash_string(str_utf8: *const c_char) -> IdHashT;
1506 pub fn hash_string_with(str_utf8: *const c_char, root: IdHashT) -> IdHashT;
1507 pub fn hash_int(val: i32) -> IdHashT;
1508 pub fn hash_int_with(val: i32, root: IdHashT) -> IdHashT;
1509}
1510
1511impl Hash {
1512 /// This will hash the UTF8 representation of the given string into a hash value that StereoKit can use.
1513 /// <https://stereokit.net/Pages/StereoKit/Hash/String.html>
1514 /// * `str` - A string (UTF8),that will be hashed.
1515 ///
1516 /// Returns a StereoKit hash representing the provided string.
1517 /// see also [`hash_string`] [`Hash::string_with`]
1518 /// ### Examples
1519 /// ```
1520 /// use stereokit_rust::util::Hash;
1521 ///
1522 /// let hash = Hash::string("Hello World");
1523 /// assert_eq!(hash, 4420528118743043111);
1524 /// ```
1525 pub fn string(str: impl AsRef<str>) -> IdHashT {
1526 let c_str = CString::new(str.as_ref()).unwrap();
1527 unsafe { hash_string(c_str.as_ptr()) }
1528 }
1529
1530 /// This will hash the UTF8 representation of the given string into a hash value that StereoKit can use. This
1531 /// overload allows you to combine your hash with an existing hash.
1532 /// <https://stereokit.net/Pages/StereoKit/Hash/String.html>
1533 /// * `str` - A string (UTF8),that will be hashed.
1534 /// * `root` - The hash value this new hash will start from.
1535 ///
1536 /// Returns a StereoKit hash representing a combination of the provided string and the root hash.
1537 /// see also [`hash_string_with`] [`Hash::string`]
1538 /// ### Examples
1539 /// ```
1540 /// use stereokit_rust::util::Hash;
1541 ///
1542 /// let hash1 = Hash::string("Hello");
1543 /// assert_eq!(hash1, 7201466553693376363);
1544 ///
1545 /// let hash2 = Hash::string_with(" World", hash1);
1546 /// assert_eq!(hash2, 4420528118743043111);
1547 /// ```
1548 pub fn string_with(str: impl AsRef<str>, root: IdHashT) -> IdHashT {
1549 let c_str = CString::new(str.as_ref()).unwrap();
1550 unsafe { hash_string_with(c_str.as_ptr(), root) }
1551 }
1552
1553 /// This will hash an integer into a hash value that StereoKit can use. This is helpful for adding in some
1554 /// uniqueness using something like a for loop index. This may be best when combined with additional hashes.
1555 /// <https://stereokit.net/Pages/StereoKit/Hash/Int.html>
1556 /// * `val` - An integer that will be hashed.
1557 ///
1558 /// Returns a StereoKit hash representing the provided integer.
1559 /// see also [`hash_int`] [`Hash::int_with`]
1560 /// ### Examples
1561 /// ```
1562 /// use stereokit_rust::util::Hash;
1563 ///
1564 /// let hash = Hash::int(123456789);
1565 /// assert_eq!(hash, 8379007418144316681);
1566 /// ```
1567 pub fn int(val: i32) -> IdHashT {
1568 unsafe { hash_int(val) }
1569 }
1570
1571 /// This will hash an integer into a hash value that StereoKit can use. This is helpful for adding in some
1572 /// uniqueness using something like a for loop index. This overload allows you to combine your hash with an existing
1573 /// hash.
1574 /// <https://stereokit.net/Pages/StereoKit/Hash/Int.html>
1575 /// * `val` - An integer that will be hashed.
1576 /// * `root` - The hash value this new hash will start from.
1577 ///
1578 /// Returns a StereoKit hash representing a combination of the provided string and the root hash.
1579 /// see also [`hash_int_with`] [`Hash::int`]
1580 /// ### Examples
1581 /// ```
1582 /// use stereokit_rust::util::Hash;
1583 ///
1584 /// let hash1 = Hash::int(123456789);
1585 /// assert_eq!(hash1, 8379007418144316681);
1586 ///
1587 /// let hash2 = Hash::int_with(123456789, hash1);
1588 /// assert_eq!(hash2, 13864748593440029765);
1589 /// ```
1590 pub fn int_with(int: i32, root: IdHashT) -> IdHashT {
1591 unsafe { hash_int_with(int, root) }
1592 }
1593}
1594
1595/// TODO: UNSTABLE: When opening the Platform.FilePicker, this enum describes how the picker should look and behave.
1596/// <https://stereokit.net/Pages/StereoKit/PickerMode.html>
1597#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1598#[repr(u32)]
1599pub enum PickerMode {
1600 /// Allow opening a single file.
1601 Open = 0,
1602 /// Allow the user to enter or select the name of the destination file.
1603 Save = 1,
1604}
1605
1606/// This class provides some platform related code that runs cross-platform. You might be able to do many of these
1607/// things with rust or C#, but you might not be able to do them in as a portable manner as these methods do!
1608/// <https://stereokit.net/Pages/StereoKit/Platform.html>
1609///
1610/// ### Examples
1611/// ```
1612/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1613/// use stereokit_rust::{util::{Platform}, system::TextContext};
1614///
1615/// Platform::keyboard_show(true, TextContext::Text);
1616///
1617/// let mut path = std::env::current_dir().expect("Current directory should be readable");
1618/// path.push("assets/textures/");
1619/// assert!(path.is_dir());
1620/// path.push("readme.md");
1621/// assert!(path.is_file());
1622///
1623/// let file_content = Platform::read_file_text(&path)
1624/// .expect("File should be readable");
1625/// assert!(file_content.starts_with("# Images "));
1626///
1627/// assert!(Platform::write_file_text(path, file_content).is_ok());
1628/// ```
1629pub struct Platform;
1630
1631unsafe extern "C" {
1632 pub fn platform_file_picker(
1633 mode: PickerMode,
1634 callback_data: *mut c_void,
1635 picker_callback: ::std::option::Option<
1636 unsafe extern "C" fn(callback_data: *mut c_void, confirmed: Bool32T, filename: *const c_char),
1637 >,
1638 filters: *const FileFilter,
1639 filter_count: i32,
1640 );
1641 pub fn platform_file_picker_sz(
1642 mode: PickerMode,
1643 callback_data: *mut c_void,
1644 picker_callback_sz: ::std::option::Option<
1645 unsafe extern "C" fn(
1646 callback_data: *mut c_void,
1647 confirmed: Bool32T,
1648 filename_ptr: *const c_char,
1649 filename_length: i32,
1650 ),
1651 >,
1652 in_arr_filters: *const FileFilter,
1653 filter_count: i32,
1654 );
1655 pub fn platform_file_picker_close();
1656 pub fn platform_file_picker_visible() -> Bool32T;
1657 pub fn platform_read_file(
1658 filename_utf8: *const c_char,
1659 out_data: *mut *mut c_void,
1660 out_size: *mut usize,
1661 ) -> Bool32T;
1662 pub fn platform_write_file(filename_utf8: *const c_char, data: *mut c_void, size: usize) -> Bool32T;
1663 pub fn platform_write_file_text(filename_utf8: *const c_char, text_utf8: *const c_char) -> Bool32T;
1664 pub fn platform_keyboard_get_force_fallback() -> Bool32T;
1665 pub fn platform_keyboard_set_force_fallback(force_fallback: Bool32T);
1666 pub fn platform_keyboard_show(visible: Bool32T, type_: TextContext);
1667 pub fn platform_keyboard_visible() -> Bool32T;
1668 pub fn platform_keyboard_set_layout(
1669 type_: TextContext,
1670 keyboard_layout: *const *const c_char,
1671 layouts_num: i32,
1672 ) -> Bool32T;
1673}
1674
1675/// File_picker trampoline
1676///
1677/// see also [`Plaform::file_picker`]
1678unsafe extern "C" fn fp_trampoline<FS: FnMut(&str), FC: FnMut()>(
1679 user_data: *mut c_void,
1680 confirmed: Bool32T,
1681 filename: *const c_char,
1682) {
1683 let data = unsafe { &mut *(user_data as *mut (&mut FS, &mut FC)) };
1684 let (update, cancel) = data;
1685 if confirmed != 0 {
1686 let c_str = unsafe { CStr::from_ptr(filename).to_str().unwrap() };
1687 update(c_str)
1688 } else {
1689 cancel()
1690 }
1691}
1692
1693/// File_picker_sz trampoline
1694///
1695/// see also [`Plaform::file_picker`]
1696unsafe extern "C" fn fp_sz_trampoline<F: FnMut(bool, &str)>(
1697 user_data: *mut c_void,
1698 confirmed: Bool32T,
1699 filename: *const c_char,
1700 filename_length: i32,
1701) {
1702 let closure = unsafe { &mut *(user_data as *mut &mut F) };
1703 if confirmed != 0 && filename_length > 0 {
1704 let c_str = unsafe { CStr::from_ptr(filename).to_str().unwrap() };
1705 closure(true, c_str)
1706 } else {
1707 let c_str = "";
1708 closure(false, c_str)
1709 }
1710}
1711
1712impl Platform {
1713 /// Force the use of StereoKit’s built-in fallback keyboard instead of the system keyboard. This may be great for
1714 /// testing or look and feel matching, but the system keyboard should generally be preferred for accessibility
1715 /// reasons.
1716 /// <https://stereokit.net/Pages/StereoKit/Platform/ForceFallbackKeyboard.html>
1717 ///
1718 /// see also [`platform_keyboard_set_force_fallback`]
1719 /// ### Examples
1720 /// ```
1721 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1722 /// use stereokit_rust::util::Platform;
1723 ///
1724 /// assert_eq!(Platform::get_force_fallback_keyboard(), false);
1725 ///
1726 /// Platform::force_fallback_keyboard(true);
1727 /// assert_eq!(Platform::get_force_fallback_keyboard(), true);
1728 ///
1729 /// Platform::force_fallback_keyboard(false);
1730 /// assert_eq!(Platform::get_force_fallback_keyboard(), false);
1731 /// ```
1732 pub fn force_fallback_keyboard(force_fallback: bool) {
1733 unsafe { platform_keyboard_set_force_fallback(force_fallback as Bool32T) }
1734 }
1735
1736 /// TODO: UNSTABLE: Starts a file picker window! This will create a native file picker window if one is available in the current
1737 /// setup, and if it is not, it’ll create a fallback filepicker build using StereoKit’s UI.
1738 ///
1739 /// Flatscreen apps will show traditional file pickers, and UWP has an OS provided file picker that works in MR. All
1740 /// others currently use the fallback system.
1741 ///
1742 /// A note for UWP apps, UWP generally does not have permission to access random files, unless the user has chosen
1743 /// them with the picker! This picker properly handles permissions for individual files on UWP, but may have issues
1744 /// with files that reference other files, such as .gltf files with external textures. See [`Platform::write_file`]
1745 /// and [`Platform.read_file`] for manually reading and writing files in a cross-platfom manner.
1746 /// <https://stereokit.net/Pages/StereoKit/Platform/FilePicker.html>
1747 /// * `mode` - Are we trying to Open a file, or Save a file? This changes the appearance and behavior of the picker
1748 /// to support the specified action.
1749 /// * `on_select_file` - This Action will be called with the proper filename when the picker has successfully
1750 /// completed! On a cancel or close event, this Action is not called.
1751 /// * `on_cancel` - If the user cancels the file picker, or the picker is closed via FilePickerClose, this Action is
1752 /// called.
1753 /// * `filters` - A list of file extensions that the picker should filter for. This is in the format of “.glb” and
1754 /// is case insensitive.
1755 ///
1756 /// see also [`platform_file_picker`]
1757 pub fn file_picker<FS: FnMut(&str), FC: FnMut()>(
1758 mode: PickerMode,
1759 mut on_select_file: FS,
1760 mut on_cancel: FC,
1761 filters: &[impl AsRef<str>],
1762 ) {
1763 let mut c_filters = Vec::new();
1764 for filter in filters {
1765 c_filters.push(FileFilter::new(filter));
1766 }
1767
1768 let mut closure = (&mut on_select_file, &mut on_cancel);
1769 unsafe {
1770 platform_file_picker(
1771 mode,
1772 &mut closure as *mut _ as *mut c_void,
1773 Some(fp_trampoline::<FS, FC>),
1774 c_filters.as_slice().as_ptr(),
1775 c_filters.len() as i32,
1776 )
1777 }
1778 }
1779
1780 /// TODO: UNSTABLE: Starts a file picker window! This will create a native file picker window if one is available in the current
1781 /// setup, and if it is not, it’ll create a fallback filepicker build using StereoKit’s UI.
1782 ///
1783 /// Flatscreen apps will show traditional file pickers, and UWP has an OS provided file picker that works in MR. All
1784 /// others currently use the fallback system. Some pickers will block the system and return right away, but others
1785 /// will stick around and let users continue to interact with the app.
1786 ///
1787 /// A note for UWP apps, UWP generally does not have permission to access random files, unless the user has chosen
1788 /// them with the picker! This picker properly handles permissions for individual files on UWP, but may have issues
1789 /// with files that reference other files, such as .gltf files with external textures. See [`Platform::write_file`]
1790 /// and [`Platform.read_file`] for manually reading and writing files in a cross-platfom manner.
1791 /// <https://stereokit.net/Pages/StereoKit/Platform/FilePicker.html>
1792 /// * `mode` - Are we trying to Open a file, or Save a file? This changes the appearance and behavior of the picker
1793 /// to support the specified action.
1794 /// * on_complete - This action will be called when the file picker has finished, either via a cancel event, or from
1795 /// a confirm event. First parameter is a bool, where true indicates the presence of a valid filename, and false
1796 /// indicates a failure or cancel event.
1797 /// * `filters` - A list of file extensions that the picker should filter for. This is in the format of “.glb” and
1798 /// is case insensitive.
1799 ///
1800 /// see also [`platform_file_picker_sz`]
1801 pub fn file_picker_sz<F: FnMut(bool, &str)>(mode: PickerMode, mut on_complete: F, filters: &[impl AsRef<str>]) {
1802 let mut c_filters = Vec::new();
1803 for filter in filters {
1804 c_filters.push(FileFilter::new(filter));
1805 }
1806
1807 let mut closure = &mut on_complete;
1808 unsafe {
1809 platform_file_picker_sz(
1810 mode,
1811 &mut closure as *mut _ as *mut c_void,
1812 Some(fp_sz_trampoline::<F>),
1813 c_filters.as_slice().as_ptr(),
1814 c_filters.len() as i32,
1815 )
1816 }
1817 }
1818
1819 /// TODO: UNSTABLE: If the picker is visible, this will close it and immediately trigger a cancel event for the active picker.
1820 /// <https://stereokit.net/Pages/StereoKit/Platform/FilePickerClose.html>
1821 ///
1822 /// see also [`platform_file_picker_close`]
1823 pub fn file_picker_close() {
1824 unsafe { platform_file_picker_close() }
1825 }
1826
1827 /// Request or hide a soft keyboard for the user to type on. StereoKit will surface OS provided soft keyboards where
1828 /// available, and use a fallback keyboard when not. On systems with physical keyboards, soft keyboards generally
1829 /// will not be shown if the user has interacted with their physical keyboard recently.
1830 /// <https://stereokit.net/Pages/StereoKit/Platform/KeyboardShow.html>
1831 /// * `show` - Tells whether or not to open or close the soft keyboard.
1832 /// * `input_type` - Soft keyboards can change layout to optimize for the type of text that’s required. StereoKit
1833 /// will request the soft keyboard layout that most closely represents the TextContext provided.
1834 ///
1835 /// see also [`platform_keyboard_show`]
1836 /// ### Examples
1837 /// ```
1838 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1839 /// use stereokit_rust::{util::Platform, system::TextContext};
1840 ///
1841 /// // On desktop, this will not show the keyboard, as it is assumed that the user has a physical keyboard.
1842 /// Platform::keyboard_show(true, TextContext::Text);
1843 /// assert_eq!(Platform::is_keyboard_visible(), false);
1844 ///
1845 /// Platform::keyboard_show(false, TextContext::Text);
1846 /// assert_eq!(Platform::is_keyboard_visible(), false);
1847 /// ```
1848 pub fn keyboard_show(show: bool, input_type: TextContext) {
1849 unsafe { platform_keyboard_show(show as Bool32T, input_type) }
1850 }
1851
1852 /// Replace the default keyboard type with a custom layout.
1853 /// <https://stereokit.net/Pages/StereoKit/Platform/KeyboardSetLayout.html>
1854 /// * `keyboard_type` - The type of keyboard to replace.
1855 /// * `keyboard_layouts` - Custom keyboard layout to replace the defualt layout.
1856 ///
1857 /// Returns `true` if keyboard type was swapped with the provided layout.
1858 /// see also [`platform_keyboard_set_layout`]
1859 /// ### Examples
1860 /// ```
1861 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1862 /// use stereokit_rust::{util::Platform, system::TextContext};
1863 ///
1864 /// pub const FR_KEY_TEXT: &str = r#"²|&|é|"|'|(|\-|è|_|ç|à|)|=|{|}|spr:sk/ui/backspace-\b-8-3|spr:sk/ui/close----close
1865 /// Tab-\t-9-3|a|z|e|r|t|y|u|i|o|p|^|$|[|]|\|
1866 /// Entrée-\n-13-4|q|s|d|f|g|h|j|k|l|m|ù|*|#|Entrée-\n-13-3
1867 /// 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
1868 /// 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|"#;
1869 ///
1870 /// 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
1871 /// Tab-\t-9-3|A|Z|E|R|T|Y|U|I|O|P|¨|£|Ê|É|È
1872 /// Entrée-\n-13-4|Q|S|D|F|G|H|J|K|L|M|%|µ|Ç|Entrée-\n-13-3
1873 /// 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
1874 /// 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|"#;
1875 ///
1876 /// pub const FR_KEY_TEXT_ALT: &str = r#"*|/|~|#|{|[|\||`|\\|^|@|]|}|æ|œ|spr:sk/ui/backspace-\b-8-3|spr:sk/ui/close----close
1877 /// Tab-\t-9-3|à|â|ä|ç|é|è|ê|ë|î|ï|ô|ö|«|»|¤
1878 /// Entrée-\n-13-4|ù|û|ü|ÿ|À|Â|Ä|Ç|É|È|Ê|Ë|%|Entrée-\n-13-3
1879 /// spr:sk/ui/shift--16-3-go_1|Î|Ï|Ô|Ö|Ù|Û|Ü|Ÿ|$|£|€|¥|✋|spr:sk/ui/shift--16-2-go_1|spr:sk/ui/arrow_up--38
1880 /// 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|"#;
1881 ///
1882 /// let keyboard_layouts = vec![FR_KEY_TEXT, FR_KEY_TEXT_SHIFT, FR_KEY_TEXT_ALT];
1883 ///
1884 /// assert_eq!(Platform::keyboard_set_layout(TextContext::Text, &keyboard_layouts), true);
1885 /// ```
1886 pub fn keyboard_set_layout(keyboard_type: TextContext, keyboard_layouts: &Vec<&str>) -> bool {
1887 let mut keyboard_layouts_c = vec![];
1888 for str in keyboard_layouts {
1889 let c_str = CString::new(*str).unwrap().into_raw() as *const c_char;
1890 keyboard_layouts_c.push(c_str);
1891 }
1892 unsafe {
1893 platform_keyboard_set_layout(
1894 keyboard_type,
1895 keyboard_layouts_c.as_slice().as_ptr(),
1896 keyboard_layouts_c.len() as i32,
1897 ) != 0
1898 }
1899 }
1900
1901 /// Reads the entire contents of the file as a UTF-8 string, taking advantage of any permissions that may have been
1902 /// granted by Platform::file_picker(_sz?). Returns Err on failure.
1903 /// <https://stereokit.net/Pages/StereoKit/Platform/ReadFileText.html>
1904 /// * `filename` - The path to the file to read.
1905 ///
1906 /// see also [`platform_read_file`]
1907 /// ### Examples
1908 /// ```
1909 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1910 /// use stereokit_rust::{util::{Platform}, system::TextContext};
1911 ///
1912 /// let mut path = std::env::current_dir().expect("Current directory should be readable");
1913 /// path.push("config.toml");
1914 /// assert!(path.is_file());
1915 ///
1916 /// let file_content = Platform::read_file_text(&path)
1917 /// .expect("File should be readable");
1918 /// assert!(file_content.starts_with("[env]"));
1919 /// ```
1920 pub fn read_file_text<'a>(filename: impl AsRef<Path>) -> Result<&'a str, StereoKitError> {
1921 let path_buf = filename.as_ref().to_path_buf();
1922 let c_str = CString::new(
1923 path_buf
1924 .clone()
1925 .to_str() //
1926 .ok_or(StereoKitError::ReadFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
1927 )?;
1928 let out_data = CString::new("H")?.into_raw() as *mut *mut c_void;
1929 let mut len = 0usize;
1930 let len_ptr: *mut usize = &mut len;
1931 if unsafe { platform_read_file(c_str.as_ptr(), out_data, len_ptr) != 0 } {
1932 unsafe { CStr::from_ptr(*out_data as *const c_char) }
1933 .to_str()
1934 .map_err(|e| StereoKitError::ReadFileError(path_buf.clone(), e.to_string()))
1935 } else {
1936 Err(StereoKitError::ReadFileError(path_buf, "Failed to read file".into()))
1937 }
1938 }
1939
1940 /// Reads the entire contents of the file as a byte array, taking advantage of any permissions that may have been
1941 /// granted by Platform.FilePicker.
1942 /// <https://stereokit.net/Pages/StereoKit/Platform/ReadFile.html>
1943 /// * `filename` - The path to the file to read.
1944 ///
1945 /// see also [`platform_read_file`]
1946 /// ### Examples
1947 /// ```
1948 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1949 /// use stereokit_rust::{util::{Platform}, system::TextContext};
1950 ///
1951 /// let mut path = std::env::current_dir().expect("Current directory should be readable");
1952 /// path.push("assets/textures/");
1953 /// assert!(path.is_dir());
1954 /// path.push("readme.md");
1955 /// assert!(path.is_file());
1956 ///
1957 /// let file_content = Platform::read_file(&path)
1958 /// .expect("File should be readable");
1959 /// assert!(file_content.starts_with(b"# Images "));
1960 /// ```
1961 pub fn read_file<'a>(filename: impl AsRef<Path>) -> Result<&'a [u8], StereoKitError> {
1962 let path_buf = filename.as_ref().to_path_buf();
1963 let c_str = CString::new(
1964 path_buf
1965 .clone()
1966 .to_str() //
1967 .ok_or(StereoKitError::ReadFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
1968 )?;
1969 let out_data = CString::new("H")?.into_raw() as *mut *mut c_void;
1970 let mut len = 0usize;
1971 let len_ptr: *mut usize = &mut len;
1972 if unsafe { platform_read_file(c_str.as_ptr(), out_data, len_ptr) != 0 } {
1973 Ok(unsafe { std::slice::from_raw_parts(*out_data as *const u8, len) })
1974 } else {
1975 Err(StereoKitError::ReadFileError(path_buf, "Failed to read file".into()))
1976 }
1977 }
1978
1979 /// Writes a UTF-8 text file to the filesystem, taking advantage of any permissions that may have been granted by
1980 /// Platform::file_picker.
1981 /// <https://stereokit.net/Pages/StereoKit/Platform/WriteFile.html>
1982 /// * `filename` - The path to the file to write.
1983 /// * `text` - A string to write to the file. This gets converted to a UTF-8 encoding.
1984 ///
1985 /// see also [`platform_write_file_text`]
1986 /// ### Examples
1987 /// ```
1988 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1989 /// use stereokit_rust::{util::{Platform}, system::TextContext};
1990 ///
1991 /// let mut path = std::env::current_dir().expect("Current directory should be readable");
1992 /// path.push("assets/icons/");
1993 /// assert!(path.is_dir());
1994 /// path.push("readme.md");
1995 /// assert!(path.is_file());
1996 ///
1997 /// let file_content = Platform::read_file_text(&path)
1998 /// .expect("File should be readable");
1999 /// assert!(file_content.starts_with("# Images "));
2000 ///
2001 /// assert!(Platform::write_file_text(path, file_content).is_ok());
2002 /// ```
2003 pub fn write_file_text<S: AsRef<str>>(filename: impl AsRef<Path>, text: S) -> Result<bool, StereoKitError> {
2004 let path_buf = filename.as_ref().to_path_buf();
2005 let c_str = CString::new(
2006 path_buf
2007 .clone()
2008 .to_str() //
2009 .ok_or(StereoKitError::WriteFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
2010 )?;
2011 let in_data = CString::new(text.as_ref())?.into_raw() as *const c_char;
2012 if unsafe { platform_write_file_text(c_str.as_ptr(), in_data) != 0 } {
2013 Ok(true)
2014 } else {
2015 Err(StereoKitError::WriteFileError(path_buf, "Failed to write file".into()))
2016 }
2017 }
2018
2019 /// Writes an array of bytes to the filesystem, taking advantage of any permissions that may have been granted by
2020 /// Platform::file_picker.
2021 /// <https://stereokit.net/Pages/StereoKit/Platform/WriteFile.html>
2022 /// * `filename` - The path to the file to write.
2023 /// * `data` - The data to write to the file.
2024 ///
2025 /// see also [`platform_write_file`]
2026 /// ### Examples
2027 /// ```
2028 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2029 /// use stereokit_rust::{util::{Platform}, system::TextContext};
2030 ///
2031 /// let mut path = std::env::current_dir().expect("Current directory should be readable");
2032 /// path.push("assets/icons/");
2033 /// assert!(path.is_dir());
2034 /// path.push("readme.md");
2035 /// assert!(path.is_file());
2036 ///
2037 /// let file_content = Platform::read_file(&path)
2038 /// .expect("File should be readable");
2039 /// assert!(file_content.starts_with(b"# Images "));
2040 ///
2041 /// assert!(Platform::write_file(path, file_content).is_ok());
2042 /// ```
2043 pub fn write_file(filename: impl AsRef<Path>, data: &[u8]) -> Result<bool, StereoKitError> {
2044 let path_buf = filename.as_ref().to_path_buf();
2045 let c_str = CString::new(
2046 path_buf
2047 .clone()
2048 .to_str() //
2049 .ok_or(StereoKitError::WriteFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
2050 )?;
2051 if unsafe { platform_write_file(c_str.as_ptr(), data.as_ptr() as *mut c_void, data.len()) != 0 } {
2052 Ok(true)
2053 } else {
2054 Err(StereoKitError::WriteFileError(path_buf, "Failed to write file".into()))
2055 }
2056 }
2057
2058 /// TODO: UNSTABLE: This will check if the file picker interface is currently visible. Some pickers will never show this, as they
2059 /// block the application until the picker has completed.
2060 /// <https://stereokit.net/Pages/StereoKit/Platform/FilePickerVisible.html>
2061 ///
2062 /// see also [`platform_file_picker_visible`]
2063 /// ### Examples
2064 /// ```
2065 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2066 /// use stereokit_rust::util::Platform;
2067 ///
2068 /// assert_eq!(Platform::get_file_picker_visible(), false);
2069 /// ```
2070 pub fn get_file_picker_visible() -> bool {
2071 unsafe { platform_file_picker_visible() != 0 }
2072 }
2073
2074 /// Force the use of StereoKit’s built-in fallback keyboard instead of the system keyboard. This may be great for
2075 /// testing or look and feel matching, but the system keyboard should generally be preferred for accessibility
2076 /// reasons.
2077 /// <https://stereokit.net/Pages/StereoKit/Platform/ForceFallbackKeyboard.html>
2078 ///
2079 /// see also [`platform_keyboard_get_force_fallback`]
2080 /// see example [`Platform::force_fallback_keyboard`]
2081 pub fn get_force_fallback_keyboard() -> bool {
2082 unsafe { platform_keyboard_get_force_fallback() != 0 }
2083 }
2084
2085 /// Check if a soft keyboard is currently visible. This may be an OS provided keyboard or StereoKit’s fallback
2086 /// keyboard, but will not indicate the presence of a physical keyboard.
2087 /// <https://stereokit.net/Pages/StereoKit/Platform/KeyboardVisible.html>
2088 ///
2089 /// see also [`platform_keyboard_visible`]
2090 /// see example [`Platform::keyboard_show`]
2091 pub fn is_keyboard_visible() -> bool {
2092 unsafe { platform_keyboard_visible() != 0 }
2093 }
2094}
2095
2096/// A light source used for creating SphericalHarmonics data.
2097/// <https://stereokit.net/Pages/StereoKit/SHLight.html>
2098///
2099/// see [`SphericalHarmonics`] see also [`crate::tex::SHCubemap`]
2100/// ### Examples
2101/// ```
2102/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2103/// use stereokit_rust::{maths::Vec3, tex::SHCubemap, util::{SHLight, named_colors, Color128}};
2104///
2105/// let light0 = SHLight::new([1.0, 0.2, 0.3], named_colors::RED);
2106/// let light1 = SHLight::new(Vec3::new(1.0, 0.2, 0.3), Color128::new(1.0, 0.0, 0.0, 1.0));
2107/// let light2 = SHLight{ dir_to: Vec3::new(1.0, 0.2, 0.3), color: named_colors::RED.into()};
2108///
2109/// assert_eq!(light0, light1);
2110/// assert_eq!(light1, light2);
2111/// ```
2112#[derive(Debug, Copy, Clone, PartialEq)]
2113#[repr(C)]
2114pub struct SHLight {
2115 /// Direction to the light source.
2116 pub dir_to: Vec3,
2117 /// Color of the light in linear space! Values here can exceed 1.
2118 pub color: Color128,
2119}
2120impl SHLight {
2121 /// A basic constructor for SHLight.
2122 /// <https://stereokit.net/Pages/StereoKit/SHLight.html>
2123 /// * `dir_to` - Direction to the light source.
2124 /// * `color` - Color of the light in linear space! Values here can exceed 1.
2125 pub fn new(dir_to: impl Into<Vec3>, color: impl Into<Color128>) -> Self {
2126 Self { dir_to: dir_to.into(), color: color.into() }
2127 }
2128}
2129/// Spherical Harmonics are kinda like Fourier, but on a sphere. That doesn’t mean terribly much to me, and could be
2130/// wrong, but check out here for more details about how Spherical Harmonics work in this context!
2131///
2132/// However, the more prctical thing is, SH can be a function that describes a value over the surface of a sphere! This
2133/// is particularly useful for lighting, since you can basically store the lighting information for a space in this
2134/// value! This is often used for lightmap data, or a light probe grid, but StereoKit just uses a single SH for the
2135/// entire scene. It’s a gross oversimplification, but looks quite good, and is really fast! That’s extremely great
2136/// when you’re trying to hit 60fps, or even 144fps.
2137/// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics.html>
2138///
2139/// see also: [`crate::tex::SHCubemap`]
2140/// ### Examples
2141/// ```
2142/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2143/// use stereokit_rust::{maths::Vec3,
2144/// util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2145///
2146/// let light0 = SHLight::new([1.0, 0.0, 0.0], named_colors::RED);
2147/// let light1 = SHLight::new([0.0, 1.0, 0.0], named_colors::GREEN);
2148/// let light2 = SHLight::new([0.0, 0.0, 1.0], named_colors::BLUE);
2149///
2150/// let mut sh = SphericalHarmonics::from_lights(&[light0, light1, light2]);
2151/// sh.brightness(1.0)
2152/// .add(Vec3::NEG_Y, named_colors::GREEN)
2153/// .add(Vec3::NEG_Z, named_colors::BLUE)
2154/// .add(Vec3::NEG_X, named_colors::RED);
2155///
2156/// assert_eq!(sh.get_sample(Vec3::UP), Color128 { r: 0.5813507, g: 0.8046322, b: 0.5813487, a: 1.0 });
2157/// assert_eq!(sh.get_dominent_light_direction(), Vec3 { x: 0.27644092, y: 0.2728996, z: 0.9214696 });
2158/// ```
2159#[derive(Debug, Default, Copy, Clone, PartialEq)]
2160#[repr(C)]
2161pub struct SphericalHarmonics {
2162 pub coefficients: [Vec3; 9usize],
2163}
2164
2165unsafe extern "C" {
2166 pub fn sh_create(in_arr_lights: *const SHLight, light_count: i32) -> SphericalHarmonics;
2167 pub fn sh_brightness(ref_harmonics: *mut SphericalHarmonics, scale: f32);
2168 pub fn sh_add(ref_harmonics: *mut SphericalHarmonics, light_dir: Vec3, light_color: Vec3);
2169 pub fn sh_lookup(harmonics: *const SphericalHarmonics, normal: Vec3) -> Color128;
2170 pub fn sh_dominant_dir(harmonics: *const SphericalHarmonics) -> Vec3;
2171}
2172impl SphericalHarmonics {
2173 /// Creates a SphericalHarmonics approximation of the irradiance given from a set of directional lights!
2174 /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/FromLights.html>
2175 /// * `lights` - A list of directional lights!
2176 ///
2177 /// Returns a SphericalHarmonics approximation of the irradiance given from a set of directional lights!
2178 /// see also [`SHLight`]
2179 /// ### Examples
2180 /// ```
2181 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2182 /// use stereokit_rust::{maths::Vec3,
2183 /// util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2184 ///
2185 /// let light0 = SHLight::new([1.0, 1.0, 1.0], named_colors::RED);
2186 /// let light1 = SHLight::new([0.5, 0.5, 0.5], named_colors::RED);
2187 /// let light2 = SHLight::new([0.25, 0.25, 0.25], named_colors::RED);
2188 ///
2189 /// let sh = SphericalHarmonics::from_lights(&[light0, light1, light2]);
2190 ///
2191 /// assert_eq!(sh.get_sample(Vec3::UP), Color128 { r: 2.2098913, g: 0.0, b: 0.0, a: 1.0 });
2192 /// assert_eq!(sh.get_dominent_light_direction(), -Vec3::ONE.get_normalized());
2193 /// ```
2194 pub fn from_lights(lights: &[SHLight]) -> Self {
2195 unsafe { sh_create(lights.as_ptr(), lights.len() as i32) }
2196 }
2197
2198 /// Creates a SphericalHarmonic from an array of coefficients. Useful for loading stored data!
2199 /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/SphericalHarmonics.html>
2200 /// * `coefficients` - Must be an array with a length of 9!
2201 ///
2202 /// see also [`sh_create`]
2203 /// ### Examples
2204 /// ```
2205 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2206 /// use stereokit_rust::{maths::Vec3,
2207 /// util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2208 ///
2209 /// let mut sh0 = SphericalHarmonics::default();
2210 /// sh0.add([1.0, 0.0, 1.0], named_colors::RED);
2211 /// let coefficient = sh0.coefficients;
2212 ///
2213 /// let sh = SphericalHarmonics::new(coefficient);
2214 ///
2215 /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 11.453729, g: 0.0, b: 0.0, a: 1.0 });
2216 /// assert_eq!(sh.get_dominent_light_direction(), Vec3::new(-1.0, 0.0, -1.0).get_normalized());
2217 /// ```
2218 pub fn new(coefficients: [Vec3; 9]) -> Self {
2219 SphericalHarmonics { coefficients }
2220 }
2221
2222 /// Adds a ‘directional light’ to the lighting approximation. This can be used to bake a multiple light setup, or
2223 /// accumulate light
2224 /// from a field of points.
2225 /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/Add.html>
2226 /// * `light_dir` - The direction of the light source.
2227 /// * `light_color` - Color of the light, in linear color space.
2228 ///
2229 /// see also [`sh_add`]
2230 /// ### Examples
2231 /// ```
2232 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2233 /// use stereokit_rust::{maths::Vec3,
2234 /// util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2235 ///
2236 /// let mut sh = SphericalHarmonics::default();
2237 /// sh.add([1.0, 0.0, 1.0], named_colors::RED)
2238 /// .add([0.0, 1.0, 0.0], named_colors::GREEN)
2239 /// .add([0.0, 0.0, 1.0], named_colors::BLUE);
2240 ///
2241 /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 11.453729, g: -0.2956792, b: 4.4505944, a: 1.0 });
2242 /// assert_eq!(sh.get_dominent_light_direction(), Vec3 { x: -0.21951628, y: -0.21670417, z: -0.95123714 });
2243 ///```
2244 pub fn add(&mut self, light_dir: impl Into<Vec3>, light_color: impl Into<Color128>) -> &mut Self {
2245 let light_dir = light_dir.into();
2246 let color = light_color.into();
2247 unsafe { sh_add(self, light_dir, Vec3 { x: color.r, y: color.g, z: color.b }) };
2248 self
2249 }
2250
2251 /// Scales all the SphericalHarmonic’s coefficients! This behaves as if you’re modifying the brightness of the
2252 /// lighting this object represents.
2253 /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/Brightness.html>
2254 /// * `scale` - A multiplier for the coefficients! A value of 1 will leave everything the same, 0.5 will cut the
2255 /// brightness in half, and a 2 will double the brightness.
2256 ///
2257 /// see also [`sh_brightness`]
2258 /// ### Examples
2259 /// ```
2260 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2261 /// use stereokit_rust::{maths::Vec3,
2262 /// util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2263 ///
2264 /// let mut sh = SphericalHarmonics::default();
2265 /// sh.add([1.0, 0.0, 1.0], named_colors::RED)
2266 /// .brightness(0.5);
2267 ///
2268 /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 5.726864, g: 0.0, b: 0.0, a: 1.0 });
2269 /// assert_eq!(sh.get_dominent_light_direction(), Vec3::new(-1.0, 0.0, -1.0).get_normalized());
2270 ///
2271 /// sh.brightness(2.0);
2272 /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 11.453729, g: 0.0, b: 0.0, a: 1.0 });
2273 /// assert_eq!(sh.get_dominent_light_direction(), Vec3::new(-1.0, 0.0, -1.0).get_normalized());
2274 ///
2275 ///
2276 /// sh.brightness(0.0);
2277 /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128::BLACK);
2278 /// assert_eq!(sh.get_dominent_light_direction().x.is_nan(), true);
2279 /// ```
2280 pub fn brightness(&mut self, scale: f32) -> &mut Self {
2281 unsafe { sh_brightness(self, scale) };
2282 self
2283 }
2284
2285 /// Look up the color information in a particular direction!
2286 /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/Sample.html>
2287 /// * `normal` - The direction to look in. Should be normalized.
2288 ///
2289 /// Returns the color represented by the SH in the given direction.
2290 /// see also [`sh_brightness`]
2291 pub fn get_sample(&self, normal: impl Into<Vec3>) -> Color128 {
2292 unsafe { sh_lookup(self, normal.into()) }
2293 }
2294
2295 /// Returns the dominant direction of the light represented by this spherical harmonics data. The direction value is
2296 /// normalized.
2297 /// You can get the color of the light in this direction by using the struct’s Sample method:
2298 /// light.get_sample(-light.get_dominent_light_direction()).
2299 /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/DominantLightDirection.html>
2300 ///
2301 /// see also [`sh_brightness`]
2302 /// ### Examples
2303 /// ```
2304 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2305 /// use stereokit_rust::{maths::Vec3,
2306 /// util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2307 ///
2308 /// let mut sh = SphericalHarmonics::default();
2309 /// sh.add([1.0, 0.0, 1.0], named_colors::RED)
2310 /// .add([1.0, 1.0, 0.0], named_colors::GREEN)
2311 /// .add([0.0, 1.0, 1.0], named_colors::BLUE);
2312 ///
2313 /// assert_eq!(sh.get_dominent_light_direction(), Vec3 { x: -0.3088678, y: -0.6715365, z: -0.6735276 });
2314 /// ```
2315 pub fn get_dominent_light_direction(&self) -> Vec3 {
2316 unsafe { sh_dominant_dir(self) }
2317 }
2318
2319 /// Converts the SphericalHarmonic into a vector of coefficients 9 long. Useful for storing calculated data!
2320 /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/ToArray.html>
2321 ///
2322 /// Returns an array of coefficients 9 long.
2323 /// see also [`sh_brightness`]
2324 pub fn to_array(&self) -> [Vec3; 9] {
2325 self.coefficients
2326 }
2327}
2328
2329/// This class contains time information for the current session and frame!
2330/// <https://stereokit.net/Pages/StereoKit/Time.html>
2331///
2332/// ### Examples
2333/// ```
2334/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2335/// use stereokit_rust::util::Time;
2336///
2337/// // These are the expected results for tests on a PC:
2338/// assert_eq!(Time::get_totalf(), 0.0);
2339/// assert_eq!(Time::get_total(), 0.0);
2340/// assert_eq!(Time::get_stepf(), 0.0);
2341/// assert_eq!(Time::get_step(), 0.0);
2342///
2343/// // Time passes slowly:
2344/// Time::scale(0.5);
2345///
2346/// let mut total = 0.0f64;
2347/// let mut totalf = 0.0f32;
2348/// number_of_steps = 100;
2349/// test_steps!( // !!!! Get a proper main loop !!!!
2350/// if iter < number_of_steps + 2 {
2351/// assert_eq!(Time::get_frame(), iter + 1);
2352///
2353/// assert_eq!(Time::get_step_unscaled(), Time::get_step() * 2.0);
2354///
2355/// assert_eq!(Time::get_total(), total + Time::get_step());
2356/// assert_eq!(Time::get_total_unscaled(), total * 2.0 + Time::get_step() * 2.0);
2357///
2358/// // precision is worse for f32
2359/// assert!((Time::get_totalf()
2360/// - totalf - Time::get_stepf()).abs() < 0.000001);
2361/// assert!((Time::get_total_unscaledf()
2362/// - totalf * 2.0 - Time::get_stepf() * 2.0).abs() < 0.000001);
2363/// }
2364/// totalf = Time::get_totalf();
2365/// total = Time::get_total();
2366/// );
2367/// ```
2368pub struct Time;
2369
2370unsafe extern "C" {
2371 // Deprecated: pub fn time_get_raw() -> f64;
2372 // Deprecated: pub fn time_getf_unscaled() -> f32;
2373 // Deprecated: pub fn time_get_unscaled() -> f64;
2374 // Deprecated: pub fn time_getf() -> f32;
2375 // Deprecated: pub fn time_get() -> f64;
2376 // Deprecated: pub fn time_elapsedf_unscaled() -> f32;
2377 // Deprecated: pub fn time_elapsed_unscaled() -> f64;
2378 // Deprecated: pub fn time_elapsedf() -> f32;
2379 // Deprecated: pub fn time_elapsed() -> f64;
2380 pub fn time_total_raw() -> f64;
2381 pub fn time_totalf_unscaled() -> f32;
2382 pub fn time_total_unscaled() -> f64;
2383 pub fn time_totalf() -> f32;
2384 pub fn time_total() -> f64;
2385 pub fn time_stepf_unscaled() -> f32;
2386 pub fn time_step_unscaled() -> f64;
2387 pub fn time_stepf() -> f32;
2388 pub fn time_step() -> f64;
2389 pub fn time_scale(scale: f64);
2390 pub fn time_set_time(total_seconds: f64, frame_elapsed_seconds: f64);
2391 pub fn time_frame() -> u64;
2392}
2393
2394impl Time {
2395 /// Time is scaled by this value! Want time to pass slower? Set it to 0.5! Faster? Try 2!
2396 /// <https://stereokit.net/Pages/StereoKit/Time/Scale.html>
2397 ///
2398 /// see also [`time_scale`]
2399 /// ### Examples
2400 /// ```
2401 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2402 /// use stereokit_rust::util::Time;
2403 ///
2404 /// // Time passes faster:
2405 /// Time::scale(2.0);
2406 ///
2407 /// let mut total = 0.0f64;
2408 /// let mut totalf = 0.0f32;
2409 /// number_of_steps = 100;
2410 /// test_steps!( // !!!! Get a proper main loop !!!!
2411 /// assert_eq!(Time::get_step_unscaled(), Time::get_step() / 2.0);
2412 /// );
2413 /// ```
2414 pub fn scale(factor: f64) {
2415 unsafe { time_scale(factor) }
2416 }
2417
2418 /// This allows you to override the application time! The application will progress from this time using the current
2419 /// timescale.
2420 /// <https://stereokit.net/Pages/StereoKit/Time/SetTime.html>
2421 /// * `total_seconds` - What time should it now be? The app will progress from this point in time.
2422 /// * `frame_elapsed_seconds` - How long was the previous frame? This is a number often used in motion calculations.
2423 /// If left to zero, it’ll use the previous frame’s time, and if the previous frame’s time was also zero, it’ll
2424 /// use 1/90.
2425 ///
2426 /// see also [`time_set_time`]
2427 /// ### Examples
2428 /// ```
2429 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2430 /// use stereokit_rust::util::Time;
2431 ///
2432 /// // Time passes faster:
2433 /// Time::set_time(10.0, 0.01);
2434 ///
2435 /// assert_eq!(Time::get_total(), 10.0);
2436 /// assert_eq!(Time::get_step(), 0.01);
2437 /// ```
2438 pub fn set_time(total_seconds: f64, frame_elapsed_seconds: f64) {
2439 unsafe { time_set_time(total_seconds, frame_elapsed_seconds) }
2440 }
2441
2442 /// The number of frames/steps since the app started.
2443 /// <https://stereokit.net/Pages/StereoKit/Time/Frame.html>
2444 ///
2445 /// see also [`time_frame`]
2446 /// ### Examples
2447 /// ```
2448 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2449 /// use stereokit_rust::util::Time;
2450 ///
2451 /// assert_eq!(Time::get_frame(), 0);
2452 ///
2453 /// test_steps!( // !!!! Get a proper main loop !!!!
2454 /// if iter < number_of_steps + 2 {
2455 /// assert_eq!(Time::get_frame(), iter + 1);
2456 /// }
2457 /// );
2458 /// ```
2459 pub fn get_frame() -> u64 {
2460 unsafe { time_frame() }
2461 }
2462
2463 /// How many seconds have elapsed since the last frame? 64 bit time precision, calculated at the start of the frame.
2464 /// <https://stereokit.net/Pages/StereoKit/Time/Step.html>
2465 ///
2466 /// see also [`time_step`]
2467 /// ### Examples
2468 /// ```
2469 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2470 /// use stereokit_rust::util::Time;
2471 ///
2472 /// assert_eq!(Time::get_step(), 0.0);
2473 ///
2474 /// let mut total = 0.0f64;
2475 /// number_of_steps = 1000;
2476 /// test_steps!( // !!!! Get a proper main loop !!!!
2477 /// if iter < number_of_steps + 2 {
2478 /// assert_eq!(Time::get_total(), total + Time::get_step());
2479 /// }
2480 /// total = Time::get_total();
2481 /// );
2482 /// ```
2483 pub fn get_step() -> f64 {
2484 unsafe { time_step() }
2485 }
2486
2487 /// How many seconds have elapsed since the last frame? 32 bit time precision, calculated at the start of the frame.
2488 /// <https://stereokit.net/Pages/StereoKit/Time/Stepf.html>
2489 ///
2490 /// see also [`time_stepf`]
2491 /// ### Examples
2492 /// ```
2493 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2494 /// use stereokit_rust::util::Time;
2495 ///
2496 /// assert_eq!(Time::get_stepf(), 0.0);
2497 ///
2498 /// let mut totalf = 0.0f32;
2499 /// number_of_steps = 1000;
2500 /// test_steps!( // !!!! Get a proper main loop !!!!
2501 /// if iter < number_of_steps + 2 {
2502 /// assert!((Time::get_totalf() - totalf - Time::get_stepf()).abs() < 0.000001);
2503 /// }
2504 /// totalf = Time::get_totalf();
2505 /// );
2506 /// ```
2507 pub fn get_stepf() -> f32 {
2508 unsafe { time_stepf() }
2509 }
2510
2511 /// How many seconds have elapsed since the last frame? 64 bit time precision, calculated at the start of the frame.
2512 /// This version is unaffected by the Time::scale value!
2513 /// <https://stereokit.net/Pages/StereoKit/Time/StepUnscaled.html>
2514 ///
2515 /// see also [`time_step_unscaled`]
2516 /// ### Examples
2517 /// ```
2518 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2519 /// use stereokit_rust::util::Time;
2520 ///
2521 /// assert_eq!(Time::get_step_unscaled(), 0.0);
2522 ///
2523 /// let mut total = 0.0f64;
2524 /// number_of_steps = 1000;
2525 /// test_steps!( // !!!! Get a proper main loop !!!!
2526 /// if iter < number_of_steps + 2 {
2527 /// assert_eq!(Time::get_total_unscaled(), total + Time::get_step_unscaled());
2528 /// }
2529 /// total = Time::get_total_unscaled();
2530 /// );
2531 /// ```
2532 pub fn get_step_unscaled() -> f64 {
2533 unsafe { time_step_unscaled() }
2534 }
2535
2536 /// How many seconds have elapsed since the last frame? 32 bit time precision, calculated at the start of the frame.
2537 /// This version is unaffected by the Time.Scale value!
2538 /// <https://stereokit.net/Pages/StereoKit/Time/StepUnscaledf.html>
2539 ///
2540 /// see also [`time_stepf_unscaled`]
2541 /// ### Examples
2542 /// ```
2543 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2544 /// use stereokit_rust::util::Time;
2545 ///
2546 /// assert_eq!(Time::get_step_unscaledf(), 0.0);
2547 ///
2548 /// let mut totalf = 0.0f32;
2549 /// number_of_steps = 1000;
2550 /// test_steps!( // !!!! Get a proper main loop !!!!
2551 /// if iter < number_of_steps + 2 {
2552 /// assert!((Time::get_total_unscaledf() - totalf - Time::get_step_unscaledf().abs() < 0.000001));
2553 /// }
2554 /// totalf = Time::get_total_unscaledf();
2555 /// );
2556 /// ```
2557 pub fn get_step_unscaledf() -> f32 {
2558 unsafe { time_stepf_unscaled() }
2559 }
2560
2561 /// How many seconds have elapsed since StereoKit was initialized? 64 bit time precision, calculated at the start of
2562 /// the frame.
2563 /// <https://stereokit.net/Pages/StereoKit/Time/Total.html>
2564 ///
2565 /// see also [`time_total`]
2566 /// ### Examples
2567 /// ```
2568 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2569 /// use stereokit_rust::util::Time;
2570 ///
2571 /// // Time passes faster:
2572 /// Time::scale(2.0);
2573 ///
2574 /// assert_eq!(Time::get_total(), 0.0);
2575 ///
2576 /// number_of_steps = 1000;
2577 /// test_steps!( // !!!! Get a proper main loop !!!!
2578 /// if iter < number_of_steps + 2 {
2579 /// assert_eq!(Time::get_total(), Time::get_total_unscaled() * 2.0);
2580 /// }
2581 /// );
2582 /// ```
2583 pub fn get_total() -> f64 {
2584 unsafe { time_total() }
2585 }
2586
2587 /// How many seconds have elapsed since StereoKit was initialized? 32 bit time precision, calculated at the start of
2588 /// the frame.
2589 /// <https://stereokit.net/Pages/StereoKit/Time/Totalf.html>
2590 ///
2591 /// see also [`time_totalf`]
2592 /// ### Examples
2593 /// ```
2594 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2595 /// use stereokit_rust::util::Time;
2596 ///
2597 /// // Time passes faster:
2598 /// Time::scale(0.25);
2599 ///
2600 /// assert_eq!(Time::get_totalf(), 0.0);
2601 ///
2602 /// number_of_steps = 1000;
2603 /// test_steps!( // !!!! Get a proper main loop !!!!
2604 /// if iter < number_of_steps + 2 {
2605 /// assert_eq!(Time::get_totalf(), Time::get_total_unscaledf() / 4.0);
2606 /// }
2607 /// );
2608 /// ```
2609 pub fn get_totalf() -> f32 {
2610 unsafe { time_totalf() }
2611 }
2612
2613 /// How many seconds have elapsed since StereoKit was initialized? 64 bit time precision, calculated at the start of
2614 /// the frame. This version is unaffected by the Time::scale value!
2615 /// <https://stereokit.net/Pages/StereoKit/Time/TotalUnscaled.html>
2616 ///
2617 /// see also [`time_total_unscaled`]
2618 /// see example in [`Time::get_total`]
2619 pub fn get_total_unscaled() -> f64 {
2620 unsafe { time_total_unscaled() }
2621 }
2622
2623 /// How many seconds have elapsed since StereoKit was initialized? 32 bit time precision, calculated at the start of
2624 /// the frame. This version is unaffected by the Time::scale value!
2625 /// <https://stereokit.net/Pages/StereoKit/Time/TotalUnscaledf.html>
2626 ///
2627 /// see also [`time_totalf_unscaled`]
2628 /// see example in [`Time::get_total_unscaled`]
2629 pub fn get_total_unscaledf() -> f32 {
2630 unsafe { time_totalf_unscaled() }
2631 }
2632}