kitgit

tirbofish/dropbear · diff

e849ac2 · tk

fixed up cursor locking. it defaults to CursorGrabMode::Confined, Locked is second option (but that doesnt work for some reason).

Unverified

diff --git a/Cargo.toml b/Cargo.toml
index ecfbbed..27c4590 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -73,13 +73,14 @@ default-features = false
 features = ["png"]
 
 [profile.dev]
-opt-level = 0
+opt-level = 1
 debug = true
 codegen-units = 16
 incremental = true
 lto = false
-# it takes way too long to exit out
-panic = "abort"
+
+[profile.dev.package."*"]
+opt-level = 3
 
 # [profile.release]
 # opt-level = 3
diff --git a/dropbear-engine/src/graphics.rs b/dropbear-engine/src/graphics.rs
index a688a86..3af8b0f 100644
--- a/dropbear-engine/src/graphics.rs
+++ b/dropbear-engine/src/graphics.rs
@@ -22,16 +22,6 @@ use winit::window::Window;
 pub const NO_TEXTURE: &[u8] = include_bytes!("../../resources/textures/no-texture.png");
 pub const NO_MODEL: &[u8] = include_bytes!("../../resources/models/error.glb");
 
-#[derive(Debug)]
-pub enum GraphicsCommand {
-    WindowCommand(WindowCommand)
-}
-
-#[derive(Debug)]
-pub enum WindowCommand {
-    WindowGrab(bool)
-}
-
 pub struct RenderContext<'a> {
     pub shared: Arc<SharedGraphicsContext>,
     pub frame: FrameGraphicsContext<'a>,
diff --git a/dropbear-engine/src/lib.rs b/dropbear-engine/src/lib.rs
index e5ec99a..a507361 100644
--- a/dropbear-engine/src/lib.rs
+++ b/dropbear-engine/src/lib.rs
@@ -507,7 +507,7 @@ impl App {
                     Some(app_name.replace('-', "_").as_str()),
                     LevelFilter::Debug,
                 )
-                .filter(Some("eucalyptus_core"), LevelFilter::Trace)
+                .filter(Some("eucalyptus_core"), LevelFilter::Debug)
                 .init();
 
             // setup panic
diff --git a/eucalyptus-core/src/input.rs b/eucalyptus-core/src/input.rs
index 6e1ee86..1a4df4c 100644
--- a/eucalyptus-core/src/input.rs
+++ b/eucalyptus-core/src/input.rs
@@ -21,6 +21,7 @@ pub struct InputState {
     pub pressed_keys: HashSet<KeyCode>,
     pub mouse_delta: Option<(f64, f64)>,
     pub is_cursor_locked: bool,
+    pub is_cursor_hidden: bool,
 
     /// This is not used, the mouse delta and/or the mouse position is used instead
     pub last_mouse_pos: Option<(f64, f64)>,
@@ -48,6 +49,7 @@ impl InputState {
             double_press_threshold: Duration::from_millis(300),
             mouse_delta: None,
             is_cursor_locked: false,
+            is_cursor_hidden: false,
             last_mouse_pos: Default::default(),
             connected_gamepads: Default::default(),
             pressed_buttons: Default::default(),
diff --git a/eucalyptus-core/src/ptr.rs b/eucalyptus-core/src/ptr.rs
index a977fbb..9c91241 100644
--- a/eucalyptus-core/src/ptr.rs
+++ b/eucalyptus-core/src/ptr.rs
@@ -1,7 +1,7 @@
 use crossbeam_channel::Sender;
 use hecs::World;
-use dropbear_engine::graphics::{GraphicsCommand};
 use crate::input::InputState;
+use crate::window::GraphicsCommand;
 
 pub type WorldPtr = *mut World;
 pub type InputStatePtr = *mut InputState;
diff --git a/eucalyptus-core/src/scripting/jni/exports.rs b/eucalyptus-core/src/scripting/jni/exports.rs
index 0976435..a3ba11b 100644
--- a/eucalyptus-core/src/scripting/jni/exports.rs
+++ b/eucalyptus-core/src/scripting/jni/exports.rs
@@ -5,12 +5,12 @@ use dropbear_engine::entity::{AdoptedEntity, Transform};
 use glam::{DQuat, DVec3};
 use hecs::World;
 use jni::objects::{JClass, JObject, JPrimitiveArray, JString, JValue};
-use jni::sys::{jboolean, jclass, jdouble, jfloatArray, jint, jlong, jobject, jstring};
+use jni::sys::{jboolean, jclass, jdouble, jfloatArray, jint, jlong, jobject, jstring, JNI_FALSE};
 use jni::JNIEnv;
 use dropbear_engine::camera::Camera;
-use dropbear_engine::graphics::{GraphicsCommand, WindowCommand};
 use crate::camera::{CameraComponent, CameraType};
 use crate::states::{ModelProperties, Value};
+use crate::window::{GraphicsCommand, WindowCommand};
 
 // JNIEXPORT jlong JNICALL Java_com_dropbear_ffi_JNINative_getEntity
 //   (JNIEnv *, jclass, jlong, jstring);
@@ -328,7 +328,7 @@ pub fn Java_com_dropbear_ffi_JNINative_getMouseDelta(
     if let Some(pos) = input.mouse_delta.take() {
         new_float_array(&mut env, pos.0 as f32, pos.1 as f32)
     } else {
-        println!("[Java_com_dropbear_ffi_JNINative_getMouseDelta] [WARN] input_state.mouse_delta returns \"(None)\". Returning (0.0, 0.0)");
+        // println!("[Java_com_dropbear_ffi_JNINative_getMouseDelta] [WARN] input_state.mouse_delta returns \"(None)\". Returning (0.0, 0.0)");
         new_float_array(&mut env, 0.0, 0.0)
     }
 }
@@ -1380,4 +1380,59 @@ pub fn Java_com_dropbear_ffi_JNINative_setCamera(
     } else {
         eprintln!("[Java_com_dropbear_ffi_JNINative_setCamera] [ERROR] Unable to query camera component");
     }
+}
+
+// JNIEXPORT void JNICALL Java_com_dropbear_ffi_JNINative_setCursorHidden
+//   (JNIEnv *, jclass, jlong, jlong, jboolean);
+#[unsafe(no_mangle)]
+pub fn Java_com_dropbear_ffi_JNINative_setCursorHidden(
+    _env: JNIEnv,
+    _class: JClass,
+    input_handle: jlong,
+    graphics_handle: jlong,
+    hide: jboolean,
+) {
+    let input = input_handle as InputStatePtr;
+    if input.is_null() {
+        println!("[Java_com_dropbear_ffi_JNINative_setCursorHidden] [ERROR] Input state pointer is null");
+        return;
+    }
+    let input = unsafe { &mut *input };
+
+    let graphics = graphics_handle as GraphicsPtr;
+    if graphics.is_null() {
+        println!("[Java_com_dropbear_ffi_JNINative_setCursorHidden] [ERROR] Input state pointer is null");
+        return;
+    }
+    let graphics = unsafe { &*graphics };
+
+    let hide = hide != JNI_FALSE;
+
+    if let Err(e) = graphics.send(GraphicsCommand::WindowCommand(WindowCommand::HideCursor(hide))) {
+        println!("[Java_com_dropbear_ffi_JNINative_setCursorHidden] [ERROR] Unable to send hide cursor command: {}", e);
+    }
+
+    input.is_cursor_hidden = hide;
+}
+
+// JNIEXPORT jboolean JNICALL Java_com_dropbear_ffi_JNINative_isCursorHidden
+//   (JNIEnv *, jclass, jlong);
+#[unsafe(no_mangle)]
+pub fn Java_com_dropbear_ffi_JNINative_isCursorHidden(
+    _env: JNIEnv,
+    _class: JClass,
+    input_handle: jlong,
+) -> jboolean {
+    let input = input_handle as InputStatePtr;
+    if input.is_null() {
+        println!("[Java_com_dropbear_ffi_JNINative_isCursorHidden] [ERROR] Input state pointer is null");
+        return false.into();
+    }
+    let input = unsafe { &*input };
+
+    if input.is_cursor_hidden {
+        true.into()
+    } else {
+        false.into()
+    }
 }
\ No newline at end of file
diff --git a/eucalyptus-core/src/window.rs b/eucalyptus-core/src/window.rs
index af3bf41..d1ae90e 100644
--- a/eucalyptus-core/src/window.rs
+++ b/eucalyptus-core/src/window.rs
@@ -1,10 +1,42 @@
-use std::sync::Arc;
+use std::sync::{Arc, OnceLock};
 use crossbeam_channel::{unbounded, Receiver, Sender};
 use once_cell::sync::Lazy;
+use parking_lot::RwLock;
 use winit::window::{CursorGrabMode, Window};
-use dropbear_engine::graphics::{GraphicsCommand, WindowCommand};
 
 pub static GRAPHICS_COMMAND: Lazy<(Box<Sender<GraphicsCommand>>, Receiver<GraphicsCommand>)> = Lazy::new(|| { let (tx, rx) = unbounded::<GraphicsCommand>(); (Box::new(tx), rx) });
+static PREVIOUS_CONFIG: OnceLock<RwLock<CommandCache>> = OnceLock::new();
+
+fn get_config() -> &'static RwLock<CommandCache> {
+    PREVIOUS_CONFIG.get_or_init(|| {
+        RwLock::new(CommandCache::new())
+    })
+}
+
+struct CommandCache {
+    is_locked: bool,
+    is_hidden: bool,
+}
+
+impl CommandCache {
+    fn new() -> Self {
+        Self {
+            is_locked: false,
+            is_hidden: false,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub enum GraphicsCommand {
+    WindowCommand(WindowCommand)
+}
+
+#[derive(Debug)]
+pub enum WindowCommand {
+    WindowGrab(bool),
+    HideCursor(bool),
+}
 
 pub fn poll(window: Arc<Window>) {
     while let Ok(cmd) = GRAPHICS_COMMAND.1.try_recv() {
@@ -13,21 +45,36 @@ pub fn poll(window: Arc<Window>) {
             GraphicsCommand::WindowCommand(w_cmd) => {
                 match w_cmd {
                     WindowCommand::WindowGrab(is_locked) => {
-                        if is_locked {
-                            if let Err(e) = window
-                                .set_cursor_grab(CursorGrabMode::Locked)
-                                .or_else(|_| { window.set_cursor_grab(CursorGrabMode::Confined) })
+                        let mut cfg = get_config().write();
+                        if cfg.is_locked != is_locked {
+                            if is_locked {
+                                if let Err(e) = window
+                                    .set_cursor_grab(CursorGrabMode::Confined)
+                                    .or_else(|_| { window.set_cursor_grab(CursorGrabMode::Locked) })
+                                {
+                                    log_once::warn_once!("Failed to grab cursor: {:?}", e);
+                                } else {
+                                    log_once::info_once!("Grabbed cursor");
+                                    cfg.is_locked = true;
+                                }
+                            } else if let Err(e) = window
+                                .set_cursor_grab(CursorGrabMode::None)
                             {
-                                log_once::warn_once!("Failed to grab cursor: {:?}", e);
+                                log_once::warn_once!("Failed to release cursor: {:?}", e);
+                            } else {
+                                log_once::info_once!("Released cursor");
+                                cfg.is_locked = false;
+                            }
+                        }
+                    }
+                    WindowCommand::HideCursor(should_hide) => {
+                        let cfg = get_config().write();
+                        if cfg.is_hidden != should_hide {
+                            if should_hide {
+                                window.set_cursor_visible(false);
                             } else {
-                                log_once::info_once!("Grabbed cursor");
+                                window.set_cursor_visible(true);
                             }
-                        } else if let Err(e) = window
-                            .set_cursor_grab(CursorGrabMode::None)
-                        {
-                            log_once::warn_once!("Failed to release cursor: {:?}", e);
-                        } else {
-                            log_once::info_once!("Released cursor");
                         }
                     }
                 }
diff --git a/eucalyptus-editor/src/editor/input.rs b/eucalyptus-editor/src/editor/input.rs
index 85d83e0..dc33ac0 100644
--- a/eucalyptus-editor/src/editor/input.rs
+++ b/eucalyptus-editor/src/editor/input.rs
@@ -276,11 +276,15 @@ impl Mouse for Editor {
             }
             self.input_state.last_mouse_pos = None;
         } else {
-            if let Some(window) = &self.window {
-                window.set_cursor_visible(true);
-                if let Err(e) = window.set_cursor_grab(CursorGrabMode::None) {
-                    log_once::error_once!("Unable to release mouse grab: {}", e);
+            if !matches!(self.editor_state, EditorState::Playing) {
+                if let Some(window) = &self.window {
+                    window.set_cursor_visible(true);
+                    if let Err(e) = window.set_cursor_grab(CursorGrabMode::None) {
+                        log_once::error_once!("Unable to release mouse grab: {}", e);
+                    }
                 }
+            } else {
+                // if it is in play mode, cursor grab would be defined in the user script
             }
             self.input_state.last_mouse_pos = None;
         }
diff --git a/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt b/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt
index 750d9c9..55861b5 100644
--- a/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt
+++ b/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt
@@ -60,6 +60,8 @@ expect class NativeEngine {
     fun getMouseDelta(): Vector2D?
     fun isCursorLocked(): Boolean
     fun setCursorLocked(locked: Boolean)
+    fun isCursorHidden(): Boolean
+    fun setCursorHidden(hidden: Boolean)
     fun getLastMousePos(): Vector2D?
 //    fun getConnectedGamepads(): List<Gamepad>
 
diff --git a/src/commonMain/kotlin/com/dropbear/input/InputState.kt b/src/commonMain/kotlin/com/dropbear/input/InputState.kt
index 865b2c0..35c8ee9 100644
--- a/src/commonMain/kotlin/com/dropbear/input/InputState.kt
+++ b/src/commonMain/kotlin/com/dropbear/input/InputState.kt
@@ -37,6 +37,14 @@ class InputState(private val engine: DropbearEngine) {
         return engine.native.getLastMousePos() ?: Vector2D(0.0, 0.0)
     }
 
+    fun isCursorHidden(): Boolean {
+        return engine.native.isCursorHidden()
+    }
+
+    fun setCursorHidden(hidden: Boolean) {
+        return engine.native.setCursorHidden(hidden)
+    }
+
     fun getConnectedGamepads(): List<Gamepad> {
         TODO("Not yet implemented")
 //        return engine.native.getConnectedGamepads()
diff --git a/src/commonMain/kotlin/com/dropbear/math/Quaternion.kt b/src/commonMain/kotlin/com/dropbear/math/Quaternion.kt
index ac42b8f..fc9dccb 100644
--- a/src/commonMain/kotlin/com/dropbear/math/Quaternion.kt
+++ b/src/commonMain/kotlin/com/dropbear/math/Quaternion.kt
@@ -3,6 +3,7 @@
 package com.dropbear.math
 
 import kotlin.jvm.JvmField
+import kotlin.math.acos
 import kotlin.math.asin
 import kotlin.math.atan2
 import kotlin.math.cos
@@ -92,4 +93,32 @@ class Quaternion<T: Number>(
 
         return Vector3D(pitch, yaw, roll)
     }
+
+    fun slerp(other: Quaternion<Double>, t: Double): Quaternion<Double> {
+        var dot = x.toDouble() * other.x + y.toDouble() * other.y + z.toDouble() * other.z + w.toDouble() * other.w
+        dot = min(1.0, max(-1.0, dot))
+
+        if (dot > 0.9995) {
+            return Quaternion(
+                x.toDouble() + t * (other.x - x.toDouble()),
+                y.toDouble() + t * (other.y - y.toDouble()),
+                z.toDouble() + t * (other.z - z.toDouble()),
+                w.toDouble() + t * (other.w - w.toDouble())
+            ).normalized()
+        }
+
+        val theta0 = acos(dot)
+        val sinTheta0 = sin(theta0)
+        val theta = theta0 * t
+        val sinTheta = sin(theta)
+        val s0 = cos(theta) - dot * sinTheta / sinTheta0
+        val s1 = sinTheta / sinTheta0
+
+        return Quaternion(
+            x.toDouble() * s0 + other.x * s1,
+            y.toDouble() * s0 + other.y * s1,
+            z.toDouble() * s0 + other.z * s1,
+            w.toDouble() * s0 + other.w * s1
+        ).normalized()
+    }
 }
\ No newline at end of file
diff --git a/src/jvmMain/java/com/dropbear/ffi/JNINative.java b/src/jvmMain/java/com/dropbear/ffi/JNINative.java
index f38c6d2..d7b4d17 100644
--- a/src/jvmMain/java/com/dropbear/ffi/JNINative.java
+++ b/src/jvmMain/java/com/dropbear/ffi/JNINative.java
@@ -42,4 +42,6 @@ public class JNINative {
     public static native boolean isCursorLocked(long inputHandle);
     public static native void setCursorLocked(long inputHandle, long graphicsHandle, boolean locked);
     public static native float[] getLastMousePos(long inputHandle);
+    public static native boolean isCursorHidden(long inputHandle);
+    public static native void setCursorHidden(long inputHandle, long graphicsHandle, boolean hidden);
 }
diff --git a/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt b/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
index 2ebda68..f70dd9d 100644
--- a/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
+++ b/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
@@ -11,6 +11,16 @@ import com.dropbear.math.Vector2D
 actual class NativeEngine {
     private var worldHandle: Long = 0L
     private var inputHandle: Long = 0L
+
+    /**
+     * The handle/pointer to the graphics queue.
+     *
+     * Contrary-to-belief, this is different from the `Arc<SharedGraphicsContext>` handle
+     * as such in the game engine, but rather a pointer to a static variable called `GRAPHICS_COMMANDS`.
+     *
+     * Since winit (the windowing library) requires all commands to be done on the main thread, this variable
+     * allows for "commands" to be sent over and processed on the main thread with the crossbeam_channels library.
+     */
     private var graphicsHandle: Long = 0L
 
     actual fun getEntity(label: String): Long? {
@@ -156,4 +166,12 @@ actual class NativeEngine {
     actual fun setCamera(camera: Camera) {
         JNINative.setCamera(worldHandle, camera)
     }
+
+    actual fun isCursorHidden(): Boolean {
+        return JNINative.isCursorHidden(inputHandle)
+    }
+
+    actual fun setCursorHidden(hidden: Boolean) {
+        JNINative.setCursorHidden(inputHandle, graphicsHandle, hidden)
+    }
 }
\ No newline at end of file
diff --git a/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt b/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt
index 9487e0b..4ef5345 100644
--- a/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt
+++ b/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt
@@ -314,4 +314,11 @@ actual class NativeEngine {
 
     actual fun setCamera(camera: Camera) {
     }
+
+    actual fun isCursorHidden(): Boolean {
+        TODO("Not yet implemented")
+    }
+
+    actual fun setCursorHidden(hidden: Boolean) {
+    }
 }
\ No newline at end of file