asciirend/extra/
mod.rs

1//! Extra misceleneous structures.
2
3#[cfg(feature = "bindings")]
4pub mod bindings;
5#[cfg(feature = "global-state")]
6pub mod global_state;
7
8pub mod camera_controller;
9use super::*;
10
11#[derive(Default)]
12pub struct Modifiers {
13    pub shift: bool,
14}
15
16#[derive(Default)]
17pub struct Pointer {
18    pub primary_down: bool,
19    pub secondary_down: bool,
20    pub interact_pos: Option<Vector2>,
21    pub modifiers: Modifiers,
22}
23
24#[derive(Default)]
25pub struct Input {
26    pub pointer: Pointer,
27    pub scroll_delta: Vector2,
28    pub screen_rect: Vector4,
29}
30
31/// Defines a context state.
32///
33/// This state is used by other systems, such as
34/// [`CameraController`](camera_controller::CameraController) to perform frame update changes. It
35/// is up to the user to decide how to fill the data of `Ctx`, however, with `crossterm` feature
36/// enabled, there are specific functions [`Ctx::new_frame`] and [`Ctx::event`] that help with
37/// processing raw terminal input.
38pub struct Ctx {
39    pub focused: bool,
40    pub input: Input,
41    pub should_stop: bool,
42}
43
44impl Default for Ctx {
45    fn default() -> Self {
46        Self {
47            focused: true,
48            input: Default::default(),
49            should_stop: false,
50        }
51    }
52}
53
54impl Ctx {
55    /// Prepares a new frame with given screen dimensions.
56    pub fn new_frame(&mut self, x: u16, y: u16, w: u16, h: u16) {
57        self.input.pointer.modifiers = Default::default();
58        self.input.scroll_delta = Vector2::default();
59        self.input.screen_rect = Vector4::new(x as f32, y as f32, w as f32, h as f32);
60    }
61}
62
63#[cfg(feature = "crossterm")]
64use crossterm::event::{
65    Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind,
66};
67
68#[cfg(feature = "crossterm")]
69impl Ctx {
70    /// Processes a crossterm event.
71    pub fn event(&mut self, e: Event) {
72        match e {
73            Event::FocusGained => self.focused = true,
74            Event::FocusLost => self.focused = false,
75            Event::Key(KeyEvent {
76                code, modifiers, ..
77            }) => {
78                if code == KeyCode::Char('c') && modifiers.contains(KeyModifiers::CONTROL) {
79                    self.should_stop = true;
80                }
81            }
82            Event::Mouse(MouseEvent {
83                kind,
84                column,
85                row,
86                modifiers,
87                ..
88            }) => {
89                self.input.pointer.modifiers.shift = modifiers.contains(KeyModifiers::SHIFT);
90                self.input.pointer.interact_pos = Some(Vector2::new(column as f32, row as f32));
91                match kind {
92                    MouseEventKind::Down(b) => match b {
93                        MouseButton::Left => self.input.pointer.primary_down = true,
94                        MouseButton::Right => self.input.pointer.secondary_down = true,
95                        _ => (),
96                    },
97                    MouseEventKind::Up(b) => {
98                        self.input.pointer.interact_pos = None;
99                        match b {
100                            MouseButton::Left => self.input.pointer.primary_down = false,
101                            MouseButton::Right => self.input.pointer.secondary_down = false,
102                            _ => (),
103                        }
104                    }
105                    MouseEventKind::Drag(b) => match b {
106                        MouseButton::Left => self.input.pointer.primary_down = true,
107                        MouseButton::Right => self.input.pointer.secondary_down = true,
108                        _ => (),
109                    },
110                    MouseEventKind::ScrollUp => {
111                        self.input.scroll_delta.y += 1.0;
112                    }
113                    MouseEventKind::ScrollDown => {
114                        self.input.scroll_delta.y -= 1.0;
115                    }
116                    MouseEventKind::ScrollLeft => {
117                        self.input.scroll_delta.x -= 1.0;
118                    }
119                    MouseEventKind::ScrollRight => {
120                        self.input.scroll_delta.x += 1.0;
121                    }
122                    _ => (),
123                }
124            }
125            _ => (),
126        }
127    }
128}
129
130pub fn create_transform(
131    position: na::Vector3<f32>,
132    rotation: na::UnitQuaternion<f32>,
133    scale: na::Vector3<f32>,
134) -> na::Transform3<f32> {
135    // Create a translation matrix
136    let translation = na::Translation3::from(position).to_homogeneous();
137
138    // Convert quaternion to a rotation matrix
139    let rotation_matrix = rotation.to_homogeneous();
140
141    // Create a scaling matrix
142    let scale_matrix = na::Matrix4::new_nonuniform_scaling(&scale);
143
144    // Combine the transformations: T * R * S
145    let mat = translation * rotation_matrix * scale_matrix; //translation * rotation_matrix * scale_matrix;
146
147    na::Transform3::from_matrix_unchecked(mat)
148}
149
150pub fn ortho_proj(
151    left: f32,
152    right: f32,
153    bottom: f32,
154    top: f32,
155    znear: f32,
156    zfar: f32,
157) -> na::Orthographic3<f32> {
158    let m = *na::Orthographic3::new(left, right, bottom, top, znear, zfar).as_matrix();
159
160    #[rustfmt::skip]
161    pub const OPENGL_TO_AR_MATRIX: na::Matrix4<f32> = na::matrix![
162        1.0, 0.0, 0.0, 0.0;
163        0.0, 1.0, 0.0, 0.0;
164        0.0, 0.0, 0.5, 0.5;
165        0.0, 0.0, 0.0, 1.0
166    ];
167
168    na::Orthographic3::from_matrix_unchecked(OPENGL_TO_AR_MATRIX * m)
169}