tirbofish/dropbear · diff
fixed up cursor locking. it defaults to CursorGrabMode::Confined, Locked is second option (but that doesnt work for some reason).
Signature present but could not be verified.
Unverified
@@ -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 @@ -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>, @@ -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 @@ -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(), @@ -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; @@ -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() + } } @@ -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"); } } } @@ -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; } @@ -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> @@ -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() @@ -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() + } } @@ -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); } @@ -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) + } } @@ -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) { + } }