kitgit

tirbofish/dropbear · diff

d920dc1 · tk

input detection and more docs

Unverified

diff --git a/README.md b/README.md
index 34deab0..bf5232c 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,8 @@ If you do not want to build it locally, you are able to download the latest acti
 
 Depsite it looking like a dependency for `eucalyptus`, it can serve as a framework too. Looking through the `docs.rs` will you find related documentation onhow to use it and for rendering your own projects.
 
+The rhai reference for the eucalyptus editor is under the /docs folder of this repository, so take a look there. [Here is the entrance](https://github.com/4tkbytes/dropbear/blob/main/docs/README.md)
+
 ## Compability
 
 |            | Windows | macOS | Linux | Web | Android | iOS |
diff --git a/docs/README.md b/docs/README.md
index d863d15..1d4cd8e 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -8,3 +8,7 @@ Related links: [https://rhai.rs/book](https://rhai.rs/book) and [https://rhai.rs
 
 ---
 
+| Math            | https://github.com/4tkbytes/dropbear/blob/main/docs/math.md      |
+|-----------------|------------------------------------------------------------------|
+| Utils           | https://github.com/4tkbytes/dropbear/blob/main/docs/utils.md     |
+| Transformations | https://github.com/4tkbytes/dropbear/blob/main/docs/transform.md |
\ No newline at end of file
diff --git a/docs/input.md b/docs/input.md
new file mode 100644
index 0000000..c1e0766
--- /dev/null
+++ b/docs/input.md
@@ -0,0 +1,2 @@
+# Input
+
diff --git a/docs/todo.md b/docs/todo.md
index 06b267f..69e676f 100644
--- a/docs/todo.md
+++ b/docs/todo.md
@@ -23,6 +23,7 @@ Just a list of stuff i need to work on/a list of items i need to implement
 - [ ] autosave (would be easy to impl)
 - [ ] attaching texture onto model
 - [ ] user defined keybinds
+- [ ] console tab
 
 ## fixing
 
diff --git a/docs/transform.md b/docs/transform.md
new file mode 100644
index 0000000..cddf359
--- /dev/null
+++ b/docs/transform.md
@@ -0,0 +1,117 @@
+# Transform
+
+This page documents the functions and types related to position, rotation, and scale manipulation.
+
+## Types
+
+### Transform
+Represents a 3D transformation, including position, rotation, and scale.
+
+### Quaternion
+Represents a quaternion rotation (DQuat).
+
+### Vector3
+Represents a 3D vector (DVec3).
+
+## Functions
+
+### new_transform
+Creates a new `Transform` with default values.
+
+#### Example
+```rhai
+let t = new_transform();
+```
+
+### new_vec3
+Creates a new `Vector3` with the given x, y, z values, or returns a vector with all components set to 1.0.
+
+#### Parameters
+- `x` - An f64 value for the x component.
+- `y` - An f64 value for the y component.
+- `z` - An f64 value for the z component.
+
+#### Example
+```rhai
+let v = new_vec3(1.0, 2.0, 3.0);
+let v = new_vec3(); // returns Vector3(1.0, 1.0, 1.0)
+```
+
+### new_quat
+Creates a new `Quaternion` with given angle and axis values, or returns an identity Quaternion.
+
+#### Parameters
+
+- `axis` - A Vector3 axis value.
+- `angle` - The angle of rotation in radians.
+
+#### Example
+```rhai
+let q = new_quat(new_vec3(), degrees_to_radians(90));
+let q = new_quat();
+```
+
+## Properties
+
+### Transform
+
+#### position
+Gets or sets the position of the transform as a `Vector3`.
+
+#### Example
+```rhai
+let t = new_transform();
+t.position = new_vec3(1.0, 2.0, 3.0);
+let pos = t.position;
+```
+
+#### rotation
+Gets or sets the rotation of the transform as a `Quaternion`.
+
+#### Example
+```rhai
+let t = new_transform();
+t.rotation = some_quaternion;
+let rot = t.rotation;
+```
+
+#### scale
+Gets or sets the scale of the transform as a `Vector3`.
+
+#### Example
+```rhai
+let t = new_transform();
+t.scale = new_vec3(2.0, 2.0, 2.0);
+let s = t.scale;
+```
+
+### Vector3
+
+#### x
+Gets or sets the x component.
+
+#### Example
+```rhai
+let v = new_vec3(1.0, 2.0, 3.0);
+v.x = 5.0;
+let x = v.x;
+```
+
+#### y
+Gets or sets the y component.
+
+#### Example
+```rhai
+let v = new_vec3(1.0, 2.0, 3.0);
+v.y = 6.0;
+let y = v.y;
+```
+
+#### z
+Gets or sets the z component.
+
+#### Example
+```rhai
+let v = new_vec3(1.0, 2.0, 3.0);
+v.z = 7.0;
+```
\ No newline at end of file
diff --git a/docs/utils.md b/docs/utils.md
index 4a394af..6011aa9 100644
--- a/docs/utils.md
+++ b/docs/utils.md
@@ -1,9 +1,11 @@
 # Utils
 
-In this page, it will show you the different utility functions available. 
+In this page, it will show you the different utility functions available.
 
 ## log
-The log function is used to (as the name suggests) log a string to the console. 
+
+The log function is used to (as the name suggests) log a string to the console.
+
 ### Parameters
 
 - `str` - The message to be printed
@@ -15,12 +17,14 @@ log("Hello log function!");
 ```
 
 ## time
-The time function returns an `f64` value to the current `UNIX_EPOCH` time of the computer running. This is particularly useful for stopwatches and comparing time. 
+The time function returns an `f64` value to the current `UNIX_EPOCH` time of the computer running. This is particularly useful for stopwatches and comparing time.
 
 ### Parameters
+
 No params for this one!
 
 ### Example
+
 ```rhai
 time() // returns the current unix_epoch time
 ```
diff --git a/eucalyptus/src/editor/dock.rs b/eucalyptus/src/editor/dock.rs
index 68d49f6..8c0b013 100644
--- a/eucalyptus/src/editor/dock.rs
+++ b/eucalyptus/src/editor/dock.rs
@@ -204,19 +204,19 @@ impl<'a> TabViewer for EditorTabViewer<'a> {
                             camera.zfar,
                         );
 
-                        log::debug!(
-                            "Camera - eye: {:?}, target: {:?}, up: {:?}",
-                            camera.eye,
-                            camera.target,
-                            camera.up
-                        );
-                        log::debug!(
-                            "Camera - fov_y: {:.3}°, aspect: {:.3}, znear: {:.3}, zfar: {:.3}",
-                            camera.fov_y,
-                            camera.aspect,
-                            camera.znear,
-                            camera.zfar
-                        );
+                        // log::debug!(
+                        //     "Camera - eye: {:?}, target: {:?}, up: {:?}",
+                        //     camera.eye,
+                        //     camera.target,
+                        //     camera.up
+                        // );
+                        // log::debug!(
+                        //     "Camera - fov_y: {:.3}°, aspect: {:.3}, znear: {:.3}, zfar: {:.3}",
+                        //     camera.fov_y,
+                        //     camera.aspect,
+                        //     camera.znear,
+                        //     camera.zfar
+                        // );
 
                         if !view_matrix.is_finite() {
                             log::error!("Invalid view matrix");
@@ -265,13 +265,13 @@ impl<'a> TabViewer for EditorTabViewer<'a> {
                             return;
                         }
 
-                        log::debug!(
-                            "Click pos: {:?}, NDC: ({:.3}, {:.3})",
-                            click_pos,
-                            ndc_x,
-                            ndc_y
-                        );
-                        log::debug!("Ray start: {:?}, direction: {:?}", ray_start, ray_direction);
+                        // log::debug!(
+                        //     "Click pos: {:?}, NDC: ({:.3}, {:.3})",
+                        //     click_pos,
+                        //     ndc_x,
+                        //     ndc_y
+                        // );
+                        // log::debug!("Ray start: {:?}, direction: {:?}", ray_start, ray_direction);
 
                         let mut closest_distance = f64::INFINITY;
                         let mut selected_entity_id: Option<hecs::Entity> = None;
@@ -287,28 +287,28 @@ impl<'a> TabViewer for EditorTabViewer<'a> {
                             let to_sphere = entity_pos - ray_start;
                             let projection = to_sphere.dot(ray_direction);
 
-                            log::debug!(
-                                "Entity {:?}: pos={:?}, scale={:?}, radius={:.3}",
-                                entity_id,
-                                entity_pos,
-                                transform.scale,
-                                sphere_radius
-                            );
-                            log::debug!(
-                                "  to_sphere={:?}, projection={:.3}",
-                                to_sphere,
-                                projection
-                            );
+                            // log::debug!(
+                            //     "Entity {:?}: pos={:?}, scale={:?}, radius={:.3}",
+                            //     entity_id,
+                            //     entity_pos,
+                            //     transform.scale,
+                            //     sphere_radius
+                            // );
+                            // log::debug!(
+                            //     "  to_sphere={:?}, projection={:.3}",
+                            //     to_sphere,
+                            //     projection
+                            // );
 
                             if projection > 0.0 {
                                 let closest_point = ray_start + ray_direction * projection;
                                 let distance_to_sphere = (closest_point - entity_pos).length();
 
-                                log::debug!(
-                                    "  closest_point={:?}, distance_to_sphere={:.3}",
-                                    closest_point,
-                                    distance_to_sphere
-                                );
+                                // log::debug!(
+                                //     "  closest_point={:?}, distance_to_sphere={:.3}",
+                                //     closest_point,
+                                //     distance_to_sphere
+                                // );
 
                                 if distance_to_sphere <= sphere_radius {
                                     let discriminant = sphere_radius * sphere_radius
@@ -316,10 +316,10 @@ impl<'a> TabViewer for EditorTabViewer<'a> {
                                     if discriminant >= 0.0 {
                                         let intersection_distance =
                                             projection - discriminant.sqrt();
-                                        log::debug!(
-                                            "  HIT! intersection_distance={:.3}",
-                                            intersection_distance
-                                        );
+                                        // log::debug!(
+                                        //     "  HIT! intersection_distance={:.3}",
+                                        //     intersection_distance
+                                        // );
 
                                         if intersection_distance < closest_distance {
                                             closest_distance = intersection_distance;
@@ -327,14 +327,14 @@ impl<'a> TabViewer for EditorTabViewer<'a> {
                                         }
                                     }
                                 } else {
-                                    log::debug!(
-                                        "  MISS: distance {:.3} > radius {:.3}",
-                                        distance_to_sphere,
-                                        sphere_radius
-                                    );
+                                    // log::debug!(
+                                    //     "  MISS: distance {:.3} > radius {:.3}",
+                                    //     distance_to_sphere,
+                                    //     sphere_radius
+                                    // );
                                 }
                             } else {
-                                log::debug!("  BEHIND: projection {:.3} <= 0", projection);
+                                // log::debug!("  BEHIND: projection {:.3} <= 0", projection);
                             }
                         }
 
diff --git a/eucalyptus/src/editor/input.rs b/eucalyptus/src/editor/input.rs
index d7aa78a..b5a40d0 100644
--- a/eucalyptus/src/editor/input.rs
+++ b/eucalyptus/src/editor/input.rs
@@ -13,17 +13,17 @@ use winit::{
 impl Keyboard for Editor {
     fn key_down(&mut self, key: KeyCode, _event_loop: &ActiveEventLoop) {
         #[cfg(not(target_os = "macos"))]
-        let ctrl_pressed = self.pressed_keys.contains(&KeyCode::ControlLeft)
-            || self.pressed_keys.contains(&KeyCode::ControlRight);
+        let ctrl_pressed = self.input_state.pressed_keys.contains(&KeyCode::ControlLeft)
+            || self.input_state.pressed_keys.contains(&KeyCode::ControlRight);
         #[cfg(target_os = "macos")]
-        let ctrl_pressed = self.pressed_keys.contains(&KeyCode::SuperLeft)
-            || self.pressed_keys.contains(&KeyCode::SuperRight);
+        let ctrl_pressed = self.input_state.pressed_keys.contains(&KeyCode::SuperLeft)
+            || self.input_state.pressed_keys.contains(&KeyCode::SuperRight);
 
-        let _alt_pressed = self.pressed_keys.contains(&KeyCode::AltLeft)
-            || self.pressed_keys.contains(&KeyCode::AltRight);
+        let _alt_pressed = self.input_state.pressed_keys.contains(&KeyCode::AltLeft)
+            || self.input_state.pressed_keys.contains(&KeyCode::AltRight);
 
-        let shift_pressed = self.pressed_keys.contains(&KeyCode::ShiftLeft)
-            || self.pressed_keys.contains(&KeyCode::ShiftRight);
+        let shift_pressed = self.input_state.pressed_keys.contains(&KeyCode::ShiftLeft)
+            || self.input_state.pressed_keys.contains(&KeyCode::ShiftRight);
 
         let is_double_press = self.is_double_key_press(key);
 
@@ -39,7 +39,7 @@ impl Keyboard for Editor {
                         window.set_cursor_visible(true);
                     }
                 } else {
-                    self.pressed_keys.insert(key);
+                    self.input_state.pressed_keys.insert(key);
                 }
             }
             KeyCode::KeyF => {
@@ -57,7 +57,7 @@ impl Keyboard for Editor {
                         let _ = window.set_cursor_position(center);
                     }
                 } else {
-                    self.pressed_keys.insert(key);
+                    self.input_state.pressed_keys.insert(key);
                 }
             }
             KeyCode::Delete => {
@@ -68,7 +68,7 @@ impl Keyboard for Editor {
                         crate::warn!("Failed to delete: No entity selected");
                     }
                 } else {
-                    self.pressed_keys.insert(key);
+                    self.input_state.pressed_keys.insert(key);
                 }
             }
             KeyCode::Escape => {
@@ -84,7 +84,7 @@ impl Keyboard for Editor {
                         window.set_cursor_visible(true);
                     }
                 } else {
-                    self.pressed_keys.insert(key);
+                    self.input_state.pressed_keys.insert(key);
                 }
             }
             KeyCode::KeyQ => {
@@ -138,7 +138,7 @@ impl Keyboard for Editor {
                     crate::info!("GizmoMode set to scale");
                     self.gizmo_mode = GizmoMode::all_scale();
                 } else {
-                    self.pressed_keys.insert(key);
+                    self.input_state.pressed_keys.insert(key);
                 }
             }
             KeyCode::KeyV => {
@@ -153,7 +153,7 @@ impl Keyboard for Editor {
                     }
                 } 
                 else {
-                    self.pressed_keys.insert(key);
+                    self.input_state.pressed_keys.insert(key);
                 }
             }
             KeyCode::KeyS => {
@@ -172,7 +172,7 @@ impl Keyboard for Editor {
                     }
                     
                 } else {
-                    self.pressed_keys.insert(key);
+                    self.input_state.pressed_keys.insert(key);
                 }
             }
             KeyCode::KeyZ => {
@@ -188,7 +188,7 @@ impl Keyboard for Editor {
                     crate::info!("GizmoMode set to translate");
                     self.gizmo_mode = GizmoMode::all_translate();
                 } else {
-                    self.pressed_keys.insert(key);
+                    self.input_state.pressed_keys.insert(key);
                 }
             }
             KeyCode::F1 => {
@@ -205,17 +205,17 @@ impl Keyboard for Editor {
                     crate::info!("GizmoMode set to rotate");
                     self.gizmo_mode = GizmoMode::all_rotate();
                 } else {
-                    self.pressed_keys.insert(key);
+                    self.input_state.pressed_keys.insert(key);
                 }
             }
             _ => {
-                self.pressed_keys.insert(key);
+                self.input_state.pressed_keys.insert(key);
             }
         }
     }
 
     fn key_up(&mut self, key: KeyCode, _event_loop: &ActiveEventLoop) {
-        self.pressed_keys.remove(&key);
+        self.input_state.pressed_keys.remove(&key);
     }
 }
 
@@ -238,17 +238,17 @@ impl Mouse for Editor {
                 window.set_cursor_visible(false);
             }
         }
-        self.mouse_pos = (position.x, position.y);
+        self.input_state.mouse_pos = (position.x, position.y);
     }
 
     fn mouse_down(&mut self, button: MouseButton) {
         match button {
-            _ => { self.mouse_button.insert(button); }
+            _ => { self.input_state.mouse_button.insert(button); }
         }
     }
 
     fn mouse_up(&mut self, button: MouseButton) {
-        self.mouse_button.remove(&button);
+        self.input_state.mouse_button.remove(&button);
     }
 }
 
diff --git a/eucalyptus/src/editor/mod.rs b/eucalyptus/src/editor/mod.rs
index ad9e9ac..1fef0f4 100644
--- a/eucalyptus/src/editor/mod.rs
+++ b/eucalyptus/src/editor/mod.rs
@@ -25,14 +25,14 @@ use log;
 use parking_lot::Mutex;
 use transform_gizmo_egui::{EnumSet, Gizmo, GizmoMode};
 use wgpu::{Color, Extent3d, RenderPipeline};
-use winit::{event::MouseButton, keyboard::KeyCode, window::Window};
+use winit::{keyboard::KeyCode, window::Window};
 
 use crate::{
     camera::{
         CameraAction, CameraManager, CameraType, DebugCameraController, PlayerCameraController,
     },
-    scripting::{ScriptAction, ScriptManager},
-    states::{EntityNode, ModelProperties, PROJECT, SCENES, SceneEntity, ScriptComponent},
+    scripting::{input::InputState, ScriptAction, ScriptManager},
+    states::{EntityNode, ModelProperties, SceneEntity, ScriptComponent, PROJECT, SCENES},
     utils::ViewportMode,
 };
 
@@ -46,10 +46,8 @@ pub struct Editor {
     color: Color,
 
     camera_manager: CameraManager,
-    mouse_delta: Option<(f64, f64)>,
 
     is_viewport_focused: bool,
-    pressed_keys: HashSet<KeyCode>,
     // is_cursor_locked: bool,
     window: Option<Arc<Window>>,
 
@@ -66,16 +64,21 @@ pub struct Editor {
     undo_stack: Vec<UndoableAction>,
     // todo: add redo (later)
     // redo_stack: Vec<UndoableAction>,
-    last_key_press_times: HashMap<KeyCode, Instant>,
-    double_press_threshold: Duration,
-    mouse_pos: (f64, f64),
-    mouse_button: HashSet<MouseButton>,
+
+    // last_key_press_times: HashMap<KeyCode, Instant>,
+    // double_press_threshold: Duration,
+    // mouse_pos: (f64, f64),
+    // mouse_button: HashSet<MouseButton>,    
+    // pressed_keys: HashSet<KeyCode>,
+    // mouse_delta: Option<(f64, f64)>,
 
     editor_state: EditorState,
     gizmo_mode: EnumSet<GizmoMode>,
 
     script_manager: ScriptManager,
     play_mode_backup: Option<PlayModeBackup>,
+
+    input_state: InputState,
 }
 
 #[derive(Clone)]
@@ -214,7 +217,6 @@ impl Editor {
             camera_manager: CameraManager::new(),
             color: Color::default(),
             is_viewport_focused: false,
-            pressed_keys: HashSet::new(),
             // is_cursor_locked: false,
             window: None,
             world: World::new(),
@@ -227,15 +229,17 @@ impl Editor {
             viewport_mode: ViewportMode::None,
             signal: Signal::None,
             undo_stack: Vec::new(),
-            last_key_press_times: HashMap::new(),
-            double_press_threshold: Duration::from_millis(300),
             script_manager: ScriptManager::new(),
-            mouse_delta: None,
             editor_state: EditorState::Editing,
             gizmo_mode: EnumSet::empty(),
-            mouse_pos: Default::default(),
-            mouse_button: Default::default(),
             play_mode_backup: None,
+            input_state: InputState::new(),
+            // mouse_pos: Default::default(),
+            // mouse_button: Default::default(),
+            // pressed_keys: HashSet::new(),
+            // last_key_press_times: HashMap::new(),
+            // double_press_threshold: Duration::from_millis(300),
+            // mouse_delta: None,
             // ..Default::default()
             // note to self: DO NOT USE ..DEFAULT::DEFAULT(), IT WILL CAUSE OVERFLOW
         }
@@ -244,16 +248,16 @@ impl Editor {
     fn is_double_key_press(&mut self, key: KeyCode) -> bool {
         let now = Instant::now();
 
-        if let Some(last_time) = self.last_key_press_times.get(&key) {
+        if let Some(last_time) = self.input_state.last_key_press_times.get(&key) {
             let time_diff = now.duration_since(*last_time);
 
-            if time_diff <= self.double_press_threshold {
-                self.last_key_press_times.remove(&key);
+            if time_diff <= self.input_state.double_press_threshold {
+                self.input_state.last_key_press_times.remove(&key);
                 return true;
             }
         }
 
-        self.last_key_press_times.insert(key, now);
+        self.input_state.last_key_press_times.insert(key, now);
         false
     }
     // pub fn save_project_config(&self) -> anyhow::Result<()> {
diff --git a/eucalyptus/src/editor/scene.rs b/eucalyptus/src/editor/scene.rs
index a7c8d38..a0464b6 100644
--- a/eucalyptus/src/editor/scene.rs
+++ b/eucalyptus/src/editor/scene.rs
@@ -118,7 +118,7 @@ impl Scene for Editor {
         }
 
         if matches!(self.editor_state, EditorState::Playing) {
-            if self.pressed_keys.contains(&KeyCode::Escape) {
+            if self.input_state.pressed_keys.contains(&KeyCode::Escape) {
                 self.signal = Signal::StopPlaying;
             }
             
@@ -132,7 +132,7 @@ impl Scene for Editor {
             }
             
             for (entity_id, script_name) in script_entities {
-                if let Err(e) = self.script_manager.update_entity_script(entity_id, &script_name, &mut self.world, dt) {
+                if let Err(e) = self.script_manager.update_entity_script(entity_id, &script_name, &mut self.world, &self.input_state, dt) {
                     log::warn!("Failed to update script '{}' for entity {:?}: {}", script_name, entity_id, e);
                 }
             }
@@ -143,6 +143,7 @@ impl Scene for Editor {
         // && self.is_using_debug_camera()
         {
             let movement_keys: std::collections::HashSet<KeyCode> = self
+                .input_state
                 .pressed_keys
                 .iter()
                 .filter(|&&key| {
@@ -160,7 +161,7 @@ impl Scene for Editor {
                 .collect();
 
             self.camera_manager
-                .handle_input(&movement_keys, self.mouse_delta);
+                .handle_input(&movement_keys, self.input_state.mouse_delta);
         }
 
         match &self.signal {
@@ -405,7 +406,7 @@ impl Scene for Editor {
                     for (entity_id, script) in script_entities {
                         match self.script_manager.load_script(&script.path) {
                             Ok(script_name) => {
-                                if let Err(e) = self.script_manager.init_entity_script(entity_id, &script_name, &mut self.world) {
+                                if let Err(e) = self.script_manager.init_entity_script(entity_id, &script_name, &mut self.world, &self.input_state) {
                                     log::warn!("Failed to initialise script '{}' for entity {:?}: {}", script.name, entity_id, e);
                                 }
                             }
diff --git a/eucalyptus/src/scripting/input.rs b/eucalyptus/src/scripting/input.rs
new file mode 100644
index 0000000..66be731
--- /dev/null
+++ b/eucalyptus/src/scripting/input.rs
@@ -0,0 +1,101 @@
+use std::{collections::{HashMap, HashSet}, time::{Duration, Instant}};
+
+use rhai::*;
+use winit::{event::MouseButton, keyboard::KeyCode};
+
+#[derive(rhai::CustomType, Clone)]
+pub struct InputState {
+    pub last_key_press_times: HashMap<KeyCode, Instant>,
+    pub double_press_threshold: Duration,
+    pub mouse_pos: (f64, f64),
+    pub mouse_button: HashSet<MouseButton>,    
+    pub pressed_keys: HashSet<KeyCode>,
+    pub mouse_delta: Option<(f64, f64)>,
+}
+
+impl Default for InputState {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl InputState {
+    pub fn new() -> Self {
+        Self {
+            mouse_pos: Default::default(),
+            mouse_button: Default::default(),
+            pressed_keys: HashSet::new(),
+            last_key_press_times: HashMap::new(),
+            double_press_threshold: Duration::from_millis(300),
+            mouse_delta: None,
+        }
+    }
+
+    pub fn register_input_modules(engine: &mut Engine) {
+        engine.register_type_with_name::<InputState>("InputState");
+        engine.register_type_with_name::<Key>("Key");
+
+        engine
+            .register_fn("is_pressed", |s: &mut InputState, k: Key| s.pressed_keys.contains(&k.0))
+            .register_get("mouse_x", |s: &mut InputState| s.mouse_pos.0)
+            .register_get("mouse_y", |s: &mut InputState| s.mouse_pos.1)
+            .register_get("has_mouse_delta", |s: &mut InputState| s.mouse_delta.is_some())
+            .register_fn("mouse_dx", |s: &mut InputState| s.mouse_delta.map(|d| d.0).unwrap_or(0.0))
+            .register_fn("mouse_dy", |s: &mut InputState| s.mouse_delta.map(|d| d.1).unwrap_or(0.0));
+
+        let mut kmod = Module::new();
+
+        macro_rules! add_key {
+            ($name:ident, $kc:expr) => {
+                kmod.set_var(stringify!($name), Key($kc));
+            };
+        }
+
+        add_key!(A, KeyCode::KeyA);
+        add_key!(B, KeyCode::KeyB);
+        add_key!(C, KeyCode::KeyC);
+        add_key!(D, KeyCode::KeyD);
+        add_key!(E, KeyCode::KeyE);
+        add_key!(F, KeyCode::KeyF);
+        add_key!(G, KeyCode::KeyG);
+        add_key!(H, KeyCode::KeyH);
+        add_key!(I, KeyCode::KeyI);
+        add_key!(J, KeyCode::KeyJ);
+        add_key!(K, KeyCode::KeyK);
+        add_key!(L, KeyCode::KeyL);
+        add_key!(M, KeyCode::KeyM);
+        add_key!(N, KeyCode::KeyN);
+        add_key!(O, KeyCode::KeyO);
+        add_key!(P, KeyCode::KeyP);
+        add_key!(Q, KeyCode::KeyQ);
+        add_key!(R, KeyCode::KeyR);
+        add_key!(S, KeyCode::KeyS);
+        add_key!(T, KeyCode::KeyT);
+        add_key!(U, KeyCode::KeyU);
+        add_key!(V, KeyCode::KeyV);
+        add_key!(W, KeyCode::KeyW);
+        add_key!(X, KeyCode::KeyX);
+        add_key!(Y, KeyCode::KeyY);
+        add_key!(Z, KeyCode::KeyZ);
+
+        add_key!(Space, KeyCode::Space);
+        add_key!(ShiftLeft, KeyCode::ShiftLeft);
+        add_key!(ShiftRight, KeyCode::ShiftRight);
+        add_key!(ControlLeft, KeyCode::ControlLeft);
+        add_key!(ControlRight, KeyCode::ControlRight);
+        add_key!(AltLeft, KeyCode::AltLeft);
+        add_key!(AltRight, KeyCode::AltRight);
+        add_key!(Escape, KeyCode::Escape);
+        add_key!(Enter, KeyCode::Enter);
+        add_key!(Tab, KeyCode::Tab);
+        add_key!(Up, KeyCode::ArrowUp);
+        add_key!(Down, KeyCode::ArrowDown);
+        add_key!(Left, KeyCode::ArrowLeft);
+        add_key!(Right, KeyCode::ArrowRight);
+
+        engine.register_static_module("keys", kmod.into());
+    }
+}
+
+#[derive(rhai::CustomType, Clone, Copy)]
+pub struct Key(pub KeyCode);
\ No newline at end of file
diff --git a/eucalyptus/src/scripting/math.rs b/eucalyptus/src/scripting/math.rs
index d61d16a..ad70f67 100644
--- a/eucalyptus/src/scripting/math.rs
+++ b/eucalyptus/src/scripting/math.rs
@@ -56,5 +56,5 @@ pub mod math_functions {
 }
 
 pub fn register_math_functions(engine: &mut Engine) {
-    engine.register_global_module(exported_module!(math_functions).into());
+    engine.register_static_module("math", exported_module!(math_functions).into());
 }
\ No newline at end of file
diff --git a/eucalyptus/src/scripting/mod.rs b/eucalyptus/src/scripting/mod.rs
index edc188e..a2da896 100644
--- a/eucalyptus/src/scripting/mod.rs
+++ b/eucalyptus/src/scripting/mod.rs
@@ -1,4 +1,5 @@
 pub mod math;
+pub mod input;
 
 use dropbear_engine::entity::{AdoptedEntity, Transform};
 use glam::{DQuat, DVec3};
@@ -154,7 +155,6 @@ pub fn attach_script_to_entity(
     Ok(())
 }
 
-#[allow(dead_code)]
 pub struct ScriptManager {
     pub engine: rhai::Engine,
     compiled_scripts: HashMap<String, AST>,
@@ -167,6 +167,7 @@ impl ScriptManager {
 
         // REGISTER FUNCTIONS HERE
         math::register_math_functions(&mut engine);
+        input::InputState::register_input_modules(&mut engine);
         
         engine.register_type_with_name::<Transform>("Transform");
         engine.register_type_with_name::<DQuat>("Quaternion");
@@ -236,6 +237,7 @@ impl ScriptManager {
         entity_id: hecs::Entity,
         script_name: &str,
         world: &mut World,
+        input_state: &input::InputState,
     ) -> anyhow::Result<()> {
         if let Some(ast) = self.compiled_scripts.get(script_name) {
             let mut scope = Scope::new();
@@ -244,6 +246,8 @@ impl ScriptManager {
                 scope.push("transform", *transform);
             }
 
+            scope.push("input", input_state.clone());
+
             if let Ok(_) = self.engine.call_fn::<()>(&mut scope, ast, "init", ()) {
                 log::debug!("Called init for entity {:?}", entity_id);
             }
@@ -260,6 +264,7 @@ impl ScriptManager {
         entity_id: hecs::Entity,
         script_name: &str,
         world: &mut World,
+        input_state: &input::InputState,
         dt: f32,
     ) -> anyhow::Result<()> {
         if let Some(ast) = self.compiled_scripts.get(script_name) {
@@ -268,6 +273,8 @@ impl ScriptManager {
                     scope.set_value("transform", *transform);
                 }
 
+                scope.set_value("input", input_state.clone());
+
                 match self.engine.call_fn::<()>(scope, ast, "update", (dt,)) {
                     Ok(_) => {
                         match scope.get_value::<Transform>("transform") {
@@ -292,6 +299,7 @@ impl ScriptManager {
             }
         } else {
             log::error!("Unable to fetch compiled scripts for entity {:?}. Script Name: {}", entity_id, script_name);
+            println!("{:#?}", self.compiled_scripts);
         }
         Ok(())
     }
@@ -304,7 +312,10 @@ impl ScriptManager {
             .to_string_lossy()
             .to_string();
 
-        let ast = self.engine.compile(&script_content)?;
+        let ast = self.engine.compile(&script_content).map_err(|e| {
+            log::error!("Compiling AST for script path [{}] returned an error: {}", script_path.display(), e);
+            e
+        })?;
         self.compiled_scripts.insert(script_name.clone(), ast);
 
         log::info!("Loaded script: {}", script_name);