tirbofish/dropbear · diff
started on getCamera function, handing it over now
Signature present but could not be verified.
Unverified
@@ -65,6 +65,7 @@ indexmap = "2.11" rand = "0.9" num_enum = "0.7" sha2 = "0.10" +stdext = "0.3" [workspace.dependencies.image] version = "0.25" @@ -32,7 +32,7 @@ sha2.workspace = true libloading.workspace = true crossbeam-channel.workspace = true app_dirs2.workspace = true -num_enum.workspace = true +stdext.workspace = true [features] # editor only stuff @@ -7,6 +7,7 @@ pub mod spawn; pub mod states; pub mod utils; pub mod ptr; +mod result; pub use egui; @@ -0,0 +1,80 @@ +use std::ptr; +use jni::objects::{JClass, JObject, JString}; + +/// Trait used by the `jni` crate for easier error matching. +pub trait ResultToNull { + type Output; + + /// If the output is of a type [`jni::Error`](jni::errors::Error), + /// it will return a null pointer. Pretty useful for when you don't want to have + /// to deal with error matching. + /// + /// Specifically: converts result to the inner value on [`Ok`], or a null pointer on [`Err`] + fn or_null(self) -> Self::Output; +} + +impl ResultToNull for Result<JObject<'_>, jni::errors::Error> { + type Output = JObject<'static>; + + fn or_null(self) -> Self::Output { + match self { + Ok(val) => unsafe { JObject::from_raw(val.into_raw()) }, + Err(_) => JObject::null(), + } + } +} + +impl ResultToNull for anyhow::Result<JObject<'_>> { + type Output = JObject<'static>; + + fn or_null(self) -> Self::Output { + match self { + Ok(val) => unsafe { JObject::from_raw(val.into_raw()) }, + Err(_) => JObject::null(), + } + } +} + +impl ResultToNull for Result<JClass<'_>, jni::errors::Error> { + type Output = JClass<'static>; + + fn or_null(self) -> Self::Output { + match self { + Ok(val) => unsafe { JClass::from_raw(val.into_raw()) }, + Err(_) => unsafe { JClass::from_raw(ptr::null_mut()) }, + } + } +} + +impl ResultToNull for anyhow::Result<JClass<'_>> { + type Output = JClass<'static>; + + fn or_null(self) -> Self::Output { + match self { + Ok(val) => unsafe { JClass::from_raw(val.into_raw()) }, + Err(_) => unsafe { JClass::from_raw(ptr::null_mut()) }, + } + } +} + +impl ResultToNull for Result<JString<'_>, jni::errors::Error> { + type Output = JString<'static>; + + fn or_null(self) -> Self::Output { + match self { + Ok(val) => unsafe { JString::from_raw(val.into_raw()) }, + Err(_) => unsafe { JString::from_raw(ptr::null_mut()) }, + } + } +} + +impl ResultToNull for anyhow::Result<JString<'_>> { + type Output = JString<'static>; + + fn or_null(self) -> Self::Output { + match self { + Ok(val) => unsafe { JString::from_raw(val.into_raw()) }, + Err(_) => unsafe { JString::from_raw(ptr::null_mut()) }, + } + } +} @@ -1,12 +1,14 @@ use crate::ptr::InputStatePtr; -use crate::scripting::jni::utils::{java_button_to_rust, new_float_array}; +use crate::scripting::jni::utils::{create_vector3, java_button_to_rust, new_float_array}; use crate::utils::keycode_from_ordinal; use dropbear_engine::entity::{AdoptedEntity, Transform}; use glam::{DQuat, DVec3}; use hecs::World; -use jni::objects::{JClass, JObject, JPrimitiveArray, JString}; -use jni::sys::{jboolean, jclass, jdouble, jfloatArray, jint, jlong, jstring}; +use jni::objects::{JClass, JObject, JPrimitiveArray, JString, JValue}; +use jni::sys::{jboolean, jclass, jdouble, jfloatArray, jint, jlong, jobject, jstring}; use jni::JNIEnv; +use dropbear_engine::camera::Camera; +use crate::camera::{CameraComponent, CameraType}; use crate::states::{ModelProperties, Value}; // JNIEXPORT jlong JNICALL Java_com_dropbear_ffi_JNINative_getEntity @@ -955,8 +957,126 @@ pub fn Java_com_dropbear_ffi_JNINative_setVec3Property( if let Ok((_, props)) = world.query_one_mut::<(&AdoptedEntity, &mut ModelProperties)>(entity) { - props.set_property(key, Value::Vec3([values[0], values[1], values[2]])); + props.set_property(key, Value::Vec3(values)); } else { eprintln!("[Java_com_dropbear_ffi_JNINative_setVec3Property] [ERROR] Failed to query entity for model properties"); } +} + +// JNIEXPORT jobject JNICALL Java_com_dropbear_ffi_JNINative_getCamera +// (JNIEnv *, jclass, jlong, jstring); +#[unsafe(no_mangle)] +pub fn Java_com_dropbear_ffi_JNINative_getCamera( + mut env: JNIEnv, + _class: JClass, + world_handle: jlong, + camera_name: JString, +) -> jobject { + let world = world_handle as *mut World; + if world.is_null() { + eprintln!("[Java_com_dropbear_ffi_JNINative_getCamera] [ERROR] World pointer is null"); + return std::ptr::null_mut(); + } + + let world = unsafe { &*world }; + + let label = env.get_string(&camera_name); + let label: String = if let Ok(str) = label { + str.to_string_lossy().to_string() + } else { + eprintln!("[Java_com_dropbear_ffi_JNINative_getCamera] [ERROR] Failed to get camera name"); + return std::ptr::null_mut(); + }; + + if let Some((id, (cam, comp))) = world.query::<(&Camera, &CameraComponent)>().iter().next() { + return if cam.label == label { + if matches!(comp.camera_type, CameraType::Debug) { + eprintln!("[Java_com_dropbear_ffi_JNINative_getCamera] [WARN] Querying a CameraType::Debug is illegal, returning null"); + std::ptr::null_mut() + } else { + let entity_id = if let Ok(v) = env.find_class("com/dropbear/EntityId") { + v + } else { + eprintln!("[Java_com_dropbear_ffi_JNINative_getCamera] [ERROR] Unable to find EntityId class"); + return std::ptr::null_mut(); + }; + let entity_id = if let Ok(v) = env.new_object( + entity_id, + "(J)V", + &[JValue::Long(id.id() as i64)], + ) { + v + } else { + eprintln!("[Java_com_dropbear_ffi_JNINative_getCamera] [ERROR] Unable to create new entity_id object"); + return std::ptr::null_mut(); + }; + + let label = if let Ok(v) = env.new_string(cam.label.as_str()) { + v + } else { + eprintln!("[Java_com_dropbear_ffi_JNINative_getCamera] [ERROR] Unable to create new string for label"); + return std::ptr::null_mut(); + }; + + let eye = if let Ok(v) = create_vector3(&mut env, cam.eye.x, cam.eye.y, cam.eye.z) { + v + } else { + eprintln!("[Java_com_dropbear_ffi_JNINative_getCamera] [ERROR] Unable to create vector3 for eye"); + return std::ptr::null_mut(); + }; + + let target = if let Ok(v) = create_vector3(&mut env, cam.target.x, cam.target.y, cam.target.z) { + v + } else { + eprintln!("[Java_com_dropbear_ffi_JNINative_getCamera] [ERROR] Unable to create vector3 for target"); + return std::ptr::null_mut(); + }; + + let up = if let Ok(v) = create_vector3(&mut env, cam.up.x, cam.up.y, cam.up.z) { + v + } else { + eprintln!("[Java_com_dropbear_ffi_JNINative_getCamera] [ERROR] Unable to create vector3 for up"); + return std::ptr::null_mut(); + }; + + let class = if let Ok(v) = env.find_class("com/dropbear/Camera") { + v + } else { + eprintln!("[Java_com_dropbear_ffi_JNINative_getCamera] [ERROR] Unable to locate camera class"); + return std::ptr::null_mut(); + }; + + let camera_obj = if let Ok(v) = env.new_object( + class, + "(Ljava/lang/String;Lcom/dropbear/EntityId;Lcom/dropbear/math/Vector3<Ljava/lang/Double;>;Lcom/dropbear/math/Vector3<Ljava/lang/Double;>;Lcom/dropbear/math/Vector3<Ljava/lang/Double;>;DDDDDDDD)V", + &[ + JValue::Object(&label), + JValue::Object(&entity_id), + JValue::Object(&eye), + JValue::Object(&target), + JValue::Object(&up), + JValue::Double(cam.aspect), + JValue::Double(cam.fov_y), + JValue::Double(cam.znear), + JValue::Double(cam.zfar), + JValue::Double(cam.yaw), + JValue::Double(cam.pitch), + JValue::Double(cam.speed), + JValue::Double(cam.sensitivity), + ] + ) { + v + } else { + eprintln!("[Java_com_dropbear_ffi_JNINative_getCamera] [ERROR] Unable to create the camera object"); + return std::ptr::null_mut(); + }; + + camera_obj.as_raw() + } + } else { + std::ptr::null_mut() + } + } + + std::ptr::null_mut() } @@ -1,5 +1,5 @@ use jni::JNIEnv; -use jni::objects::JFloatArray; +use jni::objects::{JFloatArray, JObject, JValue}; use jni::sys::{jfloatArray, jint}; pub fn new_float_array(env: &mut JNIEnv, x: f32, y: f32) -> jfloatArray { @@ -39,4 +39,41 @@ pub fn java_button_to_rust(button_code: jint) -> Option<winit::event::MouseButto other if other >= 0 => Some(winit::event::MouseButton::Other(other as u16)), // Assuming Other uses the int directly _ => None, } +} + +pub fn create_vector3<'a>(env: &mut JNIEnv<'a>, x: f64, y: f64, z: f64) -> anyhow::Result<JObject<'a>> { + let vector3_class = env.find_class("com/dropbear/math/Vector3")?; + + let x_obj = env.call_static_method( + "java/lang/Double", + "valueOf", + "(D)Ljava/lang/Double;", + &[JValue::Double(x)] + )?.l()?; + + let y_obj = env.call_static_method( + "java/lang/Double", + "valueOf", + "(D)Ljava/lang/Double;", + &[JValue::Double(y)] + )?.l()?; + + let z_obj = env.call_static_method( + "java/lang/Double", + "valueOf", + "(D)Ljava/lang/Double;", + &[JValue::Double(z)] + )?.l()?; + + let vector3 = env.new_object( + vector3_class, + "(Ljava/lang/Double;Ljava/lang/Double;Ljava/lang/Double;)V", + &[ + JValue::Object(&x_obj), + JValue::Object(&y_obj), + JValue::Object(&z_obj), + ] + )?; + + Ok(vector3) } @@ -0,0 +1,28 @@ +package com.dropbear + +import com.dropbear.math.Vector3D + +class Camera( + private val label: String, + private val id: EntityId, // it could be attached to nothing or an AdoptedEntity + var eye: Vector3D = Vector3D.zero(), + var target: Vector3D = Vector3D.zero(), + var up: Vector3D = Vector3D.zero(), + val aspect: Double = 0.0, + var fov_y: Double = 0.0, + var znear: Double = 0.0, + var zfar: Double = 0.0, + var yaw: Double = 0.0, + var pitch: Double = 0.0, + var speed: Double = 0.0, + var sensitivity: Double = 0.0 +) { + lateinit var engine: DropbearEngine + + /** + * Pushes the camera values to the world to be updated. + */ + fun setCamera() { + engine.native.setCamera(this) + } +} @@ -8,14 +8,22 @@ import com.dropbear.math.Transform class DropbearEngine(val native: NativeEngine) { private var inputState: InputState? = null - public fun getEntity(label: String): EntityRef? { + fun getEntity(label: String): EntityRef? { val entityId = native.getEntity(label) val entityRef = if (entityId != null) EntityRef(EntityId(entityId)) else null entityRef?.engine = this return entityRef } - public fun getInputState(): InputState { + fun getCamera(label: String): Camera? { + val result = native.getCamera(label) + if (result != null) { + result.engine = this + } + return result + } + + fun getInputState(): InputState { if (this.inputState == null) { Logger.trace("InputState not initialised, creating new one") this.inputState = InputState(this) @@ -47,4 +47,12 @@ class EntityRef(val id: EntityId = EntityId(0L)) { else -> throw IllegalArgumentException("Unsupported property type: ${value::class}") } } + + fun getAttachedCamera(): Camera? { + val result = engine.native.getAttachedCamera(id) + if (result != null) { + result.engine = this.engine + } + return result + } } @@ -1,5 +1,6 @@ package com.dropbear.ffi +import com.dropbear.Camera import com.dropbear.EntityId import com.dropbear.input.KeyCode import com.dropbear.input.MouseButton @@ -12,6 +13,10 @@ expect class NativeEngine { */ fun getEntity(label: String): Long? + fun getCamera(label: String): Camera? + fun getAttachedCamera(entityId: EntityId): Camera? + fun setCamera(camera: Camera); + /** * Fetches the [Transform] component of an entity by it's ID */ @@ -1,5 +1,6 @@ package com.dropbear.ffi; +import com.dropbear.Camera; import com.dropbear.math.Transform; public class JNINative { @@ -9,6 +10,9 @@ public class JNINative { // entity and ecs public static native long getEntity(long handle, String label); + public static native Camera getCamera(long worldHandle, String label); + public static native Camera getAttachedCamera(long worldHandle, long entityHandle); + public static native void setCamera(long worldHandle, Camera camera); // transformations public static native Transform getTransform(long handle, long entityHandle); @@ -1,5 +1,6 @@ package com.dropbear.ffi +import com.dropbear.Camera import com.dropbear.EntityId import com.dropbear.input.KeyCode import com.dropbear.input.MouseButton @@ -133,4 +134,16 @@ actual class NativeEngine { actual fun setVec3Property(entityHandle: Long, label: String, value: FloatArray) { JNINative.setVec3Property(worldHandle, entityHandle, label, value) } + + actual fun getCamera(label: String): Camera? { + return JNINative.getCamera(worldHandle, label) + } + + actual fun getAttachedCamera(entityId: EntityId): Camera? { + return JNINative.getAttachedCamera(worldHandle, entityId.id) + } + + actual fun setCamera(camera: Camera) { + JNINative.setCamera(worldHandle, camera) + } } @@ -3,6 +3,7 @@ package com.dropbear.ffi +import com.dropbear.Camera import com.dropbear.EntityId import com.dropbear.ffi.generated.* import com.dropbear.input.KeyCode @@ -298,4 +299,15 @@ actual class NativeEngine { actual fun setVec3Property(entityHandle: Long, label: String, value: FloatArray) { } + + actual fun getCamera(label: String): Camera? { + TODO("Not yet implemented") + } + + actual fun getAttachedCamera(entityId: EntityId): Camera? { + TODO("Not yet implemented") + } + + actual fun setCamera(camera: Camera) { + } }