stereokit_rust/system.rs
1use crate::{
2 StereoKitError,
3 anchor::{_AnchorT, Anchor},
4 font::{_FontT, Font, FontT},
5 material::{_MaterialT, Material, MaterialBuffer, MaterialBufferT, MaterialT},
6 maths::{Bool32T, Matrix, Pose, Quat, Ray, Rect, Vec2, Vec3, ray_from_mouse},
7 mesh::{_MeshT, Mesh, MeshT},
8 model::{_ModelT, Model, ModelT},
9 render_list::{_RenderListT, RenderList},
10 shader::{_ShaderT, Shader, ShaderT},
11 sk::{MainThreadToken, OriginMode},
12 sound::{_SoundT, Sound, SoundT},
13 sprite::{_SpriteT, Sprite},
14 tex::{_TexT, Tex, TexFormat, TexT},
15 util::{Color32, Color128, SphericalHarmonics},
16};
17use std::{
18 ffi::{CStr, CString, c_char, c_ushort, c_void},
19 fmt,
20 mem::{size_of, transmute_copy},
21 path::Path,
22 ptr::{NonNull, null, null_mut},
23};
24
25// Re-export interactor types for convenient access
26pub use crate::interactor::{
27 DefaultInteractors, Interaction, Interactor, InteractorActivation, InteractorEvent, InteractorType,
28};
29
30/// All StereoKit assets implement this interface! This is mostly to help group and hold Asset objects, and is
31/// particularly useful when working with Assets at a high level with the Assets class.
32/// <https://stereokit.net/Pages/StereoKit/IAsset.html>
33pub trait IAsset {
34 // sets the unique identifier of this asset resource! This can be helpful for debugging, managing your assets, or
35 // finding them later on!
36 // <https://stereokit.net/Pages/StereoKit/IAsset/Id.html>
37 //fn id(&mut self, id: impl AsRef<str>);
38
39 /// gets the unique identifier of this asset resource! This can be helpful for debugging, managing your assets, or
40 /// finding them later on!
41 /// <https://stereokit.net/Pages/StereoKit/IAsset/Id.html>
42 fn get_id(&self) -> &str;
43}
44
45/// StereoKit uses an asynchronous loading system to prevent assets from blocking execution! This means that asset
46/// loading systems will return an asset to you right away, even though it is still being processed in the background.
47/// <https://stereokit.net/Pages/StereoKit/AssetState.html>
48///
49/// see also: [`Tex::get_asset_state`]
50#[derive(Debug, Copy, Clone, PartialEq, Eq)]
51#[repr(C)]
52pub enum AssetState {
53 /// This asset encountered an issue when parsing the source data. Either the format is unrecognized by StereoKit,
54 /// or the data may be corrupt. Check the logs for additional details.
55 Unsupported = -3,
56 /// The asset data was not found! This is most likely an issue with a bad file path, or file permissions. Check
57 /// the logs for additional details.
58 NotFound = -2,
59 /// An unknown error occurred when trying to load the asset! Check the logs for additional details.
60 Error = -1,
61 /// This asset is in its default state. It has not been told to load anything, nor does it have any data!
62 None = 0,
63 /// This asset is currently queued for loading, but hasn’t received any data yet. Attempting to access metadata or
64 /// asset data will result in blocking the app’s execution until that data is loaded!
65 Loading = 1,
66 /// This asset is still loading, but some of the higher level data is already available for inspection without
67 /// blocking the app. Attempting to access the core asset data will result in blocking the app’s execution until
68 /// that data is loaded!
69 LoadedMeta = 2,
70 /// This asset is completely loaded without issues, and is ready for use!
71 Loaded = 3,
72}
73
74/// A flag for what ‘type’ an Asset may store.
75///
76/// None -> No type, this may come from some kind of invalid Asset id.
77/// <https://stereokit.net/Pages/StereoKit/AssetType.html>
78///
79/// see also [`Assets`] [`Asset`]
80#[derive(Debug, Copy, Clone, PartialEq, Eq)]
81#[repr(u32)]
82pub enum AssetType {
83 None = 0,
84 Mesh = 1,
85 Tex = 2,
86 Shader = 3,
87 Material = 4,
88 Model = 5,
89 Font = 6,
90 Sprite = 7,
91 Sound = 8,
92 Solid = 9,
93 Anchor = 10,
94 RenderList = 11,
95}
96
97/// If you want to manage loading assets, this is the class for you!
98/// <https://stereokit.net/Pages/StereoKit/Assets.html>
99///
100/// ### Examples
101/// ```
102/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
103/// use stereokit_rust::{maths::Matrix, system::{Assets, AssetType, Asset, Pivot},
104/// sprite::Sprite};
105///
106/// let my_sprite = Sprite::from_file("textures/open_gltf.jpeg", None, None)
107/// .expect("open_gltf.jpeg should be able to create sprite");
108///
109/// for asset in Assets::all().filter(|s| !s.to_string().contains(" default/")) {
110/// if let Asset::Sprite(sprite) = asset {
111/// if !sprite.get_id().starts_with("sk/ui/") {
112/// assert_eq!(sprite, my_sprite);
113/// }
114/// }
115/// }
116///
117/// for asset in Assets::all_of_type(AssetType::Sprite) {
118/// if let Asset::Sprite(sprite) = asset {
119/// if !sprite.get_id().starts_with("sk/ui/") {
120/// assert_eq!(sprite, my_sprite);
121/// }
122/// } else {
123/// panic!("asset should be a sprite");
124/// }
125/// }
126///
127/// filename_scr = "screenshots/assets.jpeg"; fov_scr= 55.0;
128/// test_screenshot!( // !!!! Get a proper main loop !!!!
129/// my_sprite.draw(token, Matrix::Y_180, Pivot::Center, None);
130/// );
131/// ```
132/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/assets.jpeg" alt="screenshot" width="200">
133pub struct Assets;
134
135pub type AssetT = *mut c_void;
136
137unsafe extern "C" {
138 pub fn assets_releaseref_threadsafe(asset: *mut c_void);
139 pub fn assets_current_task() -> i32;
140 pub fn assets_total_tasks() -> i32;
141 pub fn assets_current_task_priority() -> i32;
142 pub fn assets_block_for_priority(priority: i32);
143 pub fn assets_count() -> i32;
144 pub fn assets_get_index(index: i32) -> AssetT;
145 pub fn assets_get_type(index: i32) -> AssetType;
146 pub fn asset_get_type(asset: AssetT) -> AssetType;
147 pub fn asset_set_id(asset: AssetT, id: *const c_char);
148 pub fn asset_get_id(asset: AssetT) -> *const c_char;
149 pub fn asset_addref(asset: AssetT);
150 pub fn asset_release(asset: AssetT);
151}
152
153/// Non-canonical structure to store an asset and avoid reducer `Box<dyn Asset>`
154///
155/// see also [`AssetType`] [`Assets`]
156#[derive(Debug)]
157pub enum Asset {
158 None,
159 Mesh(Mesh),
160 Tex(Tex),
161 Shader(Shader),
162 Material(Material),
163 Model(Model),
164 Font(Font),
165 Sprite(Sprite),
166 Sound(Sound),
167 Solid(*mut c_void),
168 Anchor(Anchor),
169 RenderList(RenderList),
170}
171
172impl fmt::Display for Asset {
173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174 match self {
175 Asset::None => write!(f, "None"),
176 Asset::Mesh(v) => write!(f, "Mesh : {}", v.get_id()),
177 Asset::Tex(v) => write!(f, "Tex : {}", v.get_id()),
178 Asset::Shader(v) => write!(f, "Shader : {}", v.get_id()),
179 Asset::Material(v) => write!(f, "Material : {}", v.get_id()),
180 Asset::Model(v) => write!(f, "Model : {}", v.get_id()),
181 Asset::Font(v) => write!(f, "Font : {}", v.get_id()),
182 Asset::Sprite(v) => write!(f, "Sprite : {}", v.get_id()),
183 Asset::Sound(v) => write!(f, "Sound : {}", v.get_id()),
184 Asset::Solid(_) => write!(f, "Solid : ... deprecated ..."),
185 Asset::Anchor(v) => write!(f, "Anchor : {}", v.get_id()),
186 Asset::RenderList(v) => write!(f, "RenderList : {}", v.get_id()),
187 }
188 }
189}
190
191/// Iterator on [`Assets`] producing some [`Asset`]
192///
193/// see also [`Assets::all`] [`Assets::all_of_type`]
194#[derive(Debug, Copy, Clone)]
195pub struct AssetIter {
196 index: i32,
197 pub asset_type: AssetType,
198}
199
200impl Iterator for AssetIter {
201 type Item = Asset;
202
203 fn next(&mut self) -> Option<Self::Item> {
204 self.index += 1;
205 let count = unsafe { assets_count() };
206 if self.asset_type == AssetType::None {
207 if self.index < count {
208 match unsafe { assets_get_type(self.index) } {
209 AssetType::None => {
210 Log::err(format!("Asset at index {:?}/{:?} is AssetType::None", self.index, count));
211 None
212 }
213 asset_type => {
214 let asset_id = unsafe { assets_get_index(self.index) };
215 Some(self.to_asset(asset_type, asset_id))
216 }
217 }
218 } else {
219 None
220 }
221 } else {
222 while self.index < count {
223 if unsafe { assets_get_type(self.index) } == self.asset_type {
224 let asset_id = unsafe { assets_get_index(self.index) };
225 return Some(self.to_asset(self.asset_type, asset_id));
226 } else {
227 self.index += 1;
228 }
229 }
230 None
231 }
232 }
233}
234
235impl AssetIter {
236 /// Get the asset
237 fn to_asset(self, asset_type: AssetType, c_id: *mut c_void) -> Asset {
238 match asset_type {
239 AssetType::None => Asset::None,
240 AssetType::Mesh => Asset::Mesh(Mesh(NonNull::new(c_id as *mut _MeshT).unwrap())),
241 AssetType::Tex => Asset::Tex(Tex(NonNull::new(c_id as *mut _TexT).unwrap())),
242 AssetType::Shader => Asset::Shader(Shader(NonNull::new(c_id as *mut _ShaderT).unwrap())),
243 AssetType::Material => Asset::Material(Material(NonNull::new(c_id as *mut _MaterialT).unwrap())),
244 AssetType::Model => Asset::Model(Model(NonNull::new(c_id as *mut _ModelT).unwrap())),
245 AssetType::Font => Asset::Font(Font(NonNull::new(c_id as *mut _FontT).unwrap())),
246 AssetType::Sprite => Asset::Sprite(Sprite(NonNull::new(c_id as *mut _SpriteT).unwrap())),
247 AssetType::Sound => Asset::Sound(Sound(NonNull::new(c_id as *mut _SoundT).unwrap())),
248 AssetType::Solid => todo!("Solids are deprecated!"),
249 AssetType::Anchor => Asset::Anchor(Anchor(NonNull::new(c_id as *mut _AnchorT).unwrap())),
250 AssetType::RenderList => Asset::RenderList(RenderList(NonNull::new(c_id as *mut _RenderListT).unwrap())),
251 }
252 }
253
254 /// Get an iterator upon all assets loaded if asset_type is None or only assets of the given AssetType
255 /// <https://stereokit.net/Pages/StereoKit/Assets.html>
256 pub fn iterate(asset_type: Option<AssetType>) -> AssetIter {
257 let asset_type = asset_type.unwrap_or(AssetType::None);
258 AssetIter { index: -1, asset_type }
259 }
260}
261
262impl Assets {
263 /// A list of supported model format extensions. This pairs pretty well with Platform::file_picker when attempting to
264 /// load a Model!
265 /// <https://stereokit.net/Pages/StereoKit/Assets/ModelFormats.html>
266 pub const MODEL_FORMATS: [&'static str; 5] = [".gltf", ".glb", ".obj", ".stl", ".ply"];
267
268 /// A list of supported texture format extensions. This pairs pretty well with Platform::file_picker when attempting
269 /// to load a Tex!
270 /// <https://stereokit.net/Pages/StereoKit/Assets/TextureFormats.html>
271 pub const TEXTURE_FORMATS: [&'static str; 11] =
272 [".jpg", ".jpeg", ".png", ".hdr", ".tga", ".bmp", ".psd", ".pic", ".qoi", ".gif", ".ktx2"];
273
274 /// supported sound format by asset Sound <https://stereokit.net/Pages/StereoKit/Sound.html>
275 pub const SOUND_FORMATS: [&'static str; 2] = [".wav", ".mp3"];
276
277 /// This is an iterator upon all assets loaded by StereoKit at the current moment.
278 /// <https://stereokit.net/Pages/StereoKit/Assets/All.html>
279 ///
280 /// see also [`AssetIter`] [`Asset`]
281 /// ### Examples
282 /// ```
283 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
284 /// use stereokit_rust::{maths::Matrix, system::{Assets, AssetType, Asset},
285 /// sprite::Sprite};
286 ///
287 /// let my_sprite = Sprite::from_file("textures/open_gltf.jpeg", None, None)
288 /// .expect("open_gltf.jpeg should be able to create sprite");
289 ///
290 /// let all = Assets::all();
291 ///
292 /// let mut sprite_count = 0 ; let mut texture_count = 0;
293 /// let mut model_count = 0 ; let mut sound_count = 0;
294 /// let mut material_count = 0 ; let mut shader_count = 0;
295 /// let mut font_count = 0 ; let mut other_count = 0;
296 /// let mut mesh_count = 0 ; let mut render_list_count = 0;
297 /// for asset in all {
298 /// match asset {
299 /// Asset::Sprite(sprite) => sprite_count += 1,
300 /// Asset::Model(model) => model_count +=1,
301 /// Asset::Sound(sound) => sound_count +=1,
302 /// Asset::Tex(texture) => texture_count +=1,
303 /// Asset::Material(material) => material_count +=1,
304 /// Asset::Font(font) => font_count +=1,
305 /// Asset::Mesh(mesh) => mesh_count +=1,
306 /// Asset::Shader(shader) => shader_count +=1,
307 /// Asset::RenderList(render_list) => render_list_count +=1,
308 /// _ => other_count +=1,
309 ///
310 /// }
311 /// }
312 /// assert_eq!(sprite_count, 13 + 1 );
313 /// assert_eq!(texture_count, 23 + 1 );
314 /// assert_eq!(model_count, 2);
315 /// assert_eq!(sound_count, 5);
316 /// assert_eq!(material_count, 37 + 1 );
317 /// assert_eq!(shader_count, 15);
318 /// assert_eq!(font_count, 1);
319 /// assert_eq!(mesh_count, 26);
320 /// assert_eq!(render_list_count, 1);
321 /// assert_eq!(other_count, 0);
322 /// ```
323 pub fn all() -> AssetIter {
324 AssetIter::iterate(None)
325 }
326
327 /// This is an iterator upon all assets matching the specified type.
328 /// <https://stereokit.net/Pages/StereoKit/Assets/Type.html>
329 /// * `asset_type` - Any [`IAsset`] type
330 ///
331 /// see also [`AssetIter`] [`Asset`] [`IAsset`]
332 /// ### Examples
333 /// ```
334 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
335 /// use stereokit_rust::{system::{Assets, AssetType, Asset},
336 /// sprite::Sprite};
337 ///
338 /// let my_sprite = Sprite::from_file("textures/open_gltf.jpeg", None, None)
339 /// .expect("open_gltf.jpeg should be able to create sprite");
340 ///
341 /// let all = Assets::all_of_type(AssetType::Sprite);
342 ///
343 /// let mut sprite_count = 0;
344 /// for asset in all {
345 /// match asset {
346 /// Asset::Sprite(sprite) => sprite_count += 1,
347 /// _ => panic!("asset should be a sprite"),
348 /// }
349 /// }
350 /// assert_eq!(sprite_count, 13 + 1);
351 /// ```
352 pub fn all_of_type(asset_type: AssetType) -> AssetIter {
353 AssetIter::iterate(Some(asset_type))
354 }
355
356 /// This is the index of the current asset loading task. Note that to load one asset, multiple tasks are generated.
357 /// <https://stereokit.net/Pages/StereoKit/Assets/CurrentTask.html>
358 ///
359 /// see also [`assets_current_task`] [`Assets::total_tasks`]
360 /// ### Examples
361 /// ```
362 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
363 /// use stereokit_rust::{system::Assets, sprite::Sprite};
364 /// let my_sprite = Sprite::from_file("textures/open_gltf.jpeg", None, None)
365 /// .expect("open_gltf.jpeg should be able to create sprite");
366 ///
367 /// let current_task = Assets::current_task();
368 /// // TODO: most of the time true but ... assert_eq!(Assets::total_tasks(), 1);
369 /// number_of_steps = 200;
370 /// assert_eq!(current_task, 0);
371 /// ```
372 pub fn current_task() -> i32 {
373 unsafe { assets_current_task() }
374 }
375
376 /// StereoKit processes tasks in order of priority. This returns the priority of the current task, and can be used
377 /// to wait until all tasks within a certain priority range have been completed.
378 /// <https://stereokit.net/Pages/StereoKit/Assets/CurrentTaskPriority.html>
379 ///
380 /// see also [`assets_current_task_priority`]
381 /// ### Examples
382 /// ```
383 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
384 /// use stereokit_rust::{system::Assets, sprite::Sprite};
385 ///
386 /// let my_sprite = Sprite::from_file("textures/open_gltf.jpeg", None, None)
387 /// .expect("open_gltf.jpeg should be able to create sprite");
388 ///
389 /// let current_task_priority = Assets::current_task_priority();
390 /// assert_eq!(current_task_priority, 10);
391 /// ```
392 pub fn current_task_priority() -> i32 {
393 unsafe { assets_current_task_priority() }
394 }
395
396 /// This is the total number of tasks that have been added to the loading system, including all completed and
397 /// pending tasks. Note that to load one asset, multiple tasks are generated.
398 /// <https://stereokit.net/Pages/StereoKit/Assets/TotalTasks.html>
399 ///
400 /// see also [`assets_total_tasks`]
401 /// ### Examples
402 /// ```
403 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
404 /// use stereokit_rust::{system::Assets, sprite::Sprite};
405 ///
406 /// let my_sprite1 = Sprite::from_file("textures/open_gltf.jpeg", None, None)
407 /// .expect("open_gltf.jpeg should be able to create sprite");
408 ///
409 /// let my_sprite2 = Sprite::from_file("textures/log_viewer.jpeg", None, None)
410 /// .expect("log_viewer.jpeg should be able to create sprite");
411 ///
412 /// test_steps!( // !!!! Get a proper main loop !!!!
413 /// let total_tasks = Assets::total_tasks();
414 /// assert_eq!(total_tasks, 2);
415 /// );
416 /// ```
417 pub fn total_tasks() -> i32 {
418 unsafe { assets_total_tasks() }
419 }
420
421 /// This will block the execution of the application until all asset tasks below the priority value have completed
422 /// loading. To block until all assets are loaded, pass in i32::MAX for the priority.
423 /// <https://stereokit.net/Pages/StereoKit/Assets/BlockForPriority.html>
424 ///
425 /// see also [`assets_block_for_priority`]
426 /// ### Examples
427 /// ```
428 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
429 /// use stereokit_rust::{maths::{Vec3, Matrix}, system::{Assets, AssetState}, tex::Tex,
430 /// material::Material, mesh::Mesh, model::Model, util::named_colors};
431 ///
432 /// // The model is loaded asynchronously, so we need to wait for it to be loaded before we can screenshot it.
433 /// let model = Model::from_file("cuve.glb", None)
434 /// .expect("mobiles.gltf should be a valid model");
435 /// let transform = Matrix::t_r_s([0.15, -0.75, -1.0], [0.0, 110.0, 0.0], [0.4, 0.4, 0.4]);
436 ///
437 /// Assets::block_for_priority(i32::MAX);
438 ///
439 /// filename_scr = "screenshots/assets_block_for_priority.jpeg";
440 /// test_screenshot!( // !!!! Get a proper main loop !!!!
441 /// model.draw(token, transform, Some(named_colors::MISTY_ROSE.into()), None);
442 /// );
443 /// ```
444 /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/assets_block_for_priority.jpeg" alt="screenshot" width="200">
445 pub fn block_for_priority(priority: i32) {
446 unsafe { assets_block_for_priority(priority) }
447 }
448}
449
450/// This describes what technology is being used to power StereoKit’s XR backend.
451/// <https://stereokit.net/Pages/StereoKit/BackendXRType.html>
452///
453/// see also [`Backend::xr_type`]
454#[derive(Debug, Copy, Clone, PartialEq, Eq)]
455#[repr(u32)]
456pub enum BackendXRType {
457 /// StereoKit is not using an XR backend of any sort. That means the application is flatscreen and has the simulator
458 /// disabled.
459 None = 0,
460 /// StereoKit is using the flatscreen XR simulator. Inputs are emulated, and some advanced XR functionality may not
461 /// be available.
462 Simulator = 1,
463 /// StereoKit is currently powered by OpenXR! This means we’re running on a real XR device. Not all OpenXR runtimes
464 /// provide the same functionality, but we will have access to more fun stuff :)
465 OpenXR = 2,
466 /// StereoKit is running in a browser, and is using WebXR!
467 WebXR = 3,
468}
469
470/// This describes the platform that StereoKit is running on.
471/// <https://stereokit.net/Pages/StereoKit/BackendPlatform.html>
472///
473/// see also [`Backend::platform`]
474#[derive(Debug, Copy, Clone, PartialEq, Eq)]
475#[repr(u32)]
476pub enum BackendPlatform {
477 /// This is running as a Windows app using the Win32 APIs.
478 Win32 = 0,
479 /// This is running as a Windows app using the UWP APIs.
480 Uwp = 1,
481 /// This is running as a Linux app.
482 Linux = 2,
483 /// This is running as an Android app.
484 Android = 3,
485 /// This is running in a browser.
486 Web = 4,
487}
488
489/// This describes the graphics API thatStereoKit is using for rendering.
490/// <https://stereokit.net/Pages/StereoKit/BackendGraphics.html>
491///
492/// see also [`Backend::graphics`]
493#[derive(Debug, Copy, Clone, PartialEq, Eq)]
494#[repr(u32)]
495pub enum BackendGraphics {
496 /// An invalid default value.
497 None = 0,
498 /// DirectX’s Direct3D11 is used for rendering! This is used by default on Windows.
499 D3D11 = 1,
500 /// OpenGL is used for rendering, using GLX (OpenGL Extension to the X Window System) for loading. This is used by
501 /// default on Linux.
502 OpenGLGLX = 2,
503 /// OpenGL is used for rendering, using WGL (Windows Extensions to OpenGL) for loading. Native developers can
504 /// configure SK to use this on Windows.
505 OpenGLWGL = 3,
506 /// OpenGL ES is used for rendering, using EGL (EGL Native Platform Graphics Interface) for loading. This is used by
507 /// default on Android, and native developers can configure SK to use this on Linux.
508 OpenGLESEGL = 4,
509 /// WebGL is used for rendering. This is used by default on Web.
510 WebGL = 5,
511}
512
513/// XrInstance type
514pub type OpenXRHandleT = u64;
515
516/// This class exposes some of StereoKit’s backend functionality. This allows for tighter integration with certain
517/// platforms, but also means your code becomes less portable. Everything in this class should be guarded by
518/// availability checks.
519///
520/// <https://stereokit.net/Pages/StereoKit/Backend.html>
521/// ### Examples
522/// ```
523/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
524/// use stereokit_rust::system::{Backend, BackendGraphics, BackendPlatform, BackendXRType, BackendOpenXR};
525///
526/// let graphics = Backend::graphics();
527/// let platform = Backend::platform();
528/// let xr_type = Backend::xr_type();
529///
530/// if cfg!(target_os = "windows") {
531/// assert_eq!(graphics, BackendGraphics::D3D11);
532/// assert_eq!(platform, BackendPlatform::Win32);
533/// } else {
534/// assert_eq!(graphics, BackendGraphics::OpenGLESEGL);
535/// assert_eq!(platform, BackendPlatform::Linux);
536/// }
537/// assert_eq!(BackendOpenXR::eyes_sample_time(), 0);
538///
539/// xr_mode_stop_here!();
540/// // These are the expected results for offscreen tests on a PC:
541/// assert_eq!(xr_type, BackendXRType::None);
542///
543/// ```
544pub struct Backend;
545
546pub type VoidFunction = unsafe extern "system" fn();
547
548unsafe extern "C" {
549 pub fn backend_xr_get_type() -> BackendXRType;
550 pub fn backend_openxr_get_instance() -> OpenXRHandleT;
551 pub fn backend_openxr_get_session() -> OpenXRHandleT;
552 pub fn backend_openxr_get_system_id() -> OpenXRHandleT;
553 pub fn backend_openxr_get_space() -> OpenXRHandleT;
554 pub fn backend_openxr_get_time() -> i64;
555 pub fn backend_openxr_get_eyes_sample_time() -> i64;
556 pub fn backend_openxr_get_function(function_name: *const c_char) -> Option<VoidFunction>;
557 pub fn backend_openxr_ext_enabled(extension_name: *const c_char) -> Bool32T;
558 pub fn backend_openxr_ext_request(extension_name: *const c_char);
559 pub fn backend_openxr_ext_exclude(extension_name: *const c_char);
560 pub fn backend_openxr_use_minimum_exts(use_minimum_exts: Bool32T);
561 pub fn backend_openxr_composition_layer(XrCompositionLayerBaseHeader: *mut c_void, data_size: i32, sort_order: i32);
562 pub fn backend_openxr_end_frame_chain(XrBaseHeader: *mut c_void, data_size: i32);
563 pub fn backend_openxr_set_hand_joint_scale(joint_scale_factor: f32);
564 pub fn backend_openxr_add_callback_pre_session_create(
565 xr_pre_session_create_callback: ::std::option::Option<unsafe extern "C" fn(context: *mut c_void)>,
566 context: *mut c_void,
567 );
568 pub fn backend_openxr_add_callback_poll_event(
569 xr_poll_event_callback: ::std::option::Option<
570 unsafe extern "C" fn(context: *mut c_void, xr_event_data_buffer: *mut c_void),
571 >,
572 context: *mut c_void,
573 );
574 pub fn backend_openxr_remove_callback_poll_event(
575 xr_poll_event_callback: ::std::option::Option<
576 unsafe extern "C" fn(context: *mut c_void, xr_event_data_buffer: *mut c_void),
577 >,
578 );
579 pub fn backend_platform_get() -> BackendPlatform;
580 pub fn backend_android_get_java_vm() -> *mut c_void;
581 pub fn backend_android_get_activity() -> *mut c_void;
582 pub fn backend_android_get_jni_env() -> *mut c_void;
583 pub fn backend_graphics_get() -> BackendGraphics;
584 pub fn backend_d3d11_get_d3d_device() -> *mut c_void;
585 pub fn backend_d3d11_get_d3d_context() -> *mut c_void;
586 pub fn backend_d3d11_get_deferred_d3d_context() -> *mut c_void;
587 pub fn backend_d3d11_get_deferred_mtx() -> *mut c_void;
588 pub fn backend_d3d11_get_main_thread_id() -> u32;
589 pub fn backend_opengl_wgl_get_hdc() -> *mut c_void;
590 pub fn backend_opengl_wgl_get_hglrc() -> *mut c_void;
591 pub fn backend_opengl_glx_get_context() -> *mut c_void;
592 pub fn backend_opengl_glx_get_display() -> *mut c_void;
593 pub fn backend_opengl_glx_get_drawable() -> *mut c_void;
594 pub fn backend_opengl_egl_get_context() -> *mut c_void;
595 pub fn backend_opengl_egl_get_config() -> *mut c_void;
596 pub fn backend_opengl_egl_get_display() -> *mut c_void;
597}
598
599impl Backend {
600 /// This describes the graphics API thatStereoKit is using for rendering. StereoKit uses D3D11 for Windows platforms,
601 /// and a flavor of OpenGL for Linux, Android, and Web.
602 /// <https://stereokit.net/Pages/StereoKit/Backend/Graphics.html>
603 ///
604 /// see also [`backend_graphics_get`]
605 /// ### Examples
606 /// ```
607 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
608 /// use stereokit_rust::system::{Backend, BackendGraphics};
609 ///
610 /// let graphics = Backend::graphics();
611 /// if cfg!(target_os = "windows") {
612 /// assert_eq!(graphics, BackendGraphics::D3D11);
613 /// } else {
614 /// assert_eq!(graphics, BackendGraphics::OpenGLESEGL);
615 /// }
616 /// ```
617 pub fn graphics() -> BackendGraphics {
618 unsafe { backend_graphics_get() }
619 }
620
621 /// What kind of platform is StereoKit running on? This can be important to tell you what APIs or functionality is
622 /// available to the app.
623 /// <https://stereokit.net/Pages/StereoKit/Backend/Platform.html>
624 ///
625 /// see also [`backend_platform_get`]
626 /// ### Examples
627 /// ```
628 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
629 /// use stereokit_rust::system::{Backend, BackendPlatform};
630 ///
631 /// let platform = Backend::platform();
632 /// if cfg!(target_os = "windows") {
633 /// assert_eq!(platform, BackendPlatform::Win32);
634 /// } else {
635 /// assert_eq!(platform, BackendPlatform::Linux);
636 /// }
637 /// ```
638 pub fn platform() -> BackendPlatform {
639 unsafe { backend_platform_get() }
640 }
641
642 /// What technology is being used to drive StereoKit’s XR functionality? OpenXR is the most likely candidate here,
643 /// but if you’re running the flatscreen Simulator, or running in the web with WebXR, then this will reflect that.
644 /// <https://stereokit.net/Pages/StereoKit/Backend/XRType.html>
645 ///
646 /// see also [`backend_xr_get_type`]
647 /// ### Examples
648 /// ```
649 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
650 /// use stereokit_rust::system::{Backend, BackendXRType};
651 ///
652 /// let xr_type = Backend::xr_type();
653 ///
654 /// xr_mode_stop_here!();
655 /// // These are the expected results for offscreen tests on a PC:
656 /// assert_eq!(xr_type, BackendXRType::None);
657 /// ```
658 pub fn xr_type() -> BackendXRType {
659 unsafe { backend_xr_get_type() }
660 }
661}
662
663/// This class is NOT of general interest, unless you are trying to add support for some unusual OpenXR extension!
664/// StereoKit should do all the OpenXR work that most people will need. If you find yourself here anyhow for something
665/// you feel StereoKit should support already, please add a feature request on GitHub!
666///
667/// This class contains handles and methods for working directly with OpenXR. This may allow you to activate or work
668/// with OpenXR extensions that StereoKit hasn’t implemented or exposed yet. Check that Backend.XRType is OpenXR before
669/// using any of this.
670///
671/// These properties may best be used with some external OpenXR binding library, but you may get some limited mileage
672/// with the API as provided here.
673/// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR.html>
674///
675/// see implementations in [`crate::tools::xr_fb_passthrough`] [`crate::tools::os_api`]
676/// ### Examples
677/// ```
678/// use stereokit_rust::system::{Backend, BackendOpenXR, BackendXRType};
679///
680/// // This must be set before initializing StereoKit.
681/// BackendOpenXR::use_minimum_exts(false);
682/// BackendOpenXR::exclude_ext("XR_EXT_hand_tracking");
683/// BackendOpenXR::request_ext("XR_EXT_hand_tracking");
684///
685/// stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
686///
687/// let xr_type = Backend::xr_type();
688/// let eyes_sample_time = BackendOpenXR::eyes_sample_time();
689/// let instance = BackendOpenXR::instance();
690/// let session = BackendOpenXR::session();
691/// let space = BackendOpenXR::space();
692/// let system_id = BackendOpenXR::system_id();
693/// let time = BackendOpenXR::time();
694/// let ext_enabled = BackendOpenXR::ext_enabled("XR_EXT_hand_tracking");
695/// let get_function_ptr = BackendOpenXR::get_function_ptr("xrGetHandTrackerEXT");
696/// let get_function = BackendOpenXR::get_function::<unsafe extern "C" fn()>("xrGetHandTrackerEXT");
697/// BackendOpenXR::set_hand_joint_scale(1.0);
698/// BackendOpenXR::use_minimum_exts(true);
699///
700/// xr_mode_stop_here!();
701/// // These are the expected results for offscreen tests on a PC:
702/// assert_eq!( xr_type, BackendXRType::None);
703/// assert_ne!( xr_type, BackendXRType::OpenXR);
704/// assert_eq!(eyes_sample_time, 0);
705/// assert_eq!(instance, 0);
706/// assert_eq!(session, 0);
707/// assert_eq!(space, 0);
708/// assert_eq!(system_id, 0);
709/// assert_eq!(time, 0);
710/// assert_eq!(ext_enabled, false);
711/// assert_eq!(get_function_ptr, None);
712/// assert_eq!(get_function, None);
713/// ```
714pub struct BackendOpenXR;
715
716impl BackendOpenXR {
717 /// Type: XrTime. This is the OpenXR time of the eye tracker sample associated with the current value of.
718 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR/EyesSampleTime.html>
719 ///
720 /// see also [`backend_openxr_get_eyes_sample_time`]
721 pub fn eyes_sample_time() -> i64 {
722 unsafe { backend_openxr_get_eyes_sample_time() }
723 }
724
725 /// Type: XrInstance. StereoKit’s instance handle, valid after Sk.initialize.
726 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR/Instance.html>
727 ///
728 /// see also [`backend_openxr_get_instance`]
729 ///
730 /// ### Examples
731 /// ```
732 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
733 /// use stereokit_rust::system::BackendOpenXR;
734 ///
735 /// let instance_handle = BackendOpenXR::instance();
736 ///
737 /// // In XR mode, this would be a valid XrInstance handle for OpenXR operations
738 /// // In offscreen mode, returns 0
739 ///
740 /// offscreen_mode_stop_here!();
741 /// assert_ne!(instance_handle, 0);
742 /// ```
743 pub fn instance() -> OpenXRHandleT {
744 unsafe { backend_openxr_get_instance() }
745 }
746
747 /// Type: XrSession. StereoKit’s current session handle, this will be valid after SK.Initialize, but the session may
748 /// not be started quite so early.
749 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR/Session.html>
750 ///
751 /// see also [`backend_openxr_get_session`]
752 ///
753 /// ### Examples
754 /// ```
755 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
756 /// use stereokit_rust::system::BackendOpenXR;
757 ///
758 /// let session_handle = BackendOpenXR::session();
759 ///
760 /// // In XR mode, this would be a valid XrSession handle for OpenXR session operations
761 /// // In offscreen mode, returns 0
762 ///
763 /// offscreen_mode_stop_here!();
764 /// assert_ne!(session_handle, 0);
765 /// ```
766 pub fn session() -> OpenXRHandleT {
767 unsafe { backend_openxr_get_session() }
768 }
769
770 /// Type: XrSpace. StereoKit’s primary coordinate space, valid after SK.Initialize, this will most likely be created
771 /// from XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT or XR_REFERENCE_SPACE_TYPE_LOCAL.
772 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR/Space.html>
773 ///
774 /// see also [`backend_openxr_get_space`]
775 ///
776 /// ### Examples
777 /// ```
778 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
779 /// use stereokit_rust::system::BackendOpenXR;
780 ///
781 /// let space_handle = BackendOpenXR::space();
782 ///
783 /// // In XR mode, this would be a valid XrSpace handle for coordinate transformations
784 /// // In offscreen mode, returns 0
785 ///
786 /// offscreen_mode_stop_here!();
787 /// assert_ne!(space_handle, 0);
788 /// ```
789 pub fn space() -> OpenXRHandleT {
790 unsafe { backend_openxr_get_space() }
791 }
792
793 /// Type: XrSystemId. This is the id of the device StereoKit is currently using! This is the result of calling
794 /// xrGetSystem with XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY.
795 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR/SystemId.html>
796 ///
797 /// see also [`backend_openxr_get_system_id`]
798 ///
799 /// ### Examples
800 /// ```
801 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
802 /// use stereokit_rust::system::BackendOpenXR;
803 ///
804 /// let system_id = BackendOpenXR::system_id();
805 ///
806 /// // In XR mode, this would be a valid XrSystemId for the current XR device
807 /// // In offscreen mode, returns 0
808 ///
809 /// offscreen_mode_stop_here!();
810 /// assert_ne!(system_id, 0);
811 /// ```
812 pub fn system_id() -> OpenXRHandleT {
813 unsafe { backend_openxr_get_system_id() }
814 }
815
816 /// Type: XrTime. This is the OpenXR time for the current frame, and is available after Sk.initialize.
817 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR/Time.html>
818 ///
819 /// see also [`backend_openxr_get_time`]
820 ///
821 /// ### Examples
822 /// ```
823 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
824 /// use stereokit_rust::system::BackendOpenXR;
825 ///
826 /// let current_time = BackendOpenXR::time();
827 ///
828 /// // In offscreen mode, returns 0
829 ///
830 /// offscreen_mode_stop_here!();
831 /// assert_ne!(current_time, 0);
832 /// ```
833 pub fn time() -> i64 {
834 unsafe { backend_openxr_get_time() }
835 }
836
837 /// Tells StereoKit to request only the extensions that are absolutely critical to StereoKit. You can still request
838 /// extensions via OpenXR.RequestExt, and this can be used to opt-in to extensions that StereoKit would normally
839 /// request automatically.
840 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR/UseMinimumExts.html>
841 ///
842 /// see also [`backend_openxr_use_minimum_exts`]
843 pub fn use_minimum_exts(value: bool) {
844 unsafe { backend_openxr_use_minimum_exts(value as Bool32T) }
845 }
846
847 /// This allows you to add XrCompositionLayers to the list that StereoKit submits to xrEndFrame. You must call this
848 /// every frame you wish the layer to be included.
849 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR/AddCompositionLayer.html>
850 /// * `xr_composition_layer_x` - A serializable XrCompositionLayer struct that follows the
851 /// XrCompositionLayerBaseHeader data pattern.
852 /// * `sort_order` - An sort order value for sorting with other composition layers in the list. The primary
853 /// projection layer that StereoKit renders to is at 0, -1 would be before it, and +1 would be after.
854 ///
855 /// see also [`backend_openxr_composition_layer`]
856 ///
857 /// ### Examples
858 /// ```
859 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
860 /// use stereokit_rust::system::BackendOpenXR;
861 ///
862 /// // Only works in XR mode but offscreen accept it.
863 /// use openxr_sys as oxr;
864 ///
865 /// // Create projection views for left eye only
866 /// let mut projection_views = [
867 /// oxr::CompositionLayerProjectionView {
868 /// ty: oxr::StructureType::COMPOSITION_LAYER_PROJECTION_VIEW,
869 /// next: std::ptr::null(),
870 /// sub_image: oxr::SwapchainSubImage {
871 /// swapchain: oxr::Swapchain::from_raw(0), // Should be valid swapchain
872 /// image_rect: oxr::Rect2Di {
873 /// offset: oxr::Offset2Di { x: 0, y: 0 },
874 /// extent: oxr::Extent2Di { width: 1024, height: 1024 },
875 /// },
876 /// image_array_index: 0,
877 /// },
878 /// pose: oxr::Posef {
879 /// orientation: oxr::Quaternionf { x: 0.0, y: 0.0, z: 0.0, w: 1.0 },
880 /// position: oxr::Vector3f { x: -0.032, y: 0.0, z: 0.0 }, // Left eye offset
881 /// },
882 /// fov: oxr::Fovf {
883 /// angle_left: -0.7853981, // -45 degrees
884 /// angle_right: 0.7853981, // 45 degrees
885 /// angle_up: 0.7853981, // 45 degrees
886 /// angle_down: -0.7853981, // -45 degrees
887 /// },
888 /// }
889 /// ];
890 ///
891 /// let mut composition_layer = oxr::CompositionLayerProjection {
892 /// ty: oxr::StructureType::COMPOSITION_LAYER_PROJECTION,
893 /// next: std::ptr::null(),
894 /// layer_flags: oxr::CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA,
895 /// space: oxr::Space::from_raw(BackendOpenXR::space()),
896 /// view_count: 1,
897 /// views: projection_views.as_ptr(),
898 /// };
899 ///
900 /// test_steps!( // !!!! Get a proper main loop !!!!
901 /// BackendOpenXR::add_composition_layer(&mut composition_layer, 0);
902 /// );
903 /// ```
904 pub fn add_composition_layer<T>(xr_composition_layer_x: &mut T, sort_order: i32) {
905 let size = size_of::<T>();
906 let ptr = xr_composition_layer_x as *mut _ as *mut c_void;
907 unsafe { backend_openxr_composition_layer(ptr, size as i32, sort_order) }
908 }
909
910 /// This adds an item to the chain of objects submitted to StereoKit’s xrEndFrame call!
911 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR/AddEndFrameChain.html>
912 /// * `xr_base_header` - An OpenXR object that will be chained into the xrEndFrame call.
913 ///
914 /// see also [`backend_openxr_end_frame_chain`]
915 ///
916 /// ### Examples
917 /// ```
918 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
919 /// use stereokit_rust::system::BackendOpenXR;
920 ///
921 /// // Only works in XR mode but offscreen accept it.
922 /// use openxr_sys as oxr;
923 /// let mut frame_end_info = oxr::FrameEndInfo {
924 /// ty: oxr::StructureType::FRAME_END_INFO,
925 /// next: std::ptr::null(),
926 /// display_time: oxr::Time::from_nanos(BackendOpenXR::time()),
927 /// environment_blend_mode: oxr::EnvironmentBlendMode::OPAQUE,
928 /// layer_count: 0,
929 /// layers: std::ptr::null_mut(), // Should be filled with valid layers
930 ///
931 /// };
932 ///
933 /// test_steps!( // !!!! Get a proper main loop !!!!
934 /// BackendOpenXR::add_end_frame_chain(&mut frame_end_info);
935 /// );
936 /// ```
937 pub fn add_end_frame_chain<T>(xr_base_header: &mut T) {
938 let size = size_of::<T>();
939 let ptr = xr_base_header as *mut _ as *mut c_void;
940 unsafe { backend_openxr_end_frame_chain(ptr, size as i32) }
941 }
942
943 /// This ensures that StereoKit does not load a particular extension! StereoKit will behave as if the extension is
944 /// not available on the device. It will also be excluded even if you explicitly requested it with RequestExt
945 /// earlier, or afterwards. This MUST be called before SK.Initialize.
946 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR/ExcludeExt.html>
947 /// * `extension_name` - The extension name as listed in the OpenXR spec. For example: “XR_EXT_hand_tracking”.
948 ///
949 /// see also [`backend_openxr_ext_exclude`]
950 ///
951 /// ### Examples
952 /// ```
953 /// use stereokit_rust::system::BackendOpenXR;
954 ///
955 /// // This must be called before SK.Initialize
956 /// BackendOpenXR::exclude_ext("XR_EXT_hand_tracking");
957 /// BackendOpenXR::exclude_ext("XR_FB_passthrough");
958 ///
959 /// stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
960 ///
961 /// offscreen_mode_stop_here!();
962 /// // Later, we can check if the extensions were not loaded:
963 /// assert_eq!(BackendOpenXR::ext_enabled("XR_EXT_hand_tracking"), false);
964 /// assert_eq!(BackendOpenXR::ext_enabled("XR_FB_passthrough"), false);
965 /// ```
966 pub fn exclude_ext(extension_name: impl AsRef<str>) {
967 let c_str = CString::new(extension_name.as_ref()).unwrap();
968 unsafe { backend_openxr_ext_exclude(c_str.as_ptr()) }
969 }
970
971 /// Requests that OpenXR load a particular extension. This MUST be called before SK.Initialize. Note that it’s
972 /// entirely possible that your extension will not load on certain runtimes, so be sure to check ExtEnabled to see
973 /// if it’s available to use.
974 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR/RequestExt.html>
975 /// * `extension_name` - The extension name as listed in the OpenXR spec. For example: “XR_EXT_hand_tracking”.
976 ///
977 /// see also [`backend_openxr_ext_request`]
978 ///
979 /// ### Examples
980 /// ```
981 /// use stereokit_rust::system::BackendOpenXR;
982 ///
983 /// // This must be called before SK.Initialize
984 /// BackendOpenXR::request_ext("XR_EXT_hand_tracking");
985 /// BackendOpenXR::request_ext("XR_the_ext_that_does_not_exist");
986 ///
987 /// stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
988 ///
989 /// offscreen_mode_stop_here!();
990 /// // Later, check if extensions were loaded as we can't be sure they are ok for this runtime:
991 /// assert_eq!(BackendOpenXR::ext_enabled("XR_EXT_hand_tracking"), true);
992 /// assert_eq!(BackendOpenXR::ext_enabled("XR_the_ext_that_does_not_exist"), false);
993 /// ```
994 pub fn request_ext(extension_name: impl AsRef<str>) {
995 let c_str = CString::new(extension_name.as_ref()).unwrap();
996 unsafe { backend_openxr_ext_request(c_str.as_ptr()) }
997 }
998
999 /// This tells if an OpenXR extension has been requested and successfully loaded by the runtime. This MUST only be
1000 /// called after SK.Initialize.
1001 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR/ExtEnabled.html>
1002 /// * `extension_name` - The extension name as listed in the OpenXR spec. For example: “XR_EXT_hand_tracking”.
1003 ///
1004 /// see also [`backend_openxr_ext_enabled`]
1005 ///
1006 /// ### Examples
1007 /// ```
1008 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1009 /// use stereokit_rust::system::BackendOpenXR;
1010 ///
1011 /// // Check if an extension is enabled (only works in XR mode)
1012 /// let hand_tracking_enabled = BackendOpenXR::ext_enabled("XR_EXT_hand_tracking");
1013 /// let imaginary_enabled = BackendOpenXR::ext_enabled("XR_the_ext_that_does_not_exist");
1014 ///
1015 /// offscreen_mode_stop_here!();
1016 /// // In offscreen mode, extensions are never enabled
1017 /// assert_eq!(hand_tracking_enabled, true);
1018 /// assert_eq!(imaginary_enabled, false);
1019 /// ```
1020 pub fn ext_enabled(extension_name: impl AsRef<str>) -> bool {
1021 if Backend::xr_type() == BackendXRType::OpenXR {
1022 let c_str = CString::new(extension_name.as_ref()).unwrap();
1023 unsafe { backend_openxr_ext_enabled(c_str.as_ptr()) != 0 }
1024 } else {
1025 false
1026 }
1027 }
1028
1029 /// This is basically xrGetInstanceProcAddr from OpenXR, you can use this to get and call functions from an
1030 /// extension you’ve loaded.
1031 /// ### Important Note
1032 /// It is more relevant to use the openxrs crate which contains the function signatures
1033 ///
1034 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR/GetFunctionPtr.html>
1035 /// * `function_name` - The name of the function to get the pointer for.
1036 ///
1037 /// see also [`backend_openxr_get_function`]
1038 ///
1039 /// ### Examples
1040 /// ```
1041 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1042 /// use stereokit_rust::system::BackendOpenXR;
1043 ///
1044 /// // Get a function pointer from an OpenXR extension (only works in XR mode)
1045 /// let hand_tracker_fn = BackendOpenXR::get_function_ptr("xrCreateHandTrackerEXT");
1046 /// let passthrough_fn = BackendOpenXR::get_function_ptr("xrInexistantFunction");
1047 ///
1048 /// offscreen_mode_stop_here!();
1049 /// // In offscreen mode, function pointers are None
1050 /// assert_eq!(hand_tracker_fn.is_some(), true);
1051 /// assert_eq!(passthrough_fn, None);
1052 /// ```
1053 pub fn get_function_ptr(function_name: impl AsRef<str>) -> Option<VoidFunction> {
1054 let c_str = CString::new(function_name.as_ref()).unwrap();
1055 unsafe { backend_openxr_get_function(c_str.as_ptr()) }
1056 }
1057
1058 /// This is basically xrGetInstanceProcAddr from OpenXR, you can use this to get and call functions from an
1059 /// extension you’ve loaded.
1060 /// ### Important Note
1061 /// It is more relevant to use the openxrs crate which contains the function signatures
1062 ///
1063 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR/GetFunctionPtr.html>
1064 /// * `function_name` - The name of the function to get the pointer for.
1065 ///
1066 /// see also [`backend_openxr_get_function`]
1067 ///
1068 /// ### Examples
1069 /// ```
1070 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1071 /// use stereokit_rust::system::BackendOpenXR;
1072 ///
1073 /// // Get a typed function pointer from an OpenXR extension (only works in XR mode)
1074 /// let hand_tracker_fn: Option<unsafe extern "C" fn()> = BackendOpenXR::get_function("xrCreateHandTrackerEXT");
1075 ///
1076 /// offscreen_mode_stop_here!();
1077 /// // In offscreen mode, function pointers are None
1078 /// assert_eq!(hand_tracker_fn.is_some(), true);
1079 /// ```
1080 pub fn get_function<T>(function_name: impl AsRef<str>) -> Option<T> {
1081 let c_str = CString::new(function_name.as_ref()).unwrap();
1082 let function = unsafe { backend_openxr_get_function(c_str.as_ptr()) };
1083 unsafe { transmute_copy(&function) }
1084 }
1085
1086 /// This sets a scaling value for joints provided by the articulated hand extension. Some systems just don’t seem to
1087 /// get their joint sizes right!
1088 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenXR/SetHandJointScale.html>
1089 /// * `joint_scale_factor` - 1 being the default value, 2 being twice as large as normal, and 0.5 being half as big
1090 /// as normal.
1091 ///
1092 /// see also [`backend_openxr_set_hand_joint_scale`]
1093 ///
1094 /// ### Example
1095 /// ```
1096 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1097 /// use stereokit_rust::system::{Backend, BackendOpenXR, BackendXRType};
1098 ///
1099 /// // Adjust hand joint scaling (only effective in XR mode with hand tracking)
1100 /// BackendOpenXR::set_hand_joint_scale(1.2); // Make joints 20% larger
1101 ///
1102 /// // Or make them smaller
1103 /// BackendOpenXR::set_hand_joint_scale(0.8); // Make joints 20% smaller
1104 ///
1105 /// // Reset to default
1106 /// BackendOpenXR::set_hand_joint_scale(1.0);
1107 /// ```
1108 pub fn set_hand_joint_scale(joint_scale_factor: f32) {
1109 unsafe { backend_openxr_set_hand_joint_scale(joint_scale_factor) }
1110 }
1111}
1112
1113/// This class contains variables that may be useful for interop with the Android operating system, or other Android
1114/// libraries.
1115///
1116/// see also `SkInfo::get_android_app`
1117/// <https://stereokit.net/Pages/StereoKit/Backend.Android.html>
1118///
1119/// ### Examples
1120/// ```
1121/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1122/// use stereokit_rust::system::BackendAndroid;
1123///
1124/// let activity = BackendAndroid::activity();
1125/// let java_vm = BackendAndroid::java_vm();
1126/// let jni_environment = BackendAndroid::jni_environment();
1127///
1128/// // These are results for a non Android environment:
1129/// assert_eq!(activity, std::ptr::null_mut());
1130/// assert_eq!(java_vm, std::ptr::null_mut());
1131/// assert_eq!(jni_environment, std::ptr::null_mut());
1132/// ```
1133pub struct BackendAndroid;
1134
1135impl BackendAndroid {
1136 /// This is the jobject activity that StereoKit uses on Android. This is only valid after Sk.initialize, on Android
1137 /// systems.
1138 /// <https://stereokit.net/Pages/StereoKit/Backend.Android/Activity.html>
1139 ///
1140 /// see also [`backend_android_get_activity`]
1141 pub fn activity() -> *mut c_void {
1142 unsafe { backend_android_get_activity() }
1143 }
1144
1145 /// This is the JavaVM* object that StereoKit uses on Android. This is only valid after Sk.initialize, on Android
1146 /// systems.
1147 /// <https://stereokit.net/Pages/StereoKit/Backend.Android/JavaVM.html>
1148 ///
1149 /// see also [`backend_android_get_java_vm`]
1150 pub fn java_vm() -> *mut c_void {
1151 unsafe { backend_android_get_java_vm() }
1152 }
1153
1154 /// This is the JNIEnv* object that StereoKit uses on Android. This is only valid after Sk.initialize, on Android
1155 /// systems.
1156 /// <https://stereokit.net/Pages/StereoKit/Backend.Android/JNIEnvironment.html>
1157 ///
1158 /// see also [`backend_android_get_jni_env`]
1159 pub fn jni_environment() -> *mut c_void {
1160 unsafe { backend_android_get_jni_env() }
1161 }
1162}
1163
1164/// When using Direct3D11 for rendering, this contains a number of variables that may be useful for doing advanced
1165/// rendering tasks. This is the default rendering backend on Windows.
1166/// <https://stereokit.net/Pages/StereoKit/Backend.D3D11.html>
1167///
1168/// ### Examples
1169/// ```
1170/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1171/// use stereokit_rust::system::BackendD3D11;
1172/// let d3d_context = BackendD3D11::d3d_context();
1173/// let d3d_device = BackendD3D11::d3d_device();
1174///
1175/// if cfg!(target_os = "windows") {
1176/// // These are results for a D3D11 environment:
1177/// assert_eq!(d3d_context, std::ptr::null_mut());
1178/// assert_ne!(d3d_device, std::ptr::null_mut());
1179/// } else {
1180/// // These are results for a non D3D11 environment:
1181/// assert_eq!(d3d_context, std::ptr::null_mut());
1182/// assert_eq!(d3d_device, std::ptr::null_mut());
1183/// }
1184/// ```
1185pub struct BackendD3D11;
1186
1187impl BackendD3D11 {
1188 /// This is the main ID3D11DeviceContext* StereoKit uses for rendering.
1189 /// <https://stereokit.net/Pages/StereoKit/Backend.D3D11/D3DContext.html>
1190 ///
1191 /// see also [`backend_d3d11_get_d3d_context`]
1192 pub fn d3d_context() -> *mut c_void {
1193 unsafe { backend_d3d11_get_d3d_context() }
1194 }
1195
1196 /// This is the main ID3D11Device* StereoKit uses for rendering.
1197 /// <https://stereokit.net/Pages/StereoKit/Backend.D3D11/D3DDevice.html>
1198 ///
1199 /// see also [`backend_d3d11_get_d3d_device`]
1200 pub fn d3d_device() -> *mut c_void {
1201 unsafe { backend_d3d11_get_d3d_device() }
1202 }
1203}
1204
1205/// When using OpenGL with the WGL loader for rendering, this contains a number of variables that may be useful for
1206/// doing advanced rendering tasks. This is Windows only, and requires gloabally defining SKG_FORCE_OPENGL when building
1207/// the core StereoKitC library.
1208/// <https://stereokit.net/Pages/StereoKit/Backend.OpenGL_WGL.html>
1209///
1210/// ### Examples
1211/// ```
1212/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1213/// use stereokit_rust::system::BackendOpenGLWGL;
1214/// let hdc = BackendOpenGLWGL::hdc();
1215/// let hglrc = BackendOpenGLWGL::hglrc();
1216///
1217/// // These are results for a non OpenGLWGL environment:
1218/// assert_eq!(hdc, std::ptr::null_mut());
1219/// assert_eq!(hglrc, std::ptr::null_mut());
1220/// ```
1221pub struct BackendOpenGLWGL;
1222
1223impl BackendOpenGLWGL {
1224 /// This is the Handle to Device Context HDC StereoKit uses with wglMakeCurrent.
1225 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenGL_WGL/HDC.html>
1226 ///
1227 /// see also [`backend_opengl_wgl_get_hdc`]
1228 pub fn hdc() -> *mut c_void {
1229 unsafe { backend_opengl_wgl_get_hdc() }
1230 }
1231
1232 /// This is the Handle to an OpenGL Rendering Context HGLRC StereoKit uses with wglMakeCurrent.
1233 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenGL_WGL/HGLRC.html>
1234 ///
1235 /// see also [`backend_opengl_wgl_get_hglrc`]
1236 pub fn hglrc() -> *mut c_void {
1237 unsafe { backend_opengl_wgl_get_hglrc() }
1238 }
1239}
1240
1241/// When using OpenGL ES with the EGL loader for rendering, this contains a number of variables that may be useful for
1242/// doing advanced rendering tasks. This is the default rendering backend for Android, and Linux builds can be
1243/// configured to use this with the SK_LINUX_EGL cmake option when building the core StereoKitC library.
1244/// <https://stereokit.net/Pages/StereoKit/Backend.OpenGLES_EGL.html>
1245///
1246/// ### Examples
1247/// ```
1248/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1249/// use stereokit_rust::system::BackendOpenGLESEGL;
1250///
1251/// if cfg!(target_os = "linux") {
1252/// // These are results for a OpenGLESEGL environment:
1253///
1254/// let context = BackendOpenGLESEGL::context();
1255/// assert_ne!(context, std::ptr::null_mut());
1256///
1257/// let display = BackendOpenGLESEGL::display();
1258/// assert_ne!(display, std::ptr::null_mut());
1259/// }
1260/// ```
1261pub struct BackendOpenGLESEGL;
1262
1263impl BackendOpenGLESEGL {
1264 /// This is the EGLContext StereoKit receives from eglCreateContext.
1265 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenGLES_EGL/Context.html>
1266 ///
1267 /// see also [`backend_opengl_egl_get_context`]
1268 pub fn context() -> *mut c_void {
1269 unsafe { backend_opengl_egl_get_context() }
1270 }
1271
1272 /// This is the EGLDisplay StereoKit receives from eglGetDisplay
1273 /// <https://stereokit.net/Pages/StereoKit/Backend.OpenGLES_EGL/Display.html>
1274 ///
1275 /// see also [`backend_opengl_egl_get_display`]
1276 pub fn display() -> *mut c_void {
1277 unsafe { backend_opengl_egl_get_display() }
1278 }
1279}
1280
1281/// When used with a hierarchy modifying function that will push/pop items onto a
1282/// stack, this can be used to change the behavior of how parent hierarchy items
1283/// will affect the item being added to the top of the stack.
1284/// <https://stereokit.net/Pages/StereoKit/HierarchyParent.html>
1285///
1286/// see also [`Hierarchy`]
1287#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1288#[repr(u32)]
1289pub enum HierarchyParent {
1290 /// Inheriting is generally the default behavior of a hierarchy stack, the
1291 /// current item will inherit the properties of the parent stack item in some
1292 /// form or another.
1293 Inherit = 0,
1294 /// Ignoring the parent hierarchy stack item will let you skip inheriting
1295 /// anything from the parent item. The new item remains exactly as provided.
1296 Ignore = 1,
1297}
1298
1299/// This class represents a stack of transform matrices that build up a transform hierarchy! This can be used like an
1300/// object-less parent-child system, where you push a parent’s transform onto the stack, render child objects relative
1301/// to that parent transform and then pop it off the stack.
1302///
1303/// Performance note: if any matrices are on the hierarchy stack, any render will cause a matrix multiplication to
1304/// occur! So if you have a collection of objects with their transforms baked and cached into matrices for performance
1305/// reasons, you’ll want to ensure there are no matrices in the hierarchy stack, or that the hierarchy is disabled!
1306/// It’ll save you a matrix multiplication in that case :)
1307/// <https://stereokit.net/Pages/StereoKit/Hierarchy.html>
1308///
1309/// ### Examples
1310/// ```
1311/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1312/// use stereokit_rust::{maths::Matrix, system::{Hierarchy, HierarchyParent}, mesh::Mesh,
1313/// material::Material, util::named_colors};
1314///
1315/// let sphere = Mesh::generate_sphere(0.2, None);
1316/// let material = Material::pbr();
1317/// let transform = Matrix::t([0.4, 0.4, 0.4]);
1318///
1319/// filename_scr = "screenshots/hierarchy.jpeg";
1320/// test_screenshot!( // !!!! Get a proper main loop !!!!
1321/// sphere.draw(token, &material, transform, None, None);
1322///
1323/// assert!(Hierarchy::is_enabled(&token));
1324///
1325/// Hierarchy::push(token, Matrix::t([0.0, -0.5, -0.5]), None);
1326/// sphere.draw(token, &material, transform, Some(named_colors::RED.into()), None);
1327/// assert_eq!(Hierarchy::to_local_point(&token, [0.4, 0.4, 0.4]), [0.4, 0.9, 0.9].into());
1328/// assert_eq!(Hierarchy::to_world_point(&token, [0.4, 0.9, 0.9]), [0.4, 0.4, 0.4].into());
1329/// Hierarchy::pop(token);
1330///
1331/// Hierarchy::push(token, Matrix::t([-0.5, -0.5, 0.25]), Some(HierarchyParent::Ignore));
1332/// sphere.draw(token, &material, transform, Some(named_colors::GREEN.into()), None);
1333/// assert_eq!(Hierarchy::to_local_point(&token, [0.4, 0.4, 0.4]), [0.9, 0.9, 0.15].into());
1334/// assert_eq!(Hierarchy::to_world_point(&token, [0.9, 0.9, 0.15]), [0.4, 0.4, 0.4].into());
1335///
1336/// Hierarchy::enabled(token, false);
1337/// sphere.draw(token, &material, Matrix::IDENTITY, Some(named_colors::BLUE.into()), None);
1338/// assert_eq!(Hierarchy::to_local_point(&token, [0.4, 0.4, 0.4]), [0.4, 0.4, 0.4].into());
1339/// Hierarchy::enabled(token, true);
1340/// Hierarchy::pop(&token);
1341/// );
1342/// ```
1343/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/hierarchy.jpeg" alt="screenshot" width="200">
1344pub struct Hierarchy;
1345
1346unsafe extern "C" {
1347 pub fn hierarchy_push(transform: *const Matrix, parent_behavior: HierarchyParent);
1348 pub fn hierarchy_push_pose(pose: *const Pose, parent_behavior: HierarchyParent);
1349 pub fn hierarchy_pop();
1350 pub fn hierarchy_set_enabled(enabled: Bool32T);
1351 pub fn hierarchy_is_enabled() -> Bool32T;
1352 pub fn hierarchy_to_world() -> *const Matrix;
1353 pub fn hierarchy_to_local() -> *const Matrix;
1354 pub fn hierarchy_to_local_point(world_pt: *const Vec3) -> Vec3;
1355 pub fn hierarchy_to_local_direction(world_dir: *const Vec3) -> Vec3;
1356 pub fn hierarchy_to_local_rotation(world_orientation: *const Quat) -> Quat;
1357 pub fn hierarchy_to_local_pose(world_pose: *const Pose) -> Pose;
1358 pub fn hierarchy_to_local_ray(world_ray: Ray) -> Ray;
1359 pub fn hierarchy_to_world_point(local_pt: *const Vec3) -> Vec3;
1360 pub fn hierarchy_to_world_direction(local_dir: *const Vec3) -> Vec3;
1361 pub fn hierarchy_to_world_rotation(local_orientation: *const Quat) -> Quat;
1362 pub fn hierarchy_to_world_pose(local_pose: *const Pose) -> Pose;
1363 pub fn hierarchy_to_world_ray(local_ray: Ray) -> Ray;
1364}
1365
1366impl Hierarchy {
1367 /// This is enabled by default. Disabling this will cause any draw call to ignore any Matrices that are on the
1368 /// Hierarchy stack.
1369 /// <https://stereokit.net/Pages/StereoKit/Hierarchy/Enabled.html>
1370 ///
1371 /// see also [`hierarchy_set_enabled`] [`Hierarchy::is_enabled`]
1372 pub fn enabled(_token: &MainThreadToken, enable: bool) {
1373 unsafe { hierarchy_set_enabled(enable as Bool32T) }
1374 }
1375
1376 /// This is enabled by default. Disabling this will cause any draw call to ignore any Matrices that are on the
1377 /// Hierarchy stack.
1378 /// <https://stereokit.net/Pages/StereoKit/Hierarchy/Enabled.html>
1379 ///
1380 /// see also [hierarchy_is_enabled] [`Hierarchy::enabled`]
1381 pub fn is_enabled(_token: &MainThreadToken) -> bool {
1382 unsafe { hierarchy_is_enabled() != 0 }
1383 }
1384
1385 /// Removes the top Matrix from the stack!
1386 /// <https://stereokit.net/Pages/StereoKit/Hierarchy/Pop.html>
1387 ///
1388 /// see also [`hierarchy_pop`] [`Hierarchy::push`]
1389 pub fn pop(_token: &MainThreadToken) {
1390 unsafe { hierarchy_pop() }
1391 }
1392
1393 /// Pushes a transform Matrix (eventually a Pose) onto the stack, and combines it with the Matrix below it. Any draw
1394 /// operation’s Matrix will now be combined with this Matrix to make it relative to the current hierarchy. Use
1395 /// Hierarchy.pop to remove it from the Hierarchy stack! All Push calls must have an accompanying Pop call.
1396 /// <https://stereokit.net/Pages/StereoKit/Hierarchy/Push.html>
1397 /// * `parent_behavior` - This determines how this matrix combines with the parent matrix below it. Normal behavior
1398 /// is to "inherit" the parent matrix, but there are cases where you may wish to entirely ignore the parent
1399 /// transform. For example, if you're in UI space, and wish to do some world space rendering. If None, has default
1400 /// value "Inherit"
1401 ///
1402 /// see also [`hierarchy_push`] [`Hierarchy::pop`]
1403 /// ### Examples
1404 /// ```
1405 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1406 /// use stereokit_rust::{maths::Matrix, system::{Hierarchy, HierarchyParent}};
1407 ///
1408 /// test_steps! { // !!!! Get a proper main loop !!!!
1409 /// Hierarchy::push(token, Matrix::t([0.0, -0.5, -0.5]), None);
1410 /// assert_eq!(Hierarchy::to_local_point(token, [0.4, 0.4, 0.4]), [0.4, 0.9, 0.9].into());
1411 /// assert_eq!(Hierarchy::to_world_point(token, [0.4, 0.9, 0.9]), [0.4, 0.4, 0.4].into());
1412 /// Hierarchy::pop(token);
1413 /// }
1414 /// ```
1415 pub fn push<M: Into<Matrix>>(_token: &MainThreadToken, transform: M, parent_behavior: Option<HierarchyParent>) {
1416 let parent_behavior = parent_behavior.unwrap_or(HierarchyParent::Inherit);
1417 unsafe { hierarchy_push(&transform.into(), parent_behavior) }
1418 }
1419
1420 /// Converts a world space point into the local space of the current Hierarchy stack!
1421 /// <https://stereokit.net/Pages/StereoKit/Hierarchy/ToLocal.html>
1422 /// * `world_point` - A point in world space.
1423 ///
1424 /// Returns the provided point now in local hierarchy space
1425 /// see also [`hierarchy_to_local_point`] [`Hierarchy::to_world_point`]
1426 /// ### Examples
1427 /// ```
1428 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1429 /// use stereokit_rust::{maths::Pose, system::{Hierarchy, HierarchyParent}};
1430 ///
1431 /// test_steps! { // !!!! Get a proper main loop !!!!
1432 /// Hierarchy::push(token, Pose::new([0.0, -0.5, -0.5], None), None);
1433 /// assert_eq!(Hierarchy::to_local_point(token, [0.4, 0.4, 0.4]), [0.4, 0.9, 0.9].into());
1434 /// assert_eq!(Hierarchy::to_world_point(token, [0.4, 0.9, 0.9]), [0.4, 0.4, 0.4].into());
1435 /// Hierarchy::pop(token);
1436 /// }
1437 /// ```
1438 pub fn to_local_point<V: Into<Vec3>>(_token: &MainThreadToken, world_point: V) -> Vec3 {
1439 unsafe { hierarchy_to_local_point(&world_point.into()) }
1440 }
1441
1442 /// Converts a world space rotation into the local space of the current Hierarchy stack!
1443 /// <https://stereokit.net/Pages/StereoKit/Hierarchy/ToLocal.html>
1444 /// * `world_orientation` - A rotation in world space
1445 ///
1446 /// Returns the provided rotation now in local hierarchy space
1447 /// see also [`hierarchy_to_local_rotation`] [`Hierarchy::to_world_rotation`]
1448 /// ### Examples
1449 /// ```
1450 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1451 /// use stereokit_rust::{maths::{Matrix, Quat, Vec3}, system::{Hierarchy, HierarchyParent}};
1452 ///
1453 /// test_steps! { // !!!! Get a proper main loop !!!!
1454 /// Hierarchy::push(token, Matrix::r([0.0, 0.0, 180.0]), None);
1455 /// let local: Vec3 = Hierarchy::to_local_rotation(token, [90.0, 180.0, 0.0])
1456 /// .to_angles_degrees().into();
1457 /// assert_eq!(local, [-90.0, 0.0, 0.0].into());
1458 ///
1459 /// let world: Vec3 = Hierarchy::to_world_rotation(token, [90.0, 180.0, 0.0])
1460 /// .to_angles_degrees().into();
1461 /// assert_eq!(world, [-90.0, 0.0, 0.0].into());
1462 /// Hierarchy::pop(token);
1463 /// }
1464 /// ```
1465 pub fn to_local_rotation<Q: Into<Quat>>(_token: &MainThreadToken, world_orientation: Q) -> Quat {
1466 unsafe { hierarchy_to_local_rotation(&world_orientation.into()) }
1467 }
1468
1469 /// Converts a world pose relative to the current hierarchy stack into local space!
1470 /// <https://stereokit.net/Pages/StereoKit/Hierarchy/ToLocal.html>
1471 /// * `world_pose` - A pose in world space.
1472 ///
1473 /// Returns the provided pose now in local hierarchy space!
1474 /// see also [`hierarchy_to_local_pose`] [Hierarchy::to_local_pose]
1475 /// ### Examples
1476 /// ```
1477 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1478 /// use stereokit_rust::{maths::{Matrix, Quat, Pose}, system::{Hierarchy, HierarchyParent}};
1479 ///
1480 /// test_steps! { // !!!! Get a proper main loop !!!!
1481 /// Hierarchy::push(token, Matrix::t([0.0, -0.5, -0.5]), None);
1482 /// let pose = Pose::new([10.0, 20.0, 30.0], None);
1483 /// assert_eq!(Hierarchy::to_local_pose(token, pose), Pose::new([10.0, 20.5, 30.5], None));
1484 /// assert_eq!(Hierarchy::to_world_pose(token, Pose::new([10.0, 20.5, 30.5], None)), pose);
1485 /// Hierarchy::pop(token);
1486 /// }
1487 /// ```
1488 pub fn to_local_pose<P: Into<Pose>>(_token: &MainThreadToken, world_pose: P) -> Pose {
1489 unsafe { hierarchy_to_local_pose(&world_pose.into()) }
1490 }
1491
1492 /// Converts a world ray relative to the current hierarchy stack into local space!
1493 /// <https://stereokit.net/Pages/StereoKit/Hierarchy/ToLocal.html>
1494 /// * `world_ray` - A ray in world space
1495 ///
1496 /// Returns the provided ray now in local hierarchy space
1497 /// see also [`hierarchy_to_local_ray`] [`Hierarchy::to_world_ray`]
1498 /// ### Examples
1499 /// ```
1500 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1501 /// use stereokit_rust::{maths::{Vec3, Matrix, Quat, Ray}, system::{Lines, Hierarchy},
1502 /// util::{named_colors}, mesh::Mesh, material::{Material, Cull}};
1503 ///
1504 /// // Create Meshes
1505 /// let cube = Mesh::generate_cube(Vec3::ONE * 0.8, None);
1506 /// let sphere = Mesh::generate_sphere(1.0, Some(4));
1507 ///
1508 /// let material = Material::pbr().copy();
1509 /// let transform = Matrix::r(Quat::from_angles(40.0, 50.0, 20.0));
1510 /// let inv = transform.get_inverse();
1511 ///
1512 /// let ray = Ray::new([1.0, 2.0, 2.5 ], [-1.0, -2.0, -2.25]);
1513 ///
1514 /// filename_scr = "screenshots/hierarchy_ray.jpeg";
1515 /// test_screenshot!( // !!!! Get a proper main loop !!!!
1516 /// let world_space_ray = Hierarchy::to_world_ray(token, ray);
1517 /// assert_eq!(world_space_ray, ray);
1518 /// Lines::add_ray(token, world_space_ray, 2.2, named_colors::WHITE, None, 0.02);
1519 ///
1520 /// Hierarchy::push(token, transform, None);
1521 /// let local_transform = Matrix::t([-0.1, 0.1, 0.1]);
1522 /// cube.draw(token, &material, local_transform, Some(named_colors::MAGENTA.into()), None);
1523 ///
1524 /// let local_space_ray = Hierarchy::to_local_ray(token, world_space_ray);
1525 /// let mesh_space_ray = local_transform.get_inverse().transform_ray(local_space_ray);
1526 /// let (contact_cube, ind_cube) = cube.intersect( mesh_space_ray, Some(Cull::Back))
1527 /// .expect("Ray should touch cube");
1528 /// assert_eq!(ind_cube, 15);
1529 ///
1530 /// let local_contact_cube = local_transform.transform_point(contact_cube);
1531 /// let transform_contact = Matrix::t_s(local_contact_cube, Vec3::ONE * 0.1);
1532 /// sphere.draw(token, &material, transform_contact, Some(named_colors::YELLOW.into()), None );
1533 /// Hierarchy::pop(token);
1534 /// );
1535 /// ```
1536 /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/hierarchy_ray.jpeg" alt="screenshot" width="200">
1537 pub fn to_local_ray<R: Into<Ray>>(_token: &MainThreadToken, world_ray: R) -> Ray {
1538 unsafe { hierarchy_to_local_ray(world_ray.into()) }
1539 }
1540
1541 /// Converts a world space direction into the local space of the current Hierarchy stack! This excludes the
1542 /// translation component normally applied to vectors, so it’s still a valid direction.
1543 /// <https://stereokit.net/Pages/StereoKit/Hierarchy/ToLocalDirection.html>
1544 /// * `world_direction` - A direction in world space
1545 ///
1546 /// Returns the provided direction now in local hierarchy space
1547 /// see also [`hierarchy_to_local_direction`] [`Hierarchy::to_world_direction`]
1548 /// ### Examples
1549 /// ```
1550 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1551 /// use stereokit_rust::{maths::{Matrix, Vec3}, system::{Hierarchy, HierarchyParent}};
1552 ///
1553 /// test_steps! { // !!!! Get a proper main loop !!!!
1554 /// Hierarchy::push(token, Matrix::r([0.0, 0.0, 180.0]), None);
1555 /// assert_eq!(Hierarchy::to_local_direction(token, [1.0, 0.0, 0.0]), [-1.0, 0.0, 0.0].into());
1556 /// assert_eq!(Hierarchy::to_world_direction(token, [-1.0, 0.0, 0.0]), [1.0, 0.0, 0.0].into());
1557 /// Hierarchy::pop(token);
1558 /// }
1559 /// ```
1560 /// see also [`hierarchy_to_local_direction`] [`hierarchy_to_world_direction`]
1561 pub fn to_local_direction<V: Into<Vec3>>(_token: &MainThreadToken, world_direction: V) -> Vec3 {
1562 unsafe { hierarchy_to_local_direction(&world_direction.into()) }
1563 }
1564
1565 /// Converts a local point relative to the current hierarchy stack into world space!
1566 /// <https://stereokit.net/Pages/StereoKit/Hierarchy/ToWorld.html>
1567 /// * `local_point` - A point in local space
1568 ///
1569 /// Returns the provided point now in world space
1570 ///
1571 /// see also [`hierarchy_to_world_point`]
1572 /// see example in [`Hierarchy::to_local_point`]
1573 pub fn to_world_point<V: Into<Vec3>>(_token: &MainThreadToken, local_point: V) -> Vec3 {
1574 unsafe { hierarchy_to_world_point(&local_point.into()) }
1575 }
1576
1577 /// Converts a local rotation relative to the current hierarchy stack into world space!
1578 /// <https://stereokit.net/Pages/StereoKit/Hierarchy/ToWorld.html>
1579 /// * `local_orientaion` - A rotation in local space
1580 ///
1581 /// Returns the provided rotation now in world space
1582 ///
1583 /// see also [`hierarchy_to_world_rotation`]
1584 /// see example in [`Hierarchy::to_local_rotation`]
1585 pub fn to_world_rotation<Q: Into<Quat>>(_token: &MainThreadToken, local_orientation: Q) -> Quat {
1586 unsafe { hierarchy_to_world_rotation(&local_orientation.into()) }
1587 }
1588
1589 /// Converts a local pose relative to the current hierarchy stack into world space!
1590 /// <https://stereokit.net/Pages/StereoKit/Hierarchy/ToWorld.html>
1591 /// * `local_pose` - A pose in local space
1592 ///
1593 /// Returns the provided pose now in world space
1594 ///
1595 /// see also [`hierarchy_to_world_pose`]
1596 /// see example in [`Hierarchy::to_local_pose`]
1597 pub fn to_world_pose<P: Into<Pose>>(_token: &MainThreadToken, local_pose: P) -> Pose {
1598 unsafe { hierarchy_to_world_pose(&local_pose.into()) }
1599 }
1600
1601 /// Converts a local ray relative to the current hierarchy stack into world space!
1602 /// <https://stereokit.net/Pages/StereoKit/Hierarchy/ToWorld.html>
1603 /// * `local_ray` - A ray in local space
1604 ///
1605 /// Returns the provided ray now in world space
1606 ///
1607 /// see also [`hierarchy_to_world_ray`]
1608 /// see example in [`Hierarchy::to_local_ray`]
1609 pub fn to_world_ray<P: Into<Ray>>(_token: &MainThreadToken, local_ray: P) -> Ray {
1610 unsafe { hierarchy_to_world_ray(local_ray.into()) }
1611 }
1612
1613 /// Converts a local direction relative to the current hierarchy stack into world space! This excludes the
1614 /// translation component normally applied to vectors, so it’s still a valid direction.
1615 /// <https://stereokit.net/Pages/StereoKit/Hierarchy/ToWorldDirection.html>
1616 /// * `local_direction` - A direction in local space
1617 ///
1618 /// Returns the provided direction now in world space
1619 ///
1620 /// see also [`hierarchy_to_world_direction`]
1621 /// see example in [`Hierarchy::to_local_direction`]
1622 pub fn to_world_direction<V: Into<Vec3>>(_token: &MainThreadToken, local_direction: V) -> Vec3 {
1623 unsafe { hierarchy_to_world_direction(&local_direction.into()) }
1624 }
1625}
1626
1627bitflags::bitflags! {
1628/// What type of device is the source of the pointer? This is a bit-flag that can contain some input source family
1629/// information.
1630/// <https://stereokit.net/Pages/StereoKit/InputSource.html>
1631///
1632/// see also [`Pointer`] [`Input`]
1633 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
1634 #[repr(C)]
1635 pub struct InputSource: u32 {
1636 /// Matches with all input sources!
1637 const Any = 2147483647;
1638 /// Zero, this is not a valid input source, and should never be used!
1639 const None = 0;
1640 /// Matches with any hand input source.
1641 const Hand = 1;
1642 /// Matches with left hand input sources.
1643 const HandLeft = 2;
1644 /// Matches with right hand input sources.
1645 const HandRight = 4;
1646 /// Matches with Gaze category input sources.
1647 #[deprecated(since = "0.4.0", note = "Use Input::get_eyes() instead")]
1648 const Gaze = 16;
1649 /// Matches with the head gaze input source.
1650 #[deprecated(since = "0.4.0", note = "Use Input::get_eyes() instead")]
1651 const GazeHead = 32;
1652 /// Matches with the eye gaze input source.
1653 #[deprecated(since = "0.4.0", note = "Use Input::get_eyes() instead")]
1654 const GazeEyes = 64;
1655 /// Matches with mouse cursor simulated gaze as an input source.
1656 #[deprecated(since = "0.4.0", note = "Use Input::get_eyes() instead")]
1657 const GazeCurzor = 128;
1658 /// Matches with any input source that has an activation button!
1659 #[deprecated(since = "0.4.0", note = "Not working any more")]
1660 const CanPress = 256;
1661 }
1662}
1663
1664/// An enum for indicating which hand to use!
1665/// <https://stereokit.net/Pages/StereoKit/Handed.html>
1666///
1667/// see also [Hand] [Controller]
1668#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1669#[repr(u32)]
1670pub enum Handed {
1671 /// Left hand.
1672 Left = 0,
1673 /// Right hand.
1674 Right = 1,
1675 /// The number of hands one generally has, this is much nicer than doing a for loop with ‘2’ as the condition! It’s
1676 /// much clearer when you can loop Hand.Max times instead.
1677 Max = 2,
1678}
1679
1680bitflags::bitflags! {
1681 /// A bit-flag for the current state of a button input.
1682 /// <https://stereokit.net/Pages/StereoKit/BtnState.html>
1683 ///
1684 /// see also [`Input`] [`Ui`]
1685 /// ### Examples
1686 /// ```
1687 /// use stereokit_rust::system::BtnState;
1688 ///
1689 /// let state = BtnState::Active | BtnState::JustActive;
1690 /// assert!(state.contains(BtnState::Active));
1691 /// assert!(state.contains(BtnState::JustActive));
1692 /// assert!(!state.contains(BtnState::JustInactive));
1693 /// assert!(!state.contains(BtnState::Changed));
1694 /// ```
1695 #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd)]
1696 #[repr(C)]
1697 pub struct BtnState: u32 {
1698 /// Is the button currently up, unpressed?
1699 const Inactive = 0;
1700 /// Is the button currently down, pressed?
1701 const Active = 1 << 0;
1702 /// Has the button just been released? Only true for a single frame.
1703 const JustInactive = 1 << 1;
1704 /// Has the button just been pressed? Only true for a single frame.
1705 const JustActive = 1 << 2;
1706 /// Has the button just changed state this frame?
1707 const Changed = Self::JustInactive.bits() | Self::JustActive.bits();
1708 /// Matches with all states!
1709 const Any = 0x7FFFFFFF;
1710 }
1711}
1712
1713/// A collection of extension methods for the BtnState enum that makes bit-field checks a little easier.
1714/// <https://stereokit.net/Pages/StereoKit/BtnStateExtensions.html>
1715impl BtnState {
1716 /// Creates a Button State using the current and previous frame's state! These two states allow us to add the
1717 /// "JustActive" and "JustInactive" bitflags when changes happen.
1718 /// <https://stereokit.net/Pages/StereoKit/BtnState/Make.html>
1719 /// * `was_active` - Was it active previously?
1720 /// * `is_active` - And is it active currently?
1721 ///
1722 /// Returns a bitflag with "Just" events added in!
1723 /// ### Examples
1724 /// ```
1725 /// use stereokit_rust::system::BtnState;
1726 ///
1727 /// // Button was not pressed, now it is pressed
1728 /// let state = BtnState::make(false, true);
1729 /// assert!(state.contains(BtnState::Active));
1730 /// assert!(state.contains(BtnState::JustActive));
1731 /// assert!(!state.contains(BtnState::JustInactive));
1732 ///
1733 /// // Button was pressed, now it is not pressed
1734 /// let state = BtnState::make(true, false);
1735 /// assert!(!state.contains(BtnState::Active));
1736 /// assert!(state.contains(BtnState::JustInactive));
1737 /// assert!(!state.contains(BtnState::JustActive));
1738 ///
1739 /// // Button was pressed, still pressed
1740 /// let state = BtnState::make(true, true);
1741 /// assert!(state.contains(BtnState::Active));
1742 /// assert!(!state.contains(BtnState::JustActive));
1743 /// assert!(!state.contains(BtnState::JustInactive));
1744 /// ```
1745 pub fn make(was_active: bool, is_active: bool) -> BtnState {
1746 let mut result = if is_active { BtnState::Active } else { BtnState::Inactive };
1747
1748 if was_active && !is_active {
1749 result |= BtnState::JustInactive;
1750 }
1751 if is_active && !was_active {
1752 result |= BtnState::JustActive;
1753 }
1754
1755 result
1756 }
1757
1758 /// Is the button pressed?
1759 /// <https://stereokit.net/Pages/StereoKit/BtnStateExtensions/IsActive.html>
1760 pub fn is_active(&self) -> bool {
1761 (*self & BtnState::Active) > BtnState::Inactive
1762 }
1763
1764 /// Has the button just been pressed this frame?
1765 /// <https://stereokit.net/Pages/StereoKit/BtnStateExtensions/IsJustActive.html>
1766 pub fn is_just_active(&self) -> bool {
1767 (*self & BtnState::JustActive) > BtnState::Inactive
1768 }
1769
1770 /// Has the button just been released this frame?
1771 /// <https://stereokit.net/Pages/StereoKit/BtnStateExtensions/IsJustInactive.html>
1772 pub fn is_just_inactive(&self) -> bool {
1773 (*self & BtnState::JustInactive) > BtnState::Inactive
1774 }
1775
1776 /// Was the button either presses or released this frame?
1777 /// <https://stereokit.net/Pages/StereoKit/BtnStateExtensions/IsChanged.html>
1778 pub fn is_changed(&self) -> bool {
1779 (*self & BtnState::Changed) > BtnState::Inactive
1780 }
1781}
1782
1783/// This is the tracking state of a sensory input in the world, like a controller’s position sensor, or a QR code
1784/// identified by a tracking system.
1785/// <https://stereokit.net/Pages/StereoKit/TrackState.html>
1786///
1787/// see also [`Controller`]
1788/// ### Examples
1789/// ```
1790/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1791/// use stereokit_rust::system::{TrackState, Input, Handed};
1792///
1793/// let controller = Input::controller(Handed::Right);
1794///
1795/// assert_eq!(controller.tracked_pos, TrackState::Lost);
1796/// assert_ne!(controller.tracked_pos, TrackState::Inferred);
1797/// assert_ne!(controller.tracked_pos, TrackState::Known);
1798///
1799/// assert_eq!(controller.tracked_rot, TrackState::Lost);
1800/// assert_ne!(controller.tracked_rot, TrackState::Inferred);
1801/// assert_ne!(controller.tracked_rot, TrackState::Known);
1802/// ```
1803#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1804#[repr(u32)]
1805pub enum TrackState {
1806 /// The system has no current knowledge about the state of this input. It may be out of visibility, or possibly just
1807 /// disconnected.
1808 Lost = 0,
1809 /// The system doesn’t know for sure where this is, but it has an educated guess that may be inferred from previous
1810 /// data at a lower quality. For example, a controller may still have accelerometer data after going out of view,
1811 /// which can still be accurate for a short time after losing optical tracking.
1812 Inferred = 1,
1813 /// The system actively knows where this input is. Within the constraints of the relevant hardware’s capabilities,
1814 /// this is as accurate as it gets!
1815 Known = 2,
1816}
1817
1818/// Pointer is an abstraction of a number of different input sources, and a way to surface input events!
1819/// <https://stereokit.net/Pages/StereoKit/Pointer.html>
1820///
1821/// see also [`Input`]
1822/// ### Examples
1823/// ```
1824/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1825/// use stereokit_rust::{system::{Input, InputSource, Pointer, BtnState, Handed, TrackState},
1826/// maths::{Vec3, Quat, Pose, Ray}};
1827///
1828/// // By default we only have the 2 hands.
1829/// assert_eq!(Input::pointer_count(None), 0);
1830/// let pointer = Input::pointer(0, None);
1831///
1832/// assert_eq!(pointer.source, InputSource::None);
1833/// assert_eq!(pointer.state, BtnState::Inactive);
1834/// assert_eq!(pointer.tracked, BtnState::Inactive);
1835/// assert_eq!(pointer.orientation, Quat::ZERO);
1836/// assert_eq!(pointer.ray, Ray::ZERO);
1837/// assert_eq!(pointer.get_pose(), Pose::ZERO);
1838///
1839/// let hand_pointer = Input::pointer(1, Some(InputSource::Hand));
1840/// assert_eq!(hand_pointer.source, InputSource::None);
1841/// assert_eq!(pointer.state, BtnState::Inactive);
1842/// assert_eq!(pointer.tracked, BtnState::Inactive);
1843/// assert_eq!(pointer.orientation, Quat::ZERO);
1844/// assert_eq!(pointer.ray, Ray::ZERO);
1845/// assert_eq!(pointer.get_pose(), Pose::ZERO);
1846/// ```
1847#[derive(Debug, Copy, Clone)]
1848#[repr(C)]
1849pub struct Pointer {
1850 /// What input source did this pointer come from? This is a bit-flag that contains input family and capability
1851 /// information.
1852 pub source: InputSource,
1853 /// Is the pointer source being tracked right now?
1854 pub tracked: BtnState,
1855 /// What is the state of the input source’s ‘button’, if it has one?
1856 pub state: BtnState,
1857 /// A ray in the direction of the pointer.
1858 pub ray: Ray,
1859 /// Orientation of the pointer! Since a Ray has no concept of ‘up’, this can be used to retrieve more orientation
1860 /// information.
1861 pub orientation: Quat,
1862}
1863
1864impl Pointer {
1865 /// Convenience function that turns ray.position and orientation into a Pose.
1866 /// <https://stereokit.net/Pages/StereoKit/Pointer/Pose.html>
1867 pub fn get_pose(&self) -> Pose {
1868 Pose::new(self.ray.position, Some(self.orientation))
1869 }
1870}
1871
1872/// Contains information to represents a joint on the hand.
1873/// <https://stereokit.net/Pages/StereoKit/HandJoint.html>
1874///
1875/// see also [`Input::hand`] [`Hand::get`] [`FingerId`] [`JointId`]
1876/// ### Examples
1877/// ```
1878/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1879/// use stereokit_rust::{system::{Input, Handed, FingerId, JointId},
1880/// maths::{Vec3, Quat, Pose, Ray}};
1881///
1882/// let hand = Input::hand(Handed::Left);
1883/// let index_root = hand.get(FingerId::Index, JointId::Root);
1884/// assert_eq!(index_root.position, Vec3 { x: -0.011, y: -0.038, z: 0.004 });
1885///
1886/// let hand = Input::hand(Handed::Right);
1887/// let index_root = hand.get(FingerId::Index, JointId::Root);
1888/// assert_eq!(index_root.position, Vec3 { x: 0.011, y: -0.038, z: 0.004 });
1889///
1890/// let index_tip = hand.get(FingerId::Index, JointId::Tip);
1891/// assert_eq!(index_tip.position, Vec3 { x: 0.029, y: 0.097, z: -0.041 });
1892/// assert_eq!(index_tip.orientation, Quat { x: -0.193, y: -0.004, z: 0.046, w: -0.98 });
1893/// assert_eq!(index_tip.radius, 0.007);
1894/// ```
1895#[derive(Debug, Copy, Clone, PartialEq)]
1896#[repr(C)]
1897pub struct HandJoint {
1898 /// The center of the joint’s world space location.
1899 pub position: Vec3,
1900 /// The joint’s world space orientation, where Forward points to the next joint down the finger, and Up will point
1901 /// towards the back of the hand. On the left hand, Right will point towards the thumb, and on the right hand, Right
1902 /// will point away from the thumb.
1903 pub orientation: Quat,
1904 /// The distance, in meters, to the surface of the hand from this joint.
1905 pub radius: f32,
1906}
1907
1908/// Index values for each finger! From 0-4, from thumb to little finger.
1909/// <https://stereokit.net/Pages/StereoKit/FingerId.html>
1910///
1911/// see also [`Input::hand`] [`Hand::get`] [`JointId`]
1912#[derive(Debug, Copy, Clone)]
1913#[repr(u32)]
1914pub enum FingerId {
1915 /// Thumb
1916 Thumb = 0,
1917 /// The primary index/pointer finger! Finger 1.
1918 Index = 1,
1919 /// next to the index finger.
1920 Middle = 2,
1921 /// ! What does one do with this finger? I guess… wear rings on it?
1922 Ring = 3,
1923 /// The smallest little finger! AKA, The Pinky.
1924 Little = 4,
1925}
1926
1927/// Here’s where hands get crazy! Technical terms, and watch out for the thumbs!
1928/// <https://stereokit.net/Pages/StereoKit/JointId.html>
1929///
1930/// see also [`Input::hand`] [`Hand::get`] [`FingerId`]
1931#[derive(Debug, Copy, Clone)]
1932#[repr(u32)]
1933pub enum JointId {
1934 /// This is at the base of the hand, right above the wrist. For the thumb, Root and KnuckleMajor have the same
1935 /// value.
1936 Root = 0,
1937 /// Joint 1. These are the knuckles at the top of the palm! For the thumb, Root and KnuckleMajor have the same
1938 /// value.
1939 KnuckleMajor = 1,
1940 /// These are the knuckles in the middle of the finger! First joints on the fingers themselves.
1941 KnuckleMid = 2,
1942 /// The joints right below the fingertip!
1943 KnuckleMinor = 3,
1944 /// The end/tip of each finger!
1945 Tip = 4,
1946}
1947
1948/// This enum provides information about StereoKit’s hand tracking data source. It allows you to distinguish between
1949/// true hand data such as that provided by a Leap Motion Controller, and simulated data that StereoKit provides when
1950/// true hand data is not present.
1951/// <https://stereokit.net/Pages/StereoKit/HandSource.html>
1952///
1953/// see also [`Input::hand_source`]
1954#[derive(Debug, Copy, Clone, PartialEq)]
1955#[repr(u32)]
1956pub enum HandSource {
1957 /// There is currently no source of hand data! This means there are no tracked controllers, or active hand tracking
1958 /// systems. This may happen if the user has hand tracking disabled, and no active controllers.
1959 None = 0,
1960 /// The current hand data is a simulation of hand data rather than true hand data. It is backed by either a
1961 /// controller, or a mouse, and may have a more limited range of motion.
1962 Simulated = 1,
1963 /// This is true hand data which exhibits the full range of motion of a normal hand. It is backed by something like
1964 /// a Leap Motion Controller, or some other optical (or maybe glove?) hand tracking system.
1965 Articulated = 2,
1966 /// This hand data is provided by your use of SK’s override functionality. What properties it exhibits depends on
1967 /// what override data you’re sending to StereoKit!
1968 Overridden = 3,
1969}
1970
1971/// Id of a simulated hand pose, for use with Input.HandSimPoseRemove
1972/// <https://stereokit.net/Pages/StereoKit/HandSimId.html>
1973///
1974/// see also [`Input::hand_sim_pose_add`] [`Input::hand_sim_pose_remove`]
1975pub type HandSimId = i32;
1976
1977#[derive(Debug, Copy, Clone)]
1978#[repr(C)]
1979/// Information about a hand!
1980/// <https://stereokit.net/Pages/StereoKit/Hand.html>
1981///
1982/// see also [`Input::hand`]
1983/// ### Examples
1984/// ```
1985/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1986/// use stereokit_rust::{system::{Hierarchy, Input, Handed, FingerId, JointId}, util::named_colors,
1987/// maths::{Vec3, Quat, Pose, Matrix}, mesh::Mesh, material::Material};
1988///
1989/// let hand = Input::hand(Handed::Left);
1990/// let thumb_tip = hand.get(FingerId::Thumb, JointId::Tip);
1991///
1992/// let sphere = Mesh::generate_sphere(1.0, Some(12));
1993/// let mut material_sphere = Material::pbr().copy();
1994/// let main_transform = Matrix::t_r([0.0, -0.05, 0.88], [0.0, 210.0, 0.0]);
1995///
1996/// filename_scr = "screenshots/hand.jpeg";
1997/// test_screenshot!( // !!!! Get a proper main loop !!!!
1998/// for finger in 0..5 {
1999/// for joint in 0..5 {
2000/// let joint_pose = hand.get_u(finger, joint);
2001/// let transform = Matrix::t_s(joint_pose.position, Vec3::ONE * joint_pose.radius);
2002/// Hierarchy::push(token, main_transform, None);
2003/// sphere.draw(token, &material_sphere, transform, Some(named_colors::BLACK.into()), None);
2004/// Hierarchy::pop(token);
2005/// }
2006/// }
2007/// );
2008/// ```
2009/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/hand.jpeg" alt="screenshot" width="200">
2010///
2011/// ```
2012/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2013/// use stereokit_rust::{system::{Input, Handed, FingerId, JointId, BtnState, Hand, HandJoint},
2014/// maths::{Vec3, Quat, Pose}};
2015///
2016/// let hand = Input::hand(Handed::Left);
2017/// let thumb_tip = hand.get(FingerId::Thumb, JointId::Tip);
2018/// assert_eq!(thumb_tip.position, Vec3 { x: -0.072, y: 0.028, z: -0.055 });
2019///
2020/// let hand = Input::hand(Handed::Right);
2021///
2022/// let thumb_tip = hand.get(FingerId::Thumb, JointId::Tip);
2023/// let thumb_tip2 = hand.get_u(0,4);
2024/// let thumb_tip3 = hand.fingers[FingerId::Thumb as usize][JointId::Tip as usize];
2025/// assert_eq!(thumb_tip.position, Vec3 { x: 0.072, y: 0.028, z: -0.055 });
2026/// assert_eq!(thumb_tip, thumb_tip2);
2027/// assert_eq!(thumb_tip, thumb_tip3);
2028///
2029/// assert_eq!(hand.wrist, Pose::ZERO);
2030/// assert_eq!(hand.palm, Pose::IDENTITY);
2031/// assert_eq!(hand.aim, Pose::ZERO);
2032/// assert_eq!(hand.pinch_pt, Vec3::ZERO);
2033/// assert_eq!(hand.handed, Handed::Right);
2034/// assert_eq!(hand.tracked, BtnState::Inactive);
2035/// assert_eq!(hand.pinch, BtnState::Inactive);
2036/// assert_eq!(hand.grip, BtnState::Inactive);
2037/// assert_eq!(hand.aim_ready, BtnState::Inactive);
2038///
2039/// assert_eq!(hand.is_gripped(), false);
2040/// assert_eq!(hand.is_just_gripped(), false);
2041/// assert_eq!(hand.is_just_ungripped(), false);
2042/// assert_eq!(hand.is_pinched(), false);
2043/// assert_eq!(hand.is_just_pinched(), false);
2044/// assert_eq!(hand.is_just_unpinched(), false);
2045/// assert_eq!(hand.is_tracked(), false);
2046/// assert_eq!(hand.is_just_tracked(), false);
2047/// assert_eq!(hand.is_just_untracked(), false);
2048/// ```
2049pub struct Hand {
2050 /// This is a 2D array with 25 HandJoints. You can get the right joint by finger*5 + joint
2051 pub fingers: [[HandJoint; 5usize]; 5usize],
2052 /// Pose of the wrist. TODO: Not populated right now.
2053 pub wrist: Pose,
2054 /// The position and orientation of the palm! Position is specifically defined as the middle of the middle finger’s
2055 /// root (metacarpal) bone. For orientation, Forward is the direction the flat of the palm is facing, “Iron Man”
2056 /// style. X+ is to the outside of the right hand, and to the inside of the left hand.
2057 pub palm: Pose,
2058 /// A pose an orientation representing where the hand is pointing to. This may be provided by the OpenXR runtime, or
2059 /// be a fallback provided by StereoKit. Typically this starts and the index finger's primary knuckle, and points in
2060 /// the same direction as a line drawn from the shoulder to the knuckle.
2061 pub aim: Pose,
2062 /// This is an approximation of where the center of a ‘pinch’ gesture occurs, and is used internally by StereoKit
2063 /// for some tasks, such as UI. For simulated hands, this position will give you the most stable pinch location
2064 /// possible. For real hands, it’ll be pretty close to the stablest point you’ll get. This is especially important
2065 /// for when the user begins and ends their pinch action, as you’ll often see a lot of extra movement in the fingers
2066 /// then.
2067 pub pinch_pt: Vec3,
2068 /// Is this a right hand, or a left hand?
2069 pub handed: Handed,
2070 /// Is the hand being tracked by the sensors right now?
2071 pub tracked: BtnState,
2072 /// Is the hand making a pinch gesture right now? Finger and thumb together.
2073 pub pinch: BtnState,
2074 /// Is the hand making a grip gesture right now? Fingers next to the palm.
2075 pub grip: BtnState,
2076 /// This is a filter state for when the hand is ready to interact with something at a distance. This often factors
2077 /// into account palm direction, as well as distance from the body, and the current pinch and tracked state.
2078 pub aim_ready: BtnState,
2079 /// This is the size of the hand, calculated by measuring the length of the middle finger! This is calculated by
2080 /// adding the distances between each joint, then adding the joint radius of the root and tip. This value is
2081 /// recalculated at relatively frequent intervals, and can vary by as much as a centimeter.
2082 pub size: f32,
2083 /// What percentage of activation is the pinch gesture right now? Where 0 is a hand in an outstretched resting
2084 /// position, and 1 is fingers touching, within a device error tolerant threshold.
2085 pub pinch_activation: f32,
2086 /// What percentage of activation is the grip gesture right now? Where 0 is a hand in an outstretched resting
2087 /// position, and 1 is ring finger touching the base of the palm, within a device error tolerant threshold.
2088 pub grip_activation: f32,
2089}
2090
2091impl Hand {
2092 /// Returns the joint information of the indicated hand joint! This also includes fingertips as a ‘joint’. This is
2093 /// the same as the [] operator. Note that for thumbs, there are only 4 ‘joints’ in reality, so StereoKit has
2094 /// JointId.Root and JointId.KnuckleMajor as the same pose, so JointId.Tip is still the tip of the thumb!
2095 /// <https://stereokit.net/Pages/StereoKit/Hand/Get.html>
2096 pub fn get(&self, finger: FingerId, joint: JointId) -> HandJoint {
2097 self.fingers[finger as usize][joint as usize]
2098 }
2099
2100 /// Returns the joint information of the indicated hand joint! This also includes fingertips as a ‘joint’. This is
2101 /// the same as the [] operator. Note that for thumbs, there are only 4 ‘joints’ in reality, so StereoKit has
2102 /// JointId.Root and JointId.KnuckleMajor as the same pose, so JointId.Tip is still the tip of the thumb!
2103 /// <https://stereokit.net/Pages/StereoKit/Hand/Get.html>
2104 pub fn get_u(&self, finger: usize, joint: usize) -> HandJoint {
2105 self.fingers[finger][joint]
2106 }
2107
2108 /// Are the fingers currently gripped?
2109 /// <https://stereokit.net/Pages/StereoKit/Hand/IsGripped.html>
2110 pub fn is_gripped(&self) -> bool {
2111 (self.grip & BtnState::Active) > BtnState::Inactive
2112 }
2113
2114 /// Have the fingers just been gripped this frame?
2115 /// <https://stereokit.net/Pages/StereoKit/Hand/IsJustGripped.html>
2116 pub fn is_just_gripped(&self) -> bool {
2117 (self.grip & BtnState::JustActive) > BtnState::Inactive
2118 }
2119
2120 /// Have the fingers just stopped being gripped this frame?
2121 /// <https://stereokit.net/Pages/StereoKit/Hand/IsJustUngripped.html>
2122 pub fn is_just_ungripped(&self) -> bool {
2123 (self.grip & BtnState::JustInactive) > BtnState::Inactive
2124 }
2125
2126 /// Are the fingers currently pinched?
2127 /// <https://stereokit.net/Pages/StereoKit/Hand/IsPinched.html>
2128 pub fn is_pinched(&self) -> bool {
2129 (self.pinch & BtnState::Active) > BtnState::Inactive
2130 }
2131
2132 /// Have the fingers just been pinched this frame?
2133 /// <https://stereokit.net/Pages/StereoKit/Hand/IsJustPinched.html>
2134 pub fn is_just_pinched(&self) -> bool {
2135 (self.pinch & BtnState::JustActive) > BtnState::Inactive
2136 }
2137
2138 /// Have the fingers just stopped being pinched this frame?
2139 /// <https://stereokit.net/Pages/StereoKit/Hand/IsJustUnpinched.html>
2140 pub fn is_just_unpinched(&self) -> bool {
2141 (self.pinch & BtnState::JustInactive) > BtnState::Inactive
2142 }
2143
2144 /// Is the hand being tracked by the sensors right now?
2145 /// <https://stereokit.net/Pages/StereoKit/Hand/IsTracked.html>
2146 pub fn is_tracked(&self) -> bool {
2147 (self.tracked & BtnState::Active) > BtnState::Inactive
2148 }
2149
2150 /// Has the hand just started being tracked this frame?
2151 /// <https://stereokit.net/Pages/StereoKit/Hand/IsJustTracked.html>
2152 pub fn is_just_tracked(&self) -> bool {
2153 (self.tracked & BtnState::JustActive) > BtnState::Inactive
2154 }
2155
2156 /// Has the hand just stopped being tracked this frame?
2157 /// <https://stereokit.net/Pages/StereoKit/Hand/IsJustUntracked.html>
2158 pub fn is_just_untracked(&self) -> bool {
2159 (self.tracked & BtnState::JustInactive) > BtnState::Inactive
2160 }
2161
2162 /// Set the Material used to render the hand! The default material uses an offset of 10 to ensure it gets drawn
2163 /// overtop of other elements.
2164 /// <https://stereokit.net/Pages/StereoKit/Hand/Material.html>
2165 ///
2166 /// see also [`input_hand_material`]
2167 /// ### Examples
2168 /// ```
2169 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2170 /// use stereokit_rust::{system::{Input, Handed, Hand}, material::Material, util::Color128};
2171 ///
2172 /// let mut hand = Input::hand(Handed::Right);
2173 ///
2174 /// let mut material = Material::hand().copy();
2175 /// material.color_tint(Color128::new(0.8, 0.5, 0.1, 1.0));
2176 /// hand.material(&material);
2177 ///
2178 /// assert_ne!(material, Material::hand())
2179 /// ```
2180 pub fn material(&mut self, material: impl AsRef<Material>) -> &mut Self {
2181 unsafe { input_hand_material(self.handed, material.as_ref().0.as_ptr()) }
2182 self
2183 }
2184
2185 /// Sets whether or not StereoKit should render this hand for you. Turn this to false if you’re going to render your
2186 /// own, or don’t need the hand itself to be visible.
2187 /// <https://stereokit.net/Pages/StereoKit/Hand/Visible.html>
2188 ///
2189 /// see also [`input_hand_visible`]
2190 pub fn visible(&mut self, visible: bool) -> &mut Self {
2191 unsafe { input_hand_visible(self.handed, visible as Bool32T) }
2192 self
2193 }
2194}
2195
2196/// Represents an input from an XR headset’s controller!
2197/// <https://stereokit.net/Pages/StereoKit/ControllerKey.html>
2198///
2199/// see also [`Input::hand_sim_pose_add`]
2200#[derive(Debug, Copy, Clone)]
2201#[repr(u32)]
2202pub enum ControllerKey {
2203 /// Doesn’t represent a key, generally means this item has not been set to any particular value!
2204 None_ = 0,
2205 /// The trigger button on the controller, where the user’s index finger typically sits.
2206 Trigger = 1,
2207 /// The grip button on the controller, usually where the fingers that are not the index finger sit.
2208 Grip = 2,
2209 /// This is the lower of the two primary thumb buttons, sometimes labelled X, and sometimes A.
2210 X1 = 3,
2211 /// This is the upper of the two primary thumb buttons, sometimes labelled Y, and sometimes B.
2212 X2 = 4,
2213 /// This is when the thumbstick on the controller is actually pressed. This has nothing to do with the horizontal
2214 /// or vertical movement of the stick.
2215 Stick = 5,
2216 /// This is the menu, or settings button of the controller.
2217 Menu = 6,
2218}
2219
2220/// This represents a physical controller input device! Tracking information, buttons, analog sticks and triggers!
2221/// There’s also a Menu button that’s tracked separately at Input.ContollerMenu.
2222/// <https://stereokit.net/Pages/StereoKit/Controller.html>
2223///
2224/// see also [`Input::controller`]
2225/// ### Examples
2226/// ```
2227/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2228/// use stereokit_rust::{system::{Hierarchy, Input, Handed},
2229/// maths::{Matrix}, model::Model, material::Material};
2230///
2231/// let model_left = Input::get_controller_model(Handed::Left);
2232/// let model_right = Input::get_controller_model(Handed::Right);
2233/// let transform_left = Matrix::t_r([-0.05, 0.0, 0.93], [90.0, 00.0, 0.0]);
2234/// let transform_right = Matrix::t_r([0.05, 0.0, 0.93], [90.0, 00.0, 0.0]);
2235///
2236/// filename_scr = "screenshots/controller.jpeg";
2237/// test_screenshot!( // !!!! Get a proper main loop !!!!
2238/// model_left.draw(token, transform_left, None, None);
2239/// model_right.draw(token, transform_right, None, None);
2240/// );
2241/// ```
2242/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/controller.jpeg" alt="screenshot" width="200">
2243///
2244/// ```
2245/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2246/// use stereokit_rust::{system::{Input, Handed, BtnState, TrackState},
2247/// maths::{Vec2, Vec3, Quat, Pose}};
2248///
2249/// let controller = Input::controller(Handed::Left);
2250///
2251/// let controller = Input::controller(Handed::Right);
2252///
2253/// assert_eq!(controller.pose, Pose::ZERO);
2254/// assert_eq!(controller.palm, Pose::ZERO);
2255/// assert_eq!(controller.aim, Pose::ZERO);
2256/// assert_eq!(controller.tracked, BtnState::Inactive);
2257/// assert_eq!(controller.tracked_pos, TrackState::Lost);
2258/// assert_eq!(controller.tracked_rot, TrackState::Lost);
2259/// assert_eq!(controller.x1, BtnState::Inactive);
2260/// assert_eq!(controller.x2, BtnState::Inactive);
2261/// assert_eq!(controller.trigger, 0.0);
2262/// assert_eq!(controller.grip, 0.0);
2263/// assert_eq!(controller.stick, Vec2::ZERO);
2264///
2265/// assert_eq!(controller.is_just_tracked(), false);
2266/// assert_eq!(controller.is_just_untracked(), false);
2267/// assert_eq!(controller.is_stick_clicked(), false);
2268/// assert_eq!(controller.is_stick_just_clicked(), false);
2269/// assert_eq!(controller.is_tracked(), false);
2270/// assert_eq!(controller.is_x1_just_pressed(), false);
2271/// assert_eq!(controller.is_x1_just_unpressed(), false);
2272/// assert_eq!(controller.is_x1_pressed(), false);
2273/// assert_eq!(controller.is_x2_just_pressed(), false);
2274/// assert_eq!(controller.is_x2_just_unpressed(), false);
2275/// assert_eq!(controller.is_x2_pressed(), false);
2276/// ```
2277#[derive(Debug, Copy, Clone)]
2278#[repr(C)]
2279pub struct Controller {
2280 /// The grip pose of the controller. This approximately represents the center of the hand’s position. Check
2281 /// trackedPos and trackedRot for the current state of the pose data.
2282 pub pose: Pose,
2283 pub palm: Pose,
2284 /// The aim pose of a controller is where the controller ‘points’ from and to. This is great for pointer rays and
2285 /// far interactions.
2286 pub aim: Pose,
2287 /// This tells the current tracking state of this controller overall. If either position or rotation are trackable,
2288 /// then this will report tracked. Typically, positional tracking will be lost first, when the controller goes out
2289 /// of view, and rotational tracking will often remain as long as the controller is still connected. This is a good
2290 /// way to check if the controller is connected to the system at all.
2291 pub tracked: BtnState,
2292 /// This tells the current tracking state of the controller’s position information. This is often the first part of
2293 /// tracking data to go, so it can often be good to account for this on occasions.
2294 pub tracked_pos: TrackState,
2295 /// This tells the current tracking state of the controller’s rotational information.
2296 pub tracked_rot: TrackState,
2297 /// This represents the click state of the controller’s analog stick or directional controller.
2298 pub stick_click: BtnState,
2299 /// The current state of the controller’s X1 button. Depending on the specific hardware, this is the first general
2300 /// purpose button on the controller. For example, on an Oculus Quest Touch controller this would represent ‘X’ on
2301 /// the left controller, and ‘A’ on the right controller.
2302 pub x1: BtnState,
2303 ///The current state of the controller’s X2 button. Depending on the specific hardware, this is the second general
2304 /// purpose button on the controller. For example, on an Oculus Quest Touch controller this would represent ‘Y’ on
2305 /// the left controller, and ‘B’ on the right controller.
2306 pub x2: BtnState,
2307 /// The trigger button at the user’s index finger. These buttons typically have a wide range of activation, so this
2308 /// is provided as a value from 0.0 -> 1.0, where 0 is no interaction, and 1 is full interaction. If a controller
2309 /// has binary activation, this will jump straight from 0 to 1.
2310 pub trigger: f32,
2311 /// The grip button typically sits under the user’s middle finger. These buttons occasionally have a wide range of
2312 /// activation, so this is provided as a value from 0.0 -> 1.0, where 0 is no interaction, and 1 is full
2313 /// interaction. If a controller has binary activation, this will jump straight from 0 to 1.
2314 pub grip: f32,
2315 /// This is the current 2-axis position of the analog stick or equivalent directional controller. This generally
2316 /// ranges from -1 to +1 on each axis. This is a raw input, so dead-zones and similar issues are not accounted for
2317 /// here, unless modified by the OpenXR platform itself.
2318 pub stick: Vec2,
2319}
2320
2321impl Controller {
2322 /// Is the controller’s X1 button currently pressed? Depending on the specific hardware, this is the first
2323 /// general purpose button on the controller. For example, on an Oculus Quest Touch controller this would
2324 /// represent ‘X’ on the left controller, and ‘A’ on the right controller.
2325 /// <https://stereokit.net/Pages/StereoKit/Controller/IsX1Pressed.html>
2326 pub fn is_x1_pressed(&self) -> bool {
2327 (self.x1 & BtnState::Active) > BtnState::Inactive
2328 }
2329
2330 /// Has the controller’s X1 button just been pressed this frame? Depending on the specific hardware, this is the
2331 /// first general purpose button on the controller. For example, on an Oculus Quest Touch controller this would
2332 /// represent ‘X’ on the left controller, and ‘A’ on the right controller.
2333 /// <https://stereokit.net/Pages/StereoKit/Controller/IsX1JustPressed.html>
2334 pub fn is_x1_just_pressed(&self) -> bool {
2335 (self.x1 & BtnState::JustActive) > BtnState::Inactive
2336 }
2337
2338 /// Has the controller’s X1 button just been released this frame? Depending on the specific hardware, this is
2339 /// the first general purpose button on the controller. For example, on an Oculus Quest Touch controller this
2340 /// would represent ‘X’ on the left controller, and ‘A’ on the right controller.
2341 /// <https://stereokit.net/Pages/StereoKit/Controller/IsX1JustUnPressed.html>
2342 pub fn is_x1_just_unpressed(&self) -> bool {
2343 (self.x1 & BtnState::JustInactive) > BtnState::Inactive
2344 }
2345
2346 /// Is the controller’s X2 button currently pressed? Depending on the specific hardware, this is the second
2347 /// general purpose button on the controller. For example, on an Oculus Quest Touch controller this would
2348 /// represent ‘Y’ on the left controller, and ‘B’ on the right controller.
2349 /// <https://stereokit.net/Pages/StereoKit/Controller/IsX2Pressed.html>
2350 pub fn is_x2_pressed(&self) -> bool {
2351 (self.x2 & BtnState::Active) > BtnState::Inactive
2352 }
2353
2354 /// Has the controller’s X2 button just been pressed this frame? Depending on the specific hardware, this is the
2355 /// second general purpose button on the controller. For example, on an Oculus Quest Touch controller this would
2356 /// represent ‘Y’ on the left controller, and ‘B’ on the right controller.
2357 /// <https://stereokit.net/Pages/StereoKit/Controller/IsX2JustPressed.html>
2358 pub fn is_x2_just_pressed(&self) -> bool {
2359 (self.x2 & BtnState::JustActive) > BtnState::Inactive
2360 }
2361
2362 /// Has the controller’s X2 button just been released this frame? Depending on the specific hardware, this is
2363 /// the second general purpose button on the controller. For example, on an Oculus Quest Touch controller this
2364 /// would represent ‘Y’ on the left controller, and ‘B’ on the right controller.
2365 /// <https://stereokit.net/Pages/StereoKit/Controller/IsX2JustUnPressed.html>
2366 pub fn is_x2_just_unpressed(&self) -> bool {
2367 (self.x2 & BtnState::JustInactive) > BtnState::Inactive
2368 }
2369
2370 /// Is the analog stick/directional controller button currently being actively pressed?
2371 /// <https://stereokit.net/Pages/StereoKit/Controller/IsStickClicked.html>
2372 pub fn is_stick_clicked(&self) -> bool {
2373 (self.stick_click & BtnState::Active) > BtnState::Inactive
2374 }
2375
2376 /// Is the analog stick/directional controller button currently being actively pressed?
2377 /// <https://stereokit.net/Pages/StereoKit/Controller/IsStickJustClicked.html>
2378 pub fn is_stick_just_clicked(&self) -> bool {
2379 (self.stick_click & BtnState::JustActive) > BtnState::Inactive
2380 }
2381
2382 /// Has the analog stick/directional controller button just been released this frame?
2383 /// <https://stereokit.net/Pages/StereoKit/Controller/IsStickJustUnclicked.html>
2384 pub fn is_stick_just_unclicked(&self) -> bool {
2385 (self.stick_click & BtnState::JustInactive) > BtnState::Inactive
2386 }
2387
2388 /// Is the controller being tracked by the sensors right now?
2389 /// <https://stereokit.net/Pages/StereoKit/Controller/IsTracked.html>
2390 pub fn is_tracked(&self) -> bool {
2391 (self.tracked & BtnState::Active) > BtnState::Inactive
2392 }
2393
2394 /// Has the controller just started being tracked this frame?
2395 /// <https://stereokit.net/Pages/StereoKit/Controller/IsJustTracked.html>
2396 pub fn is_just_tracked(&self) -> bool {
2397 (self.tracked & BtnState::JustActive) > BtnState::Inactive
2398 }
2399
2400 /// Has the analog stick/directional controller button just been released this frame?
2401 /// <https://stereokit.net/Pages/StereoKit/Controller/IsJustUntracked.html>
2402 pub fn is_just_untracked(&self) -> bool {
2403 (self.tracked & BtnState::JustInactive) > BtnState::Inactive
2404 }
2405}
2406
2407/// This stores information about the mouse! What’s its state, where’s it pointed, do we even have one?
2408/// <https://stereokit.net/Pages/StereoKit/Mouse.html>
2409///
2410/// see also [`Input::get_mouse`]
2411/// ### Examples
2412/// ```
2413/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2414/// use stereokit_rust::{system::Input, maths::{Vec2, Vec3}};
2415///
2416/// let mouse = Input::get_mouse();
2417///
2418/// assert_eq!(mouse.is_available(),false);
2419/// assert_eq!(mouse.pos, Vec2::ZERO);
2420/// assert_eq!(mouse.pos_change, Vec2::ZERO);
2421/// assert_eq!(mouse.scroll, 0.0);
2422/// assert_eq!(mouse.scroll_change, 0.0);
2423///
2424/// assert_eq!(mouse.get_ray().position, Vec3::ZERO);
2425/// // Warning: No ray if the mouse isn't available!
2426/// // assert_eq!(mouse.get_ray().direction, Vec3::new(f32::NAN, f32::NAN, f32::NAN));
2427/// ```
2428#[derive(Debug, Copy, Clone)]
2429#[repr(C)]
2430pub struct Mouse {
2431 /// Is the mouse available to use? Most MR systems likely won’t have a mouse!
2432 pub available: Bool32T,
2433 /// Position of the mouse relative to the window it’s in! This is the number of pixels from the top left corner of
2434 /// the screen.
2435 pub pos: Vec2,
2436 /// How much has the mouse’s position changed in the current frame? Measured in pixels.
2437 pub pos_change: Vec2,
2438 /// What’s the current scroll value for the mouse’s scroll wheel? TODO: Units
2439 pub scroll: f32,
2440 /// How much has the scroll wheel value changed during this frame? TODO: Units
2441 pub scroll_change: f32,
2442}
2443
2444impl Mouse {
2445 /// Ray representing the position and orientation that the current Input::get_mouse() is pointing in.
2446 /// <https://stereokit.net/Pages/StereoKit/Mouse/Ray.html>
2447 ///
2448 /// see also [`ray_from_mouse`]
2449 pub fn get_ray(&self) -> Ray {
2450 let mut out_ray = Ray::default();
2451 unsafe { ray_from_mouse(self.pos, &mut out_ray) };
2452 out_ray
2453 }
2454
2455 /// Is the mouse available ?
2456 /// <https://stereokit.net/Pages/StereoKit/Mouse/Available.html>
2457 pub fn is_available(&self) -> bool {
2458 self.available != 0
2459 }
2460}
2461
2462/// A collection of system key codes, representing keyboard characters and mouse buttons. Based on VK codes.
2463/// <https://stereokit.net/Pages/StereoKit/Key.html>
2464///
2465/// see also [`Input::key`] [`Input::key_inject_press`] [`Input::key_inject_release`] [`Input::hand_sim_pose_add`]
2466#[derive(Debug, Copy, Clone, PartialEq, Eq)]
2467#[repr(u32)]
2468pub enum Key {
2469 None = 0,
2470 MouseLeft = 1,
2471 MouseRight = 2,
2472 MouseCenter = 4,
2473 MouseForward = 5,
2474 MouseBack = 6,
2475 Backspace = 8,
2476 Tab = 9,
2477 Return = 13,
2478 Shift = 16,
2479 Ctrl = 17,
2480 Alt = 18,
2481 CapsLock = 20,
2482 Esc = 27,
2483 Space = 32,
2484 End = 35,
2485 Home = 36,
2486 Left = 37,
2487 Right = 39,
2488 Up = 38,
2489 Down = 40,
2490 PageUp = 33,
2491 PageDown = 34,
2492 PrintScreen = 42,
2493 KeyInsert = 45,
2494 Del = 46,
2495 Key0 = 48,
2496 Key1 = 49,
2497 Key2 = 50,
2498 Key3 = 51,
2499 Key4 = 52,
2500 Key5 = 53,
2501 Key6 = 54,
2502 Key7 = 55,
2503 Key8 = 56,
2504 Key9 = 57,
2505 A = 65,
2506 B = 66,
2507 C = 67,
2508 D = 68,
2509 E = 69,
2510 F = 70,
2511 G = 71,
2512 H = 72,
2513 I = 73,
2514 J = 74,
2515 K = 75,
2516 L = 76,
2517 M = 77,
2518 N = 78,
2519 O = 79,
2520 P = 80,
2521 Q = 81,
2522 R = 82,
2523 S = 83,
2524 T = 84,
2525 U = 85,
2526 V = 86,
2527 W = 87,
2528 X = 88,
2529 Y = 89,
2530 Z = 90,
2531 Numpad0 = 96,
2532 Numpad1 = 97,
2533 Numpad2 = 98,
2534 Numpad3 = 99,
2535 Numpad4 = 100,
2536 Numpad5 = 101,
2537 Numpad6 = 102,
2538 Numpad7 = 103,
2539 Numpad8 = 104,
2540 Numpad9 = 105,
2541 F1 = 112,
2542 F2 = 113,
2543 F3 = 114,
2544 F4 = 115,
2545 F5 = 116,
2546 F6 = 117,
2547 F7 = 118,
2548 F8 = 119,
2549 F9 = 120,
2550 F10 = 121,
2551 F11 = 122,
2552 F12 = 123,
2553 Comma = 188,
2554 Period = 190,
2555 SlashFwd = 191,
2556 SlashBack = 220,
2557 Semicolon = 186,
2558 Apostrophe = 222,
2559 BracketOpen = 219,
2560 BracketClose = 221,
2561 Minus = 189,
2562 Equals = 187,
2563 Backtick = 192,
2564 LCmd = 91,
2565 RCmd = 92,
2566 Multiply = 106,
2567 Add = 107,
2568 Subtract = 109,
2569 Decimal = 110,
2570 Divide = 111,
2571}
2572
2573/// Input from the system come from this class! Hands, eyes, heads, mice and pointers!
2574/// <https://stereokit.net/Pages/StereoKit/Input.html>
2575///
2576/// ### Examples
2577/// ```
2578/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2579/// use stereokit_rust::{system::{Input, InputSource, Handed},
2580/// maths::{Vec2, Vec3, Quat, Pose}};
2581///
2582/// let controller = Input::controller(Handed::Left);
2583/// assert_eq!(controller.is_tracked(), false);
2584///
2585/// let hand = Input::hand(Handed::Right);
2586/// assert_eq!(hand.is_tracked(), false);
2587///
2588/// let head = Input::get_head();
2589/// assert_eq!(head , Pose::IDENTITY);
2590///
2591/// let mouse = Input::get_mouse();
2592/// assert_eq!(mouse.is_available(), false);
2593///
2594/// assert_eq!(Input::pointer_count(None), 0);
2595/// let pointer = Input::pointer(0, Some(InputSource::Hand));
2596/// assert_eq!(pointer.source, InputSource::None);
2597/// ```
2598pub struct Input;
2599
2600unsafe extern "C" {
2601 pub fn input_pointer_count(filter: InputSource) -> i32;
2602 pub fn input_pointer(index: i32, filter: InputSource) -> Pointer;
2603 pub fn input_hand(hand: Handed) -> *const Hand;
2604 pub fn input_hand_override(hand: Handed, in_arr_hand_joints: *const HandJoint);
2605 pub fn input_hand_source(hand: Handed) -> HandSource;
2606 pub fn input_controller(hand: Handed) -> *const Controller;
2607 pub fn input_controller_menu() -> BtnState;
2608 pub fn input_controller_model_set(hand: Handed, model: ModelT);
2609 pub fn input_controller_model_get(hand: Handed) -> ModelT;
2610 pub fn input_head() -> Pose;
2611 pub fn input_eyes() -> Pose;
2612 pub fn input_eyes_tracked() -> BtnState;
2613 pub fn input_mouse() -> *const Mouse;
2614 pub fn input_key(key: Key) -> BtnState;
2615 pub fn input_key_inject_press(key: Key);
2616 pub fn input_key_inject_release(key: Key);
2617 pub fn input_text_consume() -> u32;
2618 pub fn input_text_reset();
2619 pub fn input_text_inject_char(character: u32);
2620 pub fn input_hand_visible(hand: Handed, visible: Bool32T);
2621 // Deprecated: pub fn input_hand_solid(hand: Handed, solid: Bool32T);
2622 pub fn input_hand_material(hand: Handed, material: MaterialT);
2623 pub fn input_get_finger_glow() -> Bool32T;
2624 pub fn input_set_finger_glow(visible: Bool32T);
2625 pub fn input_hand_sim_pose_add(
2626 in_arr_palm_relative_hand_joints_25: *const Pose,
2627 button1: ControllerKey,
2628 and_button2: ControllerKey,
2629 or_hotkey1: Key,
2630 and_hotkey2: Key,
2631 ) -> HandSimId;
2632 pub fn input_hand_sim_pose_remove(id: HandSimId);
2633 pub fn input_hand_sim_pose_clear();
2634
2635 #[deprecated(since = "0.4.0", note = "Not working anymore")]
2636 pub fn input_subscribe(
2637 source: InputSource,
2638 input_event: BtnState,
2639 input_event_callback: Option<
2640 unsafe extern "C" fn(source: InputSource, input_event: BtnState, in_pointer: *const Pointer),
2641 >,
2642 );
2643
2644 #[deprecated(since = "0.4.0", note = "Not working anymore")]
2645 pub fn input_unsubscribe(
2646 source: InputSource,
2647 input_event: BtnState,
2648 input_event_callback: Option<
2649 unsafe extern "C" fn(source: InputSource, input_event: BtnState, in_pointer: *const Pointer),
2650 >,
2651 );
2652
2653 #[deprecated(since = "0.4.0", note = "Not working anymore")]
2654 pub fn input_fire_event(source: InputSource, input_event: BtnState, pointer: *const Pointer);
2655}
2656
2657impl Input {
2658 /// When StereoKit is rendering the input source, this allows you to override the controller Model SK uses. The
2659 /// Model SK uses by default may be provided from the OpenXR runtime depending on extension support, but if not, SK
2660 /// does have a default Model.
2661 /// Setting this to None will restore SK's default.
2662 /// <https://stereokit.net/Pages/StereoKit/Input/ControllerModelSet.html>
2663 /// * `handed` - The hand to assign the Model to.
2664 /// * `model` - The Model to use to represent the controller.
2665 /// None is valid, and will restore SK's default model.
2666 ///
2667 /// see also [`input_controller_model_set`] [`Input::get_controller_model`]
2668 /// ### Examples
2669 /// ```
2670 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2671 /// use stereokit_rust::{system::{Input, Handed}, model::Model};
2672 ///
2673 /// assert_eq!(Input::get_controller_model(Handed::Left).get_id(), "default/model_controller_l");
2674 ///
2675 /// let model_left = Model::from_file("center.glb", None)
2676 /// .expect("mobiles.gltf should be a valid model");
2677 ///
2678 /// Input::set_controller_model(Handed::Left, Some(&model_left));
2679 /// assert_eq!(Input::get_controller_model(Handed::Left).get_id(), "center.glb");
2680 ///
2681 /// Input::set_controller_model(Handed::Left, None);
2682 /// assert_eq!(Input::get_controller_model(Handed::Left).get_id(), "default/model_controller_l");
2683 /// ```
2684 pub fn set_controller_model(handed: Handed, model: Option<&Model>) {
2685 match model {
2686 Some(model) => unsafe { input_controller_model_set(handed, model.0.as_ptr()) },
2687 None => unsafe { input_controller_model_set(handed, null_mut()) },
2688 }
2689 }
2690
2691 /// Gets raw controller input data from the system. Note that not all buttons provided here are guaranteed to be
2692 /// present on the user’s physical controller. Controllers are also not guaranteed to be available on the system,
2693 /// and are never simulated.
2694 /// <https://stereokit.net/Pages/StereoKit/Input/Controller.html>
2695 /// * `handed` - The handedness of the controller to get the state of.
2696 ///
2697 /// Returns a reference to a class that contains state information about the indicated controller.
2698 /// see also [`input_controller`]
2699 /// ### Examples
2700 /// ```
2701 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2702 /// use stereokit_rust::system::{Input, Handed, TrackState};
2703 ///
2704 /// let controller = Input::controller(Handed::Right);
2705 ///
2706 /// assert_eq!(controller.tracked_pos, TrackState::Lost);
2707 /// assert_eq!(controller.tracked_rot, TrackState::Lost);
2708 /// ```
2709 pub fn controller(handed: Handed) -> Controller {
2710 unsafe { *input_controller(handed) }
2711 }
2712
2713 /// This function allows you to artifically insert an input event, simulating any device source and event type you
2714 /// want.
2715 /// <https://stereokit.net/Pages/StereoKit/Input/FireEvent.html>
2716 /// * `event_source` - The event source to simulate, this is a bit flag.
2717 /// * `event_types` - The event type to simulate, this is a bit flag.
2718 /// * `pointer` - The pointer data to pass along with this simulated input event.
2719 ///
2720 /// see also [`input_fire_event`]
2721 /// ### Examples
2722 /// ```
2723 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2724 /// use stereokit_rust::{system::{Input, InputSource, Pointer, BtnState, Handed, TrackState},
2725 /// maths::{Vec3, Quat, Pose, Ray}};
2726 ///
2727 /// let pointer = Input::pointer(0, None);
2728 /// assert_eq!(pointer.source, InputSource::None);
2729 ///
2730 /// test_steps!( // !!!! Get a proper main loop !!!!
2731 /// assert_eq!(pointer.state, BtnState::Inactive);
2732 /// assert_eq!(pointer.tracked, BtnState::Inactive);
2733 /// if iter == 0 {
2734 /// Input::fire_event(InputSource::CanPress, BtnState::Active, &pointer);
2735 /// } else if iter == 1 {
2736 /// Input::fire_event(InputSource::Hand | InputSource::HandLeft, BtnState::JustInactive, &pointer);
2737 /// }
2738 /// );
2739 /// ```
2740 #[deprecated(since = "0.4.0", note = "Not working anymore")]
2741 #[allow(deprecated)]
2742 pub fn fire_event(event_source: InputSource, event_types: BtnState, pointer: &Pointer) {
2743 unsafe { input_fire_event(event_source, event_types, pointer) };
2744 }
2745
2746 /// Retrieves all the information about the user’s hand! StereoKit will always provide hand information, however
2747 /// sometimes that information is simulated, like in the case of a mouse, or controllers.
2748 ///
2749 /// Note that this is a pointer to the hand information, and it’s a good chunk of data, so it’s a good idea to grab
2750 /// it once and keep it around for the frame, or at least function, rather than asking for it again and again each
2751 /// time you want to touch something.
2752 /// <https://stereokit.net/Pages/StereoKit/Input/Hand.html>
2753 /// * `handed` - Do you want the left or the right hand? 0 is left, and 1 is right.
2754 ///
2755 /// Returns a copy of the entire set of hand data!
2756 /// see also [`input_hand`]
2757 /// ### Examples
2758 /// ```
2759 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2760 /// use stereokit_rust::system::{Input, Handed, Hand, HandJoint, FingerId, JointId};
2761 /// use stereokit_rust::maths::{Vec3, Quat, Pose};
2762 ///
2763 /// let hand = Input::hand(Handed::Left);
2764 /// let thumb_tip = hand.get(FingerId::Thumb, JointId::Tip);
2765 /// assert_eq!(thumb_tip.position, Vec3 { x: -0.072, y: 0.028, z: -0.055 });
2766 ///
2767 /// let hand = Input::hand(Handed::Right);
2768 /// let thumb_tip = hand.get(FingerId::Thumb, JointId::Tip);
2769 /// assert_eq!(thumb_tip.position, Vec3 { x: 0.072, y: 0.028, z: -0.055 });
2770 /// ```
2771 pub fn hand(handed: Handed) -> Hand {
2772 unsafe { *input_hand(handed) }
2773 }
2774
2775 /// Clear out the override status from Input::hand_override, and restore the user’s control over it again.
2776 /// <https://stereokit.net/Pages/StereoKit/Input/HandClearOverride.html>
2777 /// * `hand` - Which hand are we clearing the override on?
2778 ///
2779 /// see also [`input_hand_override`]
2780 /// ### Examples
2781 /// ```
2782 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2783 /// use stereokit_rust::{system::{Input, Handed, HandJoint, FingerId, JointId},
2784 /// maths::{Vec3, Quat}};
2785 ///
2786 /// let mut hand_joints = [HandJoint { position: Vec3::ZERO, orientation: Quat::IDENTITY, radius: 0.0 }; 25];
2787 ///
2788 /// Input::hand_override(Handed::Left, &hand_joints);
2789 ///
2790 ///
2791 /// test_steps!( // !!!! Get a proper main loop !!!!
2792 /// if iter == 0 {
2793 /// let hand = Input::hand(Handed::Left);
2794 /// let thumb_tip = hand.get(FingerId::Thumb, JointId::Tip);
2795 /// assert_eq!(thumb_tip.position, Vec3::ZERO);
2796 /// } else if iter == 1 {
2797 /// Input::hand_clear_override(Handed::Left);
2798 /// }
2799 /// );
2800 /// ```
2801 pub fn hand_clear_override(hand: Handed) {
2802 unsafe { input_hand_override(hand, null()) };
2803 }
2804
2805 /// This allows you to completely override the hand’s pose information! It is still treated like the user’s hand,
2806 /// so this is great for simulating input for testing purposes. It will remain overridden until you call
2807 /// Input::hand_clear_override.
2808 /// <https://stereokit.net/Pages/StereoKit/Input/HandOverride.html>
2809 /// * `hand` - Which hand should be overridden?
2810 /// * `joints` - A 2D array of 25 joints that should be used as StereoKit's hand information. See `Hand.fingers`
2811 /// for more information.
2812 ///
2813 /// see also [`input_hand_override`]
2814 /// see example in [`Input::hand_clear_override`]
2815 pub fn hand_override(hand: Handed, joints: &[HandJoint]) {
2816 unsafe { input_hand_override(hand, joints.as_ptr()) };
2817 }
2818
2819 /// Set the Material used to render the hand! The default material uses an offset of 10 to ensure it gets drawn
2820 /// overtop of other elements.
2821 /// <https://stereokit.net/Pages/StereoKit/Input/HandMaterial.html>
2822 /// * `hand` - The hand to assign the Material to. If Handed::Max, this will set the value for both hands.
2823 /// * `material` - The new material. If None, will reset to the default value
2824 ///
2825 /// see also [`input_hand_material`] [`Material::hand`]
2826 /// ### Examples
2827 /// ```
2828 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2829 /// use stereokit_rust::{system::{Input, Handed}, util::named_colors,
2830 /// material::Material};
2831 ///
2832 /// let mut hand_material = Material::hand().copy();
2833 /// hand_material.color_tint(named_colors::YELLOW).id("My_hand_material");
2834 /// Input::hand_material(Handed::Left, Some(hand_material));
2835 ///
2836 /// test_steps! ( // !!!! Get a proper main loop !!!!
2837 /// if iter == 0 {
2838 /// // Of course, Material::hand() is not modified.
2839 /// assert_eq!(Material::hand().get_id(), "default/material_hand");
2840 /// } else if iter == 1 {
2841 /// // The reason why Material::hand() should not be modified:
2842 /// Input::hand_material(Handed::Left, None);
2843 /// }
2844 /// );
2845 /// ```
2846 pub fn hand_material(hand: Handed, material: Option<Material>) {
2847 match material {
2848 Some(material) => unsafe { input_hand_material(hand, material.0.as_ptr()) },
2849 None => unsafe { input_hand_material(hand, null_mut()) },
2850 }
2851 }
2852
2853 /// StereoKit will use controller inputs to simulate an articulated hand. This function allows you to add new
2854 /// simulated poses to different controller or keyboard buttons!
2855 /// <https://stereokit.net/Pages/StereoKit/Input/HandSimPoseAdd.html>
2856 /// * `hand_joints_palm_relative_25` - 25 joint poses, thumb to pinky, and root to tip with two duplicate poses for
2857 /// the thumb root joint. These should be right handed, and relative to the palm joint.
2858 /// * `button1` - Controller button to activate this pose, can/ be None if this is a keyboard only pose.
2859 /// * `and_button2` - Second controller button required to activate this pose. First must also be pressed. Can be
2860 /// None if it's only a single button pose.
2861 /// * `or_hotkey1` - Keyboard key to activate this pose, can be None if this is a controller only pose.
2862 /// * `and_hotkey2` - Second keyboard key required to activatethis pose. First must also be pressed. Can be None if
2863 /// it's only a single key pose.
2864 ///
2865 /// Returns the id of the hand sim pose, so it can be removed later.
2866 /// see also [`input_hand_sim_pose_add`]
2867 /// ### Examples
2868 /// ```
2869 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2870 /// use stereokit_rust::{system::{Input, Handed, HandJoint, FingerId, JointId, ControllerKey, Key},
2871 /// maths::{Vec3, Quat, Pose}};
2872 ///
2873 /// let hand_joints = [Pose::IDENTITY;25];
2874 ///
2875 /// let id = Input::hand_sim_pose_add(&hand_joints, ControllerKey::Trigger, ControllerKey::None_, Key::None, Key::None);
2876 /// assert_eq!(id, 5);
2877 ///
2878 /// let hand = Input::hand(Handed::Left);
2879 ///
2880 /// Input::hand_sim_pose_remove(id);
2881 ///
2882 /// let id = Input::hand_sim_pose_add(&hand_joints, ControllerKey::Grip, ControllerKey::None_, Key::None, Key::None);
2883 /// assert_eq!(id, 6);
2884 ///
2885 /// Input::hand_sim_pose_clear();
2886 ///
2887 /// let id = Input::hand_sim_pose_add(&hand_joints, ControllerKey::X1, ControllerKey::None_, Key::None, Key::None);
2888 /// assert_eq!(id, 7);
2889 /// ```
2890 pub fn hand_sim_pose_add(
2891 hand_joints_palm_relative_25: &[Pose],
2892 button1: ControllerKey,
2893 and_button2: ControllerKey,
2894 or_hotkey1: Key,
2895 and_hotkey2: Key,
2896 ) -> HandSimId {
2897 unsafe {
2898 input_hand_sim_pose_add(
2899 hand_joints_palm_relative_25.as_ptr(),
2900 button1,
2901 and_button2,
2902 or_hotkey1,
2903 and_hotkey2,
2904 )
2905 }
2906 }
2907
2908 /// This clears all registered hand simulation poses, including the ones that StereoKit registers by default!
2909 /// <https://stereokit.net/Pages/StereoKit/Input/HandSimPoseClear.html>
2910 ///
2911 /// see also [`input_hand_sim_pose_clear`]
2912 /// see example in [`Input::hand_sim_pose_add`]
2913 pub fn hand_sim_pose_clear() {
2914 unsafe { input_hand_sim_pose_clear() };
2915 }
2916
2917 /// Lets you remove an existing hand pose.
2918 /// <https://stereokit.net/Pages/StereoKit/Input/HandSimPoseRemove.html>
2919 /// * `id` - Any valid or invalid hand sim pose id.
2920 ///
2921 /// see also [`input_hand_sim_pose_remove`]
2922 /// see example in [`Input::hand_sim_pose_add`]
2923 pub fn hand_sim_pose_remove(id: HandSimId) {
2924 unsafe { input_hand_sim_pose_remove(id) };
2925 }
2926
2927 /// This gets the current source of the hand joints! This allows you to distinguish between fully articulated
2928 /// joints, and simulated hand joints that may not have the same range of mobility. Note that this may change during
2929 /// a session, the user may put down their controllers, automatically switching to hands, or visa versa.
2930 /// <https://stereokit.net/Pages/StereoKit/Input/HandSource.html>
2931 /// * `hand` - Do you want the left or right hand?
2932 ///
2933 /// Returns information about hand tracking data source.
2934 /// see also [`input_hand_source`]
2935 /// ### Examples
2936 /// ```
2937 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2938 /// use stereokit_rust::system::{Input, Handed, HandSource};
2939 ///
2940 /// let hand_source = Input::hand_source(Handed::Left);
2941 /// let hand_source = Input::hand_source(Handed::Right);
2942 ///
2943 /// xr_mode_stop_here!();
2944 /// // These are the expected results for offscreen tests on a PC:
2945 /// assert_eq!(hand_source, HandSource::None);
2946 /// ```
2947 pub fn hand_source(hand: Handed) -> HandSource {
2948 unsafe { input_hand_source(hand) }
2949 }
2950
2951 /// Sets whether or not StereoKit should render the hand for you. Turn this to false if you’re going to render your
2952 /// own, or don’t need the hand itself to be visible.
2953 /// <https://stereokit.net/Pages/StereoKit/Input/HandVisible.html>
2954 /// * `hand` - If Handed.Max, this will set the value for both hands.
2955 /// * `visible` - True, StereoKit renders this. False, it doesn't.
2956 ///
2957 /// see also [`input_hand_visible`]
2958 /// ### Examples
2959 /// ```
2960 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2961 /// use stereokit_rust::system::{Input, Handed, HandSource};
2962 ///
2963 /// Input::hand_visible(Handed::Right, false);
2964 /// Input::hand_visible(Handed::Max, false);
2965 /// Input::hand_visible(Handed::Left, true);
2966 /// ```
2967 pub fn hand_visible(hand: Handed, visible: bool) {
2968 unsafe { input_hand_visible(hand, visible as Bool32T) };
2969 }
2970
2971 /// This controls the visibility of StereoKit's finger glow effect on the UI. When true, SK will fill out global
2972 /// shader variable `sk_fingertip[2]` with the location of the pointer finger's tips. When false, or the hand is
2973 /// untracked, the location will be set to an unlikely faraway position.
2974 /// <https://stereokit.net/Pages/StereoKit/Input/FingerGlow.html>
2975 /// * `visible` - True, StereoKit renders this. False, it doesn't.
2976 ///
2977 /// see also [`input_set_finger_glow`]
2978 /// ### Examples
2979 /// ```
2980 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2981 /// use stereokit_rust::system::{Input, Handed, HandSource};
2982 ///
2983 /// assert_eq!(Input::get_finger_glow(), true);
2984 ///
2985 /// Input::finger_glow(false);
2986 ///
2987 /// assert_eq!(Input::get_finger_glow(), false);
2988 ///
2989 /// Input::finger_glow(true);
2990 /// assert_eq!(Input::get_finger_glow(), true);
2991 /// ```
2992 pub fn finger_glow(visible: bool) {
2993 unsafe { input_set_finger_glow(visible as Bool32T) };
2994 }
2995
2996 /// Keyboard key state! On desktop this is super handy, but even standalone MR devices can have bluetooth keyboards,
2997 /// or even just holographic system keyboards!
2998 /// <https://stereokit.net/Pages/StereoKit/Input/Key.html>
2999 /// * `key` - The key to get the state of. Any key!
3000 ///
3001 /// Returns a BtnState with a number of different bits of info about whether or not the key was pressed or released
3002 /// this frame.
3003 /// see also [`input_key`]
3004 /// ### Examples
3005 /// ```
3006 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3007 /// use stereokit_rust::system::{Input, Key, BtnState};
3008 ///
3009 /// let key_state = Input::key(Key::A);
3010 ///
3011 /// assert_eq!(key_state, BtnState::Inactive);
3012 /// assert_eq!(key_state.is_active(), false);
3013 /// assert_eq!(key_state.is_just_active(), false);
3014 /// assert_eq!(key_state.is_just_inactive(), false);
3015 /// assert_eq!(key_state.is_changed(), false);
3016 /// ```
3017 pub fn key(key: Key) -> BtnState {
3018 unsafe { input_key(key) }
3019 }
3020
3021 /// This will inject a key press event into StereoKit’s input event queue. It will be processed at the start of the
3022 /// next frame, and will be indistinguishable from a physical key press. Remember to release your key as well!
3023 ///
3024 /// This will not submit text to StereoKit’s text queue, and will not show up in places like UI.Input. For that, you
3025 /// must submit a TextInjectChar call.
3026 /// <https://stereokit.net/Pages/StereoKit/Input/KeyInjectPress.html>
3027 /// * `key` - The key to press.
3028 ///
3029 /// see also [`input_key_inject_press`]
3030 /// ### Examples
3031 /// ```
3032 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3033 /// use stereokit_rust::{system::{Input, Key, BtnState}};
3034 ///
3035 ///
3036 /// test_steps!( // !!!! Get a proper main loop !!!!
3037 /// if iter == 0 {
3038 /// assert_eq!(Input::key(Key::A).is_just_active(), false);
3039 /// Input::key_inject_press(Key::A);
3040 /// } else if iter == 1 {
3041 /// assert_eq!(Input::key(Key::A).is_just_active(), true);
3042 /// Input::key_inject_release(Key::A);
3043 /// } else if iter == 2 {
3044 /// Input::key_inject_release(Key::A);
3045 /// assert_eq!(Input::key(Key::A).is_just_inactive(), true);
3046 /// Input::key_inject_press(Key::A);
3047 /// } else if iter == 3 {
3048 /// assert_eq!(Input::key(Key::A).is_active(), true);
3049 /// }
3050 /// );
3051 /// assert_eq!(Input::key(Key::A).is_active(), true);
3052 /// ```
3053 pub fn key_inject_press(key: Key) {
3054 unsafe { input_key_inject_press(key) };
3055 }
3056
3057 /// This will inject a key release event into StereoKit’s input event queue. It will be processed at the start of
3058 /// the next frame, and will be indistinguishable from a physical key release. This should be preceded by a key
3059 /// press!
3060 ///
3061 /// This will not submit text to StereoKit’s text queue, and will not show up in places like UI.Input. For that, you
3062 /// must submit a TextInjectChar call.
3063 /// <https://stereokit.net/Pages/StereoKit/Input/KeyInjectRelease.html>
3064 /// * `key` - The key to release.
3065 ///
3066 /// see also [`input_key_inject_release`]
3067 /// see example [`Input::key_inject_press`]
3068 pub fn key_inject_release(key: Key) {
3069 unsafe { input_key_inject_release(key) };
3070 }
3071
3072 /// This gets the pointer by filter based index.
3073 /// <https://stereokit.net/Pages/StereoKit/Input/Pointer.html>
3074 /// * `index` - Index of the Pointer.
3075 /// * `filter` - Filter used to search for the Pointer. If None has default value of ANY.
3076 ///
3077 /// Returns the Pointer data.
3078 /// see also [`input_pointer`]
3079 /// ### Examples
3080 /// ```
3081 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3082 /// use stereokit_rust::{system::{Input, InputSource, Pointer, BtnState, Handed, TrackState},
3083 /// maths::{Vec3, Quat, Pose, Ray}};
3084 ///
3085 /// // By default we only have the 2 hands.
3086 /// assert_eq!(Input::pointer_count(None), 0);
3087 /// let pointer = Input::pointer(0, None);
3088 ///
3089 /// assert_eq!(pointer.source, InputSource::None);
3090 /// assert_eq!(pointer.state, BtnState::Inactive);
3091 /// assert_eq!(pointer.tracked, BtnState::Inactive);
3092 /// assert_eq!(pointer.orientation, Quat::ZERO);
3093 /// assert_eq!(pointer.ray, Ray::ZERO);
3094 /// assert_eq!(pointer.get_pose(), Pose::ZERO);
3095 /// ```
3096 pub fn pointer(index: i32, filter: Option<InputSource>) -> Pointer {
3097 let filter = filter.unwrap_or(InputSource::Any);
3098 unsafe { input_pointer(index, filter) }
3099 }
3100
3101 /// The number of Pointer inputs that StereoKit is tracking that match the given filter.
3102 /// <https://stereokit.net/Pages/StereoKit/Input/PointerCount.html>
3103 /// * `filter` - You can filter input sources using this bit flat. If None has default value of ANY
3104 ///
3105 /// Returns the number of Pointers StereoKit knows about that matches the given filter.
3106 /// see also [`input_pointer_count`]
3107 /// see example in [`Input::pointer`]
3108 pub fn pointer_count(filter: Option<InputSource>) -> i32 {
3109 let filter = filter.unwrap_or(InputSource::Any);
3110 unsafe { input_pointer_count(filter) }
3111 }
3112
3113 /// Returns the next text character from the list of characters that have been entered this frame! Will return `\0`
3114 /// if there are no more characters left in the list. These are from the system’s text entry system, and so can be
3115 /// unicode, will repeat if their ‘key’ is held down, and could arrive from something like a copy/paste operation.
3116 ///
3117 /// If you wish to reset this function to begin at the start of the read list on the next call, you can call
3118 /// Input::text_reset.
3119 /// <https://stereokit.net/Pages/StereoKit/Input/TextConsume.html>
3120 ///
3121 /// Returns the next character in this frame's list, or '\0' if none remain, or None if the value doesn't
3122 /// match char.
3123 /// see also [`input_text_consume`] [`char::from_u32`]
3124 /// ### Examples
3125 /// ```
3126 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3127 /// use stereokit_rust::system::Input;
3128 ///
3129 /// // Simulate some text input
3130 /// Input::text_inject_char('H');
3131 /// Input::text_inject_char('i');
3132 ///
3133 /// test_steps!( // !!!! Get a proper main loop !!!!
3134 /// if iter == 0 {
3135 /// assert_eq!(Input::text_consume(), Some('H'));
3136 /// assert_eq!(Input::text_consume(), Some('i'));
3137 /// Input::text_inject_char('!');
3138 /// assert_eq!(Input::text_consume(), Some('\0'));
3139 /// } else if iter == 1 {
3140 /// assert_eq!(Input::text_consume(), Some('!'));
3141 /// } else {
3142 /// assert_eq!(Input::text_consume(), Some('\0'));
3143 /// }
3144 /// );
3145 /// ```
3146 pub fn text_consume() -> Option<char> {
3147 char::from_u32(unsafe { input_text_consume() })
3148 }
3149
3150 /// Resets the Input::text_consume read list back to the start. For example, UI.Input will not call text_reset, so
3151 /// it effectively will consume those characters, hiding them from any TextConsume calls following it. If you wanted
3152 /// to check the current frame’s text, but still allow UI.Input to work later on in the frame, you would read
3153 /// everything with TextConsume, and then TextReset afterwards to reset the read list for the following UI.Input.
3154 /// <https://stereokit.net/Pages/StereoKit/Input/TextReset.html>
3155 ///
3156 /// see also [`input_text_reset`]
3157 /// ### Examples
3158 /// ```
3159 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3160 /// use stereokit_rust::system::Input;
3161 ///
3162 /// // Simulate some text input
3163 /// Input::text_inject_char('H');
3164 /// Input::text_inject_char('i');
3165 ///
3166 /// test_steps!( // !!!! Get a proper main loop !!!!
3167 /// if iter == 0 {
3168 /// assert_eq!(Input::text_consume(), Some('H'));
3169 /// assert_eq!(Input::text_consume(), Some('i'));
3170 /// Input::text_inject_char('!');
3171 /// assert_eq!(Input::text_consume(), Some('\0'));
3172 /// Input::text_reset();
3173 /// assert_eq!(Input::text_consume(), Some('H'));
3174 /// assert_eq!(Input::text_consume(), Some('i'));
3175 /// assert_eq!(Input::text_consume(), Some('\0'));
3176 /// } else if iter == 1 {
3177 /// assert_eq!(Input::text_consume(), Some('!'));
3178 /// } else {
3179 /// assert_eq!(Input::text_consume(), Some('\0'));
3180 /// }
3181 /// );
3182 /// ```
3183 pub fn text_reset() {
3184 unsafe { input_text_reset() };
3185 }
3186
3187 /// This will inject a UTF32 Unicode text character into StereoKit’s text input queue. It will be available at the
3188 /// start of the next frame, and will be indistinguishable from normal text entry.
3189 ///
3190 /// This will not submit key press/release events to StereoKit’s input queue, use key_inject_press/_release
3191 /// for that.
3192 /// <https://stereokit.net/Pages/StereoKit/Input/TextInjectChar.html>
3193 /// * `character` - An unsigned integer representing a single UTF32 character.
3194 ///
3195 /// see also [`input_text_inject_char`]
3196 /// ### Examples
3197 /// ```
3198 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3199 /// use stereokit_rust::system::Input;
3200 ///
3201 /// // Simulate some text input
3202 /// Input::text_inject_char('H');
3203 /// Input::text_inject_char('i');
3204 /// Input::text_inject_char('!');
3205 /// Input::text_inject_char('😬');
3206 ///
3207 /// test_steps!( // !!!! Get a proper main loop !!!!
3208 /// if iter == 0 {
3209 /// assert_eq!(Input::text_consume(), Some('H'));
3210 /// assert_eq!(Input::text_consume(), Some('i'));
3211 /// assert_eq!(Input::text_consume(), Some('!'));
3212 /// assert_eq!(Input::text_consume(), Some('😬'));
3213 /// } else {
3214 /// assert_eq!(Input::text_consume(), Some('\0'));
3215 /// }
3216 /// );
3217 /// ```
3218 pub fn text_inject_char(character: char) {
3219 unsafe { input_text_inject_char(character as u32) };
3220 }
3221
3222 /// This will convert an str into a number of UTF32 Unicode text characters, and inject them into StereoKit’s
3223 /// text input queue. It will be available at the start of the next frame, and will be indistinguishable from normal
3224 /// text entry.
3225 ///
3226 /// This will not submit key press/release events to StereoKit’s input queue, use key_inject_press/_release
3227 /// for that.
3228 /// <https://stereokit.net/Pages/StereoKit/Input/TextInjectChar.html>
3229 /// * `chars` - A collection of characters to submit as text input.
3230 ///
3231 /// see also [`input_text_inject_char`]
3232 /// ### Examples
3233 /// ```
3234 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3235 /// use stereokit_rust::system::Input;
3236 ///
3237 /// // Simulate some text input
3238 /// Input::text_inject_chars("Hi!❤");
3239 ///
3240 /// test_steps!( // !!!! Get a proper main loop !!!!
3241 /// if iter == 0 {
3242 /// assert_eq!(Input::text_consume(), Some('H'));
3243 /// assert_eq!(Input::text_consume(), Some('i'));
3244 /// assert_eq!(Input::text_consume(), Some('!'));
3245 /// assert_eq!(Input::text_consume(), Some('❤'));
3246 /// } else {
3247 /// assert_eq!(Input::text_consume(), Some('\0'));
3248 /// }
3249 /// );
3250 /// ```
3251 pub fn text_inject_chars(str: impl AsRef<str>) {
3252 for character in str.as_ref().chars() {
3253 unsafe { input_text_inject_char(character as u32) }
3254 }
3255 }
3256
3257 /// You can subscribe to input events from Pointer sources here. StereoKit will call your callback and pass along a
3258 /// Pointer that matches the position of that pointer at the moment the event occurred. This can be more accurate
3259 /// than polling for input data, since polling happens specifically at frame start.
3260 /// <https://stereokit.net/Pages/StereoKit/Input/Subscribe.html>
3261 /// * `event_source` - What input sources do we want to listen for. This is a bit flag.
3262 /// * `event_types` - What events do we want to listen for. This is a bit flag.
3263 /// * `on_event` - The callback to call when the event occurs!
3264 ///
3265 /// see also [`input_subscribe`]
3266 /// ### Examples
3267 /// ```
3268 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3269 /// use stereokit_rust::system::{Input, InputSource, Pointer, BtnState, Handed};
3270 ///
3271 /// let pointer_left = Input::pointer(0, None);
3272 ///
3273 /// unsafe extern "C" fn input_cb (source: InputSource, input_event: BtnState, in_pointer: *const Pointer) {
3274 /// let in_pointer = unsafe { *in_pointer };
3275 /// assert_eq!(source, InputSource::CanPress);
3276 /// assert_eq!(in_pointer.source, InputSource::None);
3277 /// assert_eq!(input_event, BtnState::JustActive);
3278 /// }
3279 ///
3280 /// number_of_steps = 8;
3281 /// test_steps!( // !!!! Get a proper main loop !!!!
3282 /// if iter == 0 {
3283 /// Input::subscribe (InputSource::CanPress, BtnState::JustActive, Some(input_cb));
3284 /// } if iter == 1 {
3285 /// Input::fire_event(InputSource::CanPress, BtnState::JustActive, &pointer_left);
3286 /// } else if iter == 8 {
3287 /// Input::unsubscribe(InputSource::Hand | InputSource::HandLeft, BtnState::JustInactive, Some(input_cb));
3288 /// }
3289 /// );
3290 /// ```
3291 #[deprecated(since = "0.4.0", note = "Not working anymore")]
3292 #[allow(deprecated)]
3293 pub fn subscribe(
3294 event_source: InputSource,
3295 event_types: BtnState,
3296 on_event: Option<unsafe extern "C" fn(source: InputSource, input_event: BtnState, in_pointer: *const Pointer)>,
3297 ) {
3298 unsafe { input_subscribe(event_source, event_types, on_event) }
3299 }
3300
3301 /// Unsubscribes a listener from input events.
3302 /// <https://stereokit.net/Pages/StereoKit/Input/Unsubscribe.html>
3303 /// * `event_source` - The sources this listener was originally registered for.
3304 /// * `event_types` - The events this listener was originally registered for.
3305 /// * `on_event` - The callback this lisener originally used.
3306 ///
3307 /// see also [`input_unsubscribe`]
3308 /// see example in [`Input::subscribe`]
3309 #[deprecated(since = "0.4.0", note = "Not working anymore")]
3310 #[allow(deprecated)]
3311 pub fn unsubscribe(
3312 event_source: InputSource,
3313 event_types: BtnState,
3314 on_event: Option<unsafe extern "C" fn(source: InputSource, input_event: BtnState, in_pointer: *const Pointer)>,
3315 ) {
3316 unsafe { input_unsubscribe(event_source, event_types, on_event) }
3317 }
3318
3319 /// This retreives the Model currently in use by StereoKit to represent the controller input source. By default,
3320 /// this will be a Model provided by OpenXR, or SK's fallback Model. This will never be null while SK is
3321 /// initialized.
3322 /// <https://stereokit.net/Pages/StereoKit/Input.html>
3323 /// * `handed` - The hand of the controller Model to retreive.
3324 ///
3325 /// Returns the current controller Model. By default, his will be a Model provided by OpenXR, or SK's fallback
3326 /// Model. This will never be null while SK is initialized.
3327 /// see also [`input_controller_model_get`]
3328 /// see example in [`Input::set_controller_model`]
3329 pub fn get_controller_model(handed: Handed) -> Model {
3330 match NonNull::new(unsafe { input_controller_model_get(handed) }) {
3331 Some(model) => Model(model),
3332 None => Model::new(),
3333 }
3334 }
3335
3336 /// This is the state of the controller’s menu button, this is not attached to any particular hand, so it’s
3337 /// independent of a left or right controller.
3338 /// <https://stereokit.net/Pages/StereoKit/Input/ControllerMenuButton.html>
3339 ///
3340 /// see also [`input_controller_menu`]
3341 /// ### Examples
3342 /// ```
3343 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3344 /// use stereokit_rust::system::{Input, BtnState};
3345 ///
3346 /// assert_eq!(Input::get_controller_menu_button(), BtnState::Inactive);
3347 /// assert_eq!(Input::get_controller_menu_button().is_just_active(), false);
3348 ///
3349 /// let button_state = Input::get_controller_menu_button();
3350 /// assert_eq!(button_state.is_active(), false);
3351 /// assert_eq!(button_state.is_just_active(), false);
3352 /// assert_eq!(button_state.is_just_inactive(), false);
3353 /// assert_eq!(button_state.is_changed(), false);
3354 /// ```
3355 pub fn get_controller_menu_button() -> BtnState {
3356 unsafe { input_controller_menu() }
3357 }
3358
3359 /// If the device has eye tracking hardware and the app has permission to use it, then this is the most recently
3360 /// tracked eye pose. Check Input.EyesTracked to see if the pose is up-to date, or if it’s a leftover!
3361 ///
3362 /// You can also check Sk::System::eye_tracking_present to see if the hardware is capable of providing eye tracking.
3363 ///
3364 /// On Flatscreen when the MR sim is still enabled, then eyes are emulated using the cursor position when the user
3365 /// holds down Alt.
3366 /// <https://stereokit.net/Pages/StereoKit/Input/Eyes.html>
3367 ///
3368 /// see also [`input_eyes`] [`Input::get_eyes_tracked`]
3369 /// ### Examples
3370 /// ```
3371 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3372 /// use stereokit_rust::{system::{Input, BtnState}, maths::Pose};
3373 ///
3374 /// let eyes_pose = Input::get_eyes();
3375 ///
3376 /// assert_eq!(eyes_pose, Pose::IDENTITY);
3377 /// assert_eq!(Input::get_eyes_tracked(), BtnState::Inactive)
3378 /// ```
3379 pub fn get_eyes() -> Pose {
3380 unsafe { input_eyes() }
3381 }
3382
3383 /// If eye hardware is available and app has permission, then this is the tracking state of the eyes. Eyes may move
3384 /// out of bounds, hardware may fail to detect eyes, or who knows what else!
3385 ///
3386 /// On Flatscreen when MR sim is still enabled, this will report whether the user is simulating eye input with the
3387 /// Alt key.
3388 ///
3389 /// Permissions:
3390 /// * You may need to add an entry to your AndroidManifest.xml (or Cargo.toml), refer to your device’s
3391 /// documentation for specifics.
3392 ///
3393 /// <https://stereokit.net/Pages/StereoKit/Input/EyesTracked.html>
3394 ///
3395 /// see also [`input_eyes_tracked`]
3396 /// ### Examples
3397 /// ```
3398 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3399 /// use stereokit_rust::{system::{Input, BtnState}, maths::Pose};
3400 ///
3401 /// let eyes_tracked = Input::get_eyes_tracked();
3402 ///
3403 /// assert_eq!(eyes_tracked.is_active(), false);
3404 /// assert_eq!(eyes_tracked, BtnState::Inactive);
3405 ///
3406 /// assert_eq!(Input::get_eyes(),Pose::IDENTITY)
3407 /// ```
3408 pub fn get_eyes_tracked() -> BtnState {
3409 unsafe { input_eyes_tracked() }
3410 }
3411
3412 /// The position and orientation of the user’s head! This is the center point between the user’s eyes, NOT the
3413 /// center of the user’s head. Forward points the same way the user’s face is facing.
3414 /// <https://stereokit.net/Pages/StereoKit/Input/Head.html>
3415 ///
3416 /// see also [`input_head`]
3417 /// ### Examples
3418 /// ```
3419 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3420 /// use stereokit_rust::{system::Input, maths::Pose};
3421 ///
3422 /// let head_pose = Input::get_head();
3423 ///
3424 /// assert_eq!(head_pose, Pose::IDENTITY);
3425 /// ```
3426 pub fn get_head() -> Pose {
3427 unsafe { input_head() }
3428 }
3429
3430 /// Information about this system’s mouse, or lack thereof!
3431 /// <https://stereokit.net/Pages/StereoKit/Input/Mouse.html>
3432 ///
3433 /// see also [`input_mouse`]
3434 /// ### Examples
3435 /// ```
3436 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3437 /// use stereokit_rust::{system::Input, maths::{Vec2, Vec3}};
3438 ///
3439 /// let mouse = Input::get_mouse();
3440 ///
3441 /// assert_eq!(mouse.is_available(),false);
3442 /// assert_eq!(mouse.pos, Vec2::ZERO);
3443 /// assert_eq!(mouse.pos_change, Vec2::ZERO);
3444 /// assert_eq!(mouse.scroll, 0.0);
3445 /// assert_eq!(mouse.scroll_change, 0.0);
3446 ///
3447 /// assert_eq!(mouse.get_ray().position, Vec3::ZERO);
3448 /// // Warning: No ray if the mouse isn't available!
3449 /// // assert_eq!(mouse.get_ray().direction, Vec3::new(f32::NAN, f32::NAN, f32::NAN));
3450 /// ```
3451 pub fn get_mouse() -> Mouse {
3452 unsafe { *input_mouse() }
3453 }
3454
3455 /// This controls the visibility of StereoKit's finger glow effect on the UI. When true, SK will fill out global
3456 /// shader variable `sk_fingertip[2]` with the location of the pointer finger's tips. When false, or the hand is
3457 /// untracked, the location will be set to an unlikely faraway position.
3458 /// <https://stereokit.net/Pages/StereoKit/Input/FingerGlow.html>
3459 ///
3460 /// Returns true if StereoKit renders this. False, it doesn't.
3461 /// see also [`input_set_finger_glow`]
3462 /// see example in [`Input::finger_glow`]
3463 pub fn get_finger_glow() -> bool {
3464 unsafe { input_get_finger_glow() != 0 }
3465 }
3466}
3467
3468/// Used to represent lines for the line drawing functions! This is just a snapshot of information about each individual
3469/// point on a line.
3470/// <https://stereokit.net/Pages/StereoKit/LinePoint.html>
3471/// ### Examples
3472/// ```
3473/// use stereokit_rust::{system::LinePoint, util::named_colors};
3474///
3475/// let line_point = LinePoint::new( [0.1, 0.2, 0.3], 0.01, named_colors::CYAN);
3476///
3477/// assert_eq!(line_point, LinePoint {pt: [0.1, 0.2, 0.3].into(), thickness: 0.01, color: named_colors::CYAN});
3478/// ```
3479#[derive(Debug, Copy, Clone, PartialEq)]
3480#[repr(C)]
3481pub struct LinePoint {
3482 pub pt: Vec3,
3483 pub thickness: f32,
3484 pub color: Color32,
3485}
3486
3487impl LinePoint {
3488 /// Create a new LinePoint.
3489 pub fn new(pt: impl Into<Vec3>, thickness: f32, color: Color32) -> Self {
3490 Self { pt: pt.into(), thickness, color }
3491 }
3492}
3493
3494/// A line drawing class! This is an easy way to visualize lines or relationships between objects. The current
3495/// implementation uses a quad strip that always faces the user, via vertex shader manipulation.
3496/// <https://stereokit.net/Pages/StereoKit/Lines.html>
3497///
3498/// ### Examples
3499/// ```
3500/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3501/// use stereokit_rust::{maths::{Vec3, Pose, Ray}, system::{Lines, LinePoint},
3502/// util::{named_colors}};
3503///
3504/// let ray = Ray::new([-0.3, -0.8, 0.2], [1.0, 0.0, 0.0]);
3505///
3506/// let axis_pose = Pose::new([0.0, -0.35, 0.0], None);
3507///
3508/// filename_scr = "screenshots/lines.jpeg";
3509/// test_screenshot!( // !!!! Get a proper main loop !!!!
3510/// Lines::add(token, [0.7, 0.7, 0.2], [ 0.7,-0.7, 0.2], named_colors::LIME, None, 0.06);
3511/// Lines::add(token, [0.7, 0.7, 0.2], [-0.7, 0.7, 0.2], named_colors::RED, None, 0.03);
3512///
3513/// Lines::add_list(token, &[
3514/// LinePoint {pt: [-0.7,-0.7, 0.2].into(), thickness: 0.08, color: named_colors::FUCHSIA},
3515/// LinePoint::new([-0.5,-0.1, 0.2], 0.08, named_colors::BLACK),
3516/// LinePoint::new([-0.7, 0.7, 0.2], 0.01, named_colors::YELLOW),
3517/// ]);
3518///
3519/// Lines::add_ray(token, ray, 0.6, named_colors::RED, None, 0.08 );
3520///
3521/// Lines::add_axis(token, axis_pose, Some(0.7), Some(0.04));
3522/// );
3523/// ```
3524/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/lines.jpeg" alt="screenshot" width="200">
3525pub struct Lines;
3526
3527unsafe extern "C" {
3528 pub fn line_add(start: Vec3, end: Vec3, color_start: Color32, color_end: Color32, thickness: f32);
3529 pub fn line_addv(start: LinePoint, end: LinePoint);
3530 pub fn line_add_axis(pose: Pose, size: f32);
3531 pub fn line_add_list(points: *const Vec3, count: i32, color: Color32, thickness: f32);
3532 pub fn line_add_listv(in_arr_points: *const LinePoint, count: i32);
3533}
3534
3535impl Lines {
3536 /// Adds a line to the environment for the current frame.
3537 /// <https://stereokit.net/Pages/StereoKit/Lines/Add.html>
3538 /// * `start` - The start of the line.
3539 /// * `end` - The end of the line.
3540 /// * `color_start` - Color for the start of the line, this is embedded in the vertex color of the line.
3541 /// * `color_end` - Color for the end of the line, this is embedded in the vertex color of the line. If None,
3542 /// uses color_start.
3543 /// * `thickness` - The thickness of the line.
3544 ///
3545 /// see also [line_add]
3546 /// ### Examples
3547 /// ```
3548 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3549 /// use stereokit_rust::{maths::{Vec3, Pose, Ray}, system::{Lines, LinePoint},
3550 /// util::{named_colors}};
3551 ///
3552 /// test_steps!( // !!!! Get a proper main loop !!!!
3553 /// Lines::add(token, [0.7, 0.7, 0.2], [ 0.7,-0.7, 0.2], named_colors::LIME, None, 0.06);
3554 ///
3555 /// Lines::add(token, [0.7, 0.7, 0.2], [-0.7, 0.7, 0.2], named_colors::RED, None, 0.03);
3556 /// );
3557 /// ```
3558 pub fn add<V: Into<Vec3>>(
3559 _token: &MainThreadToken,
3560 start: V,
3561 end: V,
3562 color_start: Color32,
3563 color_end: Option<Color32>,
3564 thickness: f32,
3565 ) {
3566 let color_end = color_end.unwrap_or(color_start);
3567 unsafe { line_add(start.into(), end.into(), color_start, color_end, thickness) }
3568 }
3569
3570 /// Adds a line based on a ray to the environment for the current frame.
3571 /// <https://stereokit.net/Pages/StereoKit/Lines/Add.html>
3572 /// * `ray` - The ray we want to visualize!
3573 /// * `length` - How long should the ray be? Actual length will be ray.direction.Magnitude * length.
3574 /// * `color_start` - Color for the start of the line, this is embedded in the vertex color of the line.
3575 /// * `color_end` - Color for the end of the line, this is embedded in the vertex color of the line. If None,
3576 /// uses color_start.
3577 /// * `thickness` - The thickness of the line.
3578 ///
3579 /// see also [line_add]
3580 /// ### Examples
3581 /// ```
3582 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3583 /// use stereokit_rust::{maths::{Vec3, Ray}, system::{Lines, LinePoint},
3584 /// util::{named_colors}};
3585 ///
3586 /// // axis at the origins:
3587 /// let ray1 = Ray::new(Vec3::ZERO, Vec3::X);
3588 /// let ray2 = Ray::new(Vec3::ZERO, Vec3::Y);
3589 /// let ray3 = Ray::new(Vec3::ZERO, Vec3::Z);
3590 ///
3591 ///
3592 /// test_steps!( // !!!! Get a proper main loop !!!!
3593 /// Lines::add_ray(token, ray1, 1.0, named_colors::WHITE, Some(named_colors::RED), 0.03 );
3594 /// Lines::add_ray(token, ray2, 1.0, named_colors::WHITE, Some(named_colors::GREEN), 0.03 );
3595 /// Lines::add_ray(token, ray2, 1.0, named_colors::WHITE, Some(named_colors::BLUE), 0.03 );
3596 /// );
3597 /// ```
3598 pub fn add_ray<R: Into<Ray>>(
3599 _token: &MainThreadToken,
3600 ray: R,
3601 length: f32,
3602 color_start: Color32,
3603 color_end: Option<Color32>,
3604 thickness: f32,
3605 ) {
3606 let color_end = color_end.unwrap_or(color_start);
3607 let ray: Ray = ray.into();
3608 unsafe { line_add(ray.position, ray.get_at(length), color_start, color_end, thickness) }
3609 }
3610
3611 /// Adds a line from a list of line points to the environment. This does not close the path, so if you want it
3612 /// closed, you’ll have to add an extra point or two at the end yourself!
3613 /// <https://stereokit.net/Pages/StereoKit/Lines/Add.html>
3614 /// * `points` - An array of LinePoint.
3615 ///
3616 /// see also [line_add]
3617 /// ### Examples
3618 /// ```
3619 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3620 /// use stereokit_rust::{maths::{Vec3, Pose, Ray}, system::{Lines, LinePoint},
3621 /// util::{named_colors}};
3622 /// test_steps!( // !!!! Get a proper main loop !!!!
3623 ///
3624 /// Lines::add_list(token, &[
3625 /// LinePoint {pt: [-0.7,-0.7, 0.2].into(), thickness: 0.08, color: named_colors::FUCHSIA},
3626 /// LinePoint::new([-0.5,-0.1, 0.2], 0.08, named_colors::BLACK),
3627 /// LinePoint::new([-0.7, 0.7, 0.2], 0.01, named_colors::YELLOW),
3628 /// ]);
3629 ///
3630 /// );
3631 /// ```
3632 pub fn add_list(_token: &MainThreadToken, points: &[LinePoint]) {
3633 unsafe { line_add_listv(points.as_ptr(), points.len() as i32) }
3634 }
3635
3636 /// Displays an RGB/XYZ axis widget at the pose! Each line is extended along the positive direction of each axis, so
3637 /// the red line is +X, green is +Y, and blue is +Z. A white line is drawn along -Z to indicate the Forward vector
3638 /// of the pose (-Z is forward in StereoKit).
3639 /// <https://stereokit.net/Pages/StereoKit/Lines/AddAxis.html>
3640 /// * `at_pose` - What position and orientation do we want this axis widget at?
3641 /// * `size` - How long should the widget lines be, in meters? If None, has value of 1 cm
3642 /// * `thickness` - How thick should the lines be, in meters? If None, will use a faster renderer with a thickness of
3643 /// one tenth of the size.
3644 ///
3645 /// see also [line_add]
3646 /// ### Examples
3647 /// ```
3648 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3649 /// use stereokit_rust::{maths::{Vec3, Pose, Ray}, system::{Lines, LinePoint},
3650 /// util::{named_colors}};
3651 ///
3652 /// // Axis at the origins:
3653 /// let axis_pose = Pose::IDENTITY;
3654 ///
3655 /// test_steps!( // !!!! Get a proper main loop !!!!
3656 ///
3657 /// Lines::add_axis(token, axis_pose, Some(0.7), Some(0.02));
3658 ///
3659 /// );
3660 /// ```
3661 pub fn add_axis<P: Into<Pose>>(token: &MainThreadToken, at_pose: P, size: Option<f32>, thickness: Option<f32>) {
3662 let at_pose: Pose = at_pose.into();
3663 let size = size.unwrap_or(0.01);
3664 match thickness {
3665 Some(thickness) => {
3666 Self::add(
3667 token,
3668 at_pose.position,
3669 at_pose.orientation.mul_vec3(at_pose.position + Vec3::X) * size,
3670 Color32::new(255, 0, 0, 255),
3671 None,
3672 thickness,
3673 );
3674 Self::add(
3675 token,
3676 at_pose.position,
3677 at_pose.orientation.mul_vec3(at_pose.position + Vec3::Y) * size,
3678 Color32::new(0, 255, 0, 255),
3679 None,
3680 thickness,
3681 );
3682 Self::add(
3683 token,
3684 at_pose.position,
3685 at_pose.orientation.mul_vec3(at_pose.position + Vec3::Z) * size,
3686 Color32::new(0, 0, 255, 255),
3687 None,
3688 thickness,
3689 );
3690 Self::add(
3691 token,
3692 at_pose.position,
3693 at_pose.orientation.mul_vec3(at_pose.position + Vec3::FORWARD) * size * 0.5,
3694 Color32::new(255, 255, 255, 255),
3695 None,
3696 thickness,
3697 )
3698 }
3699 None => unsafe { line_add_axis(at_pose, size) },
3700 }
3701 }
3702}
3703
3704/// The log tool will write to the console with annotations for console colors, which helps with readability, but isn’t
3705/// always supported. These are the options available for configuring those colors.
3706/// <https://stereokit.net/Pages/StereoKit/LogColors.html>
3707///
3708/// see also [`Log`]
3709#[derive(Debug, Copy, Clone, PartialEq, Eq)]
3710#[repr(u32)]
3711pub enum LogColors {
3712 /// Use console coloring annotations.
3713 Ansi = 0,
3714 ///Scrape out any color annotations, so logs are all completely plain text.
3715 None = 1,
3716}
3717
3718/// Severity of a log item.
3719/// <https://stereokit.net/Pages/StereoKit/LogLevel.html>
3720#[derive(Debug, Copy, Clone, PartialEq, Eq)]
3721#[repr(u32)]
3722pub enum LogLevel {
3723 /// A default log level that indicates it has not yet been set
3724 None = 0,
3725 /// This is for diagnostic information, where you need to know details about what -exactly- is going on in the
3726 /// system. This info doesn’t surface by default.
3727 Diagnostic = 1,
3728 /// This is non-critical information, just to let you know what’s going on.
3729 Inform = 2,
3730 /// Something bad has happened, but it’s still within the realm of what’s expected.
3731 Warning = 3,
3732 /// Danger Will Robinson! Something really bad just happened and needs fixing!
3733 Error = 4,
3734}
3735
3736/// Non canonical structure used for subscribed callback
3737#[derive(Debug, Clone)]
3738pub struct LogItem {
3739 pub level: LogLevel,
3740 pub text: String,
3741 pub count: i32,
3742}
3743
3744/// A class for logging errors, warnings and information!
3745/// Different levels of information can be filtered out, and supports
3746/// coloration via <`~colorCode`> and <`~clr`> tags.
3747///
3748/// Text colors can be set with a tag, and reset back to default with
3749/// <`~clr`>. Color codes are as follows:
3750///
3751/// | Dark | Bright | Description |
3752/// |------|--------|-------------|
3753/// | DARK | BRIGHT | DESCRIPTION |
3754/// | blk | BLK | Black |
3755/// | red | RED | Red |
3756/// | grn | GRN | Green |
3757/// | ylw | YLW | Yellow |
3758/// | blu | BLU | Blue |
3759/// | mag | MAG | Magenta |
3760/// | cyn | cyn | Cyan |
3761/// | grn | GRN | Green |
3762/// | wht | WHT | White |
3763///
3764/// <https://stereokit.net/Pages/StereoKit/Log.html>
3765///
3766/// ### Examples
3767/// ```
3768/// use stereokit_rust::system::{Log, LogColors, LogLevel};
3769///
3770/// Log::colors(LogColors::Ansi);
3771/// Log::filter(LogLevel::Diagnostic);
3772///
3773/// Log::info("model <~GRN>node count<~clr> : <~RED>6589<~clr> !!!");
3774///
3775/// let value = 42;
3776/// Log::diag(format!("My value is {}", value));
3777///
3778/// Log::warn("This is not very good!");
3779///
3780/// Log::err("This is very bad!!!");
3781///
3782/// Log::write(LogLevel::Diagnostic, format!("Again, my value is {}", 2));
3783/// ```
3784pub struct Log;
3785
3786unsafe extern "C" {
3787 pub fn log_diag(text: *const c_char);
3788 //pub fn log_diagf(text: *const c_char, ...);
3789 pub fn log_info(text: *const c_char);
3790 //pub fn log_infof(text: *const c_char, ...);
3791 pub fn log_warn(text: *const c_char);
3792 //pub fn log_warnf(text: *const c_char, ...);
3793 pub fn log_err(text: *const c_char);
3794 //pub fn log_errf(text: *const c_char, ...);
3795 //pub fn log_writef(level: LogLevel, text: *const c_char, ...);
3796 pub fn log_write(level: LogLevel, text: *const c_char);
3797 pub fn log_set_filter(level: LogLevel);
3798 pub fn log_set_colors(colors: LogColors);
3799 pub fn log_subscribe(
3800 log_callback: Option<unsafe extern "C" fn(context: *mut c_void, level: LogLevel, text: *const c_char)>,
3801 context: *mut c_void,
3802 );
3803 pub fn log_unsubscribe(
3804 log_callback: Option<unsafe extern "C" fn(context: *mut c_void, level: LogLevel, text: *const c_char)>,
3805 context: *mut c_void,
3806 );
3807}
3808
3809/// Log subscribe trampoline
3810///
3811/// see also [`Log::subscribe`]
3812unsafe extern "C" fn log_trampoline<'a, F: FnMut(LogLevel, &str) + 'a>(
3813 context: *mut c_void,
3814 log_level: LogLevel,
3815 text: *const c_char,
3816) {
3817 let closure = unsafe { &mut *(context as *mut &mut F) };
3818 let c_str = unsafe { CStr::from_ptr(text).to_str().unwrap().trim_end() };
3819 closure(log_level, c_str)
3820}
3821
3822impl Log {
3823 /// What's the lowest level of severity logs to display on the console? Default is LogLevel::Info. This property
3824 /// can safely be set before SK initialization.
3825 /// <https://stereokit.net/Pages/StereoKit/Log.html>
3826 ///
3827 /// see also [`log_set_filter`]
3828 /// ### Examples
3829 /// ```
3830 /// use stereokit_rust::system::{Log, LogLevel};
3831 ///
3832 /// // Set the log filter to only show errors and above.
3833 /// Log::filter(LogLevel::Error);
3834 ///
3835 /// // Set the log filter to only show warnings and above (errors)
3836 /// Log::filter(LogLevel::Warning);
3837 ///
3838 /// // Set the log filter to only show infos and above (wanings and errors)
3839 /// Log::filter(LogLevel::Inform);
3840 ///
3841 /// // Set the log filter to show every logs
3842 /// Log::filter(LogLevel::Diagnostic);
3843 /// ```
3844 pub fn filter(filter: LogLevel) {
3845 unsafe { log_set_filter(filter) }
3846 }
3847
3848 /// Set the colors
3849 /// <https://stereokit.net/Pages/StereoKit/Log.html>
3850 ///
3851 /// see also [`log_set_colors`]
3852 /// ### Examples
3853 /// ```
3854 /// use stereokit_rust::system::{Log, LogColors};
3855 ///
3856 /// // Set the log colors to use ANSI color codes.
3857 /// Log::colors(LogColors::Ansi);
3858 ///
3859 /// // Set the log colors to use no color codes.
3860 /// Log::colors(LogColors::None);
3861 /// ```
3862 pub fn colors(colors: LogColors) {
3863 unsafe { log_set_colors(colors) }
3864 }
3865
3866 /// Writes a formatted line to the log using a LogLevel.Error severity level!
3867 /// <https://stereokit.net/Pages/StereoKit/Log.html>
3868 ///
3869 /// see also [`log_err`]
3870 /// ### Examples
3871 /// ```
3872 /// use stereokit_rust::system::Log;
3873 ///
3874 /// Log::err("This is very bad!!!");
3875 ///
3876 /// let value = 42;
3877 /// Log::err(format!("My problematic value is {}", value));
3878 /// ```
3879 pub fn err<S: AsRef<str>>(text: S) {
3880 let c_str = CString::new(text.as_ref()).unwrap();
3881 unsafe { log_err(c_str.as_ptr()) }
3882 }
3883
3884 /// Writes a formatted line to the log using a LogLevel.Inform severity level!
3885 /// <https://stereokit.net/Pages/StereoKit/Log.html>
3886 ///
3887 /// see also [`log_info`]
3888 /// ### Examples
3889 /// ```
3890 /// use stereokit_rust::system::Log;
3891 ///
3892 /// Log::info("This is good!");
3893 ///
3894 /// let value = 42;
3895 /// Log::info(format!("My value is {}", value));
3896 /// ```
3897 pub fn info<S: AsRef<str>>(text: S) {
3898 let c_str = CString::new(text.as_ref()).unwrap();
3899 unsafe { log_info(c_str.as_ptr()) }
3900 }
3901
3902 /// Writes a formatted line to the log using a LogLevel.Warning severity level!
3903 /// <https://stereokit.net/Pages/StereoKit/Log.html>
3904 ///
3905 /// see also [`log_warn`]
3906 /// ### Examples
3907 /// ```
3908 /// use stereokit_rust::system::Log;
3909 ///
3910 /// Log::warn("This is not very good!");
3911 ///
3912 /// let value = 42;
3913 /// Log::warn(format!("My not so good value is {}", value));
3914 /// ```
3915 pub fn warn<S: AsRef<str>>(text: S) {
3916 let c_str = CString::new(text.as_ref()).unwrap();
3917 unsafe { log_warn(c_str.as_ptr()) }
3918 }
3919
3920 /// Writes a formatted line to the log using a LogLevel.Diagnostic severity level!
3921 /// <https://stereokit.net/Pages/StereoKit/Log.html>
3922 ///
3923 /// see also [`log_diag`]
3924 /// ### Examples
3925 /// ```
3926 /// use stereokit_rust::system::Log;
3927 ///
3928 /// Log::diag("This is something to check!");
3929 ///
3930 /// let value = 42;
3931 /// Log::diag(format!("My value to check is {}", value));
3932 /// ```
3933 pub fn diag<S: AsRef<str>>(text: S) {
3934 let c_str = CString::new(text.as_ref()).unwrap();
3935 unsafe { log_diag(c_str.as_ptr()) }
3936 }
3937
3938 /// Writes a formatted line to the log with the specified severity level!
3939 /// <https://stereokit.net/Pages/StereoKit/Log.html>
3940 ///
3941 /// see also [`log_write`]
3942 /// ### Examples
3943 /// ```
3944 /// use stereokit_rust::system::{Log, LogLevel};
3945 ///
3946 /// Log::write(LogLevel::Diagnostic, "This is something to check!!!");
3947 ///
3948 /// let value = 42;
3949 /// Log::write(LogLevel::Error, format!("My problematic value is {}", value));
3950 /// ```
3951 pub fn write<S: AsRef<str>>(level: LogLevel, text: S) {
3952 let c_str = CString::new(text.as_ref()).unwrap();
3953 unsafe { log_write(level, c_str.as_ptr()) }
3954 }
3955
3956 /// Allows you to listen in on log events! Any callback subscribed here will be called when something is logged.
3957 /// This does honor the Log.Filter, so filtered logs will not be received here. This method can safely be called
3958 /// before SK initialization.
3959 /// <https://stereokit.net/Pages/StereoKit/Log/Subscribe.html>
3960 ///
3961 /// see also [`log_subscribe`] [`Log::unsubscribe`]
3962 /// ### Examples
3963 /// ```
3964 /// use stereokit_rust::system::{Log, LogLevel, LogItem};
3965 /// use std::sync::{Arc, Mutex};
3966 ///
3967 /// /// Somewhere to copy the log
3968 /// static LOG_LOG: Mutex<Vec<LogItem>> = Mutex::new(vec![]);
3969 ///
3970 /// let fn_mut = |level: LogLevel, log_text: &str| {
3971 /// let mut items = LOG_LOG.lock().unwrap();
3972 /// items.push(LogItem { level, text: log_text.to_owned(), count: 1 });
3973 /// };
3974 /// Log::subscribe( fn_mut );
3975 ///
3976 /// Log::info("This is an info message");
3977 /// Log::warn("This is a warning message");
3978 /// Log::err("This is an error message");
3979 ///
3980 /// let messages = LOG_LOG.lock().unwrap();
3981 /// assert_eq!(messages.len(), 3);
3982 /// assert_eq!(messages[0].level, LogLevel::Inform);
3983 /// assert_eq!(messages[1].text, "This is a warning message");
3984 ///
3985 /// Log::unsubscribe( fn_mut );
3986 /// ```
3987 pub fn subscribe<'a, F: FnMut(LogLevel, &str) + 'a>(mut on_log: F) {
3988 let mut closure = &mut on_log;
3989 unsafe { log_subscribe(Some(log_trampoline::<F>), &mut closure as *mut _ as *mut c_void) }
3990 }
3991
3992 /// If you subscribed to the log callback, you can unsubscribe that callback here! This method can safely be
3993 /// called before initialization.
3994 /// <https://stereokit.net/Pages/StereoKit/Log/Unsubscribe.html>
3995 ///
3996 /// see also [`log_unsubscribe`]
3997 /// see example in [`Log::subscribe`]
3998 pub fn unsubscribe<'a, F: FnMut(LogLevel, &str) + 'a>(mut on_log: F) {
3999 let mut closure = &mut on_log;
4000 unsafe { log_unsubscribe(Some(log_trampoline::<F>), &mut closure as *mut _ as *mut c_void) }
4001 }
4002}
4003
4004/// This class provides access to the hardware’s microphone, and stores it in a Sound stream. Start and Stop recording,
4005/// and check the Sound property for the results! Remember to ensure your application has microphone permissions enabled!
4006/// <https://stereokit.net/Pages/StereoKit/Microphone.html>
4007///
4008/// see also: [`Sound`]
4009/// /// ### Examples
4010/// ```
4011/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4012/// use stereokit_rust::{maths::{Vec3, Matrix}, mesh::Mesh, material::Material,
4013/// sound::Sound, system::Microphone, util::named_colors};
4014///
4015/// let sphere = Mesh::generate_cube(Vec3::ONE * 0.5, None);
4016/// let material = Material::pbr().tex_file_copy("textures/micro.jpeg", true, None)
4017/// .expect("sound.jpeg should be there");
4018/// let mut position = Vec3::new( 0.0, 0.0, 0.5);
4019/// let transform = Matrix::t(position);
4020///
4021/// let micros = Microphone::get_devices();
4022///
4023/// if micros.len() > 0 {
4024/// let first_in_list = micros[0].clone();
4025/// if Microphone::start(Some(first_in_list)) {
4026/// assert!(Microphone::is_recording());
4027/// } else {
4028/// assert!(!Microphone::is_recording());
4029/// }
4030/// }
4031///
4032/// filename_scr = "screenshots/microphone.jpeg";
4033/// test_screenshot!( // !!!! Get a proper main loop !!!!
4034/// sphere.draw(token, &material, transform, Some(named_colors::LIGHT_BLUE.into()), None );
4035/// if iter == 1990 && Microphone::is_recording() {
4036/// let micro_sound = Microphone::sound().expect("Microphone should be recording");
4037/// let mut read_samples: Vec<f32> = vec![0.0; 48000];
4038/// let recorded_data = micro_sound.read_samples(read_samples.as_mut_slice(), None);
4039/// Microphone::stop();
4040/// //assert_ne!(recorded_data, 0);
4041/// }
4042/// );
4043/// ```
4044/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/microphone.jpeg" alt="screenshot" width="200">
4045#[repr(C)]
4046#[derive(Debug, PartialEq)]
4047pub struct Microphone {
4048 sound: Sound,
4049}
4050
4051unsafe extern "C" {
4052 pub fn mic_get_stream() -> SoundT;
4053 pub fn mic_is_recording() -> Bool32T;
4054 pub fn mic_device_count() -> i32;
4055 pub fn mic_device_name(index: i32) -> *const c_char;
4056 pub fn mic_start(device_name: *const c_char) -> Bool32T;
4057 pub fn mic_stop();
4058}
4059
4060impl Microphone {
4061 /// This is the sound stream of the Microphone when it is recording. This Asset is created the first time it is
4062 /// accessed via this property, or during Start, and will persist. It is re-used for the Microphone stream if you
4063 /// start/stop/switch devices.
4064 /// <https://stereokit.net/Pages/StereoKit/Microphone/Sound.html>
4065 ///
4066 /// see also [mic_get_stream]
4067 pub fn sound() -> Result<Sound, StereoKitError> {
4068 Ok(Sound(
4069 NonNull::new(unsafe { mic_get_stream() })
4070 .ok_or(StereoKitError::SoundCreate("microphone stream".to_string()))?,
4071 ))
4072 }
4073
4074 /// Is the microphone currently recording?
4075 /// <https://stereokit.net/Pages/StereoKit/Microphone/IsRecording.html>
4076 ///
4077 /// see also [`mic_is_recording`]
4078 pub fn is_recording() -> bool {
4079 unsafe { mic_is_recording() != 0 }
4080 }
4081
4082 /// Constructs a list of valid Microphone devices attached to the system. These names can be passed into Start to
4083 /// select a specific device to record from. It’s recommended to cache this list if you’re using it frequently, as
4084 /// this list is constructed each time you call it.
4085 ///
4086 /// It’s good to note that a user might occasionally plug or unplug microphone devices from their system, so this
4087 /// list may occasionally change.
4088 /// <https://stereokit.net/Pages/StereoKit/Microphone/GetDevices.html>
4089 ///
4090 /// see also [`mic_device_count`] [`mic_device_name`]
4091 pub fn get_devices() -> Vec<String> {
4092 let mut devices = Vec::new();
4093 for iter in 0..unsafe { mic_device_count() } {
4094 let device_name = unsafe { CStr::from_ptr(mic_device_name(iter)) }.to_str().unwrap().to_string();
4095 devices.push(device_name);
4096 }
4097 devices
4098 }
4099
4100 /// This begins recording audio from the Microphone! Audio is stored in Microphone.Sound as a stream of audio. If
4101 /// the Microphone is already recording with a different device, it will stop the previous recording and start again
4102 /// with the new device.
4103 ///
4104 /// If null is provided as the device, then they system’s default input device will be used. Some systems may not
4105 /// provide access to devices other than the system’s default.
4106 /// <https://stereokit.net/Pages/StereoKit/Microphone/Start.html>
4107 /// * `device_name` - The name of the microphone device to use, as seen in the GetDevices list. None will use the
4108 /// system’s default device preference.
4109 ///
4110 /// see also [`mic_start`] [`Microphone::get_devices`] [`Microphone::stop`]
4111 pub fn start(device_name: Option<String>) -> bool {
4112 if let Some(device_name) = device_name
4113 && !device_name.is_empty()
4114 {
4115 let cstr = CString::new(device_name).unwrap();
4116 return unsafe { mic_start(cstr.as_ptr() as *const c_char) != 0 };
4117 }
4118 // Here we call for a null_mut device_name
4119 unsafe { mic_start(null_mut() as *const c_char) != 0 }
4120 }
4121
4122 /// Stops recording audio from the microphone.
4123 /// <https://stereokit.net/Pages/StereoKit/Microphone/Stop.html>
4124 ///
4125 /// see also [mic_stop]
4126 pub fn stop() {
4127 unsafe { mic_stop() }
4128 }
4129}
4130
4131/// When rendering to a rendertarget, this tells if and what of the rendertarget gets cleared before rendering. For
4132/// example, if you are assembling a sheet of images, you may want to clear everything on the first image draw, but not
4133/// clear on subsequent draws.
4134/// <https://stereokit.net/Pages/StereoKit/RenderClear.html>
4135///
4136/// see also [`Renderer`]
4137#[derive(Debug, Copy, Clone, PartialEq, Eq)]
4138#[repr(u32)]
4139pub enum RenderClear {
4140 /// Don’t clear anything, leave it as it is.
4141 None = 0,
4142 /// Clear the rendertarget’s color data.
4143 Color = 1,
4144 /// Clear the rendertarget’s depth data, if present.
4145 Depth = 2,
4146 /// Clear both color and depth data.
4147 All = 3,
4148}
4149
4150bitflags::bitflags! {
4151 /// When rendering content, you can filter what you’re rendering by the RenderLayer that they’re on. This allows
4152 /// you to draw items that are visible in one render, but not another. For example, you may wish to draw a player’s
4153 /// avatar in a ‘mirror’ rendertarget, but not in the primary display. See Renderer.LayerFilter for configuring
4154 /// what the primary display renders.
4155 /// <https://stereokit.net/Pages/StereoKit/RenderLayer.html>
4156 ///
4157 /// see also [`Renderer`] [`Mesh::draw`] [`Model::draw`] [`Model::draw_mat`] [`RenderList`]
4158 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
4159 #[repr(C)]
4160 pub struct RenderLayer: u32 {
4161 /// The default render layer. All Draw use this layer unless otherwise specified.
4162 const Layer0 = 1 << 0;
4163 /// Render layer 1.
4164 const Layer1 = 1 << 1;
4165 /// Render layer 2.
4166 const Layer2 = 1 << 2;
4167 /// Render layer 3.
4168 const Layer3 = 1 << 3;
4169 /// Render layer 4.
4170 const Layer4 = 1 << 4;
4171 /// Render layer 5.
4172 const Layer5 = 1 << 5;
4173 /// Render layer 6.
4174 const Layer6 = 1 << 6;
4175 /// Render layer 7.
4176 const Layer7 = 1 << 7;
4177 /// Render layer 8.
4178 const Layer8 = 1 << 8;
4179 /// Render layer 9.
4180 const Layer9 = 1 << 9;
4181 /// The default VFX layer, StereoKit draws some non-standard mesh content using this flag, such as lines.
4182 const VFX = 10;
4183 /// For items that should only be drawn from the first person perspective. By default, this is enabled for
4184 /// renders that are from a 1st person viewpoint.
4185 const FirstPerson = 1 << 11;
4186 /// For items that should only be drawn from the third person perspective. By default, this is enabled for
4187 /// renders that are from a 3rd person viewpoint.
4188 const ThirdPerson = 1 << 12;
4189 /// This is a flag that specifies all possible layers. If you want to render all layers, then this is the layer
4190 /// filter you would use. This is the default for render filtering.
4191 const All = 0xFFFF;
4192 /// This is a combination of all layers that are not the VFX layer.
4193 const AllRegular = Self::Layer0.bits() | Self::Layer1.bits() | Self::Layer2.bits() | Self::Layer3.bits() | Self::Layer4.bits() | Self::Layer5.bits() | Self::Layer6.bits() | Self::Layer7.bits() | Self::Layer8.bits() | Self::Layer9.bits();
4194 /// All layers except for the third person layer.
4195 const AllFirstPerson = Self::All.bits() & !Self::ThirdPerson.bits();
4196 ///All layers except for the first person layer.
4197 const AllThirdPerson = Self::All.bits() & !Self::FirstPerson.bits();
4198 }
4199}
4200
4201impl Default for RenderLayer {
4202 /// Layer_all is the default.
4203 fn default() -> Self {
4204 RenderLayer::All
4205 }
4206}
4207
4208/// The projection mode used by StereoKit for the main camera! You can use this with Renderer.Projection. These options
4209/// are only available in flatscreen mode, as MR headsets provide very specific projection matrices.
4210/// <https://stereokit.net/Pages/StereoKit/Projection.html>
4211///
4212/// see also [`Renderer`]
4213#[derive(Debug, Copy, Clone, PartialEq, Eq)]
4214#[repr(u32)]
4215pub enum Projection {
4216 /// This is the default projection mode, and the one you’re most likely to be familiar with! This is where parallel
4217 /// lines will converge as they go into the distance.
4218 Perspective = 0,
4219 /// Orthographic projection mode is often used for tools, 2D rendering, thumbnails of 3D objects, or other similar
4220 /// cases. In this mode, parallel lines remain parallel regardless of how far they travel.
4221 Orthographic = 1,
4222}
4223
4224/// Do you need to draw something? Well, you’re probably in the right place! This static class includes a variety of
4225/// different drawing methods, from rendering Models and Meshes, to setting rendering options and drawing to offscreen
4226/// surfaces! Even better, it’s entirely a static class, so you can call it from anywhere :)
4227/// <https://stereokit.net/Pages/StereoKit/Renderer.html>
4228///
4229/// ### Examples
4230/// ```
4231/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4232/// use stereokit_rust::{system::{Renderer, RenderLayer}, maths::{Vec3, Matrix, Pose},
4233/// render_list::RenderList,
4234/// mesh::Mesh, model::Model, material::Material, util::named_colors};
4235///
4236/// let sun = Mesh::generate_sphere(5.0, None);
4237/// let material = Material::pbr();
4238/// let transform_sun = Matrix::t([-6.0, -4.0, -10.0]);
4239///
4240/// let plane = Model::from_file("plane.glb", None).expect("plane.glb should be there");
4241/// let transform_plane = Matrix::t_r_s([0.0, 0.2, -0.7], [0.0, 120.0, 0.0], [0.15, 0.15, 0.15]);
4242///
4243/// // We want to replace the gray background with a dark blue sky:
4244/// let mut primary = RenderList::primary();
4245/// assert_eq!(primary.get_count(), 0);
4246/// Renderer::clear_color(named_colors::BLUE);
4247///
4248/// filename_scr = "screenshots/renderer.jpeg";
4249/// test_steps!( // !!!! Get a proper main loop !!!!
4250///
4251/// primary.clear();
4252///
4253/// Renderer::add_mesh(token, &sun, &material, transform_sun,
4254/// Some(named_colors::RED.into()), None);
4255///
4256/// Renderer::add_model(token, &plane, transform_plane,
4257/// Some(named_colors::PINK.into()), Some(RenderLayer::FirstPerson));
4258///
4259/// Renderer::layer_filter(RenderLayer::All);
4260///
4261/// if iter == number_of_steps {
4262/// // This is the way test_screenshot!() works:
4263/// Renderer::screenshot(token, filename_scr, 90, Pose::look_at(from_scr, at_scr),
4264/// width_scr, height_scr, Some(fov_scr) );
4265/// }
4266/// );
4267/// ```
4268/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/renderer.jpeg" alt="screenshot" width="200">
4269pub struct Renderer;
4270
4271unsafe extern "C" {
4272 pub fn render_set_clip(near_plane: f32, far_plane: f32);
4273 pub fn render_get_clip(out_near_plane: *mut f32, out_far_plane: *mut f32);
4274 pub fn render_set_fov(vertical_field_of_view_degrees: f32);
4275 pub fn render_get_fov() -> f32;
4276 pub fn render_set_ortho_clip(near_plane: f32, far_plane: f32);
4277 pub fn render_set_ortho_size(viewport_height_meters: f32);
4278 pub fn render_get_ortho_size() -> f32;
4279 pub fn render_set_projection(proj: Projection);
4280 pub fn render_get_projection() -> Projection;
4281 pub fn render_get_cam_root() -> Matrix;
4282 pub fn render_set_cam_root(cam_root: *const Matrix);
4283 pub fn render_set_skytex(sky_texture: TexT);
4284 pub fn render_get_skytex() -> TexT;
4285 pub fn render_set_skymaterial(sky_material: MaterialT);
4286 pub fn render_get_skymaterial() -> MaterialT;
4287 pub fn render_set_skylight(light_info: *const SphericalHarmonics);
4288 pub fn render_get_skylight() -> SphericalHarmonics;
4289 pub fn render_set_filter(layer_filter: RenderLayer);
4290 pub fn render_get_filter() -> RenderLayer;
4291 pub fn render_set_scaling(display_tex_scale: f32);
4292 pub fn render_get_scaling() -> f32;
4293 pub fn render_set_viewport_scaling(viewport_rect_scale: f32);
4294 pub fn render_get_viewport_scaling() -> f32;
4295 pub fn render_set_multisample(display_tex_multisample: i32);
4296 pub fn render_get_multisample() -> i32;
4297 pub fn render_override_capture_filter(use_override_filter: Bool32T, layer_filter: RenderLayer);
4298 pub fn render_get_capture_filter() -> RenderLayer;
4299 pub fn render_has_capture_filter() -> Bool32T;
4300 pub fn render_set_clear_color(color_gamma: Color128);
4301 pub fn render_get_clear_color() -> Color128;
4302 pub fn render_enable_skytex(show_sky: Bool32T);
4303 pub fn render_enabled_skytex() -> Bool32T;
4304
4305 pub fn render_global_texture(register_slot: i32, texture: TexT);
4306 pub fn render_global_buffer(register_slot: i32, buffer: MaterialBufferT);
4307 pub fn render_add_mesh(
4308 mesh: MeshT,
4309 material: MaterialT,
4310 transform: *const Matrix,
4311 color_linear: Color128,
4312 layer: RenderLayer,
4313 );
4314 pub fn render_add_model(model: ModelT, transform: *const Matrix, color_linear: Color128, layer: RenderLayer);
4315 pub fn render_add_model_mat(
4316 model: ModelT,
4317 material_override: MaterialT,
4318 transform: *const Matrix,
4319 color_linear: Color128,
4320 layer: RenderLayer,
4321 );
4322 pub fn render_blit(to_rendertarget: TexT, material: MaterialT);
4323
4324 pub fn render_screenshot(
4325 file_utf8: *const c_char,
4326 file_quality_100: i32,
4327 viewpoint: Pose,
4328 width: i32,
4329 height: i32,
4330 field_of_view_degrees: f32,
4331 );
4332 pub fn render_screenshot_capture(
4333 render_on_screenshot_callback: ::std::option::Option<
4334 unsafe extern "C" fn(color_buffer: *mut Color32, width: i32, height: i32, context: *mut c_void),
4335 >,
4336 viewpoint: Pose,
4337 width: i32,
4338 height: i32,
4339 field_of_view_degrees: f32,
4340 tex_format: TexFormat,
4341 context: *mut c_void,
4342 );
4343 pub fn render_screenshot_viewpoint(
4344 render_on_screenshot_callback: ::std::option::Option<
4345 unsafe extern "C" fn(color_buffer: *mut Color32, width: i32, height: i32, context: *mut c_void),
4346 >,
4347 camera: Matrix,
4348 projection: Matrix,
4349 width: i32,
4350 height: i32,
4351 layer_filter: RenderLayer,
4352 clear: RenderClear,
4353 viewport: Rect,
4354 tex_format: TexFormat,
4355 context: *mut c_void,
4356 );
4357 pub fn render_to(
4358 to_rendertarget: TexT,
4359 to_target_index: i32,
4360 override_material: MaterialT,
4361 camera: *const Matrix,
4362 projection: *const Matrix,
4363 layer_filter: RenderLayer,
4364 clear: RenderClear,
4365 viewport: Rect,
4366 );
4367
4368 pub fn render_MaterialTo(
4369 to_rendertarget: TexT,
4370 override_material: MaterialT,
4371 camera: *const Matrix,
4372 projection: *const Matrix,
4373 layer_filter: RenderLayer,
4374 clear: RenderClear,
4375 viewport: Rect,
4376 );
4377 pub fn render_get_device(device: *mut *mut c_void, context: *mut *mut c_void);
4378
4379}
4380
4381/// screenshot_capture trampoline
4382///
4383/// see also [`Renderer::screenshot_capture`]
4384unsafe extern "C" fn sc_capture_trampoline<F: FnMut(&[Color32], usize, usize)>(
4385 color_buffer: *mut Color32,
4386 width: i32,
4387 height: i32,
4388 context: *mut c_void,
4389) {
4390 let closure = unsafe { &mut *(context as *mut &mut F) };
4391 closure(
4392 unsafe { std::slice::from_raw_parts(color_buffer, (width * height) as usize) },
4393 width as usize,
4394 height as usize,
4395 )
4396}
4397
4398impl Renderer {
4399 /// Sets the root transform of the camera! This will be the identity matrix by default. The user’s head
4400 /// location will then be relative to this point. This is great to use if you’re trying to do teleportation,
4401 /// redirected walking, or just shifting the floor around.
4402 /// <https://stereokit.net/Pages/StereoKit/Renderer/CameraRoot.html>
4403 ///
4404 /// see also [`render_set_cam_root`]
4405 /// ### Examples
4406 /// ```
4407 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4408 /// use stereokit_rust::{maths::{Matrix, Vec3}, system::Renderer};
4409 ///
4410 /// let camera_root = Renderer::get_camera_root();
4411 /// assert_eq!(camera_root, Matrix::IDENTITY);
4412 ///
4413 /// let transform = Matrix::t([0.0, 0.0, -1.0]);
4414 ///
4415 /// test_steps!( // !!!! Get a proper main loop !!!!
4416 /// Renderer::camera_root(transform);
4417 /// let camera_root = Renderer::get_camera_root();
4418 /// assert_eq!(camera_root, transform);
4419 /// );
4420 /// ```
4421 pub fn camera_root(transform: impl Into<Matrix>) {
4422 unsafe { render_set_cam_root(&transform.into()) }
4423 }
4424
4425 /// This is the gamma space color the renderer will clear the screen to when beginning to draw a new frame.
4426 /// [`Color128::BLACK_TRANSPARENT`] is the default and is mandatory for some Passthrough solutions.
4427 /// <https://stereokit.net/Pages/StereoKit/Renderer/ClearColor.html>
4428 ///
4429 /// see also [`render_set_clear_color`]
4430 /// ### Examples
4431 /// ```
4432 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4433 /// use stereokit_rust::{system::Renderer,
4434 /// render_list::RenderList, util::{named_colors, Color128}};
4435 ///
4436 /// // We want to replace the gray background with a dark blue sky:
4437 /// let mut primary = RenderList::primary();
4438 /// assert_eq!(primary.get_count(), 0);
4439 ///
4440 ///
4441 /// assert_eq!(Renderer::get_clear_color(), Color128::BLACK_TRANSPARENT);
4442 /// Renderer::clear_color(named_colors::BLUE);
4443 ///
4444 /// filename_scr = "screenshots/renderer.jpeg";
4445 /// test_steps!( // !!!! Get a proper main loop !!!!
4446 ///
4447 /// primary.clear();
4448 ///
4449 /// assert_eq!(Renderer::get_clear_color(), named_colors::BLUE.into());
4450 ///
4451 /// );
4452 /// ```
4453 pub fn clear_color(color_gamma: impl Into<Color128>) {
4454 unsafe { render_set_clear_color(color_gamma.into()) }
4455 }
4456
4457 /// Enables or disables rendering of the skybox texture! It’s enabled by default on Opaque displays, and completely
4458 /// unavailable for transparent displays.
4459 /// <https://stereokit.net/Pages/StereoKit/Renderer/EnableSky.html>
4460 ///
4461 /// see also [`render_enable_skytex`] [`Renderer::clear_color`] [`crate::tex::SHCubemap`]
4462 /// ### Examples
4463 /// ```
4464 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4465 /// use stereokit_rust::system::Renderer;
4466 ///
4467 /// assert_eq!(Renderer::get_enable_sky(), true);
4468 ///
4469 /// Renderer::enable_sky(false);
4470 /// assert_eq!(Renderer::get_enable_sky(), false);
4471 ///
4472 /// Renderer::enable_sky(true);
4473 /// assert_eq!(Renderer::get_enable_sky(), true);
4474 /// ```
4475 pub fn enable_sky(enable: bool) {
4476 unsafe { render_enable_skytex(enable as Bool32T) }
4477 }
4478
4479 /// By default, StereoKit renders all first-person layers. This is a bit flag that allows you to change which layers
4480 /// StereoKit renders for the primary viewpoint. To change what layers a visual is on, use a Draw method that
4481 /// includes a RenderLayer as a parameter.
4482 /// <https://stereokit.net/Pages/StereoKit/Renderer/LayerFilter.html>
4483 ///
4484 /// see also [`render_set_filter`]
4485 /// ### Examples
4486 /// ```
4487 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4488 /// use stereokit_rust::system::{Renderer, RenderLayer};
4489 ///
4490 /// assert_eq!(Renderer::get_layer_filter(), RenderLayer::AllFirstPerson);
4491 ///
4492 /// Renderer::layer_filter(RenderLayer::All);
4493 /// assert_eq!(Renderer::get_layer_filter(), RenderLayer::All);
4494 ///
4495 /// Renderer::layer_filter(RenderLayer::AllFirstPerson);
4496 /// assert_eq!(Renderer::get_layer_filter(), RenderLayer::AllFirstPerson);
4497 /// ```
4498 pub fn layer_filter(filter: RenderLayer) {
4499 unsafe { render_set_filter(filter) }
4500 }
4501
4502 /// Allows you to set the multisample (MSAA) level of the render surface. Valid values are 1, 2, 4, 8, 16, though
4503 /// some OpenXR runtimes may clamp this to lower values. Note that while this can greatly smooth out edges, it also
4504 /// greatly increases RAM usage and fill rate, so use it sparingly. Only works in XR mode. If known in advance, set
4505 /// this via [`crate::sk::SkSettings`] in initialization. This is a very costly change to make.
4506 /// <https://stereokit.net/Pages/StereoKit/Renderer/Multisample.html>
4507 ///
4508 /// see also [`render_set_multisample`]
4509 /// ### Examples
4510 /// ```
4511 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4512 /// use stereokit_rust::system::Renderer;
4513 ///
4514 /// assert_eq!(Renderer::get_multisample(), 1);
4515 ///
4516 /// Renderer::multisample(4);
4517 /// assert_eq!(Renderer::get_multisample(), 4);
4518 ///
4519 /// Renderer::multisample(1);
4520 /// assert_eq!(Renderer::get_multisample(), 1);
4521 /// ```
4522 pub fn multisample(level: i32) {
4523 unsafe { render_set_multisample(level) }
4524 }
4525
4526 /// For flatscreen applications only! This allows you to change the camera projection between perspective and
4527 /// orthographic projection. This may be of interest for some category of UI work, but is generally a niche piece of
4528 /// functionality.
4529 /// Swapping between perspective and orthographic will also switch the clipping planes and field of view to the
4530 /// values associated with that mode. See set_clip/set_fov for perspective, and set_ortho_clip/set_ortho_size for
4531 /// orthographic.
4532 /// <https://stereokit.net/Pages/StereoKit/Renderer/Projection.html>
4533 ///
4534 /// see also [`render_set_projection`]
4535 /// ### Examples
4536 /// ```
4537 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4538 /// use stereokit_rust::system::{Renderer, Projection};
4539 ///
4540 /// assert_eq!(Renderer::get_projection(), Projection::Perspective);
4541 ///
4542 /// Renderer::projection(Projection::Orthographic);
4543 /// assert_eq!(Renderer::get_projection(), Projection::Orthographic);
4544 ///
4545 /// Renderer::projection(Projection::Perspective);
4546 /// assert_eq!(Renderer::get_projection(), Projection::Perspective);
4547 /// ```
4548 pub fn projection(projection: Projection) {
4549 unsafe { render_set_projection(projection) }
4550 }
4551
4552 /// OpenXR has a recommended default for the main render surface, this value allows you to set SK’s surface to a
4553 /// multiple of the recommended size. Note that the final resolution may also be clamped or quantized. Only works in
4554 /// XR mode. If known in advance, set this via [`crate::sk::SkSettings`] in initialization. This is a very costly change to make.
4555 /// Consider if Viewport_scaling will work for you instead, and prefer that.
4556 /// <https://stereokit.net/Pages/StereoKit/Renderer/Scaling.html>
4557 ///
4558 /// see also [`render_set_scaling`]
4559 /// ### Examples
4560 /// ```
4561 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4562 /// use stereokit_rust::system::Renderer;
4563 ///
4564 /// assert_eq!(Renderer::get_scaling(), 1.0);
4565 ///
4566 /// Renderer::scaling(0.5);
4567 /// assert_eq!(Renderer::get_scaling(), 0.5);
4568 ///
4569 /// Renderer::scaling(1.0);
4570 /// assert_eq!(Renderer::get_scaling(), 1.0);
4571 /// ```
4572 pub fn scaling(scaling: f32) {
4573 unsafe { render_set_scaling(scaling) }
4574 }
4575
4576 /// This allows you to trivially scale down the area of the swapchain that StereoKit renders to! This can be used
4577 /// to boost performance in situations where full resolution is not needed, or to reduce GPU time. This value is
4578 /// locked to the 0-1 range
4579 /// <https://stereokit.net/Pages/StereoKit/Renderer/ViewportScaling.html>
4580 ///
4581 /// see also [`render_set_viewport_scaling`]
4582 /// ### Examples
4583 /// ```
4584 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4585 /// use stereokit_rust::system::Renderer;
4586 ///
4587 /// assert_eq!(Renderer::get_viewport_scaling(), 1.0);
4588 ///
4589 /// Renderer::viewport_scaling(0.5);
4590 /// assert_eq!(Renderer::get_viewport_scaling(), 0.5);
4591 ///
4592 /// Renderer::viewport_scaling(1.0);
4593 /// assert_eq!(Renderer::get_viewport_scaling(), 1.0);
4594 /// ```
4595 pub fn viewport_scaling(scaling: f32) {
4596 unsafe { render_set_viewport_scaling(scaling) }
4597 }
4598
4599 /// Sets the lighting information for the scene! You can build one through [`SphericalHarmonics::from_lights`], or grab
4600 /// one from [`crate::tex::SHCubemap`]
4601 /// <https://stereokit.net/Pages/StereoKit/Renderer/SkyLight.html>
4602 ///
4603 /// see also [`render_set_skylight`] [`crate::tex::SHCubemap`] [`crate::util::SHLight`]
4604 /// ### Examples
4605 /// ```
4606 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4607 /// use stereokit_rust::{system::Renderer, maths::Vec3,
4608 /// util::{named_colors, SphericalHarmonics, SHLight}};
4609 ///
4610 /// let light1 = SHLight::new([0.0, 1.0, 0.0], named_colors::WHITE);
4611 /// let light2 = SHLight::new([0.0, 0.0, 1.0], named_colors::WHITE);
4612 ///
4613 /// let mut sh = SphericalHarmonics::from_lights(&[light1, light2]);
4614 ///
4615 /// Renderer::sky_light(sh);
4616 /// let sky_light = Renderer::get_sky_light();
4617 ///
4618 /// assert_eq!(sky_light, sh);
4619 /// assert_eq!(sh.get_dominent_light_direction(),
4620 /// Vec3 { x: -0.0, y: -1.0, z: -1.0 }.get_normalized())
4621 /// ```
4622 pub fn sky_light(light_info: SphericalHarmonics) {
4623 unsafe { render_set_skylight(&light_info) }
4624 }
4625
4626 /// Set a cubemap skybox texture for rendering a background! This is only visible on Opaque displays, since
4627 /// transparent displays have the real world behind them already! StereoKit has a a default procedurally generated
4628 /// skybox. You can load one with [`crate::tex::SHCubemap`]. If you’re trying to affect the lighting,
4629 /// see [`Renderer::sky_light`].
4630 /// <https://stereokit.net/Pages/StereoKit/Renderer/SkyTex.html>
4631 ///
4632 /// see also [`render_set_skytex`] [`crate::tex::SHCubemap`]
4633 /// ### Examples
4634 /// ```
4635 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4636 /// use stereokit_rust::{system::{Renderer, Assets}, tex::{Tex, TexType}};
4637 ///
4638 /// let sky_tex = Tex::from_file("hdri/sky_dawn.jpeg", true, None)
4639 /// .expect("sky_tex should be created");
4640 ///
4641 /// Assets::block_for_priority(i32::MAX);
4642 ///
4643 /// Renderer::sky_tex(&sky_tex);
4644 /// let sky_tex_get = Renderer::get_sky_tex();
4645 ///
4646 /// assert_eq!(sky_tex_get, sky_tex);
4647 /// ```
4648 pub fn sky_tex(tex: impl AsRef<Tex>) {
4649 unsafe { render_set_skytex(tex.as_ref().0.as_ptr()) }
4650 }
4651
4652 /// This is the Material that StereoKit is currently using to draw the skybox! It needs a special shader that's
4653 /// tuned for a full-screen quad. If you just want to change the skybox image, try setting [`Renderer::sky_tex`]
4654 /// instead.
4655 ///
4656 /// This value will never be null! If you try setting this to null, it will assign SK's built-in default sky
4657 /// material. If you want to turn off the skybox, see [`Renderer::enable_sky`] instead.
4658 ///
4659 /// Recommended Material settings would be:
4660 /// - DepthWrite: false
4661 /// - DepthTest: LessOrEq
4662 /// - QueueOffset: 100
4663 ///
4664 /// <https://stereokit.net/Pages/StereoKit/Renderer/SkyMaterial.html>
4665 ///
4666 /// see also [`render_set_skymaterial`] [`crate::tex::SHCubemap`]
4667 /// ### Examples
4668 /// ```
4669 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4670 /// use stereokit_rust::{system::Renderer, material::Material, util::named_colors};
4671 ///
4672 /// let material = Material::pbr().copy();
4673 /// Renderer::sky_material(&material);
4674 ///
4675 /// let same_material = Renderer::get_sky_material();
4676 /// assert_eq!(same_material, material);
4677 /// ```
4678 pub fn sky_material(material: impl AsRef<Material>) {
4679 unsafe { render_set_skymaterial(material.as_ref().0.as_ptr()) }
4680 }
4681
4682 /// Adds a mesh to the render queue for this frame! If the Hierarchy has a transform on it, that transform is
4683 /// combined with the Matrix provided here.
4684 /// <https://stereokit.net/Pages/StereoKit/Renderer/Add.html>
4685 /// * `mesh` - A valid Mesh you wish to draw.
4686 /// * `material` - A Material to apply to the Mesh.
4687 /// * `transform` - A Matrix that will transform the mesh from Model Space into the current Hierarchy Space.
4688 /// * `color` - A per-instance linear space color value to pass into the shader! Normally this gets used like a
4689 /// material tint. If you’re adventurous and don’t need per-instance colors, this is a great spot to pack in
4690 /// extra per-instance data for the shader! If None has default value of WHITE
4691 /// * `layer` - All visuals are rendered using a layer bit-flag. By default, all layers are rendered, but this can be
4692 /// useful for filtering out objects for different rendering purposes! For example: rendering a mesh over the
4693 /// user’s head from a 3rd person perspective, but filtering it out from the 1st person perspective.If None has
4694 /// default value of RenderLayer::Layer0
4695 ///
4696 /// see also [`render_add_mesh`] [`Mesh::draw`]
4697 /// ### Examples
4698 /// ```
4699 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4700 /// use stereokit_rust::{system::{Renderer, RenderLayer}, maths::{Vec3, Matrix},
4701 /// mesh::Mesh, material::Material, util::named_colors};
4702 ///
4703 /// let sphere = Mesh::generate_sphere(0.5, None);
4704 /// let material = Material::pbr();
4705 /// let transform1 = Matrix::t([-0.5, 0.0, 0.0]);
4706 /// let transform2 = Matrix::t([ 0.5, 0.0, -1.0]);
4707 ///
4708 /// test_steps!( // !!!! Get a proper main loop !!!!
4709 ///
4710 /// Renderer::add_mesh(token, &sphere, &material, transform1,
4711 /// Some(named_colors::RED.into()), Some(RenderLayer::Layer0));
4712 ///
4713 /// Renderer::add_mesh(token, &sphere, &material, transform2, None, None);
4714 /// );
4715 /// ```
4716 pub fn add_mesh(
4717 _token: &MainThreadToken,
4718 mesh: impl AsRef<Mesh>,
4719 material: impl AsRef<Material>,
4720 transform: impl Into<Matrix>,
4721 color: Option<Color128>,
4722 layer: Option<RenderLayer>,
4723 ) {
4724 let color = color.unwrap_or(Color128::WHITE);
4725 let layer = layer.unwrap_or(RenderLayer::Layer0);
4726 unsafe {
4727 render_add_mesh(mesh.as_ref().0.as_ptr(), material.as_ref().0.as_ptr(), &transform.into(), color, layer)
4728 }
4729 }
4730
4731 /// Adds a Model to the render queue for this frame! If the Hierarchy has a transform on it, that transform is
4732 /// combined with the Matrix provided here.
4733 /// <https://stereokit.net/Pages/StereoKit/Renderer/Add.html>
4734 /// * `model` - A valid Model you wish to draw.
4735 /// * `transform` - A Matrix that will transform the Model from Model Space into the current Hierarchy Space.
4736 /// * `color` - A per-instance linear space color value to pass into the shader! Normally this gets used like a
4737 /// material tint. If you’re adventurous and don’t need per-instance colors, this is a great spot to pack in
4738 /// extra per-instance data for the shader! If None has default value of WHITE
4739 /// * `layer` - All visuals are rendered using a layer bit-flag. By default, all layers are rendered, but this can
4740 /// be useful for filtering out objects for different rendering purposes! For example: rendering a mesh over the
4741 /// user’s head from a 3rd person perspective, but filtering it out from the 1st person perspective. If None has
4742 /// default value of RenderLayer::Layer0
4743 ///
4744 /// see also [`render_add_model`] [`Model::draw`] [`Model::draw_with_material`]
4745 /// ### Examples
4746 /// ```
4747 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4748 /// use stereokit_rust::{system::{Renderer, RenderLayer}, maths::{Vec3, Matrix},
4749 /// model::Model, util::named_colors};
4750 ///
4751 /// let model = Model::from_file("plane.glb", None).expect("plane.glb should be there");
4752 /// let transform1 = Matrix::t([-2.5, 0.0, -5.0]);
4753 /// let transform2 = Matrix::t([ 2.5, 0.0, -5.0]);
4754 ///
4755 /// test_steps!( // !!!! Get a proper main loop !!!!
4756 ///
4757 /// Renderer::add_model(token, &model, transform1,
4758 /// Some(named_colors::RED.into()), Some(RenderLayer::Layer0));
4759 ///
4760 /// Renderer::add_model(token, &model, transform2, None, None);
4761 /// );
4762 /// ```
4763 pub fn add_model(
4764 _token: &MainThreadToken,
4765 model: impl AsRef<Model>,
4766 transform: impl Into<Matrix>,
4767 color: Option<Color128>,
4768 layer: Option<RenderLayer>,
4769 ) {
4770 let color = color.unwrap_or(Color128::WHITE);
4771 let layer = layer.unwrap_or(RenderLayer::Layer0);
4772 unsafe { render_add_model(model.as_ref().0.as_ptr(), &transform.into(), color, layer) }
4773 }
4774
4775 /// Renders a Material onto a rendertarget texture! StereoKit uses a 4 vert quad stretched over the surface of the
4776 /// texture, and renders the material onto it to the texture.
4777 /// <https://stereokit.net/Pages/StereoKit/Renderer/Blit.html>
4778 /// * `to_render_target` - A texture that’s been set up as a render target!
4779 /// * `material` - This material is rendered onto the texture! Set it up like you would if you were applying it to
4780 /// a plane, or quad mesh.
4781 ///
4782 /// see also [`render_blit`]
4783 /// ### Examples
4784 /// ```
4785 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4786 /// use stereokit_rust::{system::Renderer, material::Material, tex::Tex};
4787 ///
4788 /// let material = Material::pbr();
4789 /// let tex = Tex::render_target(200,200, None, None, None)
4790 /// .expect("RenderTarget should be created");
4791 ///
4792 /// test_steps!( // !!!! Get a proper main loop !!!!
4793 /// Renderer::blit(&tex, &material);
4794 /// );
4795 /// ```
4796 pub fn blit(to_render_target: impl AsRef<Tex>, material: impl AsRef<Material>) {
4797 unsafe { render_blit(to_render_target.as_ref().0.as_ptr(), material.as_ref().0.as_ptr()) }
4798 }
4799
4800 /// The capture_filter is a layer mask for Mixed Reality Capture, or 2nd person observer rendering. On HoloLens and
4801 /// WMR, this is the video rendering feature. This allows you to hide, or reveal certain draw calls when rendering
4802 /// video output.
4803 ///
4804 /// By default, the capture_filter will always be the same as [`Renderer::layer_filter`], overriding this will mean this
4805 /// filter no longer updates with layer_filter.
4806 /// <https://stereokit.net/Pages/StereoKit/Renderer/OverrideCaptureFilter.html>
4807 /// * `use_override_filter` - Enables (true) or disables (false) the overridden filter value provided here.
4808 /// * `override_filter` - The filter for capture rendering to use. This is ignored if useOverrideFilter is false.
4809 ///
4810 /// see also [`render_override_capture_filter`]
4811 /// ### Examples
4812 /// ```
4813 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4814 /// use stereokit_rust::{system::{Renderer, RenderLayer},
4815 /// maths::Matrix, mesh::Mesh, material::Material};
4816 ///
4817 /// let sphere = Mesh::generate_sphere(0.2, None);
4818 /// let material = Material::pbr();
4819 ///
4820 /// assert_eq!(Renderer::has_capture_filter(), false);
4821 /// assert_eq!(Renderer::get_capture_filter(), RenderLayer::AllFirstPerson);
4822 ///
4823 /// Renderer::override_capture_filter(true, RenderLayer::Layer1);
4824 ///
4825 /// assert_eq!(Renderer::has_capture_filter(), true);
4826 /// assert_eq!(Renderer::get_capture_filter(), RenderLayer::Layer1);
4827 ///
4828 ///
4829 /// test_steps!( // !!!! Get a proper main loop !!!!
4830 /// sphere.draw(token, &material, Matrix::IDENTITY, None, Some(RenderLayer::Layer1));
4831 /// );
4832 ///
4833 /// Renderer::override_capture_filter(false, RenderLayer::Layer0);
4834 /// assert_eq!(Renderer::has_capture_filter(), false);
4835 /// ```
4836 pub fn override_capture_filter(use_override_filter: bool, override_filter: RenderLayer) {
4837 unsafe { render_override_capture_filter(use_override_filter as Bool32T, override_filter) }
4838 }
4839
4840 /// This renders the current scene to the indicated rendertarget texture, from the specified viewpoint. This call
4841 /// enqueues a render that occurs immediately before the screen itself is rendered.
4842 /// <https://stereokit.net/Pages/StereoKit/Renderer/RenderTo.html>
4843 /// * `to_render_target` - The texture to which the scene will be rendered to. This must be a Rendertarget type
4844 /// texture.
4845 /// * `to_target_index` - (Optional) Index of the render target's array slice we want to draw to. If None, defaults
4846 /// to 0. This is only relevant for array/render target textures with multiple slices.
4847 /// * `override_material` - (Optional) A material that will override all materials used during this render pass.
4848 /// This can be useful for depth pre-pass, shadow map rendering, or special effects. If None, materials attached
4849 /// to individual draw items are used (normal behavior).
4850 /// * `camera` - A TRS matrix representing the location and orientation of the camera. This matrix gets inverted
4851 /// later on, so no need to do it yourself.
4852 /// * `projection` - The projection matrix describes how the geometry is flattened onto the draw surface. Normally,
4853 /// you’d use Matrix::perspective, and occasionally Matrix::orthographic might be helpful as well.
4854 /// * `layer_filter` - This is a bit flag that allows you to change which layers StereoKit renders for this particular
4855 /// render viewpoint. To change what layers a visual is on, use a Draw method that includes a RenderLayer as a
4856 /// parameter. If None has default value of RenderLayer::ALL
4857 /// * `clear` - Describes if and how the rendertarget should be cleared before rendering. Note that clearing the
4858 /// target is unaffected by the viewport, so this will clean the entire surface! If None has default value of
4859 /// RenderClear::All
4860 /// * `vieport` - Allows you to specify a region of the rendertarget to draw to! This is in normalized coordinates,
4861 /// 0-1. If the width of this value is zero, then this will render to the entire texture. If None has default value
4862 /// of (0, 0, 0, 0)
4863 ///
4864 /// see also [`render_to`]
4865 /// ### Examples
4866 /// ```
4867 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4868 /// use stereokit_rust::{system::{Renderer, RenderLayer}, maths::{Vec3, Quat, Matrix},
4869 /// render_list::RenderList, tex::{Tex, TexType, TexFormat},
4870 /// mesh::Mesh, model::Model, material::Material, util::named_colors};
4871 ///
4872 /// let sun = Mesh::generate_sphere(5.0, None);
4873 /// let material = Material::pbr();
4874 /// let transform_sun = Matrix::t([-6.0, -1.0, -10.0]);
4875 ///
4876 /// let plane = Mesh::generate_plane_up([1.0,1.0], None, true);
4877 /// let mut material = Material::unlit().copy();
4878 /// let tex = Tex::render_target(200,200, None, None, None)
4879 /// .expect("RenderTarget should be created");
4880 /// material.diffuse_tex(&tex);
4881 /// let transform_plane = Matrix::t([0.0, -0.55, 0.0]);
4882 ///
4883 /// let camera = Matrix::t_r(Vec3::Z * 2.0, Quat::look_at(Vec3::Z, Vec3::ZERO, None));
4884 /// let projection = Matrix::perspective(90.0, 1.0, 0.1, 20.0);
4885 ///
4886 /// test_steps!( // !!!! Get a proper main loop !!!!
4887 ///
4888 /// Renderer::add_mesh(token, &sun, &material, transform_sun,
4889 /// Some(named_colors::RED.into()), None);
4890 ///
4891 /// Renderer::add_mesh(token, &plane, &material, transform_plane,
4892 /// None, None);
4893 ///
4894 /// Renderer::render_to(token, &tex, None, None, camera, projection, None, None, None);
4895 /// );
4896 /// ```
4897 #[allow(clippy::too_many_arguments)]
4898 pub fn render_to<M: Into<Matrix>>(
4899 _token: &MainThreadToken,
4900 to_render_target: impl AsRef<Tex>,
4901 to_target_index: Option<i32>,
4902 override_material: Option<&Material>,
4903 camera: M,
4904 projection: M,
4905 layer_filter: Option<RenderLayer>,
4906 clear: Option<RenderClear>,
4907 viewport: Option<Rect>,
4908 ) {
4909 let to_target_index = to_target_index.unwrap_or(0);
4910 let override_material_ptr = override_material.map(|m| m.0.as_ptr()).unwrap_or(std::ptr::null_mut());
4911 let layer_filter = layer_filter.unwrap_or(RenderLayer::All);
4912 let clear = clear.unwrap_or(RenderClear::All);
4913 let viewport = viewport.unwrap_or_default();
4914
4915 unsafe {
4916 render_to(
4917 to_render_target.as_ref().0.as_ptr(),
4918 to_target_index,
4919 override_material_ptr,
4920 &camera.into(),
4921 &projection.into(),
4922 layer_filter,
4923 clear,
4924 viewport,
4925 )
4926 }
4927 }
4928
4929 /// This attaches a texture resource globally across all shaders. StereoKit uses this to attach the sky cubemap for
4930 /// use in reflections across all materials (register 11). It can be used for things like shadowmaps, wind data, etc.
4931 /// Prefer a higher registers (11+) to prevent conflicting with normal Material textures.
4932 /// <https://stereokit.net/Pages/StereoKit/Renderer/SetGlobalTexture.html>
4933 /// * `texture_register` - The texture resource register the texture will bind to. SK uses register 11 already, so
4934 /// values above that should be fine.
4935 /// * `tex` - The texture to assign globally. Setting None here will clear any texture that is currently bound.
4936 ///
4937 /// see also [`render_global_texture`]
4938 /// ### Examples
4939 /// ```
4940 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4941 /// use stereokit_rust::{system::{Renderer, RenderLayer}, tex::{Tex, TexFormat},
4942 /// maths::Matrix, util::named_colors};
4943 ///
4944 /// let tex = Tex::from_file("hdri/sky_dawn.jpeg", true, None)
4945 /// .expect("tex should be created");
4946 ///
4947 /// test_steps!( // !!!! Get a proper main loop !!!!
4948 /// if iter < 2 {
4949 /// Renderer::set_global_texture(token, 12, Some(&tex));
4950 /// } else {
4951 /// Renderer::set_global_texture(token, 12, None);
4952 /// }
4953 /// );
4954 /// ```
4955 pub fn set_global_texture(_token: &MainThreadToken, texture_register: i32, tex: Option<&Tex>) {
4956 if let Some(tex) = tex {
4957 unsafe { render_global_texture(texture_register, tex.0.as_ptr()) }
4958 } else {
4959 unsafe { render_global_texture(texture_register, null_mut()) }
4960 }
4961 }
4962
4963 /// This attaches a buffer resource globally across all shaders. StereoKit uses this to attach the stereokit
4964 /// rendering constants. It can be used for things like shadowmaps, wind data, etc.
4965 /// <https://stereokit.net/Pages/StereoKit/Renderer/SetGlobalBuffer.html>
4966 /// * `buffer_register` - Valid values are 3-16. This is the register id that this data will be bound to. In HLSL,
4967 /// you'll see the slot id for '3' indicated like this `: register(b3)`
4968 /// * `buffer` - The data buffer you would like to bind
4969 ///
4970 /// see also [`render_global_buffer`] []
4971 /// ### Examples
4972 /// ```
4973 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4974 /// use stereokit_rust::{system::Renderer, material::MaterialBuffer};
4975 ///
4976 /// #[repr(C)]
4977 /// #[derive(Default, Copy, Clone)]
4978 /// struct Globals { time: f32, padding: [f32;3] }
4979 ///
4980 /// let mut globals = Globals { time: 1.0, ..Default::default() };
4981 /// let buffer = MaterialBuffer::<Globals>::new();
4982 /// buffer.set(&mut globals as *mut _);
4983 ///
4984 /// test_steps!( // !!!! Get a proper main loop !!!!
4985 /// // Bind the buffer to slot 3 so shaders can read it.
4986 /// Renderer::set_global_buffer( 3, &buffer);
4987 /// );
4988 /// ```
4989 pub fn set_global_buffer<T>(buffer_register: i32, buffer: &MaterialBuffer<T>) {
4990 unsafe {
4991 render_global_buffer(buffer_register, buffer.as_ref().as_ptr());
4992 }
4993 }
4994
4995 /// Unbinds any global MaterialBuffer previously bound to this register slot (3-16). Equivalent to passing None
4996 /// to [`Renderer::set_global_buffer`]. Provided as a convenience method.
4997 /// <https://stereokit.net/Pages/StereoKit/Renderer/SetGlobalBuffer.html>
4998 /// * `buffer_register` - Valid values are 3-16. This is the register id the data was bound to.
4999 ///
5000 /// see also [`render_global_buffer`] [`Renderer::set_global_buffer`]
5001 /// ### Examples
5002 /// ```
5003 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5004 /// use stereokit_rust::{system::Renderer, material::MaterialBuffer};
5005 ///
5006 /// #[repr(C)]
5007 /// #[derive(Default, Copy, Clone)]
5008 /// struct Globals { value: f32, padding: [f32;3] }
5009 /// let buffer = MaterialBuffer::<Globals>::new();
5010 /// test_steps!(
5011 /// Renderer::set_global_buffer(4, &buffer);
5012 /// // Later we decide to unbind it completely
5013 /// if iter == number_of_steps -1 {
5014 /// Renderer::unset_global_buffer(4);
5015 /// }
5016 /// );
5017 /// ```
5018 pub fn unset_global_buffer(buffer_register: i32) {
5019 unsafe { render_global_buffer(buffer_register, std::ptr::null_mut()) }
5020 }
5021
5022 /// Schedules a screenshot for the end of the frame! The view will be rendered from the given pose, with a
5023 /// resolution the same size as the screen’s surface. It’ll be saved as a JPEG or PNG file depending on the filename
5024 /// extension provided.
5025 /// <https://stereokit.net/Pages/StereoKit/Renderer/Screenshot.html>
5026 /// * `filename` - Filename to write the screenshot to! This will be a PNG if the extension ends with (case
5027 /// insensitive) “.png”, and will be a 90 quality JPEG if it ends with anything else.
5028 /// * `file_quality` - For JPEG files, this is the compression quality of the file from 0-100, 100 being highest
5029 /// quality, 0 being smallest size. SK uses a default of 90 here.
5030 /// * `viewpoint` - is Pose::look_at(from_point, looking_at_point)
5031 /// * `width` - Size of the screenshot horizontally, in pixels.
5032 /// * `height`- Size of the screenshot vertically, in pixels
5033 /// * `field_of_view` - The angle of the viewport, in degrees. If None will use default value of 90°
5034 ///
5035 /// see also [`render_screenshot`]
5036 /// see example in [`Renderer`]
5037 pub fn screenshot(
5038 _token: &MainThreadToken,
5039 filename: impl AsRef<Path>,
5040 file_quality: i32,
5041 viewpoint: Pose,
5042 width: i32,
5043 height: i32,
5044 field_of_view: Option<f32>,
5045 ) {
5046 let path = filename.as_ref();
5047 let c_str = CString::new(path.to_str().unwrap_or("!!!path.to_str error!!!").to_owned()).unwrap();
5048 let field_of_view = field_of_view.unwrap_or(90.0);
5049 unsafe { render_screenshot(c_str.as_ptr(), file_quality, viewpoint, width, height, field_of_view) }
5050 }
5051
5052 /// Schedules a screenshot for the end of the frame! The view will be rendered from the given position at the given
5053 /// point, with a resolution the same size as the screen’s surface. This overload allows for retrieval of the color
5054 /// data directly from the render thread! You can use the color data directly by saving/processing it inside your
5055 /// callback, or you can keep the data alive for as long as it is referenced.
5056 /// <https://stereokit.net/Pages/StereoKit/Renderer/Screenshot.html>
5057 /// * `on_screenshot` : closure |&[Color32], width:usize, height:usize|
5058 /// * `viewpoint` - is Pose::look_at(from_point, looking_at_point)
5059 /// * `width` - Size of the screenshot horizontally, in pixels.
5060 /// * `height`- Size of the screenshot vertically, in pixels
5061 /// * `field_of_view` - The angle of the viewport, in degrees. If None will use default value of 90°
5062 /// * `tex_format` - The pixel format of the color data. If None will use default value of TexFormat::RGBA32
5063 ///
5064 /// see also [`render_screenshot_capture`]
5065 /// ### Examples
5066 /// ```
5067 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5068 /// use stereokit_rust::{system::{Renderer, RenderLayer}, maths::{Vec3, Quat, Pose, Matrix},
5069 /// render_list::RenderList, tex::{Tex, TexType, TexFormat},
5070 /// mesh::Mesh, model::Model, material::Material, util::named_colors};
5071 ///
5072 /// let sun = Mesh::generate_sphere(7.0, None);
5073 /// let material_sun = Material::pbr();
5074 /// let transform_sun = Matrix::t([-6.0, 3.0, -10.0]);
5075 ///
5076 /// let plane = Mesh::generate_plane_up([1.0,1.0], None, true);
5077 /// let mut material = Material::unlit().copy();
5078 /// let mut tex = Tex::render_target(200,200, None, None, None)
5079 /// .expect("RenderTarget should be created");
5080 /// tex.id("CAPTURE_TEXTURE_ID");
5081 /// material.diffuse_tex(&tex);
5082 /// let transform_plane = Matrix::t([0.0, -0.55, 0.0]);
5083 ///
5084 /// let camera_pose = Pose::new([0.0, 0.0, 1.0], None);
5085 ///
5086 /// number_of_steps = 20;
5087 /// filename_scr = "screenshots/screenshot_capture.jpeg";
5088 /// test_screenshot!( // !!!! Get a proper main loop !!!!
5089 ///
5090 /// Renderer::add_mesh(token, &sun, &material_sun, transform_sun,
5091 /// Some(named_colors::RED.into()), None);
5092 ///
5093 /// Renderer::add_mesh(token, &plane, &material, transform_plane,
5094 /// None, None);
5095 ///
5096 /// Renderer::screenshot_capture( token,
5097 /// move |dots, width, height| {
5098 /// let tex = Tex::find("CAPTURE_TEXTURE_ID").ok();
5099 /// match tex {
5100 /// Some(mut tex) => tex.set_colors32(width, height, dots),
5101 /// None => panic!("CAPTURE_TEXTURE_ID not found!"),
5102 /// };
5103 /// },
5104 /// camera_pose, 200, 200, None, None
5105 /// );
5106 /// );
5107 /// ```
5108 /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/screenshot_capture.jpeg" alt="screenshot" width="200">
5109 pub fn screenshot_capture<F: FnMut(&[Color32], usize, usize)>(
5110 _token: &MainThreadToken,
5111 mut on_screenshot: F,
5112 viewpoint: Pose,
5113 width: i32,
5114 height: i32,
5115 field_of_view: Option<f32>,
5116 tex_format: Option<TexFormat>,
5117 ) {
5118 let field_of_view = field_of_view.unwrap_or(90.0);
5119 let tex_format = tex_format.unwrap_or(TexFormat::RGBA32);
5120 let mut closure = &mut on_screenshot;
5121 unsafe {
5122 render_screenshot_capture(
5123 Some(sc_capture_trampoline::<F>),
5124 viewpoint,
5125 width,
5126 height,
5127 field_of_view,
5128 tex_format,
5129 &mut closure as *mut _ as *mut c_void,
5130 )
5131 }
5132 }
5133
5134 /// Schedules a screenshot for the end of the frame! The view will be rendered from the given position at the given
5135 /// point, with a resolution the same size as the screen’s surface. This overload allows for retrieval of the color
5136 /// data directly from the render thread! You can use the color data directly by saving/processing it inside your
5137 /// callback, or you can keep the data alive for as long as it is referenced.
5138 /// <https://stereokit.net/Pages/StereoKit/Renderer/Screenshot.html>
5139 /// * `on_screenshot` : closure |&[Color32], width:usize, height:usize|
5140 /// * `camera` - A TRS matrix representing the location and orientation of the camera. This matrix gets inverted
5141 /// later on, so no need to do it yourself.
5142 /// * `projection` - The projection matrix describes how the geometry is flattened onto the draw surface. Normally,
5143 /// you’d use [`Matrix::perspective`], and occasionally [`Matrix::orthographic`] might be helpful as well.
5144 /// * `width` - Size of the screenshot horizontally, in pixels.
5145 /// * `height`- Size of the screenshot vertically, in pixels
5146 /// * `render_layer` - This is a bit flag that allows you to change which layers StereoKit renders for this
5147 /// particular render viewpoint. To change what layers a visual is on, use a Draw method that includes a
5148 /// RenderLayer as a parameter. If None will use default value of All
5149 /// * `clear` - Describes if and how the rendertarget should be cleared before rendering. Note that clearing the
5150 /// target is unaffected by the viewport, so this will clean the entire surface! If None wille use default value
5151 /// of All
5152 /// * `viewport` - Allows you to specify a region of the rendertarget to draw to! This is in normalized coordinates,
5153 /// 0-1. If the width of this value is zero, then this will render to the entire texture. If None has default value
5154 /// of (0, 0, 0, 0)
5155 /// * `tex_format` - The pixel format of the color data. If None will use default value of TexFormat::RGBA32
5156 ///
5157 /// see also [`render_screenshot_viewpoint`]
5158 /// ### Examples
5159 /// ```
5160 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5161 /// use stereokit_rust::{system::{Renderer, RenderLayer}, maths::{Vec3, Quat, Pose, Matrix},
5162 /// render_list::RenderList, tex::{Tex, TexType, TexFormat},
5163 /// mesh::Mesh, model::Model, material::Material, util::named_colors};
5164 ///
5165 /// let sun = Mesh::generate_sphere(7.0, None);
5166 /// let material_sun = Material::pbr();
5167 /// let transform_sun = Matrix::t([6.0, 3.0, -10.0]);
5168 ///
5169 /// let plane = Mesh::generate_plane_up([1.0,1.0], None, true);
5170 /// let mut material = Material::unlit().copy();
5171 /// let mut tex = Tex::gen_color(named_colors::VIOLET, 200, 200, TexType::Rendertarget, TexFormat::RGBA32);
5172 ///
5173 /// tex.id("CAPTURE_TEXTURE_ID");
5174 /// material.diffuse_tex(&tex);
5175 /// let transform_plane = Matrix::t([0.0, -0.55, 0.0]);
5176 ///
5177 /// let camera = Matrix::t_r(Vec3::Z * 2.0, Quat::look_at(Vec3::Z, Vec3::ZERO, None));
5178 /// let projection = Matrix::perspective(90.0, 1.0, 0.1, 20.0);
5179 ///
5180 /// number_of_steps = 200;
5181 /// filename_scr = "screenshots/screenshot_viewpoint.jpeg";
5182 /// test_screenshot!( // !!!! Get a proper main loop !!!!
5183 ///
5184 /// Renderer::add_mesh(token, &sun, &material_sun, transform_sun,
5185 /// Some(named_colors::RED.into()), None);
5186 ///
5187 /// Renderer::add_mesh(token, &plane, &material, transform_plane,
5188 /// None, None);
5189 ///
5190 /// Renderer::screenshot_viewpoint( token,
5191 /// move |dots, width, height| {
5192 /// let tex = Tex::find("CAPTURE_TEXTURE_ID").ok();
5193 /// match tex {
5194 /// Some(mut tex) => tex.set_colors32(width, height, dots),
5195 /// None => panic!("CAPTURE_TEXTURE_ID not found!"),
5196 /// };
5197 /// },
5198 /// camera, projection, 200, 200, None, None, None, None
5199 /// );
5200 /// );
5201 /// ```
5202 /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/screenshot_viewpoint.jpeg" alt="screenshot" width="200">
5203 #[allow(clippy::too_many_arguments)]
5204 pub fn screenshot_viewpoint<M: Into<Matrix>, F: FnMut(&[Color32], usize, usize)>(
5205 _token: &MainThreadToken,
5206 mut on_screenshot: F,
5207 camera: M,
5208 projection: M,
5209 width: i32,
5210 height: i32,
5211 render_layer: Option<RenderLayer>,
5212 clear: Option<RenderClear>,
5213 viewport: Option<Rect>,
5214 tex_format: Option<TexFormat>,
5215 ) {
5216 let tex_format = tex_format.unwrap_or(TexFormat::RGBA32);
5217 let render_layer = render_layer.unwrap_or(RenderLayer::all());
5218 let clear = clear.unwrap_or(RenderClear::All);
5219 let viewport = viewport.unwrap_or_default();
5220 let mut closure = &mut on_screenshot;
5221 unsafe {
5222 render_screenshot_viewpoint(
5223 Some(sc_capture_trampoline::<F>),
5224 camera.into(),
5225 projection.into(),
5226 width,
5227 height,
5228 render_layer,
5229 clear,
5230 viewport,
5231 tex_format,
5232 &mut closure as *mut _ as *mut c_void,
5233 )
5234 }
5235 }
5236
5237 /// Set the near and far clipping planes of the camera! These are important to z-buffer quality, especially when
5238 /// using low bit depth z-buffers as recommended for devices like the HoloLens. The smaller the range between the
5239 /// near and far planes, the better your z-buffer will look! If you see flickering on objects that are overlapping,
5240 /// try making the range smaller.
5241 ///
5242 /// These values only affect perspective mode projection, which is the default projection mode.
5243 /// <https://stereokit.net/Pages/StereoKit/Renderer/SetClip.html>
5244 /// * `near_plane` - The GPU discards pixels that are too close to the camera, this is that distance! It must be
5245 /// larger than zero, due to the projection math, which also means that numbers too close to zero will produce
5246 /// z-fighting artifacts. This has an enforced minimum of 0.001, but you should probably stay closer to 0.1.
5247 /// * `far_plane` - At what distance from the camera does the GPU discard pixel? This is not true distance, but
5248 /// rather Z-axis distance from zero in View Space coordinates!
5249 ///
5250 /// see also [`render_set_clip`] [`Renderer::get_clip`]
5251 /// ### Examples
5252 /// ```
5253 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5254 /// use stereokit_rust::system::Renderer;
5255 ///
5256 /// let (near, far) = Renderer::get_clip();
5257 /// assert_eq!(near, 0.02);
5258 /// assert_eq!(far, 50.0);
5259 ///
5260 /// Renderer::set_clip(0.01, 10.0);
5261 /// let (near, far) = Renderer::get_clip();
5262 /// assert_eq!(near, 0.01);
5263 /// assert_eq!(far, 10.0);
5264 /// ```
5265 pub fn set_clip(near_plane: f32, far_plane: f32) {
5266 unsafe { render_set_clip(near_plane, far_plane) }
5267 }
5268
5269 /// Only works for 2D windowed modes! This updates the camera's projection matrix with a new vertical field of view.
5270 ///
5271 /// This value only affects perspective mode projection, which is the default projection mode.
5272 /// <https://stereokit.net/Pages/StereoKit/Renderer/SetFOV.html>
5273 /// * `vertical_field_of_view` - Vertical field of view in degrees.`
5274 ///
5275 /// see also [`render_set_fov`] [`Renderer::get_fov`]
5276 /// ### Examples
5277 /// ```
5278 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5279 /// use stereokit_rust::system::Renderer;
5280 /// let fov = Renderer::get_fov();
5281 /// assert_eq!(fov, 90.0);
5282 ///
5283 /// Renderer::set_fov(120.0);
5284 /// let fov = Renderer::get_fov();
5285 /// assert_eq!(fov, 120.0);
5286 /// ```
5287 pub fn set_fov(vertical_field_of_view: f32) {
5288 unsafe { render_set_fov(vertical_field_of_view) }
5289 }
5290
5291 /// Set the near and far clipping planes of the camera! These are important to z-buffer quality, especially when
5292 /// using low bit depth z-buffers as recommended for devices like the HoloLens. The smaller the range between the
5293 /// near and far planes, the better your z-buffer will look! If you see flickering on objects that are overlapping,
5294 /// try making the range smaller.
5295 ///
5296 /// These values only affect orthographic mode projection, which is only available in 2D window modes.
5297 /// <https://stereokit.net/Pages/StereoKit/Renderer/SetOrthoClip.html>
5298 /// * `near_plane` - The GPU discards pixels that are too close to the camera, this is that distance! It must be
5299 /// larger than zero, due to the projection math, which also means that numbers too close to zero will produce
5300 /// z-fighting artifacts. This has an enforced minimum of 0.001, but you should probably stay closer to 0.1.
5301 /// * `far_plane` - At what distance from the camera does the GPU discard pixel? This is not true distance, but
5302 /// rather Z-axis distance from zero in View Space coordinates!
5303 ///
5304 /// see also [`render_set_ortho_clip`]
5305 /// ### Examples
5306 /// ```
5307 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5308 /// use stereokit_rust::system::Renderer;
5309 ///
5310 /// Renderer::set_ortho_clip(0.01, 5.0);
5311 /// ```
5312 pub fn set_ortho_clip(near_plane: f32, far_plane: f32) {
5313 unsafe { render_set_ortho_clip(near_plane, far_plane) }
5314 }
5315
5316 /// This sets the size of the orthographic projection’s viewport. You can use this feature to zoom in and out of the
5317 /// scene.
5318 ///
5319 /// This value only affects orthographic mode projection, which is only available in 2D window modes.
5320 /// <https://stereokit.net/Pages/StereoKit/Renderer/SetOrthoSize.html>
5321 /// * `viewport_height_meters` - The vertical size of the projection’s viewport, in meters.
5322 ///
5323 /// see also [`render_set_ortho_size`] [`Renderer::get_ortho_size`]
5324 /// ### Examples
5325 /// ```
5326 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5327 /// use stereokit_rust::system::Renderer;
5328 ///
5329 /// let ortho_size = Renderer::get_ortho_size();
5330 /// assert_eq!(ortho_size, 1.0);
5331 ///
5332 /// Renderer::set_ortho_size(12.0);
5333 /// let ortho_size = Renderer::get_ortho_size();
5334 /// assert_eq!(ortho_size, 12.0);
5335 /// ```
5336 pub fn set_ortho_size(view_port_height_meters: f32) {
5337 unsafe { render_set_ortho_size(view_port_height_meters) }
5338 }
5339
5340 /// Gets the root transform of the camera! This will be the identity matrix by default. The user’s head
5341 /// location will then be relative to this point. This is great to use if you’re trying to do teleportation,
5342 /// redirected walking, or just shifting the floor around.
5343 /// <https://stereokit.net/Pages/StereoKit/Renderer/CameraRoot.html>
5344 ///
5345 /// see also [`render_get_cam_root`]
5346 /// see example in [`Renderer::camera_root`]
5347 pub fn get_camera_root() -> Matrix {
5348 unsafe { render_get_cam_root() }
5349 }
5350
5351 /// This retrieves the current near and far clipping planes for the perspective matrix of the primary draw surface.
5352 ///
5353 /// <https://stereokit.net/Pages/StereoKit/Renderer/GetClip.html>
5354 ///
5355 /// Returns a tuple (`near_plane`, `far_plane`)
5356 /// * `near_plane` - The GPU discards pixels that are too close to the camera, this is that distance! It will be larger
5357 /// than zero, due to the projection math, which also means that numbers too close to zero will produce z-fighting artifacts. This
5358 /// has an enforced minimum of 0.001, but will probably be closer to 0.1.
5359 /// * `far_plane` - At what distance from the camera does the GPU discard pixel? This is not true distance, but rather Z-axis
5360 /// distance from zero in View Space coordinates!
5361 ///
5362 /// see also [`render_get_clip`]
5363 /// see example in [`Renderer::set_clip`]
5364 pub fn get_clip() -> (f32, f32) {
5365 let mut near_plane = 0.0;
5366 let mut far_plane = 0.0;
5367 unsafe { render_get_clip(&mut near_plane, &mut far_plane) }
5368 (near_plane, far_plane)
5369 }
5370
5371 /// Only works for 2D windowed modes! This retrieves the vertical field of view of the camera's projection matrix when in
5372 /// perspective projection mode.
5373 ///
5374 /// <https://stereokit.net/Pages/StereoKit/Renderer/GetFOV.html>
5375 ///
5376 /// see also [`render_get_fov`]
5377 /// see example in [`Renderer::set_fov`]
5378 pub fn get_fov() -> f32 {
5379 unsafe { render_get_fov() }
5380 }
5381
5382 /// This retrieves the size the primary render surface's view when using orthographic projection mode.
5383 ///
5384 /// <https://stereokit.net/Pages/StereoKit/Renderer/GetOrthoSize.html>
5385 ///
5386 /// see also [`render_get_ortho_size`] []
5387 /// see example in [`Renderer::set_ortho_size`]
5388 pub fn get_ortho_size() -> f32 {
5389 unsafe { render_get_ortho_size() }
5390 }
5391
5392 /// This is the current render layer mask for Mixed Reality Capture, or 2nd person observer rendering. By default,
5393 /// this is directly linked to Renderer::layer_filter, but this behavior can be overridden via
5394 /// Renderer::override_capture_filter.
5395 /// <https://stereokit.net/Pages/StereoKit/Renderer/CaptureFilter.html>
5396 ///
5397 /// see also [`render_get_capture_filter`]
5398 /// see example in [`Renderer::override_capture_filter`]
5399 pub fn get_capture_filter() -> RenderLayer {
5400 unsafe { render_get_capture_filter() }
5401 }
5402
5403 /// This is the gamma space color the renderer will clear the screen to when beginning to draw a new frame.
5404 /// <https://stereokit.net/Pages/StereoKit/Renderer/ClearColor.html>
5405 ///
5406 /// see also [`render_get_clear_color`]
5407 /// see example in [`Renderer::clear_color`]
5408 pub fn get_clear_color() -> Color128 {
5409 unsafe { render_get_clear_color() }
5410 }
5411
5412 /// Enables or disables rendering of the skybox texture! It’s enabled by default on Opaque displays, and completely
5413 /// unavailable for transparent displays.
5414 /// <https://stereokit.net/Pages/StereoKit/Renderer/EnableSky.html>
5415 ///
5416 /// see also [`render_enabled_skytex`]
5417 /// see example in [`Renderer::enable_sky`]
5418 pub fn get_enable_sky() -> bool {
5419 unsafe { render_enabled_skytex() != 0 }
5420 }
5421
5422 /// This tells if capture_filter has been overridden to a specific value via Renderer::override_capture_filter.
5423 /// <https://stereokit.net/Pages/StereoKit/Renderer/HasCaptureFilter.html>
5424 ///
5425 /// see also [`render_has_capture_filter`]
5426 /// see example in [`Renderer::override_capture_filter`]
5427 pub fn has_capture_filter() -> bool {
5428 unsafe { render_has_capture_filter() != 0 }
5429 }
5430
5431 /// By default, StereoKit renders all first-person layers. This is a bit flag that allows you to change which layers
5432 /// StereoKit renders for the primary viewpoint. To change what layers a visual is on, use a Draw method that
5433 /// includes a RenderLayer as a parameter.
5434 /// <https://stereokit.net/Pages/StereoKit/Renderer/LayerFilter.html>
5435 ///
5436 /// see also [`render_get_filter`]
5437 /// see example in [`Renderer::layer_filter`]
5438 pub fn get_layer_filter() -> RenderLayer {
5439 unsafe { render_get_filter() }
5440 }
5441
5442 /// Get the multisample (MSAA) level of the render surface. Valid values are 1, 2, 4, 8, 16, though
5443 /// some OpenXR runtimes may clamp this to lower values. Note that while this can greatly smooth out edges, it also
5444 /// greatly increases RAM usage and fill rate, so use it sparingly. Only works in XR mode. If known in advance, set
5445 /// this via [`crate::sk::SkSettings`] in initialization. This is a very costly change to make.
5446 /// <https://stereokit.net/Pages/StereoKit/Renderer/Multisample.html>
5447 ///
5448 /// see also [`render_get_multisample`]
5449 /// see example in [`Renderer::multisample`]
5450 pub fn get_multisample() -> i32 {
5451 unsafe { render_get_multisample() }
5452 }
5453
5454 /// For flatscreen applications only! This allows you to get the camera projection between perspective and
5455 /// orthographic projection. This may be of interest for some category of UI work, but is generally a niche piece of
5456 /// functionality.
5457 /// Swapping between perspective and orthographic will also switch the clipping planes and field of view to the
5458 /// values associated with that mode. See set_clip/set_fov for perspective, and set_ortho_clip/set_ortho_size for
5459 /// orthographic.
5460 /// <https://stereokit.net/Pages/StereoKit/Renderer/Projection.html>
5461 ///
5462 /// see also [`render_get_projection`]
5463 /// see example in [`Renderer::projection`]
5464 pub fn get_projection() -> Projection {
5465 unsafe { render_get_projection() }
5466 }
5467
5468 /// OpenXR has a recommended default for the main render surface, this value allows you to set SK’s surface to a
5469 /// multiple of the recommended size. Note that the final resolution may also be clamped or quantized. Only works in
5470 /// XR mode. If known in advance, set this via SKSettings in initialization. This is a very costly change to make.
5471 /// Consider if viewport_caling will work for you
5472 /// instead, and prefer that.
5473 /// <https://stereokit.net/Pages/StereoKit/Renderer/Scaling.html>
5474 ///
5475 /// see also [`render_get_scaling`]
5476 /// see example in [`Renderer::scaling`]
5477 pub fn get_scaling() -> f32 {
5478 unsafe { render_get_scaling() }
5479 }
5480
5481 /// This allows you to trivially scale down the area of the swapchain that StereoKit renders to! This can be used to
5482 /// boost performance in situations where full resolution is not needed, or to reduce GPU time. This value is
5483 /// locked to the 0-1 range
5484 /// <https://stereokit.net/Pages/StereoKit/Renderer/ViewportScaling.html>
5485 ///
5486 /// see also [`render_get_viewport_scaling`]
5487 /// see example in [`Renderer::viewport_scaling`]
5488 pub fn get_viewport_scaling() -> f32 {
5489 unsafe { render_get_viewport_scaling() }
5490 }
5491
5492 /// Gets the lighting information for the scene! You can build one through SphericalHarmonics::from_lights, or grab
5493 /// one from [`crate::tex::SHCubemap`].
5494 /// <https://stereokit.net/Pages/StereoKit/Renderer/SkyLight.html>
5495 ///
5496 /// see also [`render_get_skylight`]
5497 /// see example in [`Renderer::sky_light`]
5498 pub fn get_sky_light() -> SphericalHarmonics {
5499 unsafe { render_get_skylight() }
5500 }
5501
5502 /// Get the cubemap skybox texture for rendering a background! This is only visible on Opaque displays, since
5503 /// transparent displays have the real world behind them already! StereoKit has a a default procedurally generated
5504 /// skybox. You can load one with [`crate::tex::SHCubemap`]. If you’re trying to affect the lighting,
5505 /// see Renderer::sky_light.
5506 /// <https://stereokit.net/Pages/StereoKit/Renderer/SkyTex.html>
5507 ///
5508 /// see also [`render_get_skytex`]
5509 /// see example in [`Renderer::sky_tex`]
5510 pub fn get_sky_tex() -> Tex {
5511 Tex(NonNull::new(unsafe { render_get_skytex() }).unwrap())
5512 }
5513
5514 /// This is the Material that StereoKit is currently using to draw the skybox! It needs a special shader that's
5515 /// tuned for a full-screen quad. If you just want to change the skybox image, try setting [`Renderer::sky_tex`]
5516 /// instead.
5517 ///
5518 /// This value will never be null! If you try setting this to null, it will assign SK's built-in default sky
5519 /// material. If you want to turn off the skybox, see [`Renderer::enable_sky`] instead.
5520 ///
5521 /// Recommended Material settings would be:
5522 /// - DepthWrite: false
5523 /// - DepthTest: LessOrEq
5524 /// - QueueOffset: 100
5525 ///
5526 /// <https://stereokit.net/Pages/StereoKit/Renderer/SkyMaterial.html>
5527 ///
5528 /// see also [`render_get_skymaterial`]
5529 /// see example in [`Renderer::sky_material`]
5530 pub fn get_sky_material() -> Material {
5531 Material(NonNull::new(unsafe { render_get_skymaterial() }).unwrap())
5532 }
5533}
5534
5535/// A text style is a font plus size/color/material parameters, and are used to keep text looking more consistent
5536/// through the application by encouraging devs to re-use styles throughout the project. See Text.MakeStyle for making a
5537/// TextStyle object.
5538/// <https://stereokit.net/Pages/StereoKit/TextStyle.html>
5539///
5540/// ### Examples
5541/// ```
5542/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5543/// use stereokit_rust::{system::{TextStyle, Pivot, Align, Text, Lines, Hierarchy},
5544/// font::Font, material::Material, mesh::Mesh, maths::{Vec3, Matrix},
5545/// util::named_colors::{WHITE, GOLD, GREEN, BLUE, RED, BLACK}};
5546///
5547/// let font = Font::default();
5548/// let style = TextStyle::from_font(font, 0.88, WHITE);
5549/// let text = "ÂjlD;";
5550/// let size = Text::size_layout(text, Some(style), None);
5551///
5552/// let base_line_at = -style.get_cap_height();
5553/// let ascender_at = base_line_at + style.get_ascender();
5554/// let cap_height_at = base_line_at + style.get_cap_height();
5555/// let descender_at = base_line_at - style.get_descender();
5556/// let line_height_at = ascender_at - style.get_line_height_pct() * style. get_total_height();
5557///
5558/// let sizex = size.x;
5559///
5560/// let recenter = Matrix::t(Vec3::Y * 0.6);
5561///
5562/// filename_scr = "screenshots/text_style.jpeg"; fov_scr=110.0;
5563/// test_screenshot!( // !!!! Get a proper main loop !!!!
5564/// Hierarchy::push(token, recenter, None);
5565/// Text::add_at(token, text, Matrix::Y_180, Some(style), Some(GOLD.into()),
5566/// Some(Pivot::TopCenter), Some(Align::TopLeft), None, None, None);
5567///
5568/// Lines::add(token, [sizex, ascender_at, 0.0], [-sizex, ascender_at, 0.0], GREEN, None, 0.03 );
5569/// Lines::add(token, [sizex, base_line_at, 0.0], [-sizex, base_line_at, 0.0], WHITE, None, 0.03 );
5570/// Lines::add(token, [sizex, cap_height_at, 0.0], [-sizex, cap_height_at, 0.0], BLACK, None, 0.03 );
5571/// Lines::add(token, [sizex, descender_at, 0.0], [-sizex, descender_at, 0.0], BLUE, None, 0.03 );
5572/// Lines::add(token, [sizex, line_height_at, 0.0], [-sizex, line_height_at, 0.0], RED, None, 0.03 );
5573/// Hierarchy::pop(token);
5574/// );
5575/// ```
5576/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/text_style.jpeg" alt="screenshot" width="200">
5577#[derive(Debug, Copy, Clone, PartialEq, Eq)]
5578#[repr(C)]
5579pub struct TextStyle {
5580 _id: u32,
5581}
5582
5583unsafe extern "C" {
5584 pub fn text_make_style(font: FontT, layout_height: f32, color_gamma: Color128) -> TextStyle;
5585 pub fn text_make_style_shader(font: FontT, layout_height: f32, shader: ShaderT, color_gamma: Color128)
5586 -> TextStyle;
5587 pub fn text_make_style_mat(
5588 font: FontT,
5589 layout_height: f32,
5590 material: MaterialT,
5591 color_gamma: Color128,
5592 ) -> TextStyle;
5593 pub fn text_style_get_line_height_pct(style: TextStyle) -> f32;
5594 pub fn text_style_set_line_height_pct(style: TextStyle, height_percent: f32);
5595 pub fn text_style_get_layout_height(style: TextStyle) -> f32;
5596 pub fn text_style_set_layout_height(style: TextStyle, height_meters: f32);
5597 pub fn text_style_get_total_height(style: TextStyle) -> f32;
5598 pub fn text_style_set_total_height(style: TextStyle, height_meters: f32);
5599 pub fn text_style_get_material(style: TextStyle) -> MaterialT;
5600 pub fn text_style_get_ascender(style: TextStyle) -> f32;
5601 pub fn text_style_get_descender(style: TextStyle) -> f32;
5602 pub fn text_style_get_cap_height(style: TextStyle) -> f32;
5603 pub fn text_style_get_baseline(style: TextStyle) -> f32;
5604}
5605
5606impl Default for TextStyle {
5607 /// This is the default text style used by StereoKit.
5608 /// <https://stereokit.net/Pages/StereoKit/TextStyle/Default.html>
5609 fn default() -> Self {
5610 Self { _id: 0 }
5611 }
5612}
5613
5614impl TextStyle {
5615 /// Create a text style for use with other text functions! A text style is a font plus size/color/material
5616 /// parameters, and are used to keep text looking more consistent through the application by encouraging devs to
5617 /// re-use styles throughout the project.
5618 ///
5619 /// This fn will create an unique Material for this style based on Default.ShaderFont.
5620 /// <https://stereokit.net/Pages/StereoKit/TextStyle/FromFont.html>
5621 /// * `font` - Font asset you want attached to this style.
5622 /// * `layout_height_meters` - Height of a text glyph in meters. StereoKit currently bases this on CapHeight.
5623 /// * `color_gamma` - The gamma space color of the text style. This will be embedded in the vertex color of the
5624 /// text mesh.
5625 ///
5626 /// see also [`text_make_style`]
5627 /// ### Examples
5628 /// ```
5629 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5630 /// use stereokit_rust::{system::TextStyle, font::Font, util::named_colors};
5631 ///
5632 /// let font = Font::default();
5633 ///
5634 /// let text_style = TextStyle::from_font(&font, 0.02, named_colors::WHITE);
5635 ///
5636 /// assert_eq!(text_style.get_material().get_id(), "sk/text_style/2/material");
5637 /// assert_eq!(text_style.get_layout_height(), 0.02);
5638 /// ```
5639 pub fn from_font(font: impl AsRef<Font>, layout_height_meters: f32, color_gamma: impl Into<Color128>) -> Self {
5640 unsafe { text_make_style(font.as_ref().0.as_ptr(), layout_height_meters, color_gamma.into()) }
5641 }
5642
5643 /// Create a text style for use with other text functions! A text style is a font plus size/color/material
5644 /// parameters, and are used to keep text looking more consistent through the application by encouraging devs to
5645 /// re-use styles throughout the project.
5646 ///
5647 /// This function will create an unique Material for this style based on the provided Shader.
5648 /// <https://stereokit.net/Pages/StereoKit/TextStyle/FromFont.html>
5649 /// * `font` - Font asset you want attached to this style.
5650 /// * `layout_height_meters` - Height of a text glyph in meters. StereoKit currently bases this on CapHeight.
5651 /// * `shader` - This style will create and use a unique/ Material based on the Shader that you provide here.
5652 /// * `color_gamma` - The gamma space color of the text style. This will be embedded in the vertex color of the
5653 /// text mesh.
5654 ///
5655 /// see also [`text_make_style_shader`]
5656 /// ### Examples
5657 /// ```
5658 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5659 /// use stereokit_rust::{system::TextStyle, font::Font, util::named_colors, shader::Shader};
5660 ///
5661 /// let font = Font::default();
5662 /// let shader = Shader::from_file("shaders/brick_pbr.hlsl.sks")
5663 /// .expect("Brick_pbr should be a valid shader");
5664 ///
5665 /// let text_style = TextStyle::from_font_and_shader(&font, 0.02, shader, named_colors::WHITE);
5666 ///
5667 /// assert_eq!(text_style.get_material().get_id(), "sk/text_style/2/material");
5668 /// assert_eq!(text_style.get_layout_height(), 0.02);
5669 /// ```
5670 pub fn from_font_and_shader(
5671 font: impl AsRef<Font>,
5672 layout_height_meters: f32,
5673 shader: impl AsRef<Shader>,
5674 color_gamma: impl Into<Color128>,
5675 ) -> Self {
5676 unsafe {
5677 text_make_style_shader(
5678 font.as_ref().0.as_ptr(),
5679 layout_height_meters,
5680 shader.as_ref().0.as_ptr(),
5681 color_gamma.into(),
5682 )
5683 }
5684 }
5685
5686 /// Create a text style for use with other text functions! A text style is a font plus size/color/material
5687 /// parameters, and are used to keep text looking more consistent through the application by encouraging devs to
5688 /// re-use styles throughout the project.
5689 ///
5690 /// This overload allows you to set the specific Material that is used. This can be helpful if you’re keeping styles
5691 /// similar enough to re-use the material and save on draw calls. If you don’t know what that means, then prefer
5692 /// using the overload that takes a Shader, or takes neither a Shader nor a Material!
5693 /// <https://stereokit.net/Pages/StereoKit/TextStyle/FromFont.html>
5694 /// * `font` - Font asset you want attached to this style.
5695 /// * `layout_height_meters` - Height of a text glyph in meters. StereoKit currently bases this on CapHeight.
5696 /// * `material` - Which material should be used to render the text with? Note that this does NOT duplicate the
5697 /// material, so the text with? Note that this does NOT duplicate the material, so some parameters of this
5698 /// Material instance will get overwritten, like the texture used for the glyph atlas. You should either use a new
5699 /// Material, or a Material that was already used with this same font.
5700 /// * `color_gamma` - The gamma space color of the text style. This will be embedded in the vertex color of the
5701 /// text mesh.
5702 ///
5703 /// see also [`text_make_style_mat`]
5704 /// ### Examples
5705 /// ```
5706 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5707 /// use stereokit_rust::{system::TextStyle, font::Font, util::named_colors, material::Material};
5708 ///
5709 /// let font = Font::default();
5710 /// let material = Material::pbr().copy();
5711 ///
5712 /// let text_style = TextStyle::from_font_and_material(&font, 0.02, &material, named_colors::WHITE);
5713 ///
5714 /// assert_eq!(text_style.get_material(), material);
5715 /// assert_eq!(text_style.get_layout_height(), 0.02);
5716 /// ```
5717 pub fn from_font_and_material(
5718 font: impl AsRef<Font>,
5719 layout_height_meters: f32,
5720 material: impl AsRef<Material>,
5721 color_gamma: impl Into<Color128>,
5722 ) -> Self {
5723 unsafe {
5724 text_make_style_mat(
5725 font.as_ref().0.as_ptr(),
5726 layout_height_meters,
5727 material.as_ref().0.as_ptr(),
5728 color_gamma.into(),
5729 )
5730 }
5731 }
5732
5733 /// Height of a text glyph in meters. StereoKit currently bases this on the letter ‘T’.
5734 /// <https://stereokit.net/Pages/StereoKit/TextStyle/CharHeight.html>
5735 ///
5736 /// see also [`text_style_set_layout_height`]
5737 #[deprecated(since = "0.40.0", note = "please use TextStyle::layout_height")]
5738 pub fn char_height(&mut self, char_height: f32) {
5739 unsafe { text_style_set_layout_height(*self, char_height) }
5740 }
5741
5742 /// (meters) Layout height is the height of the font's CapHeight, which is used for calculating the vertical height
5743 /// of the text when doing text layouts. This does _not_ include the height of the descender , nor
5744 /// does it represent the maximum possible height a glyph may extend upwards (use Text::size_render).
5745 /// <https://stereokit.net/Pages/StereoKit/TextStyle/LayoutHeight.html>
5746 ///
5747 /// see also [`text_style_set_layout_height`]
5748 /// ### Examples
5749 /// ```
5750 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5751 /// use stereokit_rust::{font::Font, util::named_colors, system::TextStyle};
5752 ///
5753 /// let font = Font::default();
5754 /// let mut text_style = TextStyle::from_font(font, 0.02, named_colors::WHITE);
5755 /// assert_eq!(text_style.get_layout_height(), 0.02);
5756 ///
5757 /// text_style.layout_height(0.03);
5758 ///
5759 /// assert!((text_style.get_layout_height() - 0.03) < 0.0001);
5760 /// ```
5761 pub fn layout_height(&mut self, height_meters: f32) {
5762 unsafe { text_style_set_layout_height(*self, height_meters) }
5763 }
5764
5765 /// (meters) Height from the layout descender to the layout ascender. This is most equivalent to the 'font-size' in
5766 /// CSS or other text layout tools. Since ascender and descenders can vary a lot, using layout_height in many cases
5767 /// can lead to more consistency in the long run.
5768 /// <https://stereokit.net/Pages/StereoKit/TextStyle/TotalHeight.html>
5769 ///
5770 /// see also [`text_style_set_total_height`]
5771 /// ### Examples
5772 /// ```
5773 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5774 /// use stereokit_rust::{font::Font, util::named_colors, system::TextStyle};
5775 ///
5776 /// let font = Font::default();
5777 /// let mut text_style = TextStyle::from_font(font, 0.02, named_colors::WHITE);
5778 ///
5779 /// text_style.total_height(0.03);
5780 ///
5781 /// assert_eq!(text_style.get_total_height(), 0.03);
5782 /// ```
5783 pub fn total_height(&mut self, height_meters: f32) {
5784 unsafe { text_style_set_total_height(*self, height_meters) }
5785 }
5786
5787 /// This is the space a full line of text takes, from baseline to baseline, as a 0-1 percentage of the font's
5788 /// character height. This is similar to CSS line-height, a value of 1.0 means the line takes _only_
5789 /// <https://stereokit.net/Pages/StereoKit/TextStyle/LineHeightPct.html>
5790 ///
5791 /// see also [`text_style_set_line_height_pct`]
5792 /// ### Examples
5793 /// ```
5794 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5795 /// use stereokit_rust::{font::Font, util::named_colors, system::TextStyle};
5796 ///
5797 /// let font = Font::default();
5798 /// let mut text_style = TextStyle::from_font(font, 0.02, named_colors::WHITE);
5799 /// assert_eq!(text_style.get_layout_height(), 0.02);
5800 ///
5801 /// text_style.line_height_pct(30.0);
5802 ///
5803 /// assert_eq!(text_style.get_line_height_pct(), 30.0);
5804 /// ```
5805 pub fn line_height_pct(&mut self, height_percent: f32) {
5806 unsafe { text_style_set_line_height_pct(*self, height_percent) }
5807 }
5808
5809 /// This provides a reference to the Material used by this style, so you can override certain features! Note that if
5810 /// you’re creating TextStyles with manually provided Materials, this Material may not be unique to this style.
5811 /// <https://stereokit.net/Pages/StereoKit/TextStyle/Material.html>
5812 ///
5813 /// see also [`text_style_get_material`]
5814 /// see example in [`TextStyle::from_font_and material`]
5815 pub fn get_material(&self) -> Material {
5816 Material(NonNull::new(unsafe { text_style_get_material(*self) }).unwrap())
5817 }
5818
5819 /// Returns the maximum height of a text character using this style, in meters.
5820 /// <https://stereokit.net/Pages/StereoKit/TextStyle/CharHeight.html>
5821 ///
5822 /// see also [`text_style_get_layout_height`]
5823 #[deprecated(since = "0.40.0", note = "please use get_layout_height")]
5824 pub fn get_char_height(&self) -> f32 {
5825 unsafe { text_style_get_layout_height(*self) }
5826 }
5827
5828 /// (meters) Layout height is the height of the font's ascender, which is used for calculating the vertical height
5829 /// of the text when doing text layouts. This does _not_ include the height of the descender (use total_height), nor
5830 /// does it represent the maximum possible height a glyph may extend upwards (use Text::size_render).
5831 /// <https://stereokit.net/Pages/StereoKit/TextStyle/LayoutHeight.html>
5832 ///
5833 /// see also [`text_style_get_layout_height`]
5834 /// see example in [`TextStyle::layout_height`]
5835 pub fn get_layout_height(&self) -> f32 {
5836 unsafe { text_style_get_layout_height(*self) }
5837 }
5838
5839 /// (meters) Height from the layout descender to the layout ascender. This is most equivalent to the 'font-size' in
5840 /// CSS or other text layout tools. Since ascender and descenders can vary a lot, using layout_height in many cases
5841 /// can lead to more consistency in the long run.
5842 /// <https://stereokit.net/Pages/StereoKit/TextStyle/TotalHeight.html>
5843 ///
5844 /// see also [`text_style_get_total_height`]
5845 /// see example in [`TextStyle::total_height`]
5846 pub fn get_total_height(&self) -> f32 {
5847 unsafe { text_style_get_total_height(*self) }
5848 }
5849
5850 /// This is the space a full line of text takes, from baseline to baseline, as a 0-1 percentage of the font's
5851 /// character height. This is similar to CSS line-height, a value of 1.0 means the line takes _only_
5852 /// <https://stereokit.net/Pages/StereoKit/TextStyle/LineHeightPct.html>
5853 ///
5854 /// see also [`text_style_get_line_height_pct`]
5855 /// see example in [`TextStyle::line_height_pct`]
5856 pub fn get_line_height_pct(&self) -> f32 {
5857 unsafe { text_style_get_line_height_pct(*self) }
5858 }
5859 /// (meters) The height of a standard captial letter, such as 'H' or 'T'
5860 /// <https://stereokit.net/Pages/StereoKit/TextStyle/CapHeight.html>
5861 ///
5862 /// see also [`text_style_get_cap_height`]
5863 /// ### Examples
5864 /// ```
5865 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5866 /// use stereokit_rust::{font::Font, util::named_colors, system::TextStyle};
5867 ///
5868 /// let font = Font::default();
5869 /// let mut text_style = TextStyle::from_font(font, 0.02, named_colors::WHITE);
5870 /// assert_eq!(text_style.get_layout_height(), 0.02);
5871 ///
5872 /// assert_eq!(text_style.get_cap_height(), 0.02);
5873 /// ```
5874 pub fn get_cap_height(&self) -> f32 {
5875 unsafe { text_style_get_cap_height(*self) }
5876 }
5877
5878 /// (meters) The layout ascender of the font, this is the height of the "tallest" glyphs as far as layout is
5879 /// concerned. Characters such as 'l' typically rise above the CapHeight, and this value usually matches this height.
5880 /// Some glyphs such as those with hats or umlauts will almost always be taller than this height
5881 /// (see Text::size_render), but this is not used when laying out characters.
5882 /// <https://stereokit.net/Pages/StereoKit/TextStyle/Ascender.html>
5883 ///
5884 /// see also [`text_style_get_ascender`]
5885 /// ### Examples
5886 /// ```
5887 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5888 /// use stereokit_rust::{font::Font, util::named_colors, system::TextStyle};
5889 ///
5890 /// let font = Font::default();
5891 /// let mut text_style = TextStyle::from_font(font, 0.02, named_colors::WHITE);
5892 /// assert_eq!(text_style.get_layout_height(), 0.02);
5893 ///
5894 /// //TODO: linux assert_eq!(text_style.get_ascender(), 0.03);
5895 /// //TODO: windows assert_eq!(text_style.get_ascender(), 0.021176472);
5896 /// ```
5897 pub fn get_ascender(&self) -> f32 {
5898 unsafe { text_style_get_ascender(*self) }
5899 }
5900
5901 /// (meters) The layout descender of the font, this is the positive height below the baseline
5902 /// <https://stereokit.net/Pages/StereoKit/TextStyle/Descender.html>
5903 ///
5904 /// see also [`text_style_get_descender`]
5905 /// ### Examples
5906 /// ```
5907 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
5908 /// use stereokit_rust::{font::Font, util::named_colors, system::TextStyle};
5909 ///
5910 /// let font = Font::default();
5911 /// let mut text_style = TextStyle::from_font(font, 0.02, named_colors::WHITE);
5912 /// assert_eq!(text_style.get_layout_height(), 0.02);
5913 ///
5914 /// assert_ne!(text_style.get_descender(), 0.0);
5915 /// ```
5916 pub fn get_descender(&self) -> f32 {
5917 unsafe { text_style_get_descender(*self) }
5918 }
5919}
5920
5921bitflags::bitflags! {
5922 /// A bit-flag enum for describing alignment or positioning. Items can be combined using the '|' operator, like so:
5923 /// `let alignment = Align::YTop | Align::XLeft;`.
5924 /// Avoid combining multiple items of the same axis. There are also a complete list of valid bit flag combinations!
5925 /// These are the values without an axis listed in their names, 'TopLeft', 'BottomCenter',
5926 /// etc.
5927 /// <https://stereokit.net/Pages/StereoKit/Align.html>
5928 ///
5929 /// see also [`Text`] [`crate::ui::Ui`]
5930 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
5931 #[repr(C)]
5932 pub struct Align: u32 {
5933 /// On the x axis, this item should start on the left.
5934 const XLeft = 1 << 0;
5935 /// On the y axis, this item should start at the top.
5936 const YTop = 1 << 1;
5937 /// On the x axis, the item should be centered.
5938 const XCenter = 1 << 2;
5939 /// On the y axis, the item should be centered.
5940 const YCenter = 1 << 3;
5941 /// On the x axis, this item should start on the right.
5942 const XRight = 1 << 4;
5943 /// On the y axis, this item should start on the bottom.
5944 const YBottom = 1 << 5;
5945 /// Center on both X and Y axes. This is a combination of XCenter and YCenter.
5946 const Center = Self::XCenter.bits() | Self::YCenter.bits();
5947 /// Start on the left of the X axis, center on the Y axis. This is a combination of XLeft and YCenter.
5948 const CenterLeft = Self::XLeft.bits() | Self::YCenter.bits();
5949 /// Start on the right of the X axis, center on the Y axis. This is a combination of XRight and YCenter.
5950 const CenterRight = Self::XRight.bits() | Self::YCenter.bits();
5951 /// Center on the X axis, and top on the Y axis. This is a combination of XCenter and YTop.
5952 const TopCenter = Self::XCenter.bits() | Self::YTop.bits();
5953 /// Start on the left of the X axis, and top on the Y axis. This is a combination of XLeft and YTop.
5954 const TopLeft = Self::XLeft.bits() | Self::YTop.bits();
5955 /// Start on the right of the X axis, and top on the Y axis. This is a combination of XRight and YTop.
5956 const TopRight = Self::XRight.bits() | Self::YTop.bits();
5957 /// Center on the X axis, and bottom on the Y axis. This is a combination of XCenter and YBottom.
5958 const BottomCenter = Self::XCenter.bits() | Self::YBottom.bits();
5959 /// Start on the left of the X axis, and bottom on the Y axis. This is a combination of XLeft and YBottom.
5960 const BottomLeft = Self::XLeft.bits() | Self::YBottom.bits();
5961 /// Start on the right of the X axis, and bottom on the Y axis.This is a combination of XRight and YBottom.
5962 const BottomRight = Self::XRight.bits() | Self::YBottom.bits();
5963 }
5964}
5965
5966bitflags::bitflags! {
5967 /// A bit-flag enum for describing alignment or positioning. Items can be combined using the '|' operator, like so:
5968 /// `let alignment = Pivot::YTop | Pivot::XLeft;`.
5969 /// Avoid combining multiple items of the same axis. There are also a complete list of valid bit flag combinations!
5970 /// These are the values without an axis listed in their names, 'TopLeft', 'BottomCenter',
5971 /// etc.
5972 /// <https://stereokit.net/Pages/StereoKit/Pivot.html>
5973 ///
5974 /// see also [`Text`] [`Sprite`]
5975 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
5976 #[repr(C)]
5977 pub struct Pivot: u32 {
5978 /// On the x axis, this item should start on the left.
5979 const XLeft = 1 << 0;
5980 /// On the y axis, this item should start at the top.
5981 const YTop = 1 << 1;
5982 /// On the x axis, the item should be centered.
5983 const XCenter = 1 << 2;
5984 /// On the y axis, the item should be centered.
5985 const YCenter = 1 << 3;
5986 /// On the x axis, this item should start on the right.
5987 const XRight = 1 << 4;
5988 /// On the y axis, this item should start on the bottom.
5989 const YBottom = 1 << 5;
5990 /// Center on both X and Y axes. This is a combination of XCenter and YCenter.
5991 const Center = Self::XCenter.bits() | Self::YCenter.bits();
5992 /// Start on the left of the X axis, center on the Y axis. This is a combination of XLeft and YCenter.
5993 const CenterLeft = Self::XLeft.bits() | Self::YCenter.bits();
5994 /// Start on the right of the X axis, center on the Y axis. This is a combination of XRight and YCenter.
5995 const CenterRight = Self::XRight.bits() | Self::YCenter.bits();
5996 /// Center on the X axis, and top on the Y axis. This is a combination of XCenter and YTop.
5997 const TopCenter = Self::XCenter.bits() | Self::YTop.bits();
5998 /// Start on the left of the X axis, and top on the Y axis. This is a combination of XLeft and YTop.
5999 const TopLeft = Self::XLeft.bits() | Self::YTop.bits();
6000 /// Start on the right of the X axis, and top on the Y axis. This is a combination of XRight and YTop.
6001 const TopRight = Self::XRight.bits() | Self::YTop.bits();
6002 /// Center on the X axis, and bottom on the Y axis. This is a combination of XCenter and YBottom.
6003 const BottomCenter = Self::XCenter.bits() | Self::YBottom.bits();
6004 /// Start on the left of the X axis, and bottom on the Y axis. This is a combination of XLeft and YBottom.
6005 const BottomLeft = Self::XLeft.bits() | Self::YBottom.bits();
6006 /// Start on the right of the X axis, and bottom on the Y axis.This is a combination of XRight and YBottom.
6007 const BottomRight = Self::XRight.bits() | Self::YBottom.bits();
6008 }
6009}
6010
6011bitflags::bitflags! {
6012 /// This enum describes how text layout behaves within the space it is given.
6013 /// <https://stereokit.net/Pages/StereoKit/TextFit.html>
6014 ///
6015 /// see also [`Text`] [`crate::ui::Ui`]
6016 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
6017 #[repr(C)]
6018 pub struct TextFit: u32 {
6019 /// No particularly special behavior.
6020 const None = 0;
6021 /// The text will wrap around to the next line down when it reaches the end of the space on the X axis.
6022 const Wrap = 1;
6023 /// When the text reaches the end, it is simply truncated and no longer visible.
6024 const Clip = 2;
6025 /// If the text is too large to fit in the space provided, it will be scaled down to fit inside. This will not scale up.
6026 const Squeeze = 4;
6027 /// If the text is larger, or smaller than the space provided, it will scale down or up to fill the space.
6028 const Exact = 8;
6029 /// The text will ignore the containing space, and just keep on going.
6030 const Overflow = 16;
6031 }
6032}
6033
6034/// Soft keyboard layouts are often specific to the type of text that they’re editing! This enum is a collection of
6035/// common text contexts that SK can pass along to the OS’s soft keyboard for a more optimal layout.
6036/// <https://stereokit.net/Pages/StereoKit/TextContext.html>
6037///
6038/// see also [`crate::ui::Ui::input`] [`crate::ui::Ui::input_at`] [`crate::util::Platform::keyboard_show`]
6039#[derive(Debug, Copy, Clone, PartialEq, Eq)]
6040#[repr(u32)]
6041pub enum TextContext {
6042 /// General text editing, this is the most common type of text, and would result in a ‘standard’ keyboard layout.
6043 Text = 0,
6044 /// Numbers and numerical values.
6045 Number = 1,
6046 /// This text specifically represents some kind of URL/URI address.
6047 Uri = 2,
6048 /// This is a password, and should not be visible when typed!
6049 Password = 3,
6050}
6051
6052/// A collection of functions for rendering and working with text. These are a lower level access to text rendering than
6053/// the UI text functions, and are completely unaware of the UI code.
6054/// <https://stereokit.net/Pages/StereoKit/Text.html>
6055///
6056/// ### Examples
6057/// ```
6058/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6059/// use stereokit_rust::{system::{ Pivot, Align, TextFit, Text, Lines, Hierarchy },
6060/// font::Font, material::Material, mesh::Mesh, maths::{Vec3, Matrix},
6061/// util::named_colors::{WHITE, GOLD, GREEN, RED}};
6062///
6063/// let font = Font::default();
6064/// let style = Text::make_style(font, 0.28, WHITE);
6065/// let transform1 = Matrix::t([0.7, 0.7, 0.0]) * Matrix::Y_180;
6066/// let transform2 = Matrix::t([0.3, 0.1, 0.0]) * Matrix::Y_180;
6067/// let transform3 = Matrix::t([-0.1,-0.5, 0.0]) * Matrix::Y_180;
6068///
6069/// filename_scr = "screenshots/text.jpeg"; fov_scr=110.0;
6070/// test_screenshot!( // !!!! Get a proper main loop !!!!
6071/// Text::add_at(token, "Many", transform1, Some(style), Some(GOLD.into()),
6072/// Some(Pivot::TopCenter), Some(Align::TopLeft), None, None, None);
6073///
6074/// let size = Text::add_in(token, "Texts!", transform2, [0.6, 0.6], TextFit::Squeeze,
6075/// Some(style), Some(GREEN.into()), Some(Pivot::Center), Some(Align::TopLeft),
6076/// None, Some(-0.3), Some(-0.3));
6077/// assert_ne!(size , 0.0);
6078///
6079/// Text::add_at(token, "----/****", transform3, Some(style), None,
6080/// None, None, None, None, None);
6081/// );
6082/// ```
6083/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/text.jpeg" alt="screenshot" width="200">
6084pub struct Text;
6085
6086unsafe extern "C" {
6087 pub fn text_add_at(
6088 text_utf8: *const c_char,
6089 transform: *const Matrix,
6090 style: TextStyle,
6091 position: Pivot,
6092 align: Align,
6093 off_x: f32,
6094 off_y: f32,
6095 off_z: f32,
6096 vertex_tint_linear: Color128,
6097 );
6098 pub fn text_add_at_16(
6099 text_utf16: *const c_ushort,
6100 transform: *const Matrix,
6101 style: TextStyle,
6102 position: Pivot,
6103 align: Align,
6104 off_x: f32,
6105 off_y: f32,
6106 off_z: f32,
6107 vertex_tint_linear: Color128,
6108 );
6109 pub fn text_add_in(
6110 text_utf8: *const c_char,
6111 transform: *const Matrix,
6112 size: Vec2,
6113 fit: TextFit,
6114 style: TextStyle,
6115 position: Pivot,
6116 align: Align,
6117 off_x: f32,
6118 off_y: f32,
6119 off_z: f32,
6120 vertex_tint_linear: Color128,
6121 ) -> f32;
6122 pub fn text_add_in_16(
6123 text_utf16: *const c_ushort,
6124 transform: *const Matrix,
6125 size: Vec2,
6126 fit: TextFit,
6127 style: TextStyle,
6128 position: Pivot,
6129 align: Align,
6130 off_x: f32,
6131 off_y: f32,
6132 off_z: f32,
6133 vertex_tint_linear: Color128,
6134 ) -> f32;
6135 pub fn text_size_layout(text_utf8: *const c_char, style: TextStyle) -> Vec2;
6136 pub fn text_size_layout_constrained(text_utf8: *const c_char, style: TextStyle, max_width: f32) -> Vec2;
6137 pub fn text_size_layout_16(text_utf16: *const c_ushort, style: TextStyle) -> Vec2;
6138 pub fn text_size_layout_constrained_16(text_utf16: *const c_ushort, style: TextStyle, max_width: f32) -> Vec2;
6139 pub fn text_size_render(layout_size: Vec2, style: TextStyle, y_offset: *mut f32) -> Vec2;
6140 pub fn text_char_at(
6141 text_utf8: *const c_char,
6142 style: TextStyle,
6143 char_index: i32,
6144 opt_size: *mut Vec2,
6145 fit: TextFit,
6146 position: Pivot,
6147 align: Align,
6148 ) -> Vec2;
6149 pub fn text_char_at_16(
6150 text_utf16: *const c_ushort,
6151 style: TextStyle,
6152 char_index: i32,
6153 opt_size: *mut Vec2,
6154 fit: TextFit,
6155 position: Pivot,
6156 align: Align,
6157 ) -> Vec2;
6158}
6159
6160impl Text {
6161 /// Create a text style for use with other text functions! A text style is a font plus size/color/material
6162 /// parameters, and are used to keep text looking more consistent through the application by encouraging devs to
6163 /// re-use styles throughout the project.
6164 ///
6165 /// This fn will create an unique Material for this style based on Default.ShaderFont.
6166 /// <https://stereokit.net/Pages/StereoKit/Text/MakeStyle.html>
6167 /// * `font` - Font asset you want attached to this style.
6168 /// * `layout_height_meters`- Height of a text glyph in meters. StereoKit currently bases this on CapHeight.
6169 /// * `color_gamma` - The gamma space color of the text style. This will be embedded in the vertex color of the
6170 /// text mesh.
6171 ///
6172 /// Returns a text style id for use with text rendering functions.
6173 ///
6174 /// see also [`text_make_style`] same as [`TextStyle::from_font`]
6175 /// ### Examples
6176 /// ```
6177 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6178 /// use stereokit_rust::{system::Text, font::Font, util::named_colors};
6179 ///
6180 /// let font = Font::default();
6181 ///
6182 /// let text_style = Text::make_style(&font, 0.02, named_colors::WHITE);
6183 ///
6184 /// assert_eq!(text_style.get_material().get_id(), "sk/text_style/2/material");
6185 /// assert_eq!(text_style.get_layout_height(), 0.02);
6186 /// ```
6187 pub fn make_style(
6188 font: impl AsRef<Font>,
6189 layout_height_meters: f32,
6190 color_gamma: impl Into<Color128>,
6191 ) -> TextStyle {
6192 unsafe { text_make_style(font.as_ref().0.as_ptr(), layout_height_meters, color_gamma.into()) }
6193 }
6194
6195 /// Create a text style for use with other text functions! A text style is a font plus size/color/material
6196 /// parameters, and are used to keep text looking more consistent through the application by encouraging devs to
6197 /// re-use styles throughout the project.
6198 ///
6199 /// This function will create an unique Material for this style based on the provided Shader.
6200 /// <https://stereokit.net/Pages/StereoKit/Text/MakeStyle.html>
6201 /// * `font` - Font asset you want attached to this style.
6202 /// * `layout_height_meters`- Height of a text glyph in meters. StereoKit currently bases this on CapHeight.
6203 /// * `shader` - This style will create and use a unique Material based on the Shader that you provide here
6204 /// * `color_gamma` - The gamma space color of the text style. This will be embedded in the vertex color of the
6205 /// text mesh.
6206 ///
6207 /// Returns a text style id for use with text rendering functions.
6208 ///
6209 /// see also [`text_make_style_shader`] same as [`TextStyle::from_font_and_shader`]
6210 /// ### Examples
6211 /// ```
6212 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6213 /// use stereokit_rust::{system::{Assets, Text}, font::Font,
6214 /// util::named_colors, shader::Shader};
6215 ///
6216 /// let font = Font::default();
6217 /// let shader = Shader::from_file("shaders/water_pbr.hlsl.sks")
6218 /// .expect("Brick_pbr should be a valid shader");
6219 /// Assets::block_for_priority(i32::MAX);
6220 ///
6221 /// let text_style = Text::make_style_with_shader(&font, 0.02, shader, named_colors::WHITE);
6222 ///
6223 /// assert_eq!(text_style.get_material().get_id(), "sk/text_style/2/material");
6224 /// assert_eq!(text_style.get_layout_height(), 0.02);
6225 /// ```
6226 pub fn make_style_with_shader(
6227 font: impl AsRef<Font>,
6228 layout_height_meters: f32,
6229 shader: impl AsRef<Shader>,
6230 color_gamma: impl Into<Color128>,
6231 ) -> TextStyle {
6232 unsafe {
6233 text_make_style_shader(
6234 font.as_ref().0.as_ptr(),
6235 layout_height_meters,
6236 shader.as_ref().0.as_ptr(),
6237 color_gamma.into(),
6238 )
6239 }
6240 }
6241
6242 /// Create a text style for use with other text functions! A text style is a font plus size/color/material
6243 /// parameters, and are used to keep text looking more consistent through the application by encouraging devs to
6244 /// re-use styles throughout the project.
6245 ///
6246 /// This overload allows you to set the specific Material that is used. This can be helpful if you’re keeping styles
6247 /// similar enough to re-use the material and save on draw calls. If you don’t know what that means, then prefer
6248 /// using the overload that takes a Shader, or takes neither a Shader nor a Material!
6249 /// <https://stereokit.net/Pages/StereoKit/Text/MakeStyle.html>
6250 /// * `font` - Font asset you want attached to this style.
6251 /// * `layout_height_meters`- Height of a text glyph in meters. StereoKit currently bases this on CapHeight.
6252 /// * `material` - Which material should be used to render the text with? Note that this does NOT duplicate the
6253 /// material, so some parameters of this Material instance will get overwritten, like the texture used for the
6254 /// glyph atlas. You should either use a new Material, or a Material that was already used with this same font.
6255 /// * `color_gamma` - The gamma space color of the text style. This will be embedded in the vertex color of the
6256 /// text mesh.
6257 ///
6258 /// Returns a text style id for use with text rendering functions.
6259 ///
6260 /// see also [`text_make_style_mat`]
6261 /// same as [`TextStyle::from_font_and_material`] same as [TextStyle::from_font_and_material]
6262 /// ### Examples
6263 /// ```
6264 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6265 /// use stereokit_rust::{system::Text, font::Font, util::named_colors, material::Material};
6266 ///
6267 /// let font = Font::default();
6268 /// let material = Material::pbr().copy();
6269 ///
6270 /// let text_style = Text::make_style_with_material(&font, 0.02, &material, named_colors::WHITE);
6271 ///
6272 /// assert_eq!(text_style.get_material(), material);
6273 /// assert_eq!(text_style.get_layout_height(), 0.02);
6274 /// ```
6275 pub fn make_style_with_material(
6276 font: impl AsRef<Font>,
6277 layout_height_meters: f32,
6278 material: impl AsRef<Material>,
6279 color_gamma: impl Into<Color128>,
6280 ) -> TextStyle {
6281 unsafe {
6282 text_make_style_mat(
6283 font.as_ref().0.as_ptr(),
6284 layout_height_meters,
6285 material.as_ref().0.as_ptr(),
6286 color_gamma.into(),
6287 )
6288 }
6289 }
6290
6291 /// Renders text at the given location! Must be called every frame you want this text to be visible.
6292 /// <https://stereokit.net/Pages/StereoKit/Text/Add.html>
6293 /// * `text` - What text should be drawn?
6294 /// * `transform` - A Matrix representing the transform of the text mesh! Try Matrix::t_r_s().
6295 /// * `text_style` - Style information for rendering, see Text.MakeStyle or the TextStyle object. If None will use
6296 /// the TextStyle::default()
6297 /// * `vertex_tint_linear` - The vertex color of the text gets multiplied by this color. This is a linear color
6298 /// value, not a gamma corrected color value. If None will use Color128::WHITE
6299 /// * `position` - How should the text’s bounding rectangle be positioned relative to the transform? If None will
6300 /// use Pivot::Center.
6301 /// * `align` - How should the text be aligned within the text’s bounding rectangle? If None will use
6302 /// Align::Center.
6303 /// * `off_?` - An additional offset on the given axis. If None will use 0.0.
6304 ///
6305 /// see also [`text_add_at`]
6306 /// ### Examples
6307 /// ```
6308 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6309 /// use stereokit_rust::{system::{Pivot, Align, TextFit, Text},
6310 /// font::Font, maths::{Vec3, Matrix},
6311 /// util::named_colors::{WHITE, GOLD, GREEN}};
6312 ///
6313 /// let font = Font::default();
6314 /// let style = Text::make_style(font, 0.28, WHITE);
6315 /// let transform1 = Matrix::t([0.7, 0.7, 0.0]) * Matrix::Y_180;
6316 /// let transform2 = Matrix::t([0.3, 0.1, 0.0]) * Matrix::Y_180;
6317 /// let transform3 = Matrix::t([-0.1,-0.5, 0.0]) * Matrix::Y_180;
6318 ///
6319 /// test_steps!( // !!!! Get a proper main loop !!!!
6320 /// Text::add_at(token, "Many", transform1, Some(style), Some(GOLD.into()),
6321 /// Some(Pivot::TopCenter), Some(Align::TopLeft), None, None, None);
6322 ///
6323 /// Text::add_at(token, "Texts!", transform2, Some(style), Some(GREEN.into()),
6324 /// Some(Pivot::Center), Some(Align::TopLeft), None, None, Some(-0.3));
6325 ///
6326 /// Text::add_at(token, "----/****", transform3, Some(style), None,
6327 /// None, None, None, None, None);
6328 /// );
6329 /// ```
6330 #[allow(clippy::too_many_arguments)]
6331 pub fn add_at(
6332 _token: &MainThreadToken,
6333 text: impl AsRef<str>,
6334 transform: impl Into<Matrix>,
6335 text_style: Option<TextStyle>,
6336 vertex_tint_linear: Option<Color128>,
6337 position: Option<Pivot>,
6338 align: Option<Align>,
6339 off_x: Option<f32>,
6340 off_y: Option<f32>,
6341 off_z: Option<f32>,
6342 ) {
6343 let c_str = CString::new(text.as_ref()).unwrap();
6344 let style = text_style.unwrap_or_default();
6345 let vertex_tint_linear = vertex_tint_linear.unwrap_or(Color128::WHITE);
6346 let position = position.unwrap_or(Pivot::Center);
6347 let align = align.unwrap_or(Align::Center);
6348 let off_x = off_x.unwrap_or(0.0);
6349 let off_y = off_y.unwrap_or(0.0);
6350 let off_z = off_z.unwrap_or(0.0);
6351 unsafe {
6352 text_add_at(
6353 c_str.as_ptr(),
6354 &transform.into(),
6355 style,
6356 position,
6357 align,
6358 off_x,
6359 off_y,
6360 off_z,
6361 vertex_tint_linear,
6362 )
6363 }
6364 }
6365
6366 /// Renders text at the given location! Must be called every frame you want this text to be visible.
6367 /// <https://stereokit.net/Pages/StereoKit/Text/Add.html>
6368 /// * `text` - What text should be drawn?
6369 /// * `transform` - A Matrix representing the transform of the text mesh! Try Matrix::t_r_s().
6370 /// * `size` - This is the Hierarchy space rectangle that the text should try to fit inside of. This allows for text
6371 /// wrapping or scaling based on the value provided to the ‘fit’ parameter.
6372 /// * `text_fit` - Describe how the text should behave when one of its size dimensions conflicts with the provided
6373 /// ‘size’ parameter.
6374 /// * `text_style` - Style information for rendering, see Text.MakeStyle or the TextStyle object. If None will use
6375 /// the TextStyle::default()
6376 /// * `vertex_tint_linear` - The vertex color of the text gets multiplied by this color. This is a linear color
6377 /// value, not a gamma corrected color value. If None will use Color128::WHITE
6378 /// * `position` - How should the text’s bounding rectangle be positioned relative to the transform? If None will
6379 /// use Pivot::Center.
6380 /// * `align` - How should the text be aligned within the text’s bounding rectangle? If None will use
6381 /// Align::Center.
6382 /// * `off_?` - An additional offset on the given axis. If None will use 0.0.
6383 ///
6384 /// Returns the vertical space used by this text.
6385 ///
6386 /// see also [`text_add_in`]
6387 /// ### Examples
6388 /// ```
6389 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6390 /// use stereokit_rust::{system::{ Align, Pivot, TextFit, Text},
6391 /// font::Font, maths::{Vec3, Matrix},
6392 /// util::named_colors::{WHITE, GOLD, GREEN}};
6393 ///
6394 /// let font = Font::default();
6395 /// let style = Text::make_style(font, 0.28, WHITE);
6396 /// let transform1 = Matrix::Y_180;
6397 ///
6398 /// test_steps!( // !!!! Get a proper main loop !!!!
6399 /// let size = Text::add_in(token, "Many", transform1, [1.1, 1.0], TextFit::Wrap,
6400 /// Some(style), Some(GOLD.into()), Some(Pivot::BottomRight),
6401 /// Some(Align::TopLeft), None, None, None);
6402 ///
6403 /// let size = Text::add_in(token, "Texts!", transform1, [1.0, 1.0-size], TextFit::Clip,
6404 /// Some(style),None, None,
6405 /// None, None, None, None);
6406 ///
6407 /// Text::add_in(token, "----/****", transform1, [0.3, 1.0-size], TextFit::Squeeze,
6408 /// Some(style), Some(GREEN.into()), Some(Pivot::YTop),
6409 /// Some(Align::Center),None, None, Some(-0.7));
6410 /// );
6411 /// ```
6412 #[allow(clippy::too_many_arguments)]
6413 pub fn add_in(
6414 _token: &MainThreadToken,
6415 text: impl AsRef<str>,
6416 transform: impl Into<Matrix>,
6417 size: impl Into<Vec2>,
6418 fit: TextFit,
6419 text_style: Option<TextStyle>,
6420 vertex_tint_linear: Option<Color128>,
6421 position: Option<Pivot>,
6422 align: Option<Align>,
6423 off_x: Option<f32>,
6424 off_y: Option<f32>,
6425 off_z: Option<f32>,
6426 ) -> f32 {
6427 let c_str = CString::new(text.as_ref()).unwrap();
6428 let style = text_style.unwrap_or_default();
6429 let vertex_tint_linear = vertex_tint_linear.unwrap_or(Color128::WHITE);
6430 let position = position.unwrap_or(Pivot::Center);
6431 let align = align.unwrap_or(Align::Center);
6432 let off_x = off_x.unwrap_or(0.0);
6433 let off_y = off_y.unwrap_or(0.0);
6434 let off_z = off_z.unwrap_or(0.0);
6435 unsafe {
6436 text_add_in(
6437 c_str.as_ptr(),
6438 &transform.into(),
6439 size.into(),
6440 fit,
6441 style,
6442 position,
6443 align,
6444 off_x,
6445 off_y,
6446 off_z,
6447 vertex_tint_linear,
6448 )
6449 }
6450 }
6451
6452 /// Sometimes you just need to know how much room some text takes up! This finds the size of the text in meters when
6453 /// using the indicated style!
6454 /// <https://stereokit.net/Pages/StereoKit/Text/Size.html>
6455 /// * text_style - if None will use the TextStyle::default()
6456 /// * max_width - Width of the available space in meters.
6457 ///
6458 /// Returns size of the text in meters
6459 ///
6460 /// see also [`text_size_layout`] [`text_size_layout_constrained`]
6461 #[deprecated(since = "0.40.0", note = "please Text::use size_layout")]
6462 pub fn size(text: impl AsRef<str>, text_style: Option<TextStyle>, max_width: Option<f32>) -> Vec2 {
6463 let c_str = CString::new(text.as_ref()).unwrap();
6464 let style = text_style.unwrap_or_default();
6465 if let Some(max_width) = max_width {
6466 unsafe { text_size_layout_constrained(c_str.as_ptr(), style, max_width) }
6467 } else {
6468 unsafe { text_size_layout(c_str.as_ptr(), style) }
6469 }
6470 }
6471
6472 /// Sometimes you just need to know how much room some text takes up! This finds the layout size of the text in
6473 /// meters when using the indicated style! This does not include ascender and descender size, so rendering using
6474 /// this as a clipping size will result in ascenders and descenders getting clipped.
6475 /// <https://stereokit.net/Pages/StereoKit/Text/SizeLayout.html>
6476 /// * `text` - Text you want to find the size of.
6477 /// * `text_style` - if None will use the TextStyle::default()
6478 /// * `max_width` - Width of the available space in meters if you need to know how much layout space text will take
6479 /// when constrained to a certain width? This will find it using the indicated text style!
6480 ///
6481 /// Returns size of the text in meters
6482 ///
6483 /// see also [`text_size_layout`] [`text_size_layout_constrained`]
6484 /// ### Examples
6485 /// ```
6486 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6487 /// use stereokit_rust::{system::{Text, TextStyle}, font::Font,
6488 /// util::named_colors::{WHITE, GOLD, GREEN},
6489 /// mesh::Mesh, material::{Material, Cull}, maths::Matrix};
6490 ///
6491 /// let font = Font::default();
6492 /// let style = Text::make_style(font, 0.70, WHITE);
6493 /// let transform1 = Matrix::Y_180;
6494 /// let text = "Yo!";
6495 ///
6496 /// let size = Text::size_layout(text, Some(style), None);
6497 /// let cube = Mesh::generate_cube([size.x, size.y, size.y], None);
6498 /// let mut material = Material::pbr().copy();
6499 /// material.face_cull(Cull::Front);
6500 ///
6501 /// test_steps!( // !!!! Get a proper main loop !!!!
6502 /// Text::add_at(token, text, transform1, Some(style), Some(GOLD.into()),
6503 /// None, None, None, None, None);
6504 ///
6505 /// cube.draw(token, &material, transform1, Some(GREEN.into()), None);
6506 /// );
6507 /// ```
6508 pub fn size_layout(text: impl AsRef<str>, text_style: Option<TextStyle>, max_width: Option<f32>) -> Vec2 {
6509 let c_str = CString::new(text.as_ref()).unwrap();
6510 let style = text_style.unwrap_or_default();
6511 if let Some(max_width) = max_width {
6512 unsafe { text_size_layout_constrained(c_str.as_ptr(), style, max_width) }
6513 } else {
6514 unsafe { text_size_layout(c_str.as_ptr(), style) }
6515 }
6516 }
6517
6518 /// This modifies a text layout size to include the tallest and lowest possible values for the glyphs in this font.
6519 /// This is for when you need to be careful about avoiding clipping that would happen if you only used the layout size.
6520 /// <https://stereokit.net/Pages/StereoKit/Text/SizeRender.html>
6521 /// * `size_layout` - A size previously calculated using `Text.SizeLayout`.
6522 /// * `text_style` -The same style as used for calculating the sizeLayout. If None will use the TextStyle::default()
6523 /// * `y_offset` - Since the render size will ascend from the initial position, this will be the offset from the
6524 /// initial position upwards. You should add it to your Y position.
6525 ///
6526 /// Returns the sizeLayout modified to account for the size of the most extreme glyphs.
6527 ///
6528 /// see also [`text_size_layout`] [`text_size_layout_constrained`]
6529 /// ### Examples
6530 /// ```
6531 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6532 /// use stereokit_rust::{system::{Text, TextStyle}, font::Font,
6533 /// util::named_colors::{WHITE, GOLD, GREEN},
6534 /// mesh::Mesh, material::{Material, Cull}, maths::Matrix};
6535 ///
6536 /// let font = Font::default();
6537 /// let style = Text::make_style(font, 0.45, WHITE);
6538 /// let transform_text = Matrix::Y_180;
6539 /// let text = "Yo!";
6540 ///
6541 /// let size = Text::size_layout(text, Some(style), None);
6542 /// let mut render_yoff = 0.0;
6543 /// let render_size = Text::size_render(size, Some(style), &mut render_yoff);
6544 /// let cube = Mesh::generate_cube([render_size.x, render_size.y, render_size.y], None);
6545 /// let transform_cube = Matrix::t([0.0, render_yoff/2.0, 0.0]);
6546 /// let mut material = Material::pbr().copy();
6547 /// material.face_cull(Cull::Front);
6548 ///
6549 /// test_steps!( // !!!! Get a proper main loop !!!!
6550 /// Text::add_at(token, text, transform_text, Some(style), Some(GOLD.into()),
6551 /// None, None, None, None, None);
6552 ///
6553 /// cube.draw(token, &material, transform_cube, Some(GREEN.into()), None);
6554 /// );
6555 /// ```
6556 pub fn size_render(size_layout: impl Into<Vec2>, text_style: Option<TextStyle>, y_offset: &mut f32) -> Vec2 {
6557 let style = text_style.unwrap_or_default();
6558 unsafe { text_size_render(size_layout.into(), style, y_offset) }
6559 }
6560}
6561
6562/// A settings flag that lets you describe the behavior of how StereoKit will refresh data about the world mesh, if
6563/// applicable. This is used with World.RefreshType.
6564/// <https://stereokit.net/Pages/StereoKit/WorldRefresh.html>
6565///
6566/// see also [`World`]
6567#[derive(Debug, Copy, Clone, PartialEq, Eq)]
6568#[repr(u32)]
6569pub enum WorldRefresh {
6570 /// Refreshing occurs when the user leaves the area that was most recently scanned. This area is a sphere that is
6571 /// 0.5 of the World::refresh_radius.
6572 Area = 0,
6573 /// Refreshing happens at timer intervals. If an update doesn’t happen in time, the next update will happen as soon
6574 /// as possible. The timer interval is configurable via World::refresh_nterval.
6575 Timer = 1,
6576}
6577
6578/// For use with World::from_spatial_node, this indicates the type of
6579/// node that's being bridged with OpenXR.
6580/// <https://stereokit.net/Pages/StereoKit/SpatialNodeType.html>
6581///
6582/// see also [`World`]
6583#[derive(Debug, Copy, Clone, PartialEq, Eq)]
6584#[repr(u32)]
6585pub enum SpatialNodeType {
6586 /// Static spatial nodes track the pose of a fixed location in
6587 /// the world relative to reference spaces. The tracking of static
6588 /// nodes may slowly adjust the pose over time for better accuracy but
6589 /// the pose is relatively stable in the short term, such as between
6590 /// rendering frames. For example, a QR code tracking library can use a
6591 /// static node to represent the location of the tracked QR code.
6592 Static = 0,
6593 /// Dynamic spatial nodes track the pose of a physical object
6594 /// that moves continuously relative to reference spaces. The pose of
6595 /// dynamic spatial nodes can be very different within the duration of
6596 /// a rendering frame. It is important for the application to use the
6597 /// correct timestamp to query the space location. For example, a color
6598 /// camera mounted in front of a HMD is also tracked by the HMD so a
6599 /// web camera library can use a dynamic node to represent the camera
6600 /// location.
6601 Dynamic = 1,
6602}
6603
6604/// World contains information about the real world around the user. This includes things like play boundaries, scene
6605/// understanding, and other various things.
6606/// <https://stereokit.net/Pages/StereoKit/World.html>
6607pub struct World;
6608
6609unsafe extern "C" {
6610 pub fn world_has_bounds() -> Bool32T;
6611 pub fn world_get_bounds_size() -> Vec2;
6612 pub fn world_get_bounds_pose() -> Pose;
6613 pub fn world_from_spatial_graph(spatial_graph_node_id: *mut u8, dynamic: SpatialNodeType, qpc_time: i64) -> Pose;
6614 pub fn world_from_perception_anchor(perception_spatial_anchor: *mut c_void) -> Pose;
6615 pub fn world_try_from_spatial_graph(
6616 spatial_graph_node_id: *mut u8,
6617 dynamic: SpatialNodeType,
6618 qpc_time: i64,
6619 out_pose: *mut Pose,
6620 ) -> Bool32T;
6621 pub fn world_try_from_perception_anchor(perception_spatial_anchor: *mut c_void, out_pose: *mut Pose) -> Bool32T;
6622 pub fn world_raycast(ray: Ray, out_intersection: *mut Ray) -> Bool32T;
6623 pub fn world_set_occlusion_enabled(enabled: Bool32T);
6624 pub fn world_get_occlusion_enabled() -> Bool32T;
6625 pub fn world_set_raycast_enabled(enabled: Bool32T);
6626 pub fn world_get_raycast_enabled() -> Bool32T;
6627 pub fn world_set_occlusion_material(material: MaterialT);
6628 pub fn world_get_occlusion_material() -> MaterialT;
6629 pub fn world_set_refresh_type(refresh_type: WorldRefresh);
6630 pub fn world_get_refresh_type() -> WorldRefresh;
6631 pub fn world_set_refresh_radius(radius_meters: f32);
6632 pub fn world_get_refresh_radius() -> f32;
6633 pub fn world_set_refresh_interval(every_seconds: f32);
6634 pub fn world_get_refresh_interval() -> f32;
6635 pub fn world_get_tracked() -> BtnState;
6636 pub fn world_get_origin_mode() -> OriginMode;
6637 pub fn world_get_origin_offset() -> Pose;
6638 pub fn world_set_origin_offset(offset: Pose);
6639}
6640
6641impl World {
6642 /// Off by default. This tells StereoKit to load up and display an occlusion surface that allows the real world to
6643 /// occlude the application’s digital content! Most systems may allow you to customize the visual appearance of this
6644 /// occlusion surface via the World::occlusion_material. Check [`crate::sk::SystemInfo::get_world_occlusion_present`] to see if
6645 /// occlusion can be enabled. This will reset itself to false if occlusion isn’t possible. Loading occlusion data
6646 /// is asynchronous, so occlusion may not occur immediately after setting this flag.
6647 /// <https://stereokit.net/Pages/StereoKit/World/OcclusionEnabled.html>
6648 ///
6649 /// see also [world_set_occlusion_enabled]
6650 /// ### Examples
6651 /// ```
6652 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6653 /// use stereokit_rust::{system::World};
6654 ///
6655 /// let occlusion_is_present = sk.get_system().get_world_occlusion_present();
6656 ///
6657 /// // By default, occlusion is disabled.
6658 /// assert_eq!(World::get_occlusion_enabled(), false);
6659 ///
6660 /// World::occlusion_enabled(true);
6661 /// if occlusion_is_present {
6662 /// assert_eq!(World::get_occlusion_enabled(), true);
6663 /// } else {
6664 /// assert_eq!(World::get_occlusion_enabled(), false);
6665 /// }
6666 ///
6667 /// World::occlusion_enabled(false);
6668 /// assert_eq!(World::get_occlusion_enabled(), false);
6669 /// ```
6670 pub fn occlusion_enabled(enabled: bool) {
6671 unsafe { world_set_occlusion_enabled(enabled as Bool32T) }
6672 }
6673
6674 /// By default, this is a black(0,0,0,0) opaque unlit material that will occlude geometry, but won’t show up as
6675 /// visible anywhere. You can override this with whatever material you would like.
6676 /// <https://stereokit.net/Pages/StereoKit/World/OcclusionMaterial.html>
6677 ///
6678 /// see also [world_set_occlusion_material]
6679 /// ### Examples
6680 /// ```
6681 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6682 /// use stereokit_rust::{system::World, util::named_colors, material::Material};
6683 ///
6684 /// let occlusion_is_present = sk.get_system().get_world_occlusion_present();
6685 ///
6686 /// assert_eq!(World::get_occlusion_material().get_id(), "sk/world/material");
6687 ///
6688 /// let mut material = Material::unlit().copy();
6689 /// material.color_tint(named_colors::RED);
6690 ///
6691 /// World::occlusion_enabled(true);
6692 /// World::occlusion_material(&material);
6693 ///
6694 /// if occlusion_is_present {
6695 /// assert_eq!(World::get_occlusion_enabled(), true);
6696 /// assert_eq!(World::get_occlusion_material(), material);
6697 /// } else {
6698 /// assert_eq!(World::get_occlusion_enabled(), false);
6699 /// assert_eq!(World::get_occlusion_material(), material);
6700 /// }
6701 /// ```
6702 pub fn occlusion_material(material: impl AsRef<Material>) {
6703 unsafe { world_set_occlusion_material(material.as_ref().0.as_ptr()) }
6704 }
6705
6706 /// This is relative to the base reference point and is NOT in world space! The origin StereoKit uses is actually a
6707 /// base reference point combined with an offset! You can use this to read or set the offset from the OriginMode
6708 /// reference point.
6709 /// <https://stereokit.net/Pages/StereoKit/World/OriginOffset.html>
6710 ///
6711 /// see also [world_set_origin_offset] [`crate::sk::SkSettings::origin`]
6712 /// ### Examples
6713 /// ```
6714 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6715 /// use stereokit_rust::{maths::{Vec3, Pose}, system::World};
6716 ///
6717 /// let origin_offset = World::get_origin_offset();
6718 ///
6719 /// xr_mode_stop_here!();
6720 /// // These are the expected results for offscreen tests on a PC:
6721 /// assert_eq!(origin_offset, Pose::ZERO);
6722 ///
6723 /// let offset = Pose::new([0.0, 0.0, 0.01], None);
6724 /// if false {World::origin_offset(offset);}
6725 /// ```
6726 pub fn origin_offset(offset: impl Into<Pose>) {
6727 unsafe { world_set_origin_offset(offset.into()) }
6728 }
6729
6730 /// Off by default. This tells StereoKit to load up collision meshes for the environment, for use with
6731 /// World::raycast. Check [`crate::sk::SystemInfo::get_world_raycast_present`] to see if raycasting can be enabled. This will reset
6732 /// itself to false if raycasting isn’t possible. Loading raycasting data is asynchronous, so collision surfaces may
6733 /// not be available immediately after setting this flag.
6734 /// <https://stereokit.net/Pages/StereoKit/World/RaycastEnabled.html>
6735 ///
6736 /// see also [world_set_raycast_enabled]
6737 /// ### Examples
6738 /// ```
6739 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6740 /// use stereokit_rust::system::World;
6741 ///
6742 /// let raycast_is_present = sk.get_system().get_world_raycast_present();
6743 ///
6744 /// assert_eq!(World::get_raycast_enabled(), false);
6745 ///
6746 /// World::raycast_enabled(true);
6747 ///
6748 /// if raycast_is_present {
6749 /// assert_eq!(World::get_raycast_enabled(), true);
6750 /// } else {
6751 /// assert_eq!(World::get_raycast_enabled(), false);
6752 /// }
6753 /// World::raycast_enabled(false);
6754 /// assert_eq!(World::get_raycast_enabled(), false);
6755 ///
6756 /// ```
6757 pub fn raycast_enabled(enabled: bool) {
6758 unsafe { world_set_raycast_enabled(enabled as Bool32T) }
6759 }
6760
6761 /// The refresh interval speed, in seconds. This is only applicable when using WorldRefresh::Timer for the refresh
6762 /// type. Note that the system may not be able to refresh as fast as you wish, and in that case, StereoKit will
6763 /// always refresh as soon as the previous refresh finishes.
6764 /// <https://stereokit.net/Pages/StereoKit/World/RefreshInterval.html>
6765 ///
6766 /// see also [world_set_refresh_interval]
6767 /// ### Examples
6768 /// ```
6769 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6770 /// use stereokit_rust::system::World;
6771 ///
6772 /// let occlusion_is_present = sk.get_system().get_world_occlusion_present();
6773 ///
6774 /// World::occlusion_enabled(true);
6775 /// World::refresh_interval(0.01);
6776 ///
6777 /// let occlusion_enabled = World::get_occlusion_enabled();
6778 /// let refresh_interval = World::get_refresh_interval();
6779 ///
6780 /// offscreen_mode_stop_here!();
6781 /// // These are the expected results for OpenXR tests on a PC:
6782 /// assert_eq!(refresh_interval, 0.01);
6783 /// if occlusion_is_present {
6784 /// assert_eq!(occlusion_enabled, true);
6785 /// } else {
6786 /// assert_eq!(occlusion_enabled, false);
6787 /// }
6788 /// ```
6789 pub fn refresh_interval(speed: f32) {
6790 unsafe { world_set_refresh_interval(speed) }
6791 }
6792
6793 /// Radius, in meters, of the area that StereoKit should scan for world data. Default is 4. When using the
6794 /// WorldRefresh::Area refresh type, the world data will refresh when the user has traveled half this radius from
6795 /// the center of where the most recent refresh occurred.
6796 /// <https://stereokit.net/Pages/StereoKit/World/RefreshRadius.html>
6797 ///
6798 /// see also [world_set_refresh_radius]
6799 /// ### Examples
6800 /// ```
6801 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6802 /// use stereokit_rust::system::World;
6803 ///
6804 /// World::refresh_radius(3.5);
6805 ///
6806 /// let refresh_radius = World::get_refresh_radius();
6807 ///
6808 /// offscreen_mode_stop_here!();
6809 /// // These are the expected results for OpenXR tests on a PC:
6810 /// assert_eq!(refresh_radius, 3.5);
6811 /// ```
6812 pub fn refresh_radius(distance: f32) {
6813 unsafe { world_set_refresh_radius(distance) }
6814 }
6815
6816 /// What information should StereoKit use to determine when the next world data refresh happens? See the
6817 /// WorldRefresh enum for details.
6818 /// <https://stereokit.net/Pages/StereoKit/World/RefreshType.html>
6819 ///
6820 /// see also [world_set_refresh_type]
6821 /// ### Examples
6822 /// ```
6823 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6824 /// use stereokit_rust::system::{World, WorldRefresh};
6825 ///
6826 /// World::refresh_type(WorldRefresh::Timer);
6827 ///
6828 /// let refresh_type = World::get_refresh_type();
6829 ///
6830 /// offscreen_mode_stop_here!();
6831 /// // These are the expected results for OpenXR tests on a PC:
6832 /// assert_eq!(refresh_type, WorldRefresh::Timer);
6833 /// ```
6834 pub fn refresh_type(refresh_type: WorldRefresh) {
6835 unsafe { world_set_refresh_type(refresh_type) }
6836 }
6837
6838 /// Converts a Windows.Perception.Spatial.SpatialAnchor’s pose into SteroKit’s coordinate system. This can be great
6839 /// for interacting with some of the UWP spatial APIs such as WorldAnchors.
6840 ///
6841 /// This method only works on UWP platforms, check Sk.System.perception_bridge_present to see if this is available.
6842 /// <https://stereokit.net/Pages/StereoKit/World/FromPerceptionAnchor.html>
6843 ///
6844 /// see also [world_from_perception_anchor]
6845 #[allow(clippy::not_unsafe_ptr_arg_deref)]
6846 pub fn from_perception_anchor(perception_spatial_anchor: *mut c_void) -> Option<Pose> {
6847 let mut pose = Pose::IDENTITY;
6848 if unsafe { world_try_from_perception_anchor(perception_spatial_anchor, &mut pose) != 0 } {
6849 Some(pose)
6850 } else {
6851 None
6852 }
6853 }
6854 // TODO : Ask for the non try version
6855
6856 /// Converts a Windows Mirage spatial node GUID into a Pose based on its current position and rotation! Check
6857 /// Sk::System::spatial_bridge_present to see if this is available to use. Currently only on HoloLens, good for use
6858 /// with the Windows QR code package.
6859 /// <https://stereokit.net/Pages/StereoKit/World/FromSpatialNode.html>
6860 /// * `spatial_graph_node_id` - A Windows Mirage spatial node GUID acquired from a windows MR API call.
6861 /// * `spatial_node_type` - Type of spatial node to locate.
6862 /// * `qpc_time` : A windows performance counter timestamp at which the node should be located, obtained from
6863 /// another API or with System.Diagnostics.Stopwatch.GetTimestamp().
6864 ///
6865 /// see also [world_try_from_spatial_graph]
6866 /// ### Examples
6867 /// ```
6868 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6869 /// use stereokit_rust::system::{World, SpatialNodeType};
6870 ///
6871 /// let spatial_bridge_is_present = sk.get_system().get_spatial_bridge_present();
6872 ///
6873 /// World::refresh_radius(3.5);
6874 ///
6875 /// if spatial_bridge_is_present {
6876 /// World::from_spatial_node("A test", SpatialNodeType::Static, 0);
6877 /// }
6878 /// ```
6879 pub fn from_spatial_node(
6880 spatial_graph_node_id: impl AsRef<str>,
6881 spatial_node_type: SpatialNodeType,
6882 qpc_time: i64,
6883 ) -> Option<Pose> {
6884 let c_str = CString::new(spatial_graph_node_id.as_ref()).unwrap();
6885 let mut pose = Pose::IDENTITY;
6886 if unsafe {
6887 world_try_from_spatial_graph(c_str.as_ptr() as *mut u8, spatial_node_type, qpc_time, &mut pose) != 0
6888 } {
6889 Some(pose)
6890 } else {
6891 None
6892 }
6893 }
6894
6895 /// World::raycast_enabled must be set to true first! Sk::System::world_raycast_present must also be true.
6896 /// This does a ray intersection with whatever represents the environment at the moment! In this case, it’s a
6897 /// watertight collection of low resolution meshes calculated by the Scene Understanding extension, which is only
6898 /// provided by the Microsoft HoloLens runtime.
6899 /// <https://stereokit.net/Pages/StereoKit/World/Raycast.html>
6900 /// * `ray` - A world space ray that you’d like to try intersecting with the world mesh.
6901 ///
6902 /// Returns The location of the intersection, and direction of the world’s surface at that point if found.
6903 /// see also [world_raycast]
6904 /// ### Examples
6905 /// ```
6906 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6907 /// use stereokit_rust::{system::{Assets, World}, maths::{Vec3,Ray}};
6908 /// Assets::block_for_priority(i32::MAX);
6909 ///
6910 /// let raycast_is_present = sk.get_system().get_world_raycast_present();
6911 ///
6912 /// assert_eq!(World::get_raycast_enabled(), false);
6913 ///
6914 /// World::raycast_enabled(true);
6915 /// let ray = Ray::new(Vec3::ZERO, Vec3::ONE);
6916 ///
6917 /// if raycast_is_present {
6918 /// assert_eq!(World::raycast(ray), None);
6919 /// } else {
6920 /// assert_eq!(World::raycast(ray), None);
6921 /// }
6922 /// ```
6923 pub fn raycast(ray: impl Into<Ray>) -> Option<Ray> {
6924 let mut intersection = Ray::default();
6925 if unsafe { world_raycast(ray.into(), &mut intersection) != 0 } { Some(intersection) } else { None }
6926 }
6927
6928 /// This is the orientation and center point of the system’s boundary/guardian. This can be useful to find the floor
6929 /// height! Not all systems have a boundary, so be sure to check [`World::has_bounds`] first.
6930 /// <https://stereokit.net/Pages/StereoKit/World/BoundsPose.html>
6931 ///
6932 /// see also [world_get_bounds_pose]
6933 /// ### Examples
6934 /// ```
6935 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6936 /// use stereokit_rust::{system::World, maths::Pose};
6937 ///
6938 /// let bounds_pose = World::get_bounds_pose();
6939 ///
6940 /// xr_mode_stop_here!();
6941 /// // These are the expected results for offscreen tests on a PC:
6942 /// if World::has_bounds(){
6943 /// // These are results for a non OpenXR environment:
6944 /// assert_eq!(bounds_pose, Pose::IDENTITY);
6945 /// } else {
6946 /// // These are results for a non OpenXR environment:
6947 /// assert_eq!(bounds_pose, Pose::IDENTITY);
6948 /// }
6949 /// ```
6950 pub fn get_bounds_pose() -> Pose {
6951 unsafe { world_get_bounds_pose() }
6952 }
6953
6954 /// This is the size of a rectangle within the play boundary/guardian’s space, in meters if one exists. Check
6955 /// [`World::get_bounds_pose`] for the center point and orientation of the boundary, and check [`World::has_bounds`] to see if it
6956 /// exists at all!
6957 /// <https://stereokit.net/Pages/StereoKit/World/BoundsSize.html>
6958 ///
6959 /// see also [world_get_bounds_size]
6960 /// ### Examples
6961 /// ```
6962 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
6963 /// use stereokit_rust::{system::World, maths::Vec2};
6964 ///
6965 /// let bounds_size = World::get_bounds_size();
6966 ///
6967 /// if World::has_bounds(){
6968 /// // These are results for a non OpenXR environment:
6969 /// // Sometime it's ZERO (Monado/simulator) and sometimes it's a valid size (real OpenXR):
6970 /// } else {
6971 /// // These are results for a non OpenXR environment:
6972 /// assert_eq!(bounds_size, Vec2::ZERO);
6973 /// }
6974 /// ```
6975 pub fn get_bounds_size() -> Vec2 {
6976 unsafe { world_get_bounds_size() }
6977 }
6978
6979 /// This refers to the play boundary, or guardian system that the system may have! Not all systems have this, so
6980 /// it’s always a good idea to check this first!
6981 /// <https://stereokit.net/Pages/StereoKit/World/HasBounds.html>
6982 ///
6983 /// see also [world_has_bounds]
6984 /// see example in [`World::get_bounds_size`] [`World::get_bounds_pose`]
6985 pub fn has_bounds() -> bool {
6986 unsafe { world_has_bounds() != 0 }
6987 }
6988
6989 /// Off by default. This tells StereoKit to load up and display an occlusion surface that allows the real world to
6990 /// occlude the application’s digital content! Most systems may allow you to customize the visual appearance of this
6991 /// occlusion surface via the World::occlusion_material. Check SK::System::world_occlusion_present to see if
6992 /// occlusion can be enabled. This will reset itself to false if occlusion isn’t possible. Loading occlusion data
6993 /// is asynchronous, so occlusion may not occur immediately after setting this flag.
6994 /// <https://stereokit.net/Pages/StereoKit/World/OcclusionEnabled.html>
6995 ///
6996 /// see also [world_get_occlusion_enabled]
6997 /// see example in [`World::occlusion_enabled`]
6998 pub fn get_occlusion_enabled() -> bool {
6999 unsafe { world_get_occlusion_enabled() != 0 }
7000 }
7001
7002 /// By default, this is a black(0,0,0,0) opaque unlit material that will occlude geometry, but won’t show up as
7003 /// visible anywhere. You can override this with whatever material you would like.
7004 /// <https://stereokit.net/Pages/StereoKit/World/OcclusionMaterial.html>
7005 ///
7006 /// see also [world_get_occlusion_material]
7007 /// see example in [`World::occlusion_material`]
7008 pub fn get_occlusion_material() -> Material {
7009 Material(NonNull::new(unsafe { world_get_occlusion_material() }).unwrap())
7010 }
7011
7012 /// The mode or “reference space” that StereoKit uses for determining its base origin. This is determined by the
7013 /// initial value provided in [`crate::sk::SkSettings.origin`], as well as by support from the underlying runtime. The mode
7014 /// reported here will not necessarily be the one requested in initialization, as fallbacks are implemented using
7015 /// different available modes.
7016 /// <https://stereokit.net/Pages/StereoKit/World/OriginMode.html>
7017 ///
7018 /// see also [world_get_origin_mode]
7019 /// ### Examples
7020 /// ```
7021 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
7022 /// use stereokit_rust::system::World;
7023 ///
7024 /// let origin_mode_init = sk.get_settings().origin;
7025 ///
7026 /// let origin_mode = World::get_origin_mode();
7027 ///
7028 /// assert_eq!(origin_mode_init, origin_mode);
7029 /// ```
7030 pub fn get_origin_mode() -> OriginMode {
7031 unsafe { world_get_origin_mode() }
7032 }
7033
7034 /// This reports the status of the device's positional tracking. If the room is too dark, or a hand is covering
7035 /// tracking sensors, or some other similar 6dof tracking failure, this would report as not tracked.
7036 ///
7037 /// Note that this does not factor in the status of rotational tracking. Rotation is typically done via
7038 /// gyroscopes/accelerometers, which don't really fail the same way positional tracking system can.
7039 /// <https://stereokit.net/Pages/StereoKit/World/Tracked.html>
7040 ///
7041 /// see also [world_get_tracked]
7042 /// ### Examples
7043 /// ```
7044 /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
7045 /// use stereokit_rust::system::{World, BtnState};
7046 ///
7047 /// let is_tracked = World::get_tracked();
7048 ///
7049 /// xr_mode_stop_here!();
7050 /// // These are the expected results for offscreen tests on a PC:
7051 /// assert_eq!(is_tracked, BtnState::Active);
7052 /// ```
7053 pub fn get_tracked() -> BtnState {
7054 unsafe { world_get_tracked() }
7055 }
7056
7057 /// This is relative to the base reference point and is NOT in world space! The origin StereoKit uses is actually a
7058 /// base reference point combined with an offset! You can use this to read or set the offset from the OriginMode
7059 /// reference point.
7060 /// <https://stereokit.net/Pages/StereoKit/World/OriginOffset.html>
7061 ///
7062 /// see also [world_get_origin_offset]
7063 /// see example in [`World::origin_offset`]
7064 pub fn get_origin_offset() -> Pose {
7065 unsafe { world_get_origin_offset() }
7066 }
7067
7068 /// Off by default. This tells StereoKit to load up collision meshes for the environment, for use with
7069 /// World::raycast. Check SK::System::world_raycast_present to see if raycasting can be enabled. This will reset
7070 /// itself to false if raycasting isn’t possible. Loading raycasting data is asynchronous, so collision surfaces may
7071 /// not be available immediately after setting this flag.
7072 /// <https://stereokit.net/Pages/StereoKit/World/RaycastEnabled.html>
7073 ///
7074 /// see also [world_get_raycast_enabled]
7075 /// see example in [`World::raycast_enabled`]
7076 pub fn get_raycast_enabled() -> bool {
7077 unsafe { world_get_raycast_enabled() != 0 }
7078 }
7079
7080 /// The refresh interval speed, in seconds. This is only applicable when using WorldRefresh::Timer for the refresh
7081 /// type. Note that the system may not be able to refresh as fast as you wish, and in that case, StereoKit will
7082 /// always refresh as soon as the previous refresh finishes.
7083 /// <https://stereokit.net/Pages/StereoKit/World/RefreshInterval.html>
7084 ///
7085 /// see also [world_get_refresh_interval]
7086 /// see example in [`World::refresh_interval`]
7087 pub fn get_refresh_interval() -> f32 {
7088 unsafe { world_get_refresh_interval() }
7089 }
7090
7091 /// Radius, in meters, of the area that StereoKit should scan for world data. Default is 4. When using the
7092 /// WorldRefresh::Area refresh type, the world data will refresh when the user has traveled half this radius from
7093 /// the center of where the most recent refresh occurred.
7094 /// <https://stereokit.net/Pages/StereoKit/World/RefreshRadius.html>
7095 ///
7096 /// see also [world_get_refresh_radius]
7097 /// see example in [`World::refresh_radius`]
7098 pub fn get_refresh_radius() -> f32 {
7099 unsafe { world_get_refresh_radius() }
7100 }
7101
7102 /// What information should StereoKit use to determine when the next world data refresh happens? See the
7103 /// WorldRefresh enum for details.
7104 /// <https://stereokit.net/Pages/StereoKit/World/RefreshType.html>
7105 ///
7106 /// see also [world_get_refresh_type]
7107 /// see example in [`World::refresh_type`]
7108 pub fn get_refresh_type() -> WorldRefresh {
7109 unsafe { world_get_refresh_type() }
7110 }
7111}