tirbofish/dropbear · diff
refactor: move exports to `eucalyptus-exports` crate to clean up eucalyptus-core.
Signature present but could not be verified.
Unverified
@@ -11,20 +11,19 @@ If you might have not realised, all the crates/projects names are after Australi ## Projects -- [dropbear-engine](https://github.com/tirbofish/dropbear/tree/main/crates/dropbear-engine) is the rendering engine that uses wgpu and the main name of the project. -- [eucalyptus-editor](https://github.com/tirbofish/dropbear/tree/main/crates/eucalyptus-editor) is the visual editor used to create games visually, taking inspiration from Unity, Unreal, Roblox Studio and other engines. -- [eucalyptus-core](https://github.com/tirbofish/dropbear/tree/main/crates/eucalyptus-core) is the library used by both `redback-runtime` and `eucalyptus-editor` to share configs and metadata between each other. -- [redback-runtime](https://github.com/tirbofish/dropbear/tree/main/crates/redback-runtime) is the runtime used to load .eupak files and run the game loaded on them. -- [magna-carta](https://github.com/tirbofish/dropbear/tree/main/crates/magna-carta) is a rust library used to generate compile-time Kotlin/Native and Kotlin/JVM metadata for searching. -- [kino-ui](https://github.com/tirbofish/dropbear/tree/main/crates/kino-ui) is the main runtime-side UI system to render widgets and elements. - -[//]: # (- [eucalyptus-sdk](https://github.com/tirbofish/dropbear/tree/main/eucalyptus-sdk) is used to develop plugins to be used with the `eucalyptus-editor`) +- [dropbear-engine](crates/dropbear-engine) is the rendering engine that uses wgpu and the main name of the project. +- [eucalyptus-editor](crates/eucalyptus-editor) is the visual editor used to create games visually, taking inspiration from Unity, Unreal, Roblox Studio and other engines. +- [eucalyptus-core](crates/eucalyptus-core) is the library used by both `redback-runtime` and `eucalyptus-editor` to share configs and metadata between each other. +- [eucalyptus-exports](crates/eucalyptus-core) is used to scripting-based logic for the editor and the engine itself. +- [redback-runtime](crates/redback-runtime) is the runtime used to load .eupak files and run the game loaded on them. +- [magna-carta](crates/magna-carta) is a rust library used to generate compile-time Kotlin/Native and Kotlin/JVM metadata for searching. +- [kino-ui](crates/kino-ui) is the main runtime-side UI system to render widgets and elements. ### Related Projects -- [dropbear_future-queue](https://github.com/tirbofish/dropbear/tree/main/dropbear_future-queue) is a handy library for dealing with async in a sync context +- [dropbear_future-queue](crates/dropbear_future-queue) is a handy library for dealing with async in a sync context - [model_to_image](https://github.com/tirbofish/model_to_image) is a library used to generate thumbnails and images from a 3D model with the help of `russimp-ng` and a custom made rasterizer. _(very crude but usable)_ -- [slank](https://github.com/tirbofish/dropbear/tree/main/crates/slank) is a slang compiler that compiles .slang files into shaders during build.rs compilation time. +- [slank](crates/slank) is a slang compiler that compiles .slang files into shaders during build.rs compilation time. ## Build @@ -45,7 +44,7 @@ yeah thats about it <summary>Linux</summary> -With Unix systems (macOS not tested), you will have to download a couple of dependencies if building locally: +With Linux systems (macOS not tested), you will have to download a couple of dependencies if building locally: ```bash # ubuntu @@ -98,8 +97,9 @@ cargo build <summary>For the engine developers</summary> -Ensure that `cargo build` is ran for each iteration of testing instead of `cargo build -p eucalyptus-editor` due to some weird ABI issue with Rust. If you do only want to build -a specific package, ensure you run `cargo build -p {package} -p eucalyptus-core`. +Ensure that `cargo build` is run for each iteration of testing instead of `cargo build -p eucalyptus-editor` due to +some weird ABI issue with Rust. If you do only want to build a specific package, ensure +you run `cargo build -p {package} -p eucalyptus-exports`. If you get any FFI errors (likely a getter), you compiled the library wrong. @@ -39,19 +39,20 @@ fn specular( return light_color * spec; } -fn attenuation(light: Light, world_pos: vec3<f32>) -> f32 { - let d = length(light.position.xyz - world_pos); +fn attenuation(light: Light, tangent_light_pos: vec3<f32>, tangent_pos: vec3<f32>) -> f32 { + let d = length(tangent_light_pos - tangent_pos); return 1.0 / (light.constant + light.lin * d + light.quadratic * d * d); } fn calculate_light( - light: Light, - world_pos: vec3<f32>, - normal: vec3<f32>, // normalised world-space surface normal - view_dir: vec3<f32>, // normalised direction from fragment to camera - object_color: vec3<f32>, - shininess: f32, - spec_scale: f32, + light: Light, + tangent_pos: vec3<f32>, + tangent_matrix: mat3x3<f32>, + normal: vec3<f32>, // normalised tangent-space surface normal + view_dir: vec3<f32>, // normalised tangent-space direction from fragment to camera + object_color: vec3<f32>, + shininess: f32, + spec_scale: f32, ) -> vec3<f32> { let light_type = u32(light.color.w); let light_color = light.color.xyz; @@ -61,15 +62,16 @@ fn calculate_light( var intensity: f32 = 1.0; if light_type == LIGHT_DIRECTIONAL { - light_dir = normalize(-light.direction.xyz); + light_dir = normalize(tangent_matrix * (-light.direction.xyz)); } else { // point and spot included - light_dir = normalize(light.position.xyz - world_pos); - atten = attenuation(light, world_pos); + let tangent_light_pos = tangent_matrix * light.position.xyz; + light_dir = normalize(tangent_light_pos - tangent_pos); + atten = attenuation(light, tangent_light_pos, tangent_pos); if light_type == LIGHT_SPOT { // smooth edge - let theta = dot(light_dir, normalize(-light.direction.xyz)); + let theta = dot(light_dir, normalize(tangent_matrix * (-light.direction.xyz))); let inner_cutoff = light.cutoff; let outer_cutoff = light.direction.w; let epsilon = inner_cutoff - outer_cutoff; @@ -85,19 +87,21 @@ fn calculate_light( fn calculate_lighting( - world_pos: vec3<f32>, - normal: vec3<f32>, - view_dir: vec3<f32>, - object_color: vec3<f32>, - shininess: f32, - spec_scale: f32, + tangent_pos: vec3<f32>, + tangent_matrix: mat3x3<f32>, + normal: vec3<f32>, + view_dir: vec3<f32>, + object_color: vec3<f32>, + shininess: f32, + spec_scale: f32, ) -> vec3<f32> { var result = u_globals.ambient_strength * object_color; let num_lights = u_globals.num_lights; for (var i = 0u; i < num_lights; i++) { result += calculate_light( s_light_array[i], - world_pos, + tangent_pos, + tangent_matrix, normal, view_dir, object_color, @@ -47,11 +47,10 @@ struct VertexInput { struct VertexOutput { @builtin(position) clip_position: vec4<f32>, @location(0) tex_coords: vec2<f32>, - @location(1) world_normal: vec3<f32>, - @location(2) world_position: vec3<f32>, - @location(3) world_tangent: vec3<f32>, - @location(4) world_bitangent: vec3<f32>, - @location(5) world_view_position: vec3<f32>, + @location(1) world_position: vec3<f32>, + @location(2) world_tangent: vec3<f32>, + @location(3) world_bitangent: vec3<f32>, + @location(4) world_normal: vec3<f32>, }; @@ -80,17 +79,17 @@ fn vs_main(model: VertexInput, instance: InstanceInput) -> VertexOutput { let world_position = model_matrix * vec4<f32>(anim.position, 1.0); let world_normal = normalize(normal_matrix * anim.normal); - let world_tangent = normalize(normal_matrix * anim.tangent); + let model_mat3 = mat3x3<f32>(model_matrix[0].xyz, model_matrix[1].xyz, model_matrix[2].xyz); + let world_tangent = normalize(model_mat3 * anim.tangent); let world_bitangent = normalize(cross(world_normal, world_tangent) * model.tangent.w); var out: VertexOutput; - out.clip_position = u_camera.view_proj * world_position; - out.tex_coords = model.tex_coords0; - out.world_normal = world_normal; - out.world_position = world_position.xyz; - out.world_tangent = world_tangent; - out.world_bitangent = world_bitangent; - out.world_view_position = u_camera.view_pos.xyz; + out.clip_position = u_camera.view_proj * world_position; + out.tex_coords = model.tex_coords0; + out.world_position = world_position.xyz; + out.world_tangent = world_tangent; + out.world_bitangent = world_bitangent; + out.world_normal = world_normal; return out; } @@ -105,17 +104,19 @@ fn s_fs_main(in: VertexOutput) -> @location(0) vec4<f32> { discard; } - // normal - var N = normalize(in.world_normal); + let world_N = normalize(in.world_normal); + + let world_T = normalize(in.world_tangent - dot(in.world_tangent, world_N) * world_N); + let world_B_cross = cross(world_N, world_T); + let world_B = world_B_cross * sign(dot(in.world_bitangent, world_B_cross)); + let tangent_matrix = transpose(mat3x3<f32>(world_T, world_B, world_N)); + + var N: vec3<f32>; if material::u_material.has_normal_texture != 0u { - let tbn = mat3x3<f32>( - normalize(in.world_tangent), - normalize(in.world_bitangent), - N, - ); let n_sample = textureSample(material::t_normal, material::s_normal, uv).xyz; - let n_ts = (n_sample * 2.0 - 1.0) * vec3<f32>(material::u_material.normal_scale, material::u_material.normal_scale, 1.0); - N = normalize(tbn * n_ts); + N = normalize((n_sample * 2.0 - 1.0) * vec3<f32>(material::u_material.normal_scale, material::u_material.normal_scale, 1.0)); + } else { + N = vec3<f32>(0.0, 0.0, 1.0); } // occlusion @@ -127,11 +128,15 @@ fn s_fs_main(in: VertexOutput) -> @location(0) vec4<f32> { let roughness = material::u_material.roughness; let shininess = max(2.0 / max(roughness * roughness, 0.001) - 2.0, 2.0); - let spec_scale = (1.0 - roughness) * (1.0 - roughness); + let spec_scale = max((1.0 - roughness) * (1.0 - roughness), 0.04); + + let tangent_pos = tangent_matrix * in.world_position; + let tangent_view_pos = tangent_matrix * u_camera.view_pos.xyz; - let view_dir = normalize(in.world_view_position - in.world_position); + let view_dir = normalize(tangent_view_pos - tangent_pos); let lit = light::calculate_lighting( - in.world_position, + tangent_pos, + tangent_matrix, N, view_dir, albedo.rgb, @@ -6,9 +6,6 @@ license.workspace = true repository.workspace = true readme = "README.md" -[lib] -crate-type = ["rlib", "cdylib"] - [dependencies] dropbear-macro = { path = "../dropbear-macro" } magna-carta = { path = "../magna-carta" } @@ -1,40 +1,4 @@ # eucalyptus-core -The core libraries of the eucalyptus editor. Great for embedding into `redback-runtime` and `eucalyptus-editor` as one big change instead of a bunch of features. - -It also produces a shared library for Kotlin/Native and the JVM. - -Kotlin Multiplatform is prioritised and fully supported on this library, however that is not to say that you (yes you -the reader) could implement this for another language (as really it just reads from a .dll or a .jar file). - -[//]: # (## Export requirements) - -[//]: # () -[//]: # (To have your own scripting module to be able to be run, you will have to implement the following exports into) - -[//]: # (your own app:) - -[//]: # () -[//]: # (- `dropbear_init`) - -[//]: # ( - Args: `pointerContext: DropbearContext*`) - -[//]: # ( - Returns: `int`) - -[//]: # (- `dropbear_load_tagged`) - -[//]: # ( - Args: `tag: *mut c_char`) - -[//]: # ( - Returns: `int`) - -[//]: # (- `dropbear_update_all`) - -[//]: # ( - Args: `dt: float`) - -[//]: # ( - Returns: `int`) - -[//]: # (- `dropbear_update_tagged`) - -[//]: # ( - Args: `tag: *mut c_char, `) - -[//]: # ( - ) +The core libraries of the eucalyptus editor. Great for embedding into `redback-runtime` and `eucalyptus-editor` as +one big change instead of a bunch of features. @@ -1,14 +0,0 @@ -fn main() -> anyhow::Result<()> { - // fuck you windows :( - #[cfg(target_os = "windows")] - { - println!("cargo:rustc-link-arg=/FORCE:MULTIPLE"); - println!("cargo:rustc-link-arg=/NODEFAULTLIB:libcmt.lib"); - } - - goanna_gen::generate_c_header()?; - - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=src"); - Ok(()) -} @@ -30,10 +30,10 @@ impl Component for AnimationComponent { } } - fn init<'a>( - ser: &'a Self::SerializedForm, + fn init( + ser: &Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, - ) -> crate::component::ComponentInitFuture<'a, Self> { + ) -> crate::component::ComponentInitFuture<Self> { Box::pin(async move { Ok((ser.clone(),)) }) } @@ -219,395 +219,4 @@ impl InspectableComponent for AnimationComponent { } }); } -} - -fn collect_available_animations( - world: &World, - entity: Entity, - component: &AnimationComponent, -) -> Vec<String> { - if !component.available_animations.is_empty() { - return component.available_animations.clone(); - } - - let Ok(renderer) = world.get::<&MeshRenderer>(entity) else { - return Vec::new(); - }; - - let handle = renderer.model(); - if handle.is_null() { - return Vec::new(); - } - - let registry = ASSET_REGISTRY.read(); - let Some(model) = registry.get_model(handle) else { - return Vec::new(); - }; - - model - .animations - .iter() - .map(|animation| animation.name.clone()) - .collect() -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.animation.AnimationComponentNative", - func = "animationComponentExistsForEntity" - ), - c -)] -fn exists_for_entity( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<bool> { - Ok(world.get::<&AnimationComponent>(entity).is_ok()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.animation.AnimationComponentNative", - func = "getActiveAnimationIndex" - ), - c -)] -fn get_active_animation_index( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<Option<i32>> { - let component = world - .get::<&AnimationComponent>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - Ok(component.active_animation_index.map(|index| index as i32)) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.animation.AnimationComponentNative", - func = "setActiveAnimationIndex" - ), - c -)] -fn set_active_animation_index( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - index: &Option<i32>, -) -> DropbearNativeResult<()> { - let mut component = world - .get::<&mut AnimationComponent>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - - let index = match index { - Some(value) if *value >= 0 => Some(*value as usize), - Some(_) => return Err(DropbearNativeError::InvalidArgument), - None => None, - }; - - if let Some(value) = index { - if !component.available_animations.is_empty() - && value >= component.available_animations.len() - { - return Err(DropbearNativeError::InvalidArgument); - } - } - - component.active_animation_index = index; - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.animation.AnimationComponentNative", - func = "getTime" - ), - c -)] -fn get_time( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<f64> { - let component = world - .get::<&AnimationComponent>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - - if let Some(index) = component.active_animation_index { - if let Some(settings) = component.animation_settings.get(&index) { - return Ok(settings.time as f64); - } - } - - Ok(component.time as f64) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.animation.AnimationComponentNative", - func = "setTime" - ), - c -)] -fn set_time( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - value: f64, -) -> DropbearNativeResult<()> { - let mut component = world - .get::<&mut AnimationComponent>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - - component.time = value as f32; - - if let Some(index) = component.active_animation_index { - let (time, speed, looping, is_playing) = ( - component.time, - component.speed, - component.looping, - component.is_playing, - ); - let settings = component - .animation_settings - .entry(index) - .or_insert_with(|| AnimationSettings { - time, - speed, - looping, - is_playing, - }); - settings.time = time; - } - - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.animation.AnimationComponentNative", - func = "getSpeed" - ), - c -)] -fn get_speed( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<f64> { - let component = world - .get::<&AnimationComponent>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - - if let Some(index) = component.active_animation_index { - if let Some(settings) = component.animation_settings.get(&index) { - return Ok(settings.speed as f64); - } - } - - Ok(component.speed as f64) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.animation.AnimationComponentNative", - func = "setSpeed" - ), - c -)] -fn set_speed( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - value: f64, -) -> DropbearNativeResult<()> { - let mut component = world - .get::<&mut AnimationComponent>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - - component.speed = value as f32; - - if let Some(index) = component.active_animation_index { - let (time, speed, looping, is_playing) = ( - component.time, - component.speed, - component.looping, - component.is_playing, - ); - let settings = component - .animation_settings - .entry(index) - .or_insert_with(|| AnimationSettings { - time, - speed, - looping, - is_playing, - }); - settings.speed = speed; - } - - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.animation.AnimationComponentNative", - func = "getLooping" - ), - c -)] -fn get_looping( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<bool> { - let component = world - .get::<&AnimationComponent>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - - if let Some(index) = component.active_animation_index { - if let Some(settings) = component.animation_settings.get(&index) { - return Ok(settings.looping); - } - } - - Ok(component.looping) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.animation.AnimationComponentNative", - func = "setLooping" - ), - c -)] -fn set_looping( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - value: bool, -) -> DropbearNativeResult<()> { - let mut component = world - .get::<&mut AnimationComponent>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - - component.looping = value; - - if let Some(index) = component.active_animation_index { - let (time, speed, looping, is_playing) = ( - component.time, - component.speed, - component.looping, - component.is_playing, - ); - let settings = component - .animation_settings - .entry(index) - .or_insert_with(|| AnimationSettings { - time, - speed, - looping, - is_playing, - }); - settings.looping = value; - } - - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.animation.AnimationComponentNative", - func = "getIsPlaying" - ), - c -)] -fn get_is_playing( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<bool> { - let component = world - .get::<&AnimationComponent>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - - if let Some(index) = component.active_animation_index { - if let Some(settings) = component.animation_settings.get(&index) { - return Ok(settings.is_playing); - } - } - - Ok(component.is_playing) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.animation.AnimationComponentNative", - func = "setIsPlaying" - ), - c -)] -fn set_is_playing( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - value: bool, -) -> DropbearNativeResult<()> { - let mut component = world - .get::<&mut AnimationComponent>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - - component.is_playing = value; - - if let Some(index) = component.active_animation_index { - let (time, speed, looping, is_playing) = ( - component.time, - component.speed, - component.looping, - component.is_playing, - ); - let settings = component - .animation_settings - .entry(index) - .or_insert_with(|| AnimationSettings { - time, - speed, - looping, - is_playing, - }); - settings.is_playing = value; - } - - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.animation.AnimationComponentNative", - func = "getIndexFromString" - ), - c -)] -fn get_index_from_string( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, - name: String, -) -> DropbearNativeResult<Option<i32>> { - let component = world - .get::<&AnimationComponent>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - - Ok(component - .available_animations - .iter() - .enumerate() - .find_map(|(i, l)| if *l == name { Some(i as i32) } else { None })) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.animation.AnimationComponentNative", - func = "getAvailableAnimations" - ), - c -)] -fn get_available_animations( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<Vec<String>> { - let component = world - .get::<&AnimationComponent>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - Ok(collect_available_animations(world, entity, &component)) -} +} @@ -1,57 +0,0 @@ -pub mod model; -pub mod texture; - -use crate::ptr::{AssetRegistryPtr, AssetRegistryUnwrapped}; -use crate::scripting::jni::utils::FromJObject; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; -use dropbear_engine::asset::AssetKind; -use jni::objects::JObject; -use jni::{Env, jni_sig, jni_str}; - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.DropbearEngineNative", func = "getAsset"), - c(name = "dropbear_engine_get_asset") -)] -fn dropbear_asset_get_asset( - #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, - label: String, - kind: &AssetKind, -) -> DropbearNativeResult<Option<u64>> { - let reader = asset.read(); - match kind { - AssetKind::Texture => { - let result = reader.get_texture_handle_from_label(&label); - if let Some(handle) = result { - Ok(Some(handle.id)) - } else { - Ok(None) - } - } - AssetKind::Model => { - let result = reader.get_model_handle_from_label(&label); - if let Some(handle) = result { - Ok(Some(handle.id)) - } else { - Ok(None) - } - } - } -} - -impl FromJObject for AssetKind { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> - where - Self: Sized, - { - let ordinal = env - .call_method(obj, jni_str!("ordinal"), jni_sig!(() -> i32), &[])? - .i()?; - - match ordinal { - 0 => Ok(AssetKind::Texture), - 1 => Ok(AssetKind::Model), - _ => Err(DropbearNativeError::InvalidEnumOrdinal), - } - } -} @@ -1,734 +0,0 @@ -use crate::ptr::{AssetRegistryPtr, AssetRegistryUnwrapped}; -use crate::scripting::jni::utils::ToJObject; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; -use crate::types::{NQuaternion, NVector2, NVector3, NVector4}; -use dropbear_engine::asset::Handle; -use dropbear_engine::model::{ - Animation, AnimationChannel, AnimationInterpolation, ChannelValues, Material, Mesh, - ModelVertex, Node, NodeTransform, Skin, -}; -use jni::objects::{JObject, JValue}; -use jni::{Env, jni_sig, jni_str}; - -#[repr(C)] -#[derive(Clone, Debug)] -pub struct NModelVertex { - pub position: NVector3, - pub normal: NVector3, - pub tangent: NVector4, - pub tex_coords0: NVector2, - pub tex_coords1: NVector2, - pub colour0: NVector4, - pub joints0: Vec<i32>, - pub weights0: NVector4, -} - -impl ToJObject for NModelVertex { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com/dropbear/asset/model/ModelVertex")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let position = self.position.to_jobject(env)?; - let normal = self.normal.to_jobject(env)?; - let tangent = self.tangent.to_jobject(env)?; - let tex_coords0 = self.tex_coords0.to_jobject(env)?; - let tex_coords1 = self.tex_coords1.to_jobject(env)?; - let colour0 = self.colour0.to_jobject(env)?; - let joints0 = self.joints0.as_slice().to_jobject(env)?; - let weights0 = self.weights0.to_jobject(env)?; - - let args = [ - JValue::Object(&position), - JValue::Object(&normal), - JValue::Object(&tangent), - JValue::Object(&tex_coords0), - JValue::Object(&tex_coords1), - JValue::Object(&colour0), - JValue::Object(&joints0), - JValue::Object(&weights0), - ]; - - let obj = env - .new_object( - &class, - jni_sig!((com.dropbear.math.Vector3f, com.dropbear.math.Vector3f, com.dropbear.math.Vector4f, com.dropbear.math.Vector2f, com.dropbear.math.Vector2f, com.dropbear.math.Vector4f, [int], com.dropbear.math.Vector4f) -> ()), - &args, - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } -} - -#[repr(C)] -#[derive(Clone, Debug)] -pub struct NMesh { - pub name: String, - pub num_elements: i32, - pub material_index: i32, - pub vertices: Vec<NModelVertex>, -} - -impl ToJObject for NMesh { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com.dropbear.asset.model.Mesh")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let name = env - .new_string(&self.name) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - let vertices = self.vertices.to_jobject(env)?; - - let args = [ - JValue::Object(&name), - JValue::Int(self.num_elements), - JValue::Int(self.material_index), - JValue::Object(&vertices), - ]; - - let obj = env - .new_object( - &class, - jni_sig!((java.lang.String, int, int, java.util.List) -> ()), - &args, - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } -} - -#[repr(C)] -#[derive(Clone, Debug)] -pub struct NMaterial { - pub name: String, - pub diffuse_texture: u64, - pub normal_texture: Option<u64>, - pub emissive_texture: Option<u64>, - pub metallic_roughness_texture: Option<u64>, - pub occlusion_texture: Option<u64>, - pub tint: NVector4, - pub emissive_factor: NVector3, - pub metallic_factor: f32, - pub roughness_factor: f32, - pub alpha_cutoff: Option<f32>, - pub occlusion_strength: f32, - pub normal_scale: f32, - pub uv_tiling: NVector2, -} - -impl ToJObject for NMaterial { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com.dropbear.asset.model.Material")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let name = env - .new_string(&self.name) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - let diffuse_texture = new_texture(env, self.diffuse_texture)?; - let normal_texture = match self.normal_texture { - Some(id) => new_texture(env, id)?, - None => JObject::null(), - }; - let emissive_texture = match self.emissive_texture { - Some(id) => new_texture(env, id)?, - None => JObject::null(), - }; - let metallic_roughness_texture = match self.metallic_roughness_texture { - Some(id) => new_texture(env, id)?, - None => JObject::null(), - }; - let occlusion_texture = match self.occlusion_texture { - Some(id) => new_texture(env, id)?, - None => JObject::null(), - }; - let tint = self.tint.to_jobject(env)?; - let emissive_factor = self.emissive_factor.to_jobject(env)?; - let uv_tiling = self.uv_tiling.to_jobject(env)?; - let alpha_cutoff = self.alpha_cutoff.to_jobject(env)?; - - let args = [ - JValue::Object(&name), - JValue::Object(&diffuse_texture), - JValue::Object(&normal_texture), - JValue::Object(&tint), - JValue::Object(&emissive_factor), - JValue::Double(self.metallic_factor as f64), - JValue::Double(self.roughness_factor as f64), - JValue::Object(&alpha_cutoff), - JValue::Double(self.occlusion_strength as f64), - JValue::Double(self.normal_scale as f64), - JValue::Object(&uv_tiling), - JValue::Object(&emissive_texture), - JValue::Object(&metallic_roughness_texture), - JValue::Object(&occlusion_texture), - ]; - - let obj = env - .new_object( - &class, - jni_sig!(( - java.lang.String, - com.dropbear.asset.Texture, - com.dropbear.asset.Texture, - com.dropbear.math.Vector4d, - com.dropbear.math.Vector3d, - double, double, - java.lang.Double, - double, double, - com.dropbear.math.Vector2d, - com.dropbear.asset.Texture, - com.dropbear.asset.Texture, - com.dropbear.asset.Texture - ) -> ()), - &args, - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } -} - -#[repr(C)] -#[derive(Clone, Debug)] -pub struct NNodeTransform { - pub translation: NVector3, - pub rotation: NQuaternion, - pub scale: NVector3, -} - -impl ToJObject for NNodeTransform { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com.dropbear.asset.model.NodeTransform")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let translation = self.translation.to_jobject(env)?; - let rotation = self.rotation.to_jobject(env)?; - let scale = self.scale.to_jobject(env)?; - - let args = [ - JValue::Object(&translation), - JValue::Object(&rotation), - JValue::Object(&scale), - ]; - - let obj = env - .new_object( - &class, - jni_sig!("(Lcom/dropbear/math/Vector3d;Lcom/dropbear/math/Quaterniond;Lcom/dropbear/math/Vector3d;)V"), - &args, - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } -} - -#[repr(C)] -#[derive(Clone, Debug)] -pub struct NNode { - pub name: String, - pub parent: Option<i32>, - pub children: Vec<i32>, - pub transform: NNodeTransform, -} - -impl ToJObject for NNode { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com.dropbear.asset.model.Node")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let name = env - .new_string(&self.name) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - let parent = self.parent.to_jobject(env)?; - let children = self.children.as_slice().to_jobject(env)?; - let transform = self.transform.to_jobject(env)?; - - let args = [ - JValue::Object(&name), - JValue::Object(&parent), - JValue::Object(&children), - JValue::Object(&transform), - ]; - - let obj = env - .new_object( - &class, - jni_sig!("(Ljava/lang/String;Ljava/lang/Integer;Ljava/util/List;Lcom/dropbear/asset/model/NodeTransform;)V"), - &args, - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } -} - -#[repr(C)] -#[derive(Clone, Debug)] -pub struct NSkin { - pub name: String, - pub joints: Vec<i32>, - pub inverse_bind_matrices: Vec<Vec<f64>>, - pub skeleton_root: Option<i32>, -} - -impl ToJObject for NSkin { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com.dropbear.asset.model.Skin")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let name = env - .new_string(&self.name) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - let joints = self.joints.as_slice().to_jobject(env)?; - let inverse_bind_matrices = self.inverse_bind_matrices.as_slice().to_jobject(env)?; - let skeleton_root = self.skeleton_root.to_jobject(env)?; - - let args = [ - JValue::Object(&name), - JValue::Object(&joints), - JValue::Object(&inverse_bind_matrices), - JValue::Object(&skeleton_root), - ]; - - let obj = env - .new_object( - &class, - jni_sig!( - "(Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/lang/Integer;)V" - ), - &args, - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } -} - -#[repr(C)] -#[derive(Clone, Debug)] -pub struct NAnimation { - pub name: String, - pub channels: Vec<NAnimationChannel>, - pub duration: f32, -} - -impl ToJObject for NAnimation { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com.dropbear.asset.model.Animation")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let name = env - .new_string(&self.name) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - let channels = self.channels.to_jobject(env)?; - - let args = [ - JValue::Object(&name), - JValue::Object(&channels), - JValue::Double(self.duration as f64), - ]; - - let obj = env - .new_object( - &class, - jni_sig!("(Ljava/lang/String;Ljava/util/List;D)V"), - &args, - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } -} - -#[repr(C)] -#[derive(Clone, Debug)] -pub struct NAnimationChannel { - pub target_node: i32, - pub times: Vec<f64>, - pub values: NChannelValues, - pub interpolation: NAnimationInterpolation, -} - -impl ToJObject for NAnimationChannel { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com.dropbear.asset.model.AnimationChannel")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let times = self.times.as_slice().to_jobject(env)?; - let values = self.values.to_jobject(env)?; - let interpolation = self.interpolation.to_jobject(env)?; - - let args = [ - JValue::Int(self.target_node), - JValue::Object(×), - JValue::Object(&values), - JValue::Object(&interpolation), - ]; - - let obj = env - .new_object( - &class, - jni_sig!("(I[DLcom/dropbear/asset/model/ChannelValues;Lcom/dropbear/asset/model/AnimationInterpolation;)V"), - &args, - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } -} - -#[repr(C)] -#[derive(Clone, Debug)] -#[dropbear_macro::repr_c_enum] -pub enum NAnimationInterpolation { - Linear, - Step, - CubicSpline, -} - -impl ToJObject for NAnimationInterpolation { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com.dropbear.asset.model.AnimationInterpolation")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let field_name = match self { - NAnimationInterpolation::Linear => "LINEAR", - NAnimationInterpolation::Step => "STEP", - NAnimationInterpolation::CubicSpline => "CUBIC_SPLINE", - }; - - let value = env - .get_static_field( - class, - jni::strings::JNIString::from(field_name), - jni_sig!("Lcom/dropbear/asset/model/AnimationInterpolation;"), - ) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - Ok(value) - } -} - -#[repr(C)] -#[derive(Clone, Debug)] -#[dropbear_macro::repr_c_enum] -pub enum NChannelValues { - Translations { values: Vec<NVector3> }, - Rotations { values: Vec<NQuaternion> }, - Scales { values: Vec<NVector3> }, - MorphWeights { values: Vec<Vec<f64>> }, -} - -impl ToJObject for NChannelValues { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - match self { - NChannelValues::Translations { values } => { - let class = env - .load_class(jni_str!( - "com.dropbear.asset.model.ChannelValues$Translations" - )) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let list = values.to_jobject(env)?; - let obj = env - .new_object( - &class, - jni_sig!("(Ljava/util/List;)V"), - &[JValue::Object(&list)], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - Ok(obj) - } - NChannelValues::Rotations { values } => { - let class = env - .load_class(jni_str!("com.dropbear.asset.model.ChannelValues$Rotations")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let list = values.to_jobject(env)?; - let obj = env - .new_object( - &class, - jni_sig!("(Ljava/util/List;)V"), - &[JValue::Object(&list)], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - Ok(obj) - } - NChannelValues::Scales { values } => { - let class = env - .load_class(jni_str!("com.dropbear.asset.model.ChannelValues$Scales")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let list = values.to_jobject(env)?; - let obj = env - .new_object( - &class, - jni_sig!("(Ljava/util/List;)V"), - &[JValue::Object(&list)], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - Ok(obj) - } - NChannelValues::MorphWeights { values } => { - let class = env - .load_class(jni_str!( - "com.dropbear.asset.model.ChannelValues$MorphWeights" - )) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let list = values.to_jobject(env)?; - let obj = env - .new_object( - &class, - jni_sig!("(Ljava/util/List;)V"), - &[JValue::Object(&list)], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - Ok(obj) - } - } - } -} - -fn new_texture<'a>(env: &mut Env<'a>, texture_id: u64) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com.dropbear.asset.Texture")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - env.new_object(&class, jni_sig!("(J)V"), &[JValue::Long(texture_id as i64)]) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) -} - -fn map_vertex(vertex: &ModelVertex) -> NModelVertex { - NModelVertex { - position: NVector3::from(vertex.position), - normal: NVector3::from(vertex.normal), - tangent: NVector4::from(vertex.tangent), - tex_coords0: NVector2::from(vertex.tex_coords0), - tex_coords1: NVector2::from(vertex.tex_coords1), - colour0: NVector4::from(vertex.colour0), - joints0: vertex.joints0.iter().map(|v| *v as i32).collect(), - weights0: NVector4::from(vertex.weights0), - } -} - -fn map_mesh(mesh: &Mesh) -> NMesh { - NMesh { - name: mesh.name.clone(), - num_elements: mesh.num_elements as i32, - material_index: mesh.material as i32, - vertices: mesh.vertices.iter().map(map_vertex).collect(), - } -} - -fn map_material( - _registry: &dropbear_engine::asset::AssetRegistry, - material: &Material, -) -> NMaterial { - NMaterial { - name: material.name.clone(), - diffuse_texture: material.diffuse_texture.id, - normal_texture: material.normal_texture.and_then(|v| Some(v.id)), - tint: NVector4::from(material.base_colour), - emissive_factor: NVector3::from(material.emissive_factor), - metallic_factor: material.metallic_factor, - roughness_factor: material.roughness_factor, - alpha_cutoff: material.alpha_cutoff, - occlusion_strength: material.occlusion_strength, - normal_scale: material.normal_scale, - uv_tiling: NVector2::from(material.uv_tiling), - emissive_texture: material.emissive_texture.and_then(|v| Some(v.id)), - metallic_roughness_texture: material.metallic_roughness_texture.and_then(|v| Some(v.id)), - occlusion_texture: material.occlusion_texture.and_then(|v| Some(v.id)), - } -} - -fn map_node_transform(transform: &NodeTransform) -> NNodeTransform { - NNodeTransform { - translation: NVector3::from(transform.translation), - rotation: NQuaternion::from(transform.rotation), - scale: NVector3::from(transform.scale), - } -} - -fn map_node(node: &Node) -> NNode { - NNode { - name: node.name.clone(), - parent: node.parent.map(|v| v as i32), - children: node.children.iter().map(|v| *v as i32).collect(), - transform: map_node_transform(&node.transform), - } -} - -fn map_skin(skin: &Skin) -> NSkin { - let inverse_bind_matrices = skin - .inverse_bind_matrices - .iter() - .map(|matrix| matrix.to_cols_array().iter().map(|v| *v as f64).collect()) - .collect(); - - NSkin { - name: skin.name.clone(), - joints: skin.joints.iter().map(|v| *v as i32).collect(), - inverse_bind_matrices, - skeleton_root: skin.skeleton_root.map(|v| v as i32), - } -} - -fn map_interpolation(value: &AnimationInterpolation) -> NAnimationInterpolation { - match value { - AnimationInterpolation::Linear => NAnimationInterpolation::Linear, - AnimationInterpolation::Step => NAnimationInterpolation::Step, - AnimationInterpolation::CubicSpline => NAnimationInterpolation::CubicSpline, - } -} - -fn map_channel_values(values: &ChannelValues) -> NChannelValues { - match values { - ChannelValues::Translations(list) => NChannelValues::Translations { - values: list.iter().map(|v| NVector3::from(*v)).collect(), - }, - ChannelValues::Rotations(list) => NChannelValues::Rotations { - values: list.iter().map(|v| NQuaternion::from(*v)).collect(), - }, - ChannelValues::Scales(list) => NChannelValues::Scales { - values: list.iter().map(|v| NVector3::from(*v)).collect(), - }, - ChannelValues::MorphWeights(items) => NChannelValues::MorphWeights { - values: items - .iter() - .map(|weights| weights.iter().map(|v| *v as f64).collect()) - .collect(), - }, - } -} - -fn map_animation_channel(channel: &AnimationChannel) -> NAnimationChannel { - NAnimationChannel { - target_node: channel.target_node as i32, - times: channel.times.iter().map(|v| *v as f64).collect(), - values: map_channel_values(&channel.values), - interpolation: map_interpolation(&channel.interpolation), - } -} - -fn map_animation(animation: &Animation) -> NAnimation { - NAnimation { - name: animation.name.clone(), - channels: animation - .channels - .iter() - .map(map_animation_channel) - .collect(), - duration: animation.duration, - } -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.asset.ModelNative", func = "getLabel"), - c(name = "dropbear_asset_model_get_label") -)] -fn dropbear_asset_model_get_label( - #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, - model_handle: u64, -) -> DropbearNativeResult<String> { - let label = asset - .read() - .get_label_from_model_handle(Handle::new(model_handle)) - .ok_or_else(|| DropbearNativeError::InvalidHandle)?; - Ok(label) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.asset.ModelNative", func = "getMeshes"), - c(name = "dropbear_asset_model_get_meshes") -)] -fn dropbear_asset_model_get_meshes( - #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, - model_handle: u64, -) -> DropbearNativeResult<Vec<NMesh>> { - let reader = asset.read(); - let model = reader - .get_model(Handle::new(model_handle)) - .ok_or(DropbearNativeError::InvalidHandle)?; - - Ok(model.meshes.iter().map(map_mesh).collect()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.asset.ModelNative", func = "getMaterials"), - c(name = "dropbear_asset_model_get_materials") -)] -fn dropbear_asset_model_get_materials( - #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, - model_handle: u64, -) -> DropbearNativeResult<Vec<NMaterial>> { - let reader = asset.read(); - let model = reader - .get_model(Handle::new(model_handle)) - .ok_or(DropbearNativeError::InvalidHandle)?; - - Ok(model - .materials - .iter() - .map(|mat| map_material(&reader, mat)) - .collect()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.asset.ModelNative", func = "getSkins"), - c(name = "dropbear_asset_model_get_skins") -)] -pub fn dropbear_asset_model_get_skins( - #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, - model_handle: u64, -) -> DropbearNativeResult<Vec<NSkin>> { - let reader = asset.read(); - let model = reader - .get_model(Handle::new(model_handle)) - .ok_or(DropbearNativeError::InvalidHandle)?; - - Ok(model.skins.iter().map(map_skin).collect()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.asset.ModelNative", func = "getAnimations"), - c(name = "dropbear_asset_model_get_animations") -)] -pub fn dropbear_asset_model_get_animations( - #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, - model_handle: u64, -) -> DropbearNativeResult<Vec<NAnimation>> { - let reader = asset.read(); - let model = reader - .get_model(Handle::new(model_handle)) - .ok_or(DropbearNativeError::InvalidHandle)?; - - Ok(model.animations.iter().map(map_animation).collect()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.asset.ModelNative", func = "getNodes"), - c(name = "dropbear_asset_model_get_nodes") -)] -pub fn dropbear_asset_model_get_nodes( - #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, - model_handle: u64, -) -> DropbearNativeResult<Vec<NNode>> { - let reader = asset.read(); - let model = reader - .get_model(Handle::new(model_handle)) - .ok_or(DropbearNativeError::InvalidHandle)?; - - Ok(model.nodes.iter().map(map_node).collect()) -} @@ -1,47 +0,0 @@ -use crate::ptr::{AssetRegistryPtr, AssetRegistryUnwrapped}; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; -use dropbear_engine::asset::Handle; - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.asset.TextureNative", func = "getLabel"), - c(name = "dropbear_asset_texture_get_label") -)] -fn get_texture_label( - #[dropbear_macro::define(AssetRegistryPtr)] asset_manager: &AssetRegistryUnwrapped, - texture_handle: u64, -) -> DropbearNativeResult<Option<String>> { - Ok(asset_manager - .read() - .get_label_from_texture_handle(Handle::new(texture_handle))) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.asset.TextureNative", func = "getWidth"), - c(name = "dropbear_asset_texture_get_width") -)] -fn get_texture_width( - #[dropbear_macro::define(AssetRegistryPtr)] asset_manager: &AssetRegistryUnwrapped, - texture_handle: u64, -) -> DropbearNativeResult<u32> { - asset_manager - .read() - .get_texture(Handle::new(texture_handle)) - .map(|v| v.size.width) - .ok_or(DropbearNativeError::AssetNotFound) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.asset.TextureNative", func = "getHeight"), - c(name = "dropbear_asset_texture_get_height") -)] -fn get_texture_height( - #[dropbear_macro::define(AssetRegistryPtr)] asset_manager: &AssetRegistryUnwrapped, - texture_handle: u64, -) -> DropbearNativeResult<u32> { - asset_manager - .read() - .get_texture(Handle::new(texture_handle)) - .map(|v| v.size.height) - .ok_or(DropbearNativeError::AssetNotFound) -} @@ -291,383 +291,3 @@ pub mod shared { } } -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CameraNative", - func = "cameraExistsForEntity" - ), - c -)] -fn exists_for_entity( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<bool> { - Ok(shared::camera_exists_for_entity(world, entity)) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraEye"), - c -)] -fn get_eye( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<NVector3> { - match world.get::<&Camera>(entity) { - Ok(camera) => Ok(camera.eye.into()), - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraEye"), - c -)] -fn set_eye( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - eye: &NVector3, -) -> DropbearNativeResult<()> { - match world.get::<&mut Camera>(entity) { - Ok(mut camera) => { - camera.eye = (*eye).into(); - Ok(()) - } - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CameraNative", - func = "getCameraTarget" - ), - c -)] -fn get_target( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<NVector3> { - match world.get::<&Camera>(entity) { - Ok(camera) => Ok(camera.target.into()), - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CameraNative", - func = "setCameraTarget" - ), - c -)] -fn set_target( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - target: &NVector3, -) -> DropbearNativeResult<()> { - match world.get::<&mut Camera>(entity) { - Ok(mut camera) => { - camera.target = target.into(); - Ok(()) - } - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraUp"), - c -)] -fn get_up( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<NVector3> { - match world.get::<&Camera>(entity) { - Ok(camera) => Ok(camera.up.into()), - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraUp"), - c -)] -fn set_up( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - up: &NVector3, -) -> DropbearNativeResult<()> { - match world.get::<&mut Camera>(entity) { - Ok(mut camera) => { - camera.up = up.into(); - Ok(()) - } - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CameraNative", - func = "getCameraAspect" - ), - c -)] -fn get_aspect( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<f64> { - match world.get::<&Camera>(entity) { - Ok(camera) => Ok(camera.aspect.into()), - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraFovY"), - c -)] -fn get_fovy( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<f64> { - match world.get::<&Camera>(entity) { - Ok(camera) => Ok(camera.settings.fov_y.into()), - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraFovY"), - c -)] -fn set_fovy( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - fovy: f64, -) -> DropbearNativeResult<()> { - match world.get::<&mut Camera>(entity) { - Ok(mut camera) => { - camera.settings.fov_y = fovy.into(); - Ok(()) - } - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CameraNative", - func = "getCameraZNear" - ), - c -)] -fn get_znear( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<f64> { - match world.get::<&Camera>(entity) { - Ok(camera) => Ok(camera.znear.into()), - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CameraNative", - func = "setCameraZNear" - ), - c -)] -fn set_znear( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - znear: f64, -) -> DropbearNativeResult<()> { - match world.get::<&mut Camera>(entity) { - Ok(mut camera) => { - camera.znear = znear.into(); - Ok(()) - } - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraZFar"), - c -)] -fn get_zfar( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<f64> { - match world.get::<&Camera>(entity) { - Ok(camera) => Ok(camera.zfar.into()), - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraZFar"), - c -)] -fn set_zfar( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - zfar: f64, -) -> DropbearNativeResult<()> { - match world.get::<&mut Camera>(entity) { - Ok(mut camera) => { - camera.zfar = zfar.into(); - Ok(()) - } - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraYaw"), - c -)] -fn get_yaw( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<f64> { - match world.get::<&Camera>(entity) { - Ok(camera) => Ok(camera.yaw.into()), - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraYaw"), - c -)] -fn set_yaw( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - yaw: f64, -) -> DropbearNativeResult<()> { - match world.get::<&mut Camera>(entity) { - Ok(mut camera) => { - camera.yaw = yaw.into(); - Ok(()) - } - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CameraNative", - func = "getCameraPitch" - ), - c -)] -fn get_pitch( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<f64> { - match world.get::<&Camera>(entity) { - Ok(camera) => Ok(camera.pitch.into()), - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CameraNative", - func = "setCameraPitch" - ), - c -)] -fn set_pitch( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - pitch: f64, -) -> DropbearNativeResult<()> { - match world.get::<&mut Camera>(entity) { - Ok(mut camera) => { - camera.pitch = pitch.into(); - Ok(()) - } - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CameraNative", - func = "getCameraSpeed" - ), - c -)] -fn get_speed( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<f64> { - match world.get::<&Camera>(entity) { - Ok(camera) => Ok(camera.settings.speed.into()), - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CameraNative", - func = "setCameraSpeed" - ), - c -)] -fn set_speed( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - speed: f64, -) -> DropbearNativeResult<()> { - match world.get::<&mut Camera>(entity) { - Ok(mut camera) => { - camera.settings.speed = speed.into(); - Ok(()) - } - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CameraNative", - func = "getCameraSensitivity" - ), - c -)] -fn get_sensitivity( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<f64> { - match world.get::<&Camera>(entity) { - Ok(camera) => Ok(camera.settings.sensitivity.into()), - Err(e) => Err(e.into()), - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CameraNative", - func = "setCameraSensitivity" - ), - c -)] -fn set_sensitivity( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropebear_macro::entity] entity: hecs::Entity, - sensitivity: f64, -) -> DropbearNativeResult<()> { - match world.get::<&mut Camera>(entity) { - Ok(mut camera) => { - camera.settings.sensitivity = sensitivity.into(); - Ok(()) - } - Err(e) => Err(e.into()), - } -} @@ -1,6 +1,4 @@ use crate::ptr::GraphicsContextPtr; -use crate::scripting::result::DropbearNativeResult; -use crate::types::{NColour, NQuaternion, NVector3}; use dropbear_engine::graphics::SharedGraphicsContext; use glam::{Quat, Vec3}; use dropbear_engine::debug::DebugDraw; @@ -81,245 +79,3 @@ macro_rules! with_debug { }; } -#[dropbear_macro::export( - kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawLine"), - c -)] -fn draw_line( - #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, - start: &NVector3, - end: &NVector3, - colour: &NColour, -) -> DropbearNativeResult<()> { - let a = Vec3::new(start.x as f32, start.y as f32, start.z as f32); - let b = Vec3::new(end.x as f32, end.y as f32, end.z as f32); - with_debug!(graphics, |dd| dd.draw_line(a, b, colour.to_f32_array())); - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawRay"), - c -)] -fn draw_ray( - #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, - origin: &NVector3, - dir: &NVector3, - colour: &NColour, -) -> DropbearNativeResult<()> { - let o = Vec3::new(origin.x as f32, origin.y as f32, origin.z as f32); - let d = Vec3::new(dir.x as f32, dir.y as f32, dir.z as f32); - with_debug!(graphics, |dd| dd.draw_ray(o, d, colour.to_f32_array())); - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawArrow"), - c -)] -fn draw_arrow( - #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, - start: &NVector3, - end: &NVector3, - colour: &NColour, -) -> DropbearNativeResult<()> { - let a = Vec3::new(start.x as f32, start.y as f32, start.z as f32); - let b = Vec3::new(end.x as f32, end.y as f32, end.z as f32); - with_debug!(graphics, |dd| dd.draw_arrow(a, b, colour.to_f32_array())); - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawPoint"), - c -)] -fn draw_point( - #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, - pos: &NVector3, - size: f32, - colour: &NColour, -) -> DropbearNativeResult<()> { - let p = Vec3::new(pos.x as f32, pos.y as f32, pos.z as f32); - with_debug!(graphics, |dd| dd.draw_point(p, size, colour.to_f32_array())); - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawCircle"), - c -)] -fn draw_circle( - #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, - center: &NVector3, - radius: f32, - normal: &NVector3, - colour: &NColour, -) -> DropbearNativeResult<()> { - let c = Vec3::new(center.x as f32, center.y as f32, center.z as f32); - let n = Vec3::new(normal.x as f32, normal.y as f32, normal.z as f32); - with_debug!(graphics, |dd| dd.draw_circle( - c, - radius, - n, - colour.to_f32_array() - )); - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawSphere"), - c -)] -fn draw_sphere( - #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, - center: &NVector3, - radius: f32, - colour: &NColour, -) -> DropbearNativeResult<()> { - let c = Vec3::new(center.x as f32, center.y as f32, center.z as f32); - with_debug!(graphics, |dd| dd.draw_sphere( - c, - radius, - colour.to_f32_array() - )); - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawGlobe"), - c -)] -fn draw_globe( - #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, - center: &NVector3, - radius: f32, - lat_lines: u32, - lon_lines: u32, - colour: &NColour, -) -> DropbearNativeResult<()> { - let c = Vec3::new(center.x as f32, center.y as f32, center.z as f32); - with_debug!(graphics, |dd| dd.draw_globe( - c, - radius, - lat_lines, - lon_lines, - colour.to_f32_array() - )); - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawAabb"), - c -)] -fn draw_aabb( - #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, - min: &NVector3, - max: &NVector3, - colour: &NColour, -) -> DropbearNativeResult<()> { - let mn = Vec3::new(min.x as f32, min.y as f32, min.z as f32); - let mx = Vec3::new(max.x as f32, max.y as f32, max.z as f32); - with_debug!(graphics, |dd| dd.draw_aabb(mn, mx, colour.to_f32_array())); - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawObb"), - c -)] -fn draw_obb( - #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, - center: &NVector3, - half_extents: &NVector3, - rotation: &NQuaternion, - colour: &NColour, -) -> DropbearNativeResult<()> { - let c = Vec3::new(center.x as f32, center.y as f32, center.z as f32); - let he = Vec3::new( - half_extents.x as f32, - half_extents.y as f32, - half_extents.z as f32, - ); - let r = Quat::from_xyzw( - rotation.x as f32, - rotation.y as f32, - rotation.z as f32, - rotation.w as f32, - ); - with_debug!(graphics, |dd| dd.draw_obb(c, he, r, colour.to_f32_array())); - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawCapsule"), - c -)] -fn draw_capsule( - #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, - a: &NVector3, - b: &NVector3, - radius: f32, - colour: &NColour, -) -> DropbearNativeResult<()> { - let va = Vec3::new(a.x as f32, a.y as f32, a.z as f32); - let vb = Vec3::new(b.x as f32, b.y as f32, b.z as f32); - with_debug!(graphics, |dd| dd.draw_capsule( - va, - vb, - radius, - colour.to_f32_array() - )); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.rendering.DebugDrawNative", - func = "drawCylinder" - ), - c -)] -fn draw_cylinder( - #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, - center: &NVector3, - half_height: f32, - radius: f32, - axis: &NVector3, - colour: &NColour, -) -> DropbearNativeResult<()> { - let c = Vec3::new(center.x as f32, center.y as f32, center.z as f32); - let ax = Vec3::new(axis.x as f32, axis.y as f32, axis.z as f32); - with_debug!(graphics, |dd| dd.draw_cylinder( - c, - half_height, - radius, - ax, - colour.to_f32_array() - )); - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawCone"), - c -)] -fn draw_cone( - #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, - apex: &NVector3, - dir: &NVector3, - angle: f32, - length: f32, - colour: &NColour, -) -> DropbearNativeResult<()> { - let a = Vec3::new(apex.x as f32, apex.y as f32, apex.z as f32); - let d = Vec3::new(dir.x as f32, dir.y as f32, dir.z as f32); - with_debug!(graphics, |dd| dd.draw_cone( - a, - d, - angle, - length, - colour.to_f32_array() - )); - Ok(()) -} @@ -1,45 +0,0 @@ -use crate::ptr::{CommandBufferPtr, CommandBufferUnwrapped, WorldPtr}; -use crate::scripting::result::DropbearNativeResult; - -pub mod shared { - use crate::command::CommandBuffer; - use crate::scripting::native::DropbearNativeError; - use crate::scripting::result::DropbearNativeResult; - use crate::states::Label; - use hecs::{Entity, World}; - - pub fn get_entity(world: &World, label: &str) -> DropbearNativeResult<u64> { - for (id, entity_label) in world.query::<(Entity, &Label)>().iter() { - if entity_label.as_str() == label { - return Ok(id.to_bits().get()); - } - } - Err(DropbearNativeError::EntityNotFound) - } - - pub fn quit( - command_buffer: &crossbeam_channel::Sender<CommandBuffer>, - ) -> DropbearNativeResult<()> { - command_buffer - .send(CommandBuffer::Quit) - .map_err(|_| DropbearNativeError::SendError) - } -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.DropbearEngineNative", func = "getEntity",), - c -)] -fn get_entity( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - label: String, -) -> DropbearNativeResult<u64> { - shared::get_entity(&world, &label) -} - -#[dropbear_macro::export(kotlin(class = "com.dropbear.DropbearEngineNative", func = "quit",), c)] -fn quit( - #[dropbear_macro::define(CommandBufferPtr)] command_buffer: &CommandBufferUnwrapped, -) -> DropbearNativeResult<()> { - shared::quit(command_buffer) -} @@ -1,90 +0,0 @@ -use crate::hierarchy::{Children, Parent}; -use crate::ptr::WorldPtr; -use crate::scripting::result::DropbearNativeResult; -use crate::states::Label; - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.LabelNative", - func = "labelExistsForEntity" - ), - c -)] -fn label_exists_for_entity( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<bool> { - Ok(world.get::<&Label>(entity).is_ok()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.EntityRefNative", func = "getEntityLabel"), - c -)] -fn get_label( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<String> { - let label = world.get::<&Label>(entity)?.as_str().to_string(); - Ok(label) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.EntityRefNative", func = "getChildren"), - c -)] -fn get_children( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<Vec<u64>> { - if let Ok(children) = world.query_one::<&Children>(entity).get() { - let entity_bytes = children - .children() - .iter() - .map(|c| c.to_bits().get()) - .collect::<Vec<_>>(); - Ok(entity_bytes) - } else { - // could be that the entity just doesn't have any children, so no need to throw error - Ok(vec![]) - } -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.EntityRefNative", func = "getChildByLabel"), - c -)] -fn get_child_by_label( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - target: String, -) -> DropbearNativeResult<Option<u64>> { - if let Ok(children) = world.query_one::<&Children>(entity).get() { - for child in children.children() { - if let Ok(label) = world.get::<&Label>(*child) { - if label.as_str() == target { - let found = child.clone(); - return Ok(Some(found.to_bits().get())); - } - } else { - // skip if error or no entity - continue; - } - } - Ok(None) - } else { - Ok(None) - } -} - -#[dropbear_macro::export(kotlin(class = "com.dropbear.EntityRefNative", func = "getParent"), c)] -fn get_parent( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<Option<u64>> { - if let Ok(parent) = world.query_one::<&Parent>(entity).get() { - Ok(Some(parent.parent().to_bits().get())) - } else { - Ok(None) - } -} @@ -1,16 +1,9 @@ //! Input management and input state. -pub mod gamepad; pub mod ndc; -use crate::scripting::jni::utils::ToJObject; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; use dropbear_engine::gilrs::{Button, GamepadId}; use glam::Vec2; -use jni::Env; -use jni::objects::JObject; -use jni::sys::jlong; use std::sync::Arc; use std::{ collections::{HashMap, HashSet}, @@ -116,257 +109,3 @@ impl InputState { } } -pub mod shared { - use super::*; - use crate::command::{CommandBuffer, WindowCommand}; - use crate::types::NVector2; - use crossbeam_channel::Sender; - - pub fn map_ordinal_to_mouse_button(ordinal: i32) -> Option<MouseButton> { - match ordinal { - 0 => Some(MouseButton::Left), - 1 => Some(MouseButton::Right), - 2 => Some(MouseButton::Middle), - 3 => Some(MouseButton::Back), - 4 => Some(MouseButton::Forward), - ordinal if ordinal >= 0 => Some(MouseButton::Other(ordinal as u16)), - _ => None, - } - } - - pub fn is_key_pressed(input: &InputState, key_ordinal: i32) -> bool { - if let Some(key) = crate::utils::keycode_from_ordinal(key_ordinal) { - input.is_key_pressed(key) - } else { - false - } - } - - pub fn is_mouse_button_pressed(input: &InputState, btn_ordinal: i32) -> bool { - if let Some(btn) = map_ordinal_to_mouse_button(btn_ordinal) { - input.mouse_button.contains(&btn) - } else { - false - } - } - - pub fn get_mouse_position(input: &InputState) -> NVector2 { - NVector2 { - x: input.mouse_pos.0, - y: input.mouse_pos.1, - } - } - - pub fn get_mouse_delta(input: &InputState) -> NVector2 { - input - .mouse_delta - .map(NVector2::from) - .unwrap_or(NVector2 { x: 0.0, y: 0.0 }) - } - - pub fn get_last_mouse_pos(input: &InputState) -> NVector2 { - input - .last_mouse_pos - .map(NVector2::from) - .unwrap_or(NVector2 { x: 0.0, y: 0.0 }) - } - - pub fn set_cursor_locked( - input: &mut InputState, - sender: &Sender<CommandBuffer>, - locked: bool, - ) -> DropbearNativeResult<()> { - input.is_cursor_locked = locked; - - sender - .send(CommandBuffer::WindowCommand(WindowCommand::WindowGrab( - locked, - ))) - .map_err(|_| DropbearNativeError::SendError)?; - - Ok(()) - } - - pub fn set_cursor_hidden( - input: &mut InputState, - sender: &Sender<CommandBuffer>, - hidden: bool, - ) -> DropbearNativeResult<()> { - input.is_cursor_hidden = hidden; - sender - .send(CommandBuffer::WindowCommand(WindowCommand::HideCursor( - hidden, - ))) - .map_err(|_| DropbearNativeError::SendError)?; - Ok(()) - } - - pub fn get_connected_gamepads(input: &InputState) -> Vec<u64> { - input - .connected_gamepads - .iter() - .map(|id| Into::<usize>::into(*id) as u64) - .collect() - } -} - -#[repr(C)] -struct ConnectedGamepadIds { - ids: Vec<u64>, -} - -impl ToJObject for ConnectedGamepadIds { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let output = env - .new_long_array(self.ids.len()) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - let long_ids: Vec<jlong> = self.ids.iter().map(|&id| id as jlong).collect(); - output - .set_region(env, 0, &long_ids) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - Ok(JObject::from(output)) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.input.InputStateNative", - func = "printInputState" - ), - c -)] -fn print_input_state( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &InputState, -) -> DropbearNativeResult<()> { - println!("Input State: {:?}", input); - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.input.InputStateNative", func = "isKeyPressed"), - c -)] -fn is_key_pressed( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &InputState, - key_code: i32, -) -> DropbearNativeResult<bool> { - Ok(shared::is_key_pressed(input, key_code)) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.input.InputStateNative", - func = "getMousePosition" - ), - c -)] -fn get_mouse_position( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &InputState, -) -> DropbearNativeResult<crate::types::NVector2> { - Ok(shared::get_mouse_position(input)) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.input.InputStateNative", - func = "isMouseButtonPressed" - ), - c -)] -fn is_mouse_button_pressed( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &InputState, - button_ordinal: i32, -) -> DropbearNativeResult<bool> { - Ok(shared::is_mouse_button_pressed(input, button_ordinal)) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.input.InputStateNative", func = "getMouseDelta"), - c -)] -fn get_mouse_delta( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &InputState, -) -> DropbearNativeResult<crate::types::NVector2> { - Ok(shared::get_mouse_delta(input)) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.input.InputStateNative", func = "isCursorLocked"), - c -)] -fn is_cursor_locked( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &InputState, -) -> DropbearNativeResult<bool> { - Ok(input.is_cursor_locked) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.input.InputStateNative", - func = "setCursorLocked" - ), - c -)] -fn set_cursor_locked( - #[dropbear_macro::define(crate::ptr::CommandBufferPtr)] - command_buffer: &crate::ptr::CommandBufferUnwrapped, - #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &mut InputState, - locked: bool, -) -> DropbearNativeResult<()> { - shared::set_cursor_locked(input, command_buffer, locked) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.input.InputStateNative", - func = "getLastMousePos" - ), - c -)] -fn get_last_mouse_pos( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &InputState, -) -> DropbearNativeResult<crate::types::NVector2> { - Ok(shared::get_last_mouse_pos(input)) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.input.InputStateNative", func = "isCursorHidden"), - c -)] -fn is_cursor_hidden( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &InputState, -) -> DropbearNativeResult<bool> { - Ok(input.is_cursor_hidden) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.input.InputStateNative", - func = "setCursorHidden" - ), - c -)] -fn set_cursor_hidden( - #[dropbear_macro::define(crate::ptr::CommandBufferPtr)] - command_buffer: &crate::ptr::CommandBufferUnwrapped, - #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &mut InputState, - hidden: bool, -) -> DropbearNativeResult<()> { - shared::set_cursor_hidden(input, command_buffer, hidden) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.input.InputStateNative", - func = "getConnectedGamepads" - ), - c -)] -fn get_connected_gamepads( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &InputState, -) -> DropbearNativeResult<ConnectedGamepadIds> { - Ok(ConnectedGamepadIds { - ids: shared::get_connected_gamepads(input), - }) -} @@ -1,181 +0,0 @@ -use crate::input::InputState; -use crate::ptr::InputStatePtr; -use crate::scripting::result::DropbearNativeResult; -use crate::types::NVector2; - -pub mod shared { - use crate::input::InputState; - use crate::scripting::jni::utils::{FromJObject, ToJObject}; - use crate::scripting::native::DropbearNativeError; - use crate::scripting::result::DropbearNativeResult; - use crate::types::NVector2; - use jni::objects::{JObject, JValue}; - use jni::{Env, jni_sig, jni_str}; - - fn map_int_to_gamepad_button(ordinal: i32) -> Option<dropbear_engine::gilrs::Button> { - match ordinal { - 0 => Some(dropbear_engine::gilrs::Button::Unknown), - 1 => Some(dropbear_engine::gilrs::Button::South), - 2 => Some(dropbear_engine::gilrs::Button::East), - 3 => Some(dropbear_engine::gilrs::Button::North), - 4 => Some(dropbear_engine::gilrs::Button::West), - 5 => Some(dropbear_engine::gilrs::Button::C), - 6 => Some(dropbear_engine::gilrs::Button::Z), - 7 => Some(dropbear_engine::gilrs::Button::LeftTrigger), - 8 => Some(dropbear_engine::gilrs::Button::RightTrigger), - 9 => Some(dropbear_engine::gilrs::Button::LeftTrigger2), - 10 => Some(dropbear_engine::gilrs::Button::RightTrigger2), - 11 => Some(dropbear_engine::gilrs::Button::Select), - 12 => Some(dropbear_engine::gilrs::Button::Start), - 13 => Some(dropbear_engine::gilrs::Button::Mode), - 14 => Some(dropbear_engine::gilrs::Button::LeftThumb), - 15 => Some(dropbear_engine::gilrs::Button::RightThumb), - 16 => Some(dropbear_engine::gilrs::Button::DPadUp), - 17 => Some(dropbear_engine::gilrs::Button::DPadDown), - 18 => Some(dropbear_engine::gilrs::Button::DPadLeft), - 19 => Some(dropbear_engine::gilrs::Button::DPadRight), - _ => None, - } - } - - pub fn get_gamepad_id( - input: &InputState, - target: usize, - ) -> Option<dropbear_engine::gilrs::GamepadId> { - input - .connected_gamepads - .iter() - .find(|g| usize::from(**g) == target) - .copied() - } - - pub fn is_gamepad_button_pressed( - input: &InputState, - gamepad_id: u64, - button_ordinal: i32, - ) -> bool { - let Some(id) = get_gamepad_id(input, gamepad_id as usize) else { - return false; - }; - - if let Some(btn) = map_int_to_gamepad_button(button_ordinal) { - input.is_button_pressed(id, btn) - } else { - false - } - } - - pub fn get_left_stick(input: &InputState, gamepad_id: u64) -> NVector2 { - let Some(id) = get_gamepad_id(input, gamepad_id as usize) else { - return NVector2 { x: 0.0, y: 0.0 }; - }; - let (x, y) = input.get_left_stick(id); - NVector2 { - x: x as f64, - y: y as f64, - } - } - - pub fn get_right_stick(input: &InputState, gamepad_id: u64) -> NVector2 { - let Some(id) = get_gamepad_id(input, gamepad_id as usize) else { - return NVector2 { x: 0.0, y: 0.0 }; - }; - let (x, y) = input.get_right_stick(id); - NVector2 { - x: x as f64, - y: y as f64, - } - } - - impl ToJObject for NVector2 { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let cls = env - .load_class(jni_str!("com/dropbear/math/Vector2d")) - .map_err(|e| { - eprintln!("Could not find Vector2d class: {:?}", e); - DropbearNativeError::GenericError - })?; - - let obj = env - .new_object( - cls, - jni_sig!((double, double) -> void), - &[JValue::Double(self.x), JValue::Double(self.y)], - ) - .map_err(|e| { - eprintln!("Failed to create Vector2d object: {:?}", e); - DropbearNativeError::GenericError - })?; - - Ok(obj) - } - } - - impl FromJObject for NVector2 { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> - where - Self: Sized, - { - let x_val = env - .get_field(obj, jni_str!("x"), jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let y_val = env - .get_field(obj, jni_str!("y"), jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - Ok(NVector2 { x: x_val, y: y_val }) - } - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.input.GamepadNative", - func = "isGamepadButtonPressed" - ), - c -)] -fn is_button_pressed( - #[dropbear_macro::define(InputStatePtr)] input: &InputState, - gamepad_id: u64, - button_ordinal: i32, -) -> DropbearNativeResult<bool> { - Ok(shared::is_gamepad_button_pressed( - &input, - gamepad_id, - button_ordinal, - )) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.input.GamepadNative", - func = "getLeftStickPosition" - ), - c -)] -fn get_left_stick_position( - #[dropbear_macro::define(InputStatePtr)] input: &InputState, - gamepad_id: u64, -) -> DropbearNativeResult<NVector2> { - Ok(shared::get_left_stick(&input, gamepad_id)) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.input.GamepadNative", - func = "getRightStickPosition" - ), - c -)] -fn get_right_stick_position( - #[dropbear_macro::define(InputStatePtr)] input: &InputState, - gamepad_id: u64, -) -> DropbearNativeResult<NVector2> { - Ok(shared::get_right_stick(&input, gamepad_id)) -} @@ -1,13 +1,10 @@ pub mod animation; -pub mod asset; pub mod billboard; pub mod camera; pub mod command; pub mod component; pub mod config; pub mod debug; -pub mod engine; -pub mod entity; pub mod entity_status; pub mod hierarchy; pub mod input; @@ -3,11 +3,7 @@ use crate::component::{ SerializedComponent, }; use crate::ptr::WorldPtr; -use crate::scripting::jni::utils::{FromJObject, ToJObject}; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; use crate::states::SerializedLight; -use crate::types::{NColour, NVector3}; use dropbear_engine::attenuation::ATTENUATION_PRESETS; use dropbear_engine::entity::{EntityTransform, Transform, inspect_rotation_dquat}; use crate::hierarchy::EntityTransformExt; @@ -16,8 +12,6 @@ use dropbear_engine::lighting::{Light, LightType}; use egui::{CollapsingHeader, ComboBox, DragValue, Ui}; use glam::{DQuat, DVec3, Vec3}; use hecs::{Entity, World}; -use jni::objects::{JObject, JValue}; -use jni::{Env, jni_sig, jni_str}; use std::sync::Arc; const LIGHT_FORWARD_AXIS: DVec3 = DVec3::new(0.0, -1.0, 0.0); @@ -365,617 +359,3 @@ impl InspectableComponent for Light { } } -impl NColour { - fn to_linear_rgb(self) -> DVec3 { - DVec3::new( - self.r as f64 / 255.0, - self.g as f64 / 255.0, - self.b as f64 / 255.0, - ) - } - - fn from_linear_rgb(rgb: DVec3) -> Self { - fn clamp_to_u8(x: f64) -> u8 { - let v = (x * 255.0).round(); - v.clamp(0.0, 255.0) as u8 - } - - Self { - r: clamp_to_u8(rgb.x), - g: clamp_to_u8(rgb.y), - b: clamp_to_u8(rgb.z), - a: 255, - } - } -} - -impl FromJObject for NColour { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { - let class = env - .load_class(jni_str!("com/dropbear/utils/Colour")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - if !env - .is_instance_of(obj, &class) - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? - { - return Err(DropbearNativeError::InvalidArgument); - } - - let mut get_byte = |field| -> DropbearNativeResult<u8> { - let v = env - .get_field(obj, field, jni_sig!(byte)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .b() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - Ok(v as u8) - }; - - Ok(Self { - r: get_byte(jni_str!("r"))?, - g: get_byte(jni_str!("g"))?, - b: get_byte(jni_str!("b"))?, - a: get_byte(jni_str!("a"))?, - }) - } -} - -impl ToJObject for NColour { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com/dropbear/utils/Colour")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let args = [ - JValue::Byte(self.r as i8), - JValue::Byte(self.g as i8), - JValue::Byte(self.b as i8), - JValue::Byte(self.a as i8), - ]; - - env.new_object(&class, jni_sig!((byte, byte, byte, byte) -> void), &args) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) - } -} - -#[repr(C)] -#[derive(Clone, Copy, Debug)] -struct NRange { - start: f32, - end: f32, -} - -impl FromJObject for NRange { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { - let class = env - .load_class(jni_str!("com/dropbear/utils/Range")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - if !env - .is_instance_of(obj, &class) - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? - { - return Err(DropbearNativeError::InvalidArgument); - } - - let start = env - .get_field(obj, jni_str!("start"), jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32; - - let end = env - .get_field(obj, jni_str!("end"), jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32; - - Ok(Self { start, end }) - } -} - -impl ToJObject for NRange { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com/dropbear/utils/Range")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let args = [ - JValue::Double(self.start as f64), - JValue::Double(self.end as f64), - ]; - - env.new_object(&class, jni_sig!((double, double) -> void), &args) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) - } -} - -#[repr(C)] -#[derive(Clone, Copy, Debug)] -struct NAttenuation { - constant: f32, - linear: f32, - quadratic: f32, -} - -impl FromJObject for NAttenuation { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { - let class = env - .load_class(jni_str!("com/dropbear/lighting/Attenuation")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - if !env - .is_instance_of(obj, &class) - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? - { - return Err(DropbearNativeError::InvalidArgument); - } - - let constant = env - .call_method(obj, jni_str!("getConstant"), jni_sig!(() -> float), &[]) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .f() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let linear = env - .call_method(obj, jni_str!("getLinear"), jni_sig!(() -> float), &[]) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .f() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let quadratic = env - .call_method(obj, jni_str!("getQuadratic"), jni_sig!(() -> float), &[]) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .f() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - Ok(Self { - constant, - linear, - quadratic, - }) - } -} - -impl ToJObject for NAttenuation { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com/dropbear/lighting/Attenuation")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let args = [ - JValue::Float(self.constant), - JValue::Float(self.linear), - JValue::Float(self.quadratic), - ]; - - env.new_object(&class, jni_sig!((float, float, float) -> void), &args) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) - } -} - -pub mod shared { - use hecs::{Entity, World}; - - pub fn light_exists_for_entity(world: &World, entity: Entity) -> bool { - world - .get::<&dropbear_engine::lighting::LightComponent>(entity) - .is_ok() - } -} - -fn get_transform(world: &World, entity: Entity) -> DropbearNativeResult<Transform> { - if let Ok(et) = world.get::<&EntityTransform>(entity) { - Ok(et.sync()) - } else if let Ok(t) = world.get::<&Transform>(entity) { - Ok(*t) - } else { - Err(DropbearNativeError::MissingComponent) - } -} - -fn set_transform_position( - world: &mut World, - entity: Entity, - position: DVec3, -) -> DropbearNativeResult<()> { - if let Ok(mut et) = world.get::<&mut EntityTransform>(entity) { - et.local_mut().position = position; - Ok(()) - } else if let Ok(mut t) = world.get::<&mut Transform>(entity) { - t.position = position; - Ok(()) - } else { - Err(DropbearNativeError::MissingComponent) - } -} - -fn set_transform_rotation( - world: &mut World, - entity: Entity, - rotation: DQuat, -) -> DropbearNativeResult<()> { - if let Ok(mut et) = world.get::<&mut EntityTransform>(entity) { - et.local_mut().rotation = rotation; - Ok(()) - } else if let Ok(mut t) = world.get::<&mut Transform>(entity) { - t.rotation = rotation; - Ok(()) - } else { - Err(DropbearNativeError::MissingComponent) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.lighting.LightNative", - func = "lightExistsForEntity" - ), - c -)] -fn light_exists_for_entity( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<bool> { - Ok(shared::light_exists_for_entity(world, entity)) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "getPosition"), - c -)] -fn get_position( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<NVector3> { - let transform = get_transform(world, entity)?; - Ok(NVector3::from(transform.position)) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "setPosition"), - c -)] -fn set_position( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - position: &NVector3, -) -> DropbearNativeResult<()> { - set_transform_position(world, entity, (*position).into()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "getDirection"), - c -)] -fn get_direction( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<NVector3> { - let transform = get_transform(world, entity)?; - let forward = LIGHT_FORWARD_AXIS; - let dir = (transform.rotation * forward).normalize_or_zero(); - Ok(NVector3::from(dir)) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "setDirection"), - c -)] -fn set_direction( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - direction: &NVector3, -) -> DropbearNativeResult<()> { - let dir: DVec3 = (*direction).into(); - let desired = dir.normalize_or_zero(); - if desired.length_squared() < 1e-12 { - return Err(DropbearNativeError::InvalidArgument); - } - - let forward = LIGHT_FORWARD_AXIS; - let rotation = DQuat::from_rotation_arc(forward, desired); - set_transform_rotation(world, entity, rotation) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "getColour"), - c -)] -fn get_colour( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<NColour> { - let light = world - .get::<&Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - Ok(NColour::from_linear_rgb(light.component.colour)) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "setColour"), - c -)] -fn set_colour( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - colour: &NColour, -) -> DropbearNativeResult<()> { - let mut light = world - .get::<&mut Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - light.component.colour = (*colour).to_linear_rgb(); - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "getLightType"), - c -)] -fn get_light_type( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<i32> { - let light = world - .get::<&Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - Ok(light.component.light_type as i32) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "setLightType"), - c -)] -fn set_light_type( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - light_type: i32, -) -> DropbearNativeResult<()> { - let mut light = world - .get::<&mut Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - - light.component.light_type = match light_type { - 0 => LightType::Directional, - 1 => LightType::Point, - 2 => LightType::Spot, - _ => return Err(DropbearNativeError::InvalidArgument), - }; - - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "getIntensity"), - c -)] -fn get_intensity( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<f64> { - let light = world - .get::<&Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - Ok(light.component.intensity as f64) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "setIntensity"), - c -)] -fn set_intensity( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - intensity: f64, -) -> DropbearNativeResult<()> { - let mut light = world - .get::<&mut Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - light.component.intensity = intensity as f32; - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "getAttenuation"), - c -)] -fn get_attenuation( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<NAttenuation> { - let light = world - .get::<&Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - - Ok(NAttenuation { - constant: light.component.attenuation.constant, - linear: light.component.attenuation.linear, - quadratic: light.component.attenuation.quadratic, - }) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "setAttenuation"), - c -)] -fn set_attenuation( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - attenuation: &NAttenuation, -) -> DropbearNativeResult<()> { - let mut light = world - .get::<&mut Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - - light.component.attenuation.constant = attenuation.constant; - light.component.attenuation.linear = attenuation.linear; - light.component.attenuation.quadratic = attenuation.quadratic; - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "getEnabled"), - c -)] -fn get_enabled( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<bool> { - let light = world - .get::<&Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - Ok(light.component.enabled) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "setEnabled"), - c -)] -fn set_enabled( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - enabled: bool, -) -> DropbearNativeResult<()> { - let mut light = world - .get::<&mut Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - light.component.enabled = enabled; - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "getCutoffAngle"), - c -)] -fn get_cutoff_angle( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<f64> { - let light = world - .get::<&Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - Ok(light.component.cutoff_angle as f64) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "setCutoffAngle"), - c -)] -fn set_cutoff_angle( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - cutoff_angle: f64, -) -> DropbearNativeResult<()> { - let mut light = world - .get::<&mut Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - light.component.cutoff_angle = cutoff_angle as f32; - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.lighting.LightNative", - func = "getOuterCutoffAngle" - ), - c -)] -fn get_outer_cutoff_angle( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<f64> { - let light = world - .get::<&Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - Ok(light.component.outer_cutoff_angle as f64) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.lighting.LightNative", - func = "setOuterCutoffAngle" - ), - c -)] -fn set_outer_cutoff_angle( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - outer_cutoff_angle: f64, -) -> DropbearNativeResult<()> { - let mut light = world - .get::<&mut Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - light.component.outer_cutoff_angle = outer_cutoff_angle as f32; - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "getCastsShadows"), - c -)] -fn get_casts_shadows( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<bool> { - let light = world - .get::<&Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - Ok(light.component.cast_shadows) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "setCastsShadows"), - c -)] -fn set_casts_shadows( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - casts_shadows: bool, -) -> DropbearNativeResult<()> { - let mut light = world - .get::<&mut Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - light.component.cast_shadows = casts_shadows; - Ok(()) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "getDepth"), - c -)] -fn get_depth( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<NRange> { - let light = world - .get::<&Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - - Ok(NRange { - start: light.component.depth.start, - end: light.component.depth.end, - }) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "setDepth"), - c -)] -fn set_depth( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - depth: &NRange, -) -> DropbearNativeResult<()> { - if !(depth.start.is_finite() && depth.end.is_finite()) { - return Err(DropbearNativeError::InvalidArgument); - } - if depth.end <= depth.start { - return Err(DropbearNativeError::InvalidArgument); - } - - let mut light = world - .get::<&mut Light>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - light.component.depth = depth.start..depth.end; - Ok(()) -} @@ -1,241 +1 @@ -pub mod shared { - use std::sync::Arc; - use dropbear_engine::asset::AssetRegistry; - use dropbear_engine::entity::MeshRenderer; - use dropbear_engine::model::Model; - use hecs::{Entity, World}; - - pub fn mesh_renderer_exists_for_entity(world: &World, entity: Entity) -> bool { - world.get::<&MeshRenderer>(entity).is_ok() - } - - fn matches_material_label(material: &dropbear_engine::model::Material, target: &str) -> bool { - material.texture_tag.as_deref() == Some(target) || material.name == target - } - - pub fn resolve_target_material_index(model: &Model, target_identifier: &str) -> Option<usize> { - model - .materials - .iter() - .position(|mat| matches_material_label(mat, target_identifier)) - } - - pub fn resolve_target_material_name(model: &Model, target_identifier: &str) -> Option<String> { - model - .materials - .iter() - .find(|mat| matches_material_label(mat, target_identifier)) - .map(|mat| mat.name.clone()) - } - - pub fn model_for_renderer( - registry: &AssetRegistry, - renderer: &MeshRenderer, - ) -> Option<Arc<Model>> { - registry.get_model(renderer.model()) - } -} - -use crate::ptr::{AssetRegistryPtr, AssetRegistryUnwrapped, GraphicsContextPtr, WorldPtr}; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; -use dropbear_engine::asset::Handle; -use dropbear_engine::entity::MeshRenderer; -use dropbear_engine::graphics::SharedGraphicsContext; -use dropbear_engine::texture::Texture; -use std::collections::HashSet; - -#[dropbear_macro::export(kotlin( - class = "com.dropbear.components.MeshRendererNative", - func = "meshRendererExistsForEntity" -))] -fn mesh_renderer_exists_for_entity( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<bool> { - Ok(shared::mesh_renderer_exists_for_entity(world, entity)) -} - -#[dropbear_macro::export(kotlin( - class = "com.dropbear.components.MeshRendererNative", - func = "getModel" -))] -fn get_model( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<u64> { - if let Ok(mesh) = world.get::<&MeshRenderer>(entity) { - Ok(mesh.model().id) - } else { - Err(DropbearNativeError::NoSuchComponent) - } -} - -#[dropbear_macro::export(kotlin( - class = "com.dropbear.components.MeshRendererNative", - func = "setModel" -))] -fn set_model( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, - #[dropbear_macro::entity] entity: hecs::Entity, - model_handle: u64, -) -> DropbearNativeResult<()> { - let handle = Handle::new(model_handle); - if asset.read().get_model(handle).is_none() { - return Err(DropbearNativeError::InvalidHandle); - } - - if let Ok(mut mesh) = world.get::<&mut MeshRenderer>(entity) { - mesh.set_model(handle); - Ok(()) - } else { - Err(DropbearNativeError::NoSuchComponent) - } -} - -#[dropbear_macro::export(kotlin( - class = "com.dropbear.components.MeshRendererNative", - func = "getAllTextureIds" -))] -fn get_all_texture_ids( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<Vec<u64>> { - let reader = asset.read(); - let renderer = world - .get::<&MeshRenderer>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - let model = - shared::model_for_renderer(&reader, &renderer).ok_or(DropbearNativeError::AssetNotFound)?; - - let mut ids = HashSet::new(); - let mut push_handle = |handle: &Handle<Texture>| { - ids.insert(handle.id); - }; - - for material in &model.materials { - push_handle(&material.diffuse_texture); - if let Some(tex) = &material.normal_texture { - push_handle(tex); - } - if let Some(tex) = &material.emissive_texture { - push_handle(tex); - } - if let Some(tex) = &material.metallic_roughness_texture { - push_handle(tex); - } - if let Some(tex) = &material.occlusion_texture { - push_handle(tex); - } - } - - Ok(ids.into_iter().collect()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.MeshRendererNative", - func = "getTexture" - ), - c -)] -fn get_texture( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, - #[dropbear_macro::entity] entity: hecs::Entity, - material_name: String, -) -> DropbearNativeResult<Option<u64>> { - let reader = asset.read(); - let renderer = world - .get::<&MeshRenderer>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - let model = - shared::model_for_renderer(&reader, &renderer).ok_or(DropbearNativeError::AssetNotFound)?; - let idx = match shared::resolve_target_material_index(&model, &material_name) { - Some(value) => value, - None => return Ok(None), - }; - let material = model - .materials - .get(idx) - .ok_or(DropbearNativeError::InvalidArgument)?; - - Ok(Some(material.diffuse_texture.id)) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.MeshRendererNative", - func = "setTextureOverride" - ), - c -)] -fn set_texture_override( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, - #[dropbear_macro::entity] entity: hecs::Entity, - material_name: String, - texture_handle: u64, -) -> DropbearNativeResult<()> { - let reader = asset.read(); - let renderer = world - .get::<&MeshRenderer>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - let model = - shared::model_for_renderer(&reader, &renderer).ok_or(DropbearNativeError::AssetNotFound)?; - let _ = shared::resolve_target_material_name(&model, &material_name) - .ok_or(DropbearNativeError::InvalidArgument)?; - - let handle = Handle::<Texture>::new(texture_handle); - if reader.get_texture(handle).is_none() { - return Err(DropbearNativeError::InvalidHandle); - } - - let mut renderer = world - .get::<&mut MeshRenderer>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - - if let Some(v) = renderer.material_snapshot.get_mut(&material_name) { - v.diffuse_texture = handle; - } else { - return Err(DropbearNativeError::InvalidArgument); - } - - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.MeshRendererNative", - func = "setMaterialTint" - ), - c -)] -fn set_material_tint( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, - #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, - #[dropbear_macro::entity] entity: hecs::Entity, - material_name: String, - r: f32, - g: f32, - b: f32, - a: f32, -) -> DropbearNativeResult<()> { - let _ = asset; - let mut renderer = world - .get::<&mut MeshRenderer>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - - let material = renderer - .material_snapshot - .get_mut(&material_name) - .ok_or(DropbearNativeError::InvalidArgument)?; - - material.base_colour = [r, g, b, a]; - material.sync_uniform(graphics); - Ok(()) -} @@ -262,346 +262,3 @@ impl Default for PhysicsState { Self::new() } } - -pub mod shared { - use crate::physics::PhysicsState; - use crate::types::NCollider; - use crate::types::NVector3; - use hecs::Entity; - use rapier3d::prelude::ColliderHandle; - - pub fn get_gravity(physics: &PhysicsState) -> NVector3 { - NVector3::from(physics.gravity) - } - - pub fn set_gravity(physics: &mut PhysicsState, new: NVector3) { - physics.gravity = new.to_float_array(); - } - - fn collider_handle_from_ffi(collider: &NCollider) -> ColliderHandle { - ColliderHandle::from_raw_parts(collider.index.index, collider.index.generation) - } - - pub fn overlapping( - physics: &PhysicsState, - collider1: &NCollider, - collider2: &NCollider, - ) -> bool { - let h1 = collider_handle_from_ffi(collider1); - let h2 = collider_handle_from_ffi(collider2); - - if physics.colliders.get(h1).is_none() || physics.colliders.get(h2).is_none() { - return false; - } - - physics - .narrow_phase - .intersection_pair(h1, h2) - .unwrap_or(false) - } - - pub fn triggering( - physics: &PhysicsState, - collider1: &NCollider, - collider2: &NCollider, - ) -> bool { - let h1 = collider_handle_from_ffi(collider1); - let h2 = collider_handle_from_ffi(collider2); - - let is_sensor_1 = physics - .colliders - .get(h1) - .map(|c| c.is_sensor()) - .unwrap_or(false); - let is_sensor_2 = physics - .colliders - .get(h2) - .map(|c| c.is_sensor()) - .unwrap_or(false); - - (is_sensor_1 || is_sensor_2) && overlapping(physics, collider1, collider2) - } - - pub fn touching(physics: &PhysicsState, entity1: Entity, entity2: Entity) -> bool { - let Some(label1) = physics.entity_label_map.get(&entity1) else { - return false; - }; - let Some(label2) = physics.entity_label_map.get(&entity2) else { - return false; - }; - - let Some(handles1) = physics.colliders_entity_map.get(label1) else { - return false; - }; - let Some(handles2) = physics.colliders_entity_map.get(label2) else { - return false; - }; - - for (_, h1) in handles1 { - for (_, h2) in handles2 { - if let Some(pair) = physics.narrow_phase.contact_pair(*h1, *h2) { - if pair.has_any_active_contact() { - return true; - } - } - } - } - - false - } -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.PhysicsNative", func = "getGravity"), - c -)] -fn get_gravity( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, -) -> DropbearNativeResult<NVector3> { - Ok(shared::get_gravity(physics)) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.PhysicsNative", func = "setGravity"), - c -)] -fn set_gravity( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - gravity: &NVector3, -) -> DropbearNativeResult<()> { - Ok(shared::set_gravity(physics, *gravity)) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.PhysicsNative", func = "raycast"), - c -)] -fn raycast( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - origin: &NVector3, - dir: &NVector3, - time_of_impact: f64, - solid: bool, -) -> DropbearNativeResult<Option<RayHit>> { - let qp = physics.broad_phase.as_query_pipeline( - &DefaultQueryDispatcher, - &physics.bodies, - &physics.colliders, - QueryFilter::new(), - ); - - let ray = Ray::new( - point![origin.x as f32, origin.y as f32, origin.z as f32].into(), - vector![dir.x as f32, dir.y as f32, dir.z as f32].into(), - ); - - if let Some((hit, distance)) = qp.cast_ray(&ray, time_of_impact as f32, solid) { - let raw = hit.0; - - let mut found = None; - - for (l, colliders) in physics.colliders_entity_map.iter() { - for (_, c) in colliders { - if c.0 == hit.0 { - found = Some((l, c.0)); - } - } - } - - if let Some((label, _)) = found { - let entity = physics.entity_label_map.iter().find(|(_, l)| *l == label); - if let Some((e, _)) = entity { - let rayhit = RayHit { - collider: crate::types::NCollider { - index: IndexNative::from(raw), - entity_id: e.to_bits().get(), - id: raw.into_raw_parts().0, - }, - distance: distance as f64, - }; - - Ok(Some(rayhit)) - } else { - Ok(None) - } - } else { - eprintln!("Unknown collider, still returning value without entity_id"); - - let rayhit = RayHit { - collider: crate::types::NCollider { - index: IndexNative::from(raw), - entity_id: Entity::DANGLING.to_bits().get(), - id: raw.into_raw_parts().0, - }, - distance: distance as f64, - }; - Ok(Some(rayhit)) - } - } else { - Ok(None) - } -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.PhysicsNative", func = "shapeCast"), - c -)] -fn shape_cast( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - origin: &NVector3, - direction: &NVector3, - shape: &collider::ColliderShape, - time_of_impact: f64, - solid: bool, -) -> DropbearNativeResult<Option<NShapeCastHit>> { - let qp = physics.broad_phase.as_query_pipeline( - &DefaultQueryDispatcher, - &physics.bodies, - &physics.colliders, - QueryFilter::new(), - ); - - let dir_len = - ((direction.x * direction.x) + (direction.y * direction.y) + (direction.z * direction.z)) - .sqrt(); - if dir_len <= f64::EPSILON { - return Ok(None); - } - - let dir_unit = NVector3 { - x: direction.x / dir_len, - y: direction.y / dir_len, - z: direction.z / dir_len, - }; - - let cast_shape = { - match shape { - crate::physics::collider::ColliderShape::Box { half_extents } => { - rapier3d::geometry::SharedShape::cuboid( - half_extents.x as f32, - half_extents.y as f32, - half_extents.z as f32, - ) - } - crate::physics::collider::ColliderShape::Sphere { radius } => { - rapier3d::geometry::SharedShape::ball(*radius) - } - crate::physics::collider::ColliderShape::Capsule { - half_height, - radius, - } => rapier3d::geometry::SharedShape::capsule_y(*half_height, *radius), - crate::physics::collider::ColliderShape::Cylinder { - half_height, - radius, - } => rapier3d::geometry::SharedShape::cylinder(*half_height, *radius), - crate::physics::collider::ColliderShape::Cone { - half_height, - radius, - } => rapier3d::geometry::SharedShape::cone(*half_height, *radius), - } - }; - let iso: Pose3 = - nalgebra::Isometry3::translation(origin.x as f32, origin.y as f32, origin.z as f32).into(); - let vel: Vec3 = vector![dir_unit.x as f32, dir_unit.y as f32, dir_unit.z as f32].into(); - - let options = ShapeCastOptions { - max_time_of_impact: time_of_impact as f32, - target_distance: 0.0, - stop_at_penetration: solid, - compute_impact_geometry_on_penetration: true, - }; - - let Some((hit_handle, toi)) = qp.cast_shape(&iso, vel, cast_shape.as_ref(), options) else { - return Ok(None); - }; - - let collider = collider_ffi_from_handle(&physics, hit_handle); - - let hit = NShapeCastHit { - collider, - distance: toi.time_of_impact as f64, - witness1: NVector3::from([toi.witness1.x, toi.witness1.y, toi.witness1.z]), - witness2: NVector3::from([toi.witness2.x, toi.witness2.y, toi.witness2.z]), - normal1: NVector3::from([toi.normal1.x, toi.normal1.y, toi.normal1.z]), - normal2: NVector3::from([toi.normal2.x, toi.normal2.y, toi.normal2.z]), - status: toi.status.into(), - }; - - Ok(Some(hit)) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.PhysicsNative", func = "isOverlapping"), - c -)] -fn is_overlapping( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - collider1: &NCollider, - collider2: &NCollider, -) -> DropbearNativeResult<bool> { - Ok(shared::overlapping(physics, collider1, collider2)) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.PhysicsNative", func = "isTriggering"), - c -)] -fn is_triggering( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - collider1: &NCollider, - collider2: &NCollider, -) -> DropbearNativeResult<bool> { - Ok(shared::triggering(physics, collider1, collider2)) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.PhysicsNative", func = "isTouching"), - c -)] -fn is_touching( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - #[dropbear_macro::entity] entity1: Entity, - #[dropbear_macro::entity] entity2: Entity, -) -> DropbearNativeResult<bool> { - Ok(shared::touching(physics, entity1, entity2)) -} - -fn collider_ffi_from_handle( - physics: &PhysicsState, - handle: rapier3d::prelude::ColliderHandle, -) -> NCollider { - let (idx, generation) = handle.into_raw_parts(); - - let mut found_label = None; - for (label, colliders) in physics.colliders_entity_map.iter() { - for (_, c) in colliders { - if c.0 == handle.0 { - found_label = Some(label); - break; - } - } - if found_label.is_some() { - break; - } - } - - let entity_id = if let Some(label) = found_label { - physics - .entity_label_map - .iter() - .find(|(_, l)| *l == label) - .map(|(e, _)| e.to_bits().get()) - .unwrap_or(Entity::DANGLING.to_bits().get()) - } else { - Entity::DANGLING.to_bits().get() - }; - - NCollider { - index: IndexNative { - index: idx, - generation, - }, - entity_id, - id: idx, - } -} @@ -13,28 +13,18 @@ //! - `col-` or `-col` //! - `-colonly` (invisible collision mesh) -pub mod collider_group; - use crate::component::{ Component, ComponentDescriptor, DisabilityFlags, InspectableComponent, SerializedComponent, }; use crate::physics::PhysicsState; -use crate::physics::collider::shared::{get_collider, get_collider_mut}; -use crate::ptr::PhysicsStatePtr; -use crate::scripting::jni::utils::{FromJObject, ToJObject}; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; use crate::states::Label; -use crate::types::{NCollider, NVector3}; -use ::jni::objects::{JObject, JValue}; -use ::jni::{Env, jni_sig, jni_str}; use dropbear_engine::asset::ASSET_REGISTRY; use dropbear_engine::entity::{MeshRenderer, inspect_rotation_dquat}; use dropbear_engine::graphics::SharedGraphicsContext; use dropbear_engine::wgpu::util::{BufferInitDescriptor, DeviceExt}; use dropbear_engine::wgpu::{Buffer, BufferUsages}; use egui::{CollapsingHeader, ComboBox, Ui}; -use glam::DQuat; +use glam::{DQuat, Vec3}; use hecs::{Entity, World}; use rapier3d::prelude::ColliderBuilder; use rapier3d::prelude::{Rotation, SharedShape, TypedShape, Vector}; @@ -480,7 +470,7 @@ impl From<&ColliderShape> for ColliderShapeKey { #[dropbear_macro::repr_c_enum] pub enum ColliderShape { /// Box shape with half-extents (half-width, half-height, half-depth). - Box { half_extents: NVector3 }, + Box { half_extents: Vec3 }, /// Sphere shape with radius. Sphere { radius: f32 }, @@ -498,247 +488,8 @@ pub enum ColliderShape { impl Default for ColliderShape { fn default() -> Self { ColliderShape::Box { - half_extents: NVector3::from([0.5, 0.5, 0.5]), - } - } -} - -impl ToJObject for ColliderShape { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - match self { - ColliderShape::Box { half_extents } => { - let vec_cls = env - .load_class(jni_str!("com/dropbear/math/Vector3d")) - .map_err(|e| { - eprintln!("[JNI Error] Vector3d class not found: {:?}", e); - DropbearNativeError::JNIClassNotFound - })?; - - let vec_obj = env - .new_object( - &vec_cls, - jni_sig!("(DDD)V"), - &[ - JValue::Double(half_extents.x), - JValue::Double(half_extents.y), - JValue::Double(half_extents.z), - ], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - let cls = env - .load_class(jni_str!("com/dropbear/physics/ColliderShape$Box")) - .map_err(|e| { - eprintln!("[JNI Error] ColliderShape$Box class not found: {:?}", e); - DropbearNativeError::JNIClassNotFound - })?; - - let obj = env - .new_object( - &cls, - jni_sig!("(Lcom/dropbear/math/Vector3d;)V"), - &[JValue::Object(&vec_obj)], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } - ColliderShape::Sphere { radius } => { - let cls = env - .load_class(jni_str!("com/dropbear/physics/ColliderShape$Sphere")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let obj = env - .new_object(&cls, jni_sig!("(F)V"), &[JValue::Float(*radius)]) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } - ColliderShape::Capsule { - half_height, - radius, - } => { - let cls = env - .load_class(jni_str!("com/dropbear/physics/ColliderShape$Capsule")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let obj = env - .new_object( - &cls, - jni_sig!("(FF)V"), - &[JValue::Float(*half_height), JValue::Float(*radius)], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } - ColliderShape::Cylinder { - half_height, - radius, - } => { - let cls = env - .load_class(jni_str!("com/dropbear/physics/ColliderShape$Cylinder")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - // let ctor = env.get_method_id(&cls, "<init>", "(FF)V") - // .map_err(|_| DropbearNativeError::JNIMethodNotFound)?; - - let obj = env - .new_object( - &cls, - jni_sig!("(FF)V"), - &[JValue::Float(*half_height), JValue::Float(*radius)], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } - ColliderShape::Cone { - half_height, - radius, - } => { - let cls = env - .load_class(jni_str!("com/dropbear/physics/ColliderShape$Cone")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let obj = env - .new_object( - &cls, - jni_sig!("(FF)V"), - &[JValue::Float(*half_height), JValue::Float(*radius)], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } - } - } -} - -impl FromJObject for ColliderShape { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> - where - Self: Sized, - { - let is_instance = |env: &mut Env, - obj: &JObject, - class_name: &jni::strings::JNIStr| - -> bool { env.is_instance_of(obj, class_name).unwrap_or(false) }; - - if is_instance(env, obj, jni_str!("com/dropbear/physics/ColliderShape$Box")) { - let vec_obj_val = env - .get_field( - obj, - jni_str!("halfExtents"), - jni_sig!("Lcom/dropbear/math/Vector3d;"), - ) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - let vec_obj = vec_obj_val - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let x = env - .get_field(&vec_obj, jni_str!("x"), jni_sig!("D")) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .unwrap_or(0.0); - let y = env - .get_field(&vec_obj, jni_str!("y"), jni_sig!("D")) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .unwrap_or(0.0); - let z = env - .get_field(&vec_obj, jni_str!("z"), jni_sig!("D")) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .unwrap_or(0.0); - - return Ok(ColliderShape::Box { - half_extents: NVector3::from([x as f32, y as f32, z as f32]), - }); - } - - if is_instance( - env, - obj, - jni_str!("com/dropbear/physics/ColliderShape$Sphere"), - ) { - let radius = env - .get_field(obj, jni_str!("radius"), jni_sig!("F")) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .f() - .unwrap_or(0.0); - - return Ok(ColliderShape::Sphere { radius }); - } - - if is_instance( - env, - obj, - jni_str!("com/dropbear/physics/ColliderShape$Capsule"), - ) { - let hh = env - .get_field(obj, jni_str!("halfHeight"), jni_sig!("F")) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .f() - .unwrap_or(0.0); - let r = env - .get_field(obj, jni_str!("radius"), jni_sig!("F")) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .f() - .unwrap_or(0.0); - - return Ok(ColliderShape::Capsule { - half_height: hh, - radius: r, - }); - } - - if is_instance( - env, - obj, - jni_str!("com/dropbear/physics/ColliderShape$Cylinder"), - ) { - let hh = env - .get_field(obj, jni_str!("halfHeight"), jni_sig!("F")) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .f() - .unwrap_or(0.0); - let r = env - .get_field(obj, jni_str!("radius"), jni_sig!("F")) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .f() - .unwrap_or(0.0); - - return Ok(ColliderShape::Cylinder { - half_height: hh, - radius: r, - }); + half_extents: Vec3::from([0.5, 0.5, 0.5]), } - - if is_instance( - env, - obj, - jni_str!("com/dropbear/physics/ColliderShape$Cone"), - ) { - let hh = env - .get_field(obj, jni_str!("halfHeight"), jni_sig!("F")) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .f() - .unwrap_or(0.0); - let r = env - .get_field(obj, jni_str!("radius"), jni_sig!("F")) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .f() - .unwrap_or(0.0); - - return Ok(ColliderShape::Cone { - half_height: hh, - radius: r, - }); - } - - Err(DropbearNativeError::GenericError) } } @@ -768,7 +519,7 @@ impl Collider { pub fn box_collider(half_extents: [f32; 3]) -> Self { Self { shape: ColliderShape::Box { - half_extents: NVector3::from(half_extents), + half_extents: Vec3::from(half_extents), }, ..Self::new() } @@ -885,550 +636,4 @@ impl Collider { // ColliderShape::Compound { .. } => "Compound", } } -} - -pub struct WireframeGeometry { - pub vertex_buffer: Buffer, - pub index_buffer: Buffer, - pub index_count: u32, -} - -impl WireframeGeometry { - pub fn box_wireframe(graphics: Arc<SharedGraphicsContext>, half_extents: [f32; 3]) -> Self { - let [hx, hy, hz] = half_extents; - - let vertices: Vec<[f32; 3]> = vec![ - [-hx, -hy, -hz], - [-hx, -hy, hz], - [-hx, hy, -hz], - [-hx, hy, hz], - [hx, -hy, -hz], - [hx, -hy, hz], - [hx, hy, -hz], - [hx, hy, hz], - ]; - - let indices: Vec<u16> = vec![ - 0, 1, 0, 2, 0, 4, // from corner 0 - 1, 3, 1, 5, // from corner 1 - 2, 3, 2, 6, // from corner 2 - 3, 7, // from corner 3 - 4, 5, 4, 6, // from corner 4 - 5, 7, // from corner 5 - 6, 7, // from corner 6 - ]; - - let vertex_buffer = graphics.device.create_buffer_init(&BufferInitDescriptor { - label: Some("box wireframe vertex buffer"), - contents: bytemuck::cast_slice(&vertices), - usage: BufferUsages::VERTEX, - }); - - let index_buffer = graphics.device.create_buffer_init(&BufferInitDescriptor { - label: Some("box wireframe index buffer"), - contents: bytemuck::cast_slice(&indices), - usage: BufferUsages::INDEX, - }); - - Self { - vertex_buffer, - index_buffer, - index_count: indices.len() as u32, - } - } - - pub fn sphere_wireframe( - graphics: Arc<SharedGraphicsContext>, - radius: f32, - lat_segments: u32, - lon_segments: u32, - ) -> Self { - let mut vertices = Vec::new(); - let mut indices = Vec::new(); - - for lat in 0..=lat_segments { - let theta = std::f32::consts::PI * lat as f32 / lat_segments as f32; - let sin_theta = theta.sin(); - let cos_theta = theta.cos(); - - for lon in 0..=lon_segments { - let phi = 2.0 * std::f32::consts::PI * lon as f32 / lon_segments as f32; - let sin_phi = phi.sin(); - let cos_phi = phi.cos(); - - let x = radius * sin_theta * cos_phi; - let y = radius * cos_theta; - let z = radius * sin_theta * sin_phi; - - vertices.push([x, y, z]); - } - } - - for lat in 0..lat_segments { - for lon in 0..lon_segments { - let first = (lat * (lon_segments + 1) + lon) as u16; - let second = first + lon_segments as u16 + 1; - - indices.push(first); - indices.push(first + 1); - - indices.push(first); - indices.push(second); - } - } - - let vertex_buffer = graphics.device.create_buffer_init(&BufferInitDescriptor { - label: Some("sphere wireframe vertex buffer"), - contents: bytemuck::cast_slice(&vertices), - usage: BufferUsages::VERTEX, - }); - - let index_buffer = graphics.device.create_buffer_init(&BufferInitDescriptor { - label: Some("sphere wireframe index buffer"), - contents: bytemuck::cast_slice(&indices), - usage: BufferUsages::INDEX, - }); - - Self { - vertex_buffer, - index_buffer, - index_count: indices.len() as u32, - } - } - - pub fn capsule_wireframe( - graphics: Arc<SharedGraphicsContext>, - half_height: f32, - radius: f32, - segments: u32, - ) -> Self { - let mut vertices = Vec::new(); - let mut indices = Vec::new(); - - for i in 0..=segments / 2 { - let theta = std::f32::consts::PI * i as f32 / segments as f32; - let y = half_height + radius * theta.cos(); - let r = radius * theta.sin(); - - for j in 0..=segments { - let phi = 2.0 * std::f32::consts::PI * j as f32 / segments as f32; - vertices.push([r * phi.cos(), y, r * phi.sin()]); - } - } - - for j in 0..=segments { - let phi = 2.0 * std::f32::consts::PI * j as f32 / segments as f32; - vertices.push([radius * phi.cos(), half_height, radius * phi.sin()]); - vertices.push([radius * phi.cos(), -half_height, radius * phi.sin()]); - } - - for i in 0..=segments / 2 { - let theta = std::f32::consts::PI * i as f32 / segments as f32; - let y = -half_height - radius * theta.cos(); - let r = radius * theta.sin(); - - for j in 0..=segments { - let phi = 2.0 * std::f32::consts::PI * j as f32 / segments as f32; - vertices.push([r * phi.cos(), y, r * phi.sin()]); - } - } - - for i in 0..(vertices.len() as u16 - 1) { - indices.push(i); - indices.push(i + 1); - } - - let vertex_buffer = graphics.device.create_buffer_init(&BufferInitDescriptor { - label: Some("capsule wireframe vertex buffer"), - contents: bytemuck::cast_slice(&vertices), - usage: BufferUsages::VERTEX, - }); - - let index_buffer = graphics.device.create_buffer_init(&BufferInitDescriptor { - label: Some("capsule wireframe index buffer"), - contents: bytemuck::cast_slice(&indices), - usage: BufferUsages::INDEX, - }); - - Self { - vertex_buffer, - index_buffer, - index_count: indices.len() as u32, - } - } - - pub fn cylinder_wireframe( - graphics: Arc<SharedGraphicsContext>, - half_height: f32, - radius: f32, - _segments: u32, - ) -> Self { - // TODO: Implement cylinder wireframe - Self::box_wireframe(graphics, [radius, half_height, radius]) // Placeholder - } - - pub fn cone_wireframe( - graphics: Arc<SharedGraphicsContext>, - half_height: f32, - radius: f32, - _segments: u32, - ) -> Self { - // TODO: Implement cone wireframe - Self::box_wireframe(graphics, [radius, half_height, radius]) // Placeholder - } -} - -pub mod shared { - use crate::physics::PhysicsState; - use crate::types::NCollider; - use rapier3d::prelude::ColliderHandle; - - pub fn get_collider_mut<'a>( - physics: &'a mut PhysicsState, - ffi: &NCollider, - ) -> Option<&'a mut rapier3d::prelude::Collider> { - let handle = ColliderHandle::from_raw_parts(ffi.index.index, ffi.index.generation); - physics.colliders.get_mut(handle) - } - - pub fn get_collider<'a>( - physics: &'a PhysicsState, - ffi: &NCollider, - ) -> Option<&'a rapier3d::prelude::Collider> { - let handle = ColliderHandle::from_raw_parts(ffi.index.index, ffi.index.generation); - physics.colliders.get(handle) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "getColliderShape" - ), - c -)] -fn get_collider_shape( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - collider: &NCollider, -) -> DropbearNativeResult<ColliderShape> { - let collider = - get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - - let rapier_shape = collider.shape(); - let my_shape = match rapier_shape.as_typed_shape() { - TypedShape::Cuboid(c) => { - let he = c.half_extents; - ColliderShape::Box { - half_extents: NVector3::from([he.x, he.y, he.z]), - } - } - TypedShape::Ball(b) => ColliderShape::Sphere { radius: b.radius }, - TypedShape::Capsule(c) => { - let height = c.segment.length(); - ColliderShape::Capsule { - half_height: height * 0.5, - radius: c.radius, - } - } - TypedShape::Cylinder(c) => ColliderShape::Cylinder { - half_height: c.half_height, - radius: c.radius, - }, - TypedShape::Cone(c) => ColliderShape::Cone { - half_height: c.half_height, - radius: c.radius, - }, - _ => return Err(DropbearNativeError::InvalidArgument), - }; - - Ok(my_shape) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "setColliderShape" - ), - c -)] -fn set_collider_shape( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - collider: &NCollider, - shape: &ColliderShape, -) -> DropbearNativeResult<()> { - let collider = - get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - - let new_shape = match shape { - ColliderShape::Box { half_extents } => SharedShape::cuboid( - half_extents.x as f32, - half_extents.y as f32, - half_extents.z as f32, - ), - ColliderShape::Sphere { radius } => SharedShape::ball(*radius), - ColliderShape::Capsule { - half_height, - radius, - } => SharedShape::capsule_y(*half_height, *radius), - ColliderShape::Cylinder { - half_height, - radius, - } => SharedShape::cylinder(*half_height, *radius), - ColliderShape::Cone { - half_height, - radius, - } => SharedShape::cone(*half_height, *radius), - }; - - collider.set_shape(new_shape); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "getColliderDensity" - ), - c -)] -fn get_collider_density( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - collider: &NCollider, -) -> DropbearNativeResult<f64> { - let collider = - get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - Ok(collider.density() as f64) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "setColliderDensity" - ), - c -)] -fn set_collider_density( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - collider: &NCollider, - density: f64, -) -> DropbearNativeResult<()> { - let collider = - get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - collider.set_density(density as f32); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "getColliderFriction" - ), - c -)] -fn get_collider_friction( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - collider: &NCollider, -) -> DropbearNativeResult<f64> { - let collider = - get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - Ok(collider.friction() as f64) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "setColliderFriction" - ), - c -)] -fn set_collider_friction( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - collider: &NCollider, - friction: f64, -) -> DropbearNativeResult<()> { - let collider = - get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - collider.set_friction(friction as f32); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "getColliderRestitution" - ), - c -)] -fn get_collider_restitution( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - collider: &NCollider, -) -> DropbearNativeResult<f64> { - let collider = - get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - Ok(collider.restitution() as f64) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "setColliderRestitution" - ), - c -)] -fn set_collider_restitution( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - collider: &NCollider, - restitution: f64, -) -> DropbearNativeResult<()> { - let collider = - get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - collider.set_restitution(restitution as f32); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "getColliderMass" - ), - c -)] -fn get_collider_mass( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - collider: &NCollider, -) -> DropbearNativeResult<f64> { - let collider = - get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - Ok(collider.mass() as f64) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "setColliderMass" - ), - c -)] -fn set_collider_mass( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - collider: &NCollider, - mass: f64, -) -> DropbearNativeResult<()> { - let collider = - get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - collider.set_mass(mass as f32); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "getColliderIsSensor" - ), - c -)] -fn get_collider_is_sensor( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - collider: &NCollider, -) -> DropbearNativeResult<bool> { - let collider = - get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - Ok(collider.is_sensor()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "setColliderIsSensor" - ), - c -)] -fn set_collider_is_sensor( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - collider: &NCollider, - is_sensor: bool, -) -> DropbearNativeResult<()> { - let collider = - get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - collider.set_sensor(is_sensor); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "getColliderTranslation" - ), - c -)] -fn get_collider_translation( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - collider: &NCollider, -) -> DropbearNativeResult<NVector3> { - let collider = - get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - let t: Vector = collider.translation(); - Ok(NVector3::new(t.x as f64, t.y as f64, t.z as f64)) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "setColliderTranslation" - ), - c -)] -fn set_collider_translation( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - collider: &NCollider, - translation: &NVector3, -) -> DropbearNativeResult<()> { - let collider = - get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - let t = Vector::new( - translation.x as f32, - translation.y as f32, - translation.z as f32, - ); - collider.set_translation(t); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "getColliderRotation" - ), - c -)] -fn get_collider_rotation( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - collider: &NCollider, -) -> DropbearNativeResult<NVector3> { - let collider = - get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - let r: Rotation = collider.rotation(); - let q = DQuat::from_xyzw(r.x as f64, r.y as f64, r.z as f64, r.w as f64); - let euler = q.to_euler(glam::EulerRot::XYZ); - Ok(NVector3::new(euler.0, euler.1, euler.2)) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderNative", - func = "setColliderRotation" - ), - c -)] -fn set_collider_rotation( - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - collider: &NCollider, - rotation: &NVector3, -) -> DropbearNativeResult<()> { - let collider = - get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - let q = DQuat::from_euler(glam::EulerRot::XYZ, rotation.x, rotation.y, rotation.z); - let r = Rotation::from_array([q.w as f32, q.x as f32, q.y as f32, q.z as f32]); - collider.set_rotation(r); - Ok(()) -} +} @@ -1,64 +0,0 @@ -//! Scripting module for collider groups. - -use crate::physics::PhysicsState; -use crate::physics::collider::ColliderGroup; -use crate::ptr::WorldPtr; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; -use crate::types::{IndexNative, NCollider}; - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderGroupNative", - func = "colliderGroupExistsForEntity" - ), - c -)] -fn exists_for_entity( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<bool> { - Ok(world.get::<&ColliderGroup>(entity).is_ok()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.ColliderGroupNative", - func = "getColliderGroupColliders" - ), - c -)] -fn get_colliders( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(crate::ptr::PhysicsStatePtr)] physics: &PhysicsState, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<Vec<NCollider>> { - if world.get::<&ColliderGroup>(entity).is_ok() { - let handles_opt = physics - .entity_label_map - .get(&entity) - .and_then(|label| physics.colliders_entity_map.get(label)); - - let mut colliders: Vec<NCollider> = Vec::new(); - - if let Some(handles) = handles_opt { - for (_, handle) in handles { - let (idx, generation) = handle.into_raw_parts(); - - let col = NCollider { - index: IndexNative { - index: idx, - generation, - }, - entity_id: entity.to_bits().get(), - id: idx, - }; - colliders.push(col); - } - } - - Ok(colliders) - } else { - Err(DropbearNativeError::MissingComponent)? - } -} @@ -1,18 +1,14 @@ //! Module that relates to the [Kinematic Character Controller](https://rapier.rs/docs/user_guides/rust/character_controller) //! (or kcc for short) in the [rapier3d] physics engine. -pub mod character_collision; - use crate::component::{ Component, ComponentDescriptor, DisabilityFlags, InspectableComponent, SerializedComponent, }; use crate::physics::PhysicsState; use crate::ptr::WorldPtr; -use crate::scripting::jni::utils::ToJObject; use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; use crate::states::Label; -use crate::types::{IndexNative, NQuaternion, NVector3}; use dropbear_engine::graphics::SharedGraphicsContext; use egui::{ComboBox, DragValue, Ui}; use hecs::{Entity, World}; @@ -27,6 +23,8 @@ use rapier3d::na::{UnitVector3, Vector3}; use rapier3d::prelude::QueryFilter; use serde::{Deserialize, Serialize}; use std::sync::Arc; +use glam::Vec3; +use rapier3d::data::Index; /// The kinematic character controller (kcc) component. #[derive(Debug, Default, Serialize, Deserialize, Clone)] @@ -42,37 +40,11 @@ pub struct KCC { #[repr(C)] #[derive(Debug, Clone)] pub struct CharacterMovementResult { - pub translation: NVector3, + pub translation: Vec3, pub grounded: bool, pub is_sliding_down_slope: bool, } -impl ToJObject for CharacterMovementResult { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com/dropbear/physics/CharacterMovementResult")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let translation_obj = self.translation.to_jobject(env)?; - - let args = [ - JValue::Object(&translation_obj), - JValue::Bool(self.grounded), - JValue::Bool(self.is_sliding_down_slope), - ]; - - let obj = env - .new_object( - &class, - jni_sig!((com.dropbear.math.Vector3d, boolean, boolean) -> void), - &args, - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } -} - #[typetag::serde] impl SerializedComponent for KCC {} @@ -281,270 +253,5 @@ impl KCC { #[repr(C)] struct CharacterCollisionArray { entity_id: u64, - collisions: Vec<IndexNative>, -} - -impl ToJObject for CharacterCollisionArray { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let collision_cls = env - .load_class(jni_str!("com/dropbear/physics/CharacterCollision")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let entity_cls = env - .load_class(jni_str!("com/dropbear/EntityId")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let entity_obj = env - .new_object( - &entity_cls, - jni_sig!((long) -> void), - &[JValue::Long(self.entity_id as i64)], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - let out = env - .new_object_array( - self.collisions.len() as i32, - &collision_cls, - JObject::null(), - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - for (i, handle) in self.collisions.iter().enumerate() { - let index_obj = handle.to_jobject(env)?; - let collision_obj = env - .new_object( - &collision_cls, - jni_sig!((com.dropbear.EntityId, com.dropbear.physics.Index) -> void), - &[JValue::Object(&entity_obj), JValue::Object(&index_obj)], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - out.set_element(env, i, collision_obj) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - } - - Ok(JObject::from(out)) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.KinematicCharacterControllerNative", - func = "existsForEntity" - ), - c -)] -fn kcc_exists_for_entity( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<bool> { - Ok(world.get::<&KCC>(entity).is_ok()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.KinematicCharacterControllerNative", - func = "moveCharacter" - ), - c -)] -fn move_character( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(crate::ptr::PhysicsStatePtr)] physics_state: &mut PhysicsState, - #[dropbear_macro::entity] entity: hecs::Entity, - translation: &NVector3, - delta_time: f64, -) -> DropbearNativeResult<()> { - if let Ok((label, kcc)) = world.query_one::<(&Label, &mut KCC)>(entity).get() { - let rigid_body_handle = physics_state - .bodies_entity_map - .get(label) - .ok_or(DropbearNativeError::NoSuchHandle)?; - - let (body_type, body_pos) = { - let body = physics_state - .bodies - .get(*rigid_body_handle) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - (body.body_type(), *body.position()) - }; - - if body_type != RigidBodyType::KinematicPositionBased { - return Ok(()); // soft error, just tell the user - } - - let collider_handles = physics_state - .colliders_entity_map - .get(label) - .ok_or(DropbearNativeError::NoSuchHandle)?; - let (_, collider_handle) = collider_handles - .first() - .ok_or(DropbearNativeError::NoSuchHandle)?; - let collider = physics_state - .colliders - .get(*collider_handle) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - - let character_pos = if let Some(pos_wrt_parent) = collider.position_wrt_parent() { - body_pos * (*pos_wrt_parent) - } else { - *collider.position() - }; - - let filter = QueryFilter::default() - .exclude_rigid_body(*rigid_body_handle) - .exclude_sensors(); - let query_pipeline = physics_state.broad_phase.as_query_pipeline( - physics_state.narrow_phase.query_dispatcher(), - &physics_state.bodies, - &physics_state.colliders, - filter, - ); - - let movement = kcc.controller.move_shape( - delta_time as f32, - &query_pipeline, - collider.shape(), - &character_pos, - rapier3d::prelude::Vector::new( - translation.x as f32, - translation.y as f32, - translation.z as f32, - ), - |collision| { - if let Some(collisions) = - physics_state.collision_events_to_deal_with.get_mut(&entity) - { - collisions.push(collision) - } else { - physics_state - .collision_events_to_deal_with - .insert(entity, vec![collision]); - } - }, - ); - - if let Some(body) = physics_state.bodies.get_mut(*rigid_body_handle) { - let current_pos = body.translation(); - let new_pos = current_pos + movement.translation; - body.set_next_kinematic_translation(new_pos); - } - - kcc.movement = Some(CharacterMovementResult { - translation: movement.translation.into(), - grounded: movement.grounded, - is_sliding_down_slope: movement.is_sliding_down_slope, - }); - - Ok(()) - } else { - Err(DropbearNativeError::MissingComponent) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.KinematicCharacterControllerNative", - func = "setRotation" - ), - c -)] -fn set_rotation( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(crate::ptr::PhysicsStatePtr)] physics_state: &mut PhysicsState, - #[dropbear_macro::entity] entity: hecs::Entity, - rotation: &NQuaternion, -) -> DropbearNativeResult<()> { - if let Ok((label, _)) = world.query_one::<(&Label, &KCC)>(entity).get() { - let rigid_body_handle = physics_state - .bodies_entity_map - .get(label) - .ok_or(DropbearNativeError::NoSuchHandle)?; - - let body_type = { - let body = physics_state - .bodies - .get(*rigid_body_handle) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; - body.body_type() - }; - - if body_type != RigidBodyType::KinematicPositionBased { - return Err(DropbearNativeError::InvalidArgument); - } - - let len = (rotation.x * rotation.x - + rotation.y * rotation.y - + rotation.z * rotation.z - + rotation.w * rotation.w) - .sqrt(); - let (x, y, z, w) = if len > 0.0 { - ( - rotation.x / len, - rotation.y / len, - rotation.z / len, - rotation.w / len, - ) - } else { - (0.0, 0.0, 0.0, 1.0) - }; - - if let Some(body) = physics_state.bodies.get_mut(*rigid_body_handle) { - let rot = Rotation::from_xyzw(x as f32, y as f32, z as f32, w as f32); - body.set_next_kinematic_rotation(rot); - } - - Ok(()) - } else { - Err(DropbearNativeError::MissingComponent) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.KinematicCharacterControllerNative", - func = "getHit" - ), - c -)] -fn get_hit( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<CharacterCollisionArray> { - let kcc = world - .get::<&KCC>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - - let mut collisions = Vec::with_capacity(kcc.collisions.len()); - for collision in &kcc.collisions { - let (idx, generation) = collision.handle.into_raw_parts(); - collisions.push(IndexNative { - index: idx, - generation, - }); - } - - Ok(CharacterCollisionArray { - entity_id: entity.to_bits().get(), - collisions, - }) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.KinematicCharacterControllerNative", - func = "getMovementResult" - ), - c -)] -fn get_movement_result( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<Option<CharacterMovementResult>> { - world - .get::<&KCC>(entity) - .map(|kcc| kcc.movement.clone()) - .map(Ok) - .unwrap_or(Ok(None)) + collisions: Vec<Index>, } @@ -1,373 +0,0 @@ -use crate::scripting::jni::utils::ToJObject; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; -use crate::types::{IndexNative, NCollider, NShapeCastStatus, NTransform, NVector3}; -use ::jni::objects::{JObject, JValue}; -use ::jni::{Env, jni_sig, jni_str}; -use hecs::{Entity, World}; -use rapier3d::control::CharacterCollision; - -use crate::physics::collider::ColliderGroup; -use crate::physics::kcc::KCC; -use crate::ptr::WorldPtr; - -fn get_collision_from_world( - world: &World, - entity: Entity, - collision_handle: &IndexNative, -) -> DropbearNativeResult<CharacterCollision> { - let kcc = world - .get::<&KCC>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - - kcc.collisions - .iter() - .copied() - .find(|c| { - let (idx, generation) = c.handle.into_raw_parts(); - idx == collision_handle.index && generation == collision_handle.generation - }) - .ok_or(DropbearNativeError::NoSuchHandle) -} - -fn collider_ffi_from_handle( - world: &World, - handle: rapier3d::prelude::ColliderHandle, -) -> Option<NCollider> { - let (idx, generation) = handle.into_raw_parts(); - - for (entity, group) in world.query::<(Entity, &ColliderGroup)>().iter() { - if group.colliders.iter().any(|c| c.id == idx) { - return Some(NCollider { - index: IndexNative { - index: idx, - generation, - }, - entity_id: entity.to_bits().get(), - id: idx, - }); - } - } - - None -} - -impl ToJObject for NShapeCastStatus { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com/dropbear/physics/ShapeCastStatus")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let name = match self { - NShapeCastStatus::OutOfIterations => "OutOfIterations", - NShapeCastStatus::Converged => "Converged", - NShapeCastStatus::Failed => "Failed", - NShapeCastStatus::PenetratingOrWithinTargetDist => "PenetratingOrWithinTargetDist", - }; - - let name_jstring = env - .new_string(name) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - let value = env - .call_static_method( - class, - jni_str!("valueOf"), - jni_sig!((java.lang.String) -> com.dropbear.physics.ShapeCastStatus), - &[JValue::from(&name_jstring)], - ) - .map_err(|_| DropbearNativeError::JNIMethodNotFound)? - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - Ok(value) - } -} - -pub mod shared { - use super::*; - use crate::types::NVector3; - use glam::{DQuat, DVec3}; - use rapier3d::na::Quaternion; - - pub fn get_collider( - world: &World, - entity: Entity, - collision_handle: &IndexNative, - ) -> DropbearNativeResult<NCollider> { - let collision = get_collision_from_world(world, entity, collision_handle)?; - collider_ffi_from_handle(world, collision.handle) - .ok_or(DropbearNativeError::PhysicsObjectNotFound) - } - - pub fn get_character_position( - world: &World, - entity: Entity, - collision_handle: &IndexNative, - ) -> DropbearNativeResult<NTransform> { - let collision = get_collision_from_world(world, entity, collision_handle)?; - - let iso = collision.character_pos; - let t = iso.translation; - let rot = iso.rotation; - let q: Quaternion<f32> = Quaternion::from(rot); - - Ok(NTransform { - position: DVec3::new(t.x as f64, t.y as f64, t.z as f64).into(), - rotation: DQuat::from_xyzw(q.i as f64, q.j as f64, q.k as f64, q.w as f64).into(), - scale: DVec3::ONE.into(), - }) - } - - pub fn get_translation_applied( - world: &World, - entity: Entity, - collision_handle: &IndexNative, - ) -> DropbearNativeResult<NVector3> { - let collision = get_collision_from_world(world, entity, collision_handle)?; - let v = collision.translation_applied; - Ok(NVector3 { - x: v.x as f64, - y: v.y as f64, - z: v.z as f64, - }) - } - - pub fn get_translation_remaining( - world: &World, - entity: Entity, - collision_handle: &IndexNative, - ) -> DropbearNativeResult<NVector3> { - let collision = get_collision_from_world(world, entity, collision_handle)?; - let v = collision.translation_remaining; - Ok(NVector3 { - x: v.x as f64, - y: v.y as f64, - z: v.z as f64, - }) - } - - pub fn get_time_of_impact( - world: &World, - entity: Entity, - collision_handle: &IndexNative, - ) -> DropbearNativeResult<f64> { - let collision = get_collision_from_world(world, entity, collision_handle)?; - Ok(collision.hit.time_of_impact as f64) - } - - pub fn get_witness1( - world: &World, - entity: Entity, - collision_handle: &IndexNative, - ) -> DropbearNativeResult<NVector3> { - let collision = get_collision_from_world(world, entity, collision_handle)?; - let p = collision.hit.witness1; - Ok(NVector3 { - x: p.x as f64, - y: p.y as f64, - z: p.z as f64, - }) - } - - pub fn get_witness2( - world: &World, - entity: Entity, - collision_handle: &IndexNative, - ) -> DropbearNativeResult<NVector3> { - let collision = get_collision_from_world(world, entity, collision_handle)?; - let p = collision.hit.witness2; - Ok(NVector3 { - x: p.x as f64, - y: p.y as f64, - z: p.z as f64, - }) - } - - pub fn get_normal1( - world: &World, - entity: Entity, - collision_handle: &IndexNative, - ) -> DropbearNativeResult<NVector3> { - let collision = get_collision_from_world(world, entity, collision_handle)?; - let n = collision.hit.normal1; - Ok(NVector3 { - x: n.x as f64, - y: n.y as f64, - z: n.z as f64, - }) - } - - pub fn get_normal2( - world: &World, - entity: Entity, - collision_handle: &IndexNative, - ) -> DropbearNativeResult<NVector3> { - let collision = get_collision_from_world(world, entity, collision_handle)?; - let n = collision.hit.normal2; - Ok(NVector3 { - x: n.x as f64, - y: n.y as f64, - z: n.z as f64, - }) - } - - pub fn get_status( - world: &World, - entity: Entity, - collision_handle: &IndexNative, - ) -> DropbearNativeResult<NShapeCastStatus> { - let collision = get_collision_from_world(world, entity, collision_handle)?; - Ok(collision.hit.status.into()) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.CharacterCollisionNative", - func = "getCollider" - ), - c -)] -fn get_character_collision_collider( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - collision_handle: &IndexNative, -) -> DropbearNativeResult<crate::types::NCollider> { - shared::get_collider(world, entity, collision_handle) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.CharacterCollisionNative", - func = "getCharacterPosition" - ), - c -)] -fn get_character_collision_position( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - collision_handle: &IndexNative, -) -> DropbearNativeResult<NTransform> { - shared::get_character_position(world, entity, collision_handle) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.CharacterCollisionNative", - func = "getTranslationApplied" - ), - c -)] -fn get_character_collision_translation_applied( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - collision_handle: &IndexNative, -) -> DropbearNativeResult<NVector3> { - shared::get_translation_applied(world, entity, collision_handle) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.CharacterCollisionNative", - func = "getTranslationRemaining" - ), - c -)] -fn get_character_collision_translation_remaining( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - collision_handle: &IndexNative, -) -> DropbearNativeResult<NVector3> { - shared::get_translation_remaining(world, entity, collision_handle) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.CharacterCollisionNative", - func = "getTimeOfImpact" - ), - c -)] -fn get_character_collision_time_of_impact( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - collision_handle: &IndexNative, -) -> DropbearNativeResult<f64> { - shared::get_time_of_impact(world, entity, collision_handle) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.CharacterCollisionNative", - func = "getWitness1" - ), - c -)] -fn get_character_collision_witness1( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - collision_handle: &IndexNative, -) -> DropbearNativeResult<NVector3> { - shared::get_witness1(world, entity, collision_handle) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.CharacterCollisionNative", - func = "getWitness2" - ), - c -)] -fn get_character_collision_witness2( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - collision_handle: &IndexNative, -) -> DropbearNativeResult<NVector3> { - shared::get_witness2(world, entity, collision_handle) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.CharacterCollisionNative", - func = "getNormal1" - ), - c -)] -fn get_character_collision_normal1( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - collision_handle: &IndexNative, -) -> DropbearNativeResult<NVector3> { - shared::get_normal1(world, entity, collision_handle) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.CharacterCollisionNative", - func = "getNormal2" - ), - c -)] -fn get_character_collision_normal2( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - collision_handle: &IndexNative, -) -> DropbearNativeResult<NVector3> { - shared::get_normal2(world, entity, collision_handle) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.CharacterCollisionNative", - func = "getStatus" - ), - c -)] -fn get_character_collision_status( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - collision_handle: &IndexNative, -) -> DropbearNativeResult<NShapeCastStatus> { - shared::get_status(world, entity, collision_handle) -} @@ -5,13 +5,10 @@ use crate::component::{ }; use crate::physics::PhysicsState; use crate::ptr::{PhysicsStatePtr, WorldPtr}; -use crate::scripting::jni::utils::{FromJObject, ToJObject}; use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; use crate::states::Label; use crate::types::{IndexNative, NCollider, NVector3, RigidBodyContext}; -use ::jni::objects::{JObject, JValue}; -use ::jni::{Env, jni_sig, jni_str}; use dropbear_engine::graphics::SharedGraphicsContext; use egui::{CollapsingHeader, ComboBox, DragValue, Ui}; use hecs::{Entity, World}; @@ -60,64 +57,6 @@ pub struct AxisLock { pub z: bool, } -impl FromJObject for AxisLock { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> - where - Self: Sized, - { - let class = env - .load_class(jni_str!("com/dropbear/physics/AxisLock")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - if !env - .is_instance_of(obj, &class) - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? - { - return Err(DropbearNativeError::InvalidArgument); - } - - let x = env - .get_field(obj, jni_str!("x"), jni_sig!(boolean)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .z() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let y = env - .get_field(obj, jni_str!("y"), jni_sig!(boolean)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .z() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let z = env - .get_field(obj, jni_str!("z"), jni_sig!(boolean)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .z() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - Ok(AxisLock { x, y, z }) - } -} - -impl ToJObject for AxisLock { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com/dropbear/physics/AxisLock")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let args = [ - JValue::Bool(self.x), - JValue::Bool(self.y), - JValue::Bool(self.z), - ]; - - let obj = env - .new_object(&class, jni_sig!((boolean, boolean, boolean) -> void), &args) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } -} - #[derive(Debug, Serialize, Deserialize, Clone)] pub struct RigidBody { /// The entity this component is attached to. @@ -871,399 +810,3 @@ pub mod shared { } } -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "rigidBodyExistsForEntity" - ), - c -)] -fn exists_for_entity( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<Option<IndexNative>> { - Ok(shared::rigid_body_exists_for_entity(world, physics, entity)) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "getRigidBodyMode" - ), - c -)] -fn get_rigidbody_mode( - #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - rigidbody: &RigidBodyContext, -) -> DropbearNativeResult<i32> { - let body_type = shared::get_rigidbody_type(physics, rigidbody)?; - Ok(match body_type { - RigidBodyType::Dynamic => 0, - RigidBodyType::Fixed => 1, - RigidBodyType::KinematicPositionBased => 2, - RigidBodyType::KinematicVelocityBased => 3, - }) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "setRigidBodyMode" - ), - c -)] -fn set_rigidbody_mode( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - rigidbody: &RigidBodyContext, - mode: i32, -) -> DropbearNativeResult<()> { - shared::set_rigidbody_type(physics, world, rigidbody, mode as i64) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "getRigidBodyGravityScale" - ), - c -)] -fn get_rigidbody_gravity_scale( - #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - rigidbody: &RigidBodyContext, -) -> DropbearNativeResult<f64> { - shared::get_rigidbody_gravity_scale(physics, rigidbody) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "setRigidBodyGravityScale" - ), - c -)] -fn set_rigidbody_gravity_scale( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - rigidbody: &RigidBodyContext, - gravity_scale: f64, -) -> DropbearNativeResult<()> { - shared::set_rigidbody_gravity_scale(physics, world, rigidbody, gravity_scale) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "getRigidBodyLinearDamping" - ), - c -)] -fn get_rigidbody_linear_damping( - #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - rigidbody: &RigidBodyContext, -) -> DropbearNativeResult<f64> { - shared::get_rigidbody_linear_damping(physics, rigidbody) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "setRigidBodyLinearDamping" - ), - c -)] -fn set_rigidbody_linear_damping( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - rigidbody: &RigidBodyContext, - linear_damping: f64, -) -> DropbearNativeResult<()> { - shared::set_rigidbody_linear_damping(physics, world, rigidbody, linear_damping) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "getRigidBodyAngularDamping" - ), - c -)] -fn get_rigidbody_angular_damping( - #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - rigidbody: &RigidBodyContext, -) -> DropbearNativeResult<f64> { - shared::get_rigidbody_angular_damping(physics, rigidbody) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "setRigidBodyAngularDamping" - ), - c -)] -fn set_rigidbody_angular_damping( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - rigidbody: &RigidBodyContext, - angular_damping: f64, -) -> DropbearNativeResult<()> { - shared::set_rigidbody_angular_damping(physics, world, rigidbody, angular_damping) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "getRigidBodySleep" - ), - c -)] -fn get_rigidbody_sleep( - #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - rigidbody: &RigidBodyContext, -) -> DropbearNativeResult<bool> { - shared::get_rigidbody_sleep(physics, rigidbody) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "setRigidBodySleep" - ), - c -)] -fn set_rigidbody_sleep( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - rigidbody: &RigidBodyContext, - sleep: bool, -) -> DropbearNativeResult<()> { - shared::set_rigidbody_sleep(physics, world, rigidbody, sleep) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "getRigidBodyCcdEnabled" - ), - c -)] -fn get_rigidbody_ccd_enabled( - #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - rigidbody: &RigidBodyContext, -) -> DropbearNativeResult<bool> { - shared::get_rigidbody_ccd(physics, rigidbody) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "setRigidBodyCcdEnabled" - ), - c -)] -fn set_rigidbody_ccd_enabled( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - rigidbody: &RigidBodyContext, - ccd_enabled: bool, -) -> DropbearNativeResult<()> { - shared::set_rigidbody_ccd(physics, world, rigidbody, ccd_enabled) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "getRigidBodyLinearVelocity" - ), - c -)] -fn get_rigidbody_linear_velocity( - #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - rigidbody: &RigidBodyContext, -) -> DropbearNativeResult<NVector3> { - shared::get_rigidbody_linvel(physics, rigidbody) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "setRigidBodyLinearVelocity" - ), - c -)] -fn set_rigidbody_linear_velocity( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - rigidbody: &RigidBodyContext, - linear_velocity: &NVector3, -) -> DropbearNativeResult<()> { - shared::set_rigidbody_linvel(physics, world, rigidbody, *linear_velocity) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "getRigidBodyAngularVelocity" - ), - c -)] -fn get_rigidbody_angular_velocity( - #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - rigidbody: &RigidBodyContext, -) -> DropbearNativeResult<NVector3> { - shared::get_rigidbody_angvel(physics, rigidbody) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "setRigidBodyAngularVelocity" - ), - c -)] -fn set_rigidbody_angular_velocity( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - rigidbody: &RigidBodyContext, - angular_velocity: &NVector3, -) -> DropbearNativeResult<()> { - shared::set_rigidbody_angvel(physics, world, rigidbody, *angular_velocity) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "getRigidBodyLockTranslation" - ), - c -)] -fn get_rigidbody_lock_translation( - #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - rigidbody: &RigidBodyContext, -) -> DropbearNativeResult<AxisLock> { - shared::get_rigidbody_lock_translation(physics, rigidbody) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "setRigidBodyLockTranslation" - ), - c -)] -fn set_rigidbody_lock_translation( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - rigidbody: &RigidBodyContext, - lock_translation: &AxisLock, -) -> DropbearNativeResult<()> { - shared::set_rigidbody_lock_translation(physics, world, rigidbody, *lock_translation) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "getRigidBodyLockRotation" - ), - c -)] -fn get_rigidbody_lock_rotation( - #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - rigidbody: &RigidBodyContext, -) -> DropbearNativeResult<AxisLock> { - shared::get_rigidbody_lock_rotation(physics, rigidbody) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "setRigidBodyLockRotation" - ), - c -)] -fn set_rigidbody_lock_rotation( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - rigidbody: &RigidBodyContext, - lock_rotation: &AxisLock, -) -> DropbearNativeResult<()> { - shared::set_rigidbody_lock_rotation(physics, world, rigidbody, *lock_rotation) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "getRigidBodyChildren" - ), - c -)] -fn get_rigidbody_children( - #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, - rigidbody: &RigidBodyContext, -) -> DropbearNativeResult<Vec<NCollider>> { - let children = shared::get_rigidbody_children(physics, rigidbody)?; - let colliders = children - .into_iter() - .map(|handle| { - let (idx, generation) = handle.into_raw_parts(); - NCollider { - index: IndexNative { - index: idx, - generation, - }, - entity_id: rigidbody.entity_id, - id: idx, - } - }) - .collect(); - - Ok(colliders) -} - -#[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "applyImpulse"), - c -)] -fn apply_impulse( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - rigidbody: &RigidBodyContext, - x: f64, - y: f64, - z: f64, -) -> DropbearNativeResult<()> { - let impulse = NVector3::new(x, y, z); - shared::apply_impulse(physics, world, rigidbody, impulse) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.physics.RigidBodyNative", - func = "applyTorqueImpulse" - ), - c -)] -fn apply_torque_impulse( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, - rigidbody: &RigidBodyContext, - x: f64, - y: f64, - z: f64, -) -> DropbearNativeResult<()> { - let torque = NVector3::new(x, y, z); - shared::apply_torque_impulse(physics, world, rigidbody, torque) -} @@ -2,11 +2,7 @@ use crate::component::{ Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, SerializedComponent, }; -use crate::ptr::WorldPtr; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; use crate::states::Property; -use crate::types::NVector3; use crate::warn; use dropbear_engine::graphics::SharedGraphicsContext; use egui::{CollapsingHeader, ComboBox, DragValue, Grid, RichText, TextEdit, Ui}; @@ -41,10 +37,10 @@ impl Component for CustomProperties { } } - fn init<'a>( - ser: &'a Self::SerializedForm, + fn init( + ser: &Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, - ) -> ComponentInitFuture<'a, Self> { + ) -> ComponentInitFuture<Self> { Box::pin(async move { Ok((ser.clone(),)) }) } @@ -393,310 +389,3 @@ pub mod shared { } } -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CustomPropertiesNative", - func = "customPropertiesExistsForEntity" - ), - c -)] -fn custom_properties_exists_for_entity( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, -) -> DropbearNativeResult<bool> { - Ok(shared::custom_properties_exists_for_entity(world, entity)) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CustomPropertiesNative", - func = "getStringProperty" - ), - c -)] -fn get_string_property( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, - key: String, -) -> DropbearNativeResult<Option<String>> { - let props = world - .get::<&CustomProperties>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - - Ok(props.get_property(&key).and_then(|value| match value { - Value::String(s) => Some(s.clone()), - _ => None, - })) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CustomPropertiesNative", - func = "getIntProperty" - ), - c -)] -fn get_int_property( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, - key: String, -) -> DropbearNativeResult<Option<i32>> { - let props = world - .get::<&CustomProperties>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - - Ok(props.get_property(&key).and_then(|value| match value { - Value::Int(v) => Some(*v as i32), - _ => None, - })) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CustomPropertiesNative", - func = "getLongProperty" - ), - c -)] -fn get_long_property( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, - key: String, -) -> DropbearNativeResult<Option<i64>> { - let props = world - .get::<&CustomProperties>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - - Ok(props.get_property(&key).and_then(|value| match value { - Value::Int(v) => Some(*v), - _ => None, - })) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CustomPropertiesNative", - func = "getDoubleProperty" - ), - c -)] -fn get_double_property( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, - key: String, -) -> DropbearNativeResult<Option<f64>> { - let props = world - .get::<&CustomProperties>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - - Ok(props.get_property(&key).and_then(|value| match value { - Value::Double(v) => Some(*v), - _ => None, - })) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CustomPropertiesNative", - func = "getFloatProperty" - ), - c -)] -fn get_float_property( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, - key: String, -) -> DropbearNativeResult<Option<f32>> { - let props = world - .get::<&CustomProperties>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - - Ok(props.get_property(&key).and_then(|value| match value { - Value::Double(v) => Some(*v as f32), - _ => None, - })) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CustomPropertiesNative", - func = "getBoolProperty" - ), - c -)] -fn get_bool_property( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, - key: String, -) -> DropbearNativeResult<Option<bool>> { - let props = world - .get::<&CustomProperties>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - - Ok(props.get_property(&key).and_then(|value| match value { - Value::Bool(v) => Some(*v), - _ => None, - })) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CustomPropertiesNative", - func = "getVec3Property" - ), - c -)] -fn get_vec3_property( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, - key: String, -) -> DropbearNativeResult<Option<NVector3>> { - let props = world - .get::<&CustomProperties>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - - Ok(props.get_property(&key).and_then(|value| match value { - Value::Vec3(v) => Some(NVector3::from(*v)), - _ => None, - })) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CustomPropertiesNative", - func = "setStringProperty" - ), - c -)] -fn set_string_property( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - key: String, - value: String, -) -> DropbearNativeResult<()> { - let mut props = world - .get::<&mut CustomProperties>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - props.set_property(key, Value::String(value)); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CustomPropertiesNative", - func = "setIntProperty" - ), - c -)] -fn set_int_property( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - key: String, - value: i32, -) -> DropbearNativeResult<()> { - let mut props = world - .get::<&mut CustomProperties>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - props.set_property(key, Value::Int(value as i64)); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CustomPropertiesNative", - func = "setLongProperty" - ), - c -)] -fn set_long_property( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - key: String, - value: i64, -) -> DropbearNativeResult<()> { - let mut props = world - .get::<&mut CustomProperties>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - props.set_property(key, Value::Int(value)); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CustomPropertiesNative", - func = "setDoubleProperty" - ), - c -)] -fn set_double_property( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - key: String, - value: f64, -) -> DropbearNativeResult<()> { - let mut props = world - .get::<&mut CustomProperties>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - props.set_property(key, Value::Double(value)); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CustomPropertiesNative", - func = "setFloatProperty" - ), - c -)] -fn set_float_property( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - key: String, - value: f64, -) -> DropbearNativeResult<()> { - let mut props = world - .get::<&mut CustomProperties>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - props.set_property(key, Value::Double(value)); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CustomPropertiesNative", - func = "setBoolProperty" - ), - c -)] -fn set_bool_property( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - key: String, - value: bool, -) -> DropbearNativeResult<()> { - let mut props = world - .get::<&mut CustomProperties>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - props.set_property(key, Value::Bool(value)); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.CustomPropertiesNative", - func = "setVec3Property" - ), - c -)] -fn set_vec3_property( - #[dropbear_macro::define(WorldPtr)] world: &mut World, - #[dropbear_macro::entity] entity: Entity, - key: String, - value: &NVector3, -) -> DropbearNativeResult<()> { - let mut props = world - .get::<&mut CustomProperties>(entity) - .map_err(|_| DropbearNativeError::NoSuchComponent)?; - props.set_property(key, Value::Vec3(value.to_array())); - Ok(()) -} @@ -92,7 +92,6 @@ pub mod shared { let mut loader = scene_loader.lock(); if let Some(entry) = loader.get_entry_mut(scene_id) { - // Update progress from status channel if available if let Some(ref rx) = entry.status { while let Ok(status) = rx.try_recv() { match status { @@ -137,124 +136,3 @@ pub mod shared { } } -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.scene.SceneManagerNative", - func = "loadSceneAsync" - ), - c -)] -fn load_scene_async( - #[dropbear_macro::define(CommandBufferPtr)] command_buffer: &CommandBufferUnwrapped, - #[dropbear_macro::define(SceneLoaderPtr)] scene_loader: &SceneLoaderUnwrapped, - scene_name: String, -) -> DropbearNativeResult<u64> { - Ok(shared::load_scene_async( - command_buffer, - scene_loader, - scene_name, - None, - )?) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.scene.SceneManagerNative", - func = "loadSceneAsyncWithLoading" - ), - c -)] -fn load_scene_async_with_loading( - #[dropbear_macro::define(CommandBufferPtr)] command_buffer: &CommandBufferUnwrapped, - #[dropbear_macro::define(SceneLoaderPtr)] scene_loader: &SceneLoaderUnwrapped, - scene_name: String, - loading_scene: String, -) -> DropbearNativeResult<u64> { - Ok(shared::load_scene_async( - command_buffer, - scene_loader, - scene_name, - Some(loading_scene), - )?) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.scene.SceneManagerNative", - func = "switchToSceneImmediate" - ), - c -)] -fn switch_to_scene_immediate( - #[dropbear_macro::define(CommandBufferPtr)] command_buffer: &CommandBufferUnwrapped, - scene_name: String, -) -> DropbearNativeResult<()> { - Ok(shared::switch_to_scene_immediate( - command_buffer, - scene_name, - )?) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.scene.SceneLoadHandleNative", - func = "getSceneLoadHandleSceneName" - ), - c -)] -fn get_scene_load_handle_scene_name( - #[dropbear_macro::define(SceneLoaderPtr)] scene_loader: &SceneLoaderUnwrapped, - scene_id: u64, -) -> DropbearNativeResult<String> { - Ok(shared::get_scene_load_handle_scene_name( - scene_loader, - scene_id, - )?) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.scene.SceneLoadHandleNative", - func = "switchToSceneAsync" - ), - c -)] -fn switch_to_scene_async( - #[dropbear_macro::define(CommandBufferPtr)] command_buffer: &CommandBufferUnwrapped, - #[dropbear_macro::define(SceneLoaderPtr)] scene_loader: &SceneLoaderUnwrapped, - scene_id: u64, -) -> DropbearNativeResult<()> { - Ok(shared::switch_to_scene_async( - command_buffer, - scene_loader, - scene_id, - )?) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.scene.SceneLoadHandleNative", - func = "getSceneLoadProgress" - ), - c -)] -fn get_scene_load_progress( - #[dropbear_macro::define(SceneLoaderPtr)] scene_loader: &SceneLoaderUnwrapped, - scene_id: u64, -) -> DropbearNativeResult<Progress> { - Ok(shared::get_scene_load_progress(scene_loader, scene_id)?) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.scene.SceneLoadHandleNative", - func = "getSceneLoadStatus" - ), - c -)] -fn get_scene_load_status( - #[dropbear_macro::define(SceneLoaderPtr)] scene_loader: &SceneLoaderUnwrapped, - scene_id: u64, -) -> DropbearNativeResult<u32> { - Ok(shared::get_scene_load_status(scene_loader, scene_id)?) -} @@ -1,7 +1,6 @@ #![allow(non_snake_case)] //! Deals with the Java Native Interface (JNI) with the help of the [`jni`] crate -pub mod primitives; pub mod utils; use crate::APP_INFO; @@ -1,182 +0,0 @@ -use crate::scripting::jni::utils::{FromJObject, ToJObject}; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; -use jni::objects::{JObject, JValue}; -use jni::sys::{jdouble, jint, jlong}; -use jni::{Env, jni_sig, jni_str}; - -impl ToJObject for Option<i32> { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - match self { - Some(value) => { - let class = env - .load_class(jni_str!("java.lang.Integer")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - env.new_object(&class, jni_sig!((int) -> void), &[JValue::Int(*value)]) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) - } - None => Ok(JObject::null()), - } - } -} - -impl FromJObject for Option<i32> { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { - if obj.is_null() { - return Ok(None); - } - - let class = env - .load_class(jni_str!("java.lang.Integer")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - if !env - .is_instance_of(obj, &class) - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? - { - return Err(DropbearNativeError::InvalidArgument); - } - - let value = env - .call_method(obj, jni_str!("intValue"), jni_sig!(() -> i32), &[]) - .map_err(|_| DropbearNativeError::JNIMethodNotFound)? - .i() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - Ok(Some(value as i32)) - } -} - -impl ToJObject for Vec<i32> { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - self.as_slice().to_jobject(env) - } -} - -impl ToJObject for &[i32] { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let array = env - .new_int_array(self.len()) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - let buf: Vec<jint> = self.iter().map(|v| *v as jint).collect(); - array - .set_region(env, 0, &buf) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - Ok(JObject::from(array)) - } -} - -impl ToJObject for &[Vec<i32>] { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let list = new_array_list(env)?; - for value in self.iter() { - let boxed = value.as_slice().to_jobject(env)?; - array_list_add(env, &list, &boxed)?; - } - Ok(list) - } -} - -impl ToJObject for Option<f32> { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - match self { - Some(value) => { - let class = env - .load_class(jni_str!("java.lang.Float")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - env.new_object(&class, jni_sig!((f32) -> ()), &[JValue::Float(*value)]) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) - } - None => Ok(JObject::null()), - } - } -} - -impl ToJObject for Option<f64> { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - match self { - Some(value) => { - let class = env - .load_class(jni_str!("java.lang.Double")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - env.new_object(&class, jni_sig!((f64) -> ()), &[JValue::Double(*value)]) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) - } - None => Ok(JObject::null()), - } - } -} - -impl ToJObject for Vec<f64> { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - self.as_slice().to_jobject(env) - } -} - -impl ToJObject for &[f64] { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let array = env - .new_double_array(self.len()) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - let buf: Vec<jdouble> = self.iter().map(|v| *v as jdouble).collect(); - array - .set_region(env, 0, &buf) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - Ok(JObject::from(array)) - } -} - -impl ToJObject for &[Vec<f64>] { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let list = new_array_list(env)?; - for value in self.iter() { - let array = value.as_slice().to_jobject(env)?; - array_list_add(env, &list, &array)?; - } - Ok(list) - } -} - -fn new_array_list<'a>(env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("java.util.ArrayList")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - env.new_object(&class, jni_sig!(() -> ()), &[]) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) -} - -fn array_list_add(env: &mut Env, list: &JObject, item: &JObject) -> DropbearNativeResult<()> { - env.call_method( - list, - jni_str!("add"), - jni_sig!((java.lang.Object) -> boolean), - &[JValue::Object(item)], - ) - .map_err(|_| DropbearNativeError::JNIMethodNotFound)?; - Ok(()) -} - -impl ToJObject for Vec<u64> { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - self.as_slice().to_jobject(env) - } -} - -impl ToJObject for &[u64] { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let array = env.new_long_array(self.len())?; - let buf: Vec<jlong> = self.iter().map(|v| *v as jlong).collect(); - array.set_region(env, 0, &buf)?; - Ok(JObject::from(array)) - } -} - -impl ToJObject for String { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let result = JObject::from(env.new_string(self)?); - Ok(result) - } -} @@ -1,16 +1,32 @@ //! Utilities for JNI and JVM based code. +use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; +use crate::types::{CollisionEvent, CollisionEventType, ContactForceEvent, IndexNative, NCollider, NVector3}; use jni::objects::{JObject, JValue}; use jni::sys::jint; use jni::{Env, jni_sig, jni_str}; +// todo: idk what to do about this module, considering about removing it + const JAVA_MOUSE_BUTTON_LEFT: jint = 0; const JAVA_MOUSE_BUTTON_RIGHT: jint = 1; const JAVA_MOUSE_BUTTON_MIDDLE: jint = 2; const JAVA_MOUSE_BUTTON_BACK: jint = 3; const JAVA_MOUSE_BUTTON_FORWARD: jint = 4; +/// Trait that defines conversion from a Java object to a Rust struct. +pub trait FromJObject { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> + where + Self: Sized; +} + +/// Converts a Rust object into a Java [JObject]. +pub trait ToJObject { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>>; +} + pub fn Java_button_to_rust(button_code: jint) -> Option<winit::event::MouseButton> { match button_code { JAVA_MOUSE_BUTTON_LEFT => Some(winit::event::MouseButton::Left), @@ -23,19 +39,6 @@ pub fn Java_button_to_rust(button_code: jint) -> Option<winit::event::MouseButto } } -/// Trait that defines conversion from a Java object to a Rust struct. -pub trait FromJObject { - /// Converts a Java object to a Rust struct. - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> - where - Self: Sized; -} - -/// Converts a Rust object (struct or enum) into a java [JObject] -pub trait ToJObject { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>>; -} - impl<T> ToJObject for Vec<T> where T: ToJObject, @@ -87,3 +90,160 @@ where Ok(out) } } + +// ─────────────────────────────────────────────────── Event ToJObject impls ── +// These are needed by scripting/jni.rs to dispatch physics events to the JVM. + +impl ToJObject for NVector3 { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/math/Vector3d")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let args = [ + JValue::Double(self.x), + JValue::Double(self.y), + JValue::Double(self.z), + ]; + + env.new_object(&class, jni_sig!((double, double, double) -> void), &args) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +impl ToJObject for IndexNative { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let cls = env + .load_class(jni_str!("com/dropbear/physics/Index")) + .map_err(|_| DropbearNativeError::GenericError)?; + + env.new_object( + cls, + jni_sig!((int, int) -> void), + &[ + JValue::Int(self.index as i32), + JValue::Int(self.generation as i32), + ], + ) + .map_err(|_| DropbearNativeError::GenericError) + } +} + +impl ToJObject for NCollider { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let collider_cls = env + .load_class(jni_str!("com/dropbear/physics/Collider")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let index_cls = env + .load_class(jni_str!("com/dropbear/physics/Index")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let entity_cls = env + .load_class(jni_str!("com/dropbear/EntityId")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let entity_obj = env + .new_object(&entity_cls, jni_sig!((long) -> void), &[JValue::Long(self.entity_id as i64)]) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + let index_obj = env + .new_object( + &index_cls, + jni_sig!((int, int) -> void), + &[ + JValue::Int(self.index.index as i32), + JValue::Int(self.index.generation as i32), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + env.new_object( + collider_cls, + jni_sig!((com.dropbear.physics.Index, com.dropbear.EntityId, int) -> void), + &[ + JValue::Object(&index_obj), + JValue::Object(&entity_obj), + JValue::Int(self.id as i32), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +impl ToJObject for CollisionEventType { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/physics/CollisionEventType")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let name = match self { + CollisionEventType::Started => "Started", + CollisionEventType::Stopped => "Stopped", + }; + let name_jstring = env + .new_string(name) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + env.call_static_method( + class, + jni_str!("valueOf"), + jni_sig!((java.lang.String) -> com.dropbear.physics.CollisionEventType), + &[JValue::from(&name_jstring)], + ) + .map_err(|_| DropbearNativeError::JNIMethodNotFound)? + .l() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed) + } +} + +impl ToJObject for CollisionEvent { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/physics/CollisionEvent")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let event_type = self.event_type.to_jobject(env)?; + let collider1 = self.collider1.to_jobject(env)?; + let collider2 = self.collider2.to_jobject(env)?; + + env.new_object( + class, + jni_sig!("(Lcom/dropbear/physics/CollisionEventType;Lcom/dropbear/physics/Collider;Lcom/dropbear/physics/Collider;I)V"), + &[ + JValue::Object(&event_type), + JValue::Object(&collider1), + JValue::Object(&collider2), + JValue::Int(self.flags as i32), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +impl ToJObject for ContactForceEvent { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/physics/ContactForceEvent")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let collider1 = self.collider1.to_jobject(env)?; + let collider2 = self.collider2.to_jobject(env)?; + let total_force = self.total_force.to_jobject(env)?; + let max_force_direction = self.max_force_direction.to_jobject(env)?; + + env.new_object( + class, + jni_sig!("(Lcom/dropbear/physics/Collider;Lcom/dropbear/physics/Collider;Lcom/dropbear/math/Vector3d;DLcom/dropbear/math/Vector3d;D)V"), + &[ + JValue::Object(&collider1), + JValue::Object(&collider2), + JValue::Object(&total_force), + JValue::Double(self.total_force_magnitude), + JValue::Object(&max_force_direction), + JValue::Double(self.max_force_magnitude), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} @@ -1,9 +1,7 @@ //! Deals with Kotlin/Native library loading for different platforms. #![allow(clippy::missing_safety_doc)] -// pub mod exports; pub mod sig; -pub mod utils; use crate::scripting::error::LastErrorMessage; use crate::scripting::native::sig::{ @@ -1,27 +0,0 @@ -use dropbear_engine::gilrs; - -pub fn button_from_ordinal(ordinal: i32) -> Result<gilrs::Button, ()> { - match ordinal { - 0 => Ok(gilrs::Button::Unknown), - 1 => Ok(gilrs::Button::South), - 2 => Ok(gilrs::Button::East), - 3 => Ok(gilrs::Button::North), - 4 => Ok(gilrs::Button::West), - 5 => Ok(gilrs::Button::C), - 6 => Ok(gilrs::Button::Z), - 7 => Ok(gilrs::Button::LeftTrigger), - 8 => Ok(gilrs::Button::RightTrigger), - 9 => Ok(gilrs::Button::LeftTrigger2), - 10 => Ok(gilrs::Button::RightTrigger2), - 11 => Ok(gilrs::Button::Select), - 12 => Ok(gilrs::Button::Start), - 13 => Ok(gilrs::Button::Mode), - 14 => Ok(gilrs::Button::LeftThumb), - 15 => Ok(gilrs::Button::RightThumb), - 16 => Ok(gilrs::Button::DPadUp), - 17 => Ok(gilrs::Button::DPadDown), - 18 => Ok(gilrs::Button::DPadLeft), - 19 => Ok(gilrs::Button::DPadRight), - _ => Err(()), - } -} @@ -3,7 +3,6 @@ use crate::component::{ SerializedComponent, }; use crate::physics::PhysicsState; -use crate::scripting::result::DropbearNativeResult; use dropbear_engine::graphics::SharedGraphicsContext; use egui::{CollapsingHeader, Ui}; use hecs::{Entity, World}; @@ -100,56 +99,3 @@ impl InspectableComponent for KotlinComponents { }); } } - -/// Queries whether an entity has a specific Kotlin component attached. -/// -/// Called from JVM via `ComponentNative.hasKotlinComponent(worldPtr, entityId, fqcn)`. -#[dropbear_macro::export(kotlin( - class = "com.dropbear.components.ComponentNative", - func = "hasKotlinComponent", -))] -fn has_kotlin_component( - #[dropbear_macro::define(WorldPtr)] world: &World, - #[dropbear_macro::entity] entity: Entity, - fqcn: String, -) -> DropbearNativeResult<bool> { - if let Ok(kc) = world.get::<&KotlinComponents>(entity) { - Ok(kc.has(&fqcn)) - } else { - Ok(false) - } -} - -/// Registers a Kotlin component type with the engine's [`crate::component::ComponentRegistry`]. -/// -/// Called from JVM during startup via `ComponentNative.registerKotlinComponent(...)`. -/// The declaration is queued in [`crate::component::KOTLIN_COMPONENT_QUEUE`] and consumed -/// by [`crate::component::ComponentRegistry::drain_kotlin_queue`] after JVM init completes. -#[dropbear_macro::export(kotlin( - class = "com.dropbear.components.ComponentNative", - func = "registerKotlinComponent", -))] -fn register_kotlin_component_jni( - #[dropbear_macro::define(WorldPtr)] _world: &World, - fqcn: String, - type_name: String, - category: String, - description: String, -) -> DropbearNativeResult<()> { - use crate::component::{KOTLIN_COMPONENT_QUEUE, KotlinComponentDecl}; - KOTLIN_COMPONENT_QUEUE.lock().push(KotlinComponentDecl { - fqcn, - type_name, - category: if category.is_empty() { - None - } else { - Some(category) - }, - description: if description.is_empty() { - None - } else { - Some(description) - }, - }); - Ok(()) -} @@ -3,17 +3,9 @@ use crate::component::{ Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, SerializedComponent, }; -use crate::hierarchy::EntityTransformExt; use crate::physics::PhysicsState; -use crate::ptr::WorldPtr; -use crate::scripting::jni::utils::{FromJObject, ToJObject}; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; -use crate::types::{NQuaternion, NTransform, NVector3}; -use ::jni::objects::{JObject, JValue}; -use ::jni::{Env, jni_sig, jni_str}; use dropbear_engine::camera::Camera; -use dropbear_engine::entity::{EntityTransform, Transform, inspect_rotation_dquat}; +use dropbear_engine::entity::{EntityTransform, inspect_rotation_dquat}; use dropbear_engine::graphics::SharedGraphicsContext; use egui::{CollapsingHeader, ComboBox, Ui}; use glam::{DMat3, DQuat, DVec3, Vec3}; @@ -668,781 +660,3 @@ impl InspectableComponent for EntityTransform { } } -impl FromJObject for Transform { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { - let pos_val = env - .get_field( - obj, - jni_str!("position"), - jni_sig!(com.dropbear.math.Vector3d), - ) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - - let pos_obj = pos_val - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let rot_val = env - .get_field( - obj, - jni_str!("rotation"), - jni_sig!(com.dropbear.math.Quaterniond), - ) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - - let rot_obj = rot_val - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let scale_val = env - .get_field(obj, jni_str!("scale"), jni_sig!(com.dropbear.math.Vector3d)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - - let scale_obj = scale_val - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let position: DVec3 = NVector3::from_jobject(env, &pos_obj)?.into(); - let scale: DVec3 = NVector3::from_jobject(env, &scale_obj)?.into(); - - let mut get_double = |field| -> DropbearNativeResult<f64> { - env.get_field(&rot_obj, field, jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed) - }; - - let rx = get_double(jni_str!("x"))?; - let ry = get_double(jni_str!("y"))?; - let rz = get_double(jni_str!("z"))?; - let rw = get_double(jni_str!("w"))?; - - let rotation = DQuat::from_xyzw(rx, ry, rz, rw); - - Ok(Transform { - position, - rotation, - scale, - }) - } -} - -impl ToJObject for Transform { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let cls = env - .load_class(jni_str!("com/dropbear/math/Transform")) - .map_err(|e| { - eprintln!("Could not find Transform class: {:?}", e); - DropbearNativeError::JNIClassNotFound - })?; - - let p = self.position; - let r = self.rotation; - let s = self.scale; - - let args = [ - JValue::Double(p.x), - JValue::Double(p.y), - JValue::Double(p.z), - JValue::Double(r.x), - JValue::Double(r.y), - JValue::Double(r.z), - JValue::Double(r.w), - JValue::Double(s.x), - JValue::Double(s.y), - JValue::Double(s.z), - ]; - - let obj = env.new_object(cls, jni_sig!((double, double, double, double, double, double, double, double, double, double) -> void), &args).map_err(|e| { - eprintln!("Failed to create Transform object: {:?}", e); - DropbearNativeError::JNIFailedToCreateObject - })?; - - Ok(obj) - } -} - -impl FromJObject for EntityTransform { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { - let local_val = env - .get_field( - obj, - jni_str!("local"), - jni_sig!(com.dropbear.math.Transform), - ) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - - let local_obj = local_val - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let world_val = env - .get_field( - obj, - jni_str!("world"), - jni_sig!(com.dropbear.math.Transform), - ) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - - let world_obj = world_val - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let local = Transform::from_jobject(env, &local_obj)?; - let world = Transform::from_jobject(env, &world_obj)?; - - Ok(EntityTransform::new(local, world)) - } -} - -impl ToJObject for EntityTransform { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let cls = env - .load_class(jni_str!("com/dropbear/components/EntityTransform")) - .map_err(|e| { - eprintln!("Could not find EntityTransform class: {:?}", e); - DropbearNativeError::JNIClassNotFound - })?; - - let local_obj = self.local().to_jobject(env)?; - let world_obj = self.world().to_jobject(env)?; - - let args = [JValue::Object(&local_obj), JValue::Object(&world_obj)]; - - let obj = env - .new_object( - cls, - jni_sig!((com.dropbear.math.Transform, com.dropbear.math.Transform) -> void), - &args, - ) - .map_err(|e| { - eprintln!("Failed to create EntityTransform object: {:?}", e); - DropbearNativeError::JNIFailedToCreateObject - })?; - - Ok(obj) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.EntityTransformNative", - func = "entityTransformExistsForEntity" - ), - c -)] -fn exists_for_entity( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<bool> { - Ok(world.get::<&EntityTransform>(entity).is_ok()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.EntityTransformNative", - func = "getLocalTransform" - ), - c -)] -fn get_local_transform( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<NTransform> { - if let Ok(et) = world.get::<&EntityTransform>(entity) { - Ok((*et.local()).into()) - } else { - Err(DropbearNativeError::MissingComponent) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.EntityTransformNative", - func = "setLocalTransform" - ), - c -)] -fn set_local_transform( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - transform: &NTransform, -) -> DropbearNativeResult<()> { - if let Ok(mut et) = world.get::<&mut EntityTransform>(entity) { - *et.local_mut() = (*transform).into(); - - Ok(()) - } else { - Err(DropbearNativeError::MissingComponent) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.EntityTransformNative", - func = "getWorldTransform" - ), - c -)] -fn get_world_transform( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<NTransform> { - if let Ok(et) = world.get::<&EntityTransform>(entity) { - Ok((*et.world()).into()) - } else { - Err(DropbearNativeError::MissingComponent) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.EntityTransformNative", - func = "setWorldTransform" - ), - c -)] -fn set_world_transform( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - transform: &NTransform, -) -> DropbearNativeResult<()> { - if let Ok(mut et) = world.get::<&mut EntityTransform>(entity) { - *et.world_mut() = (*transform).into(); - - Ok(()) - } else { - Err(DropbearNativeError::MissingComponent) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.EntityTransformNative", - func = "propogateTransform" - ), - c -)] -fn propagate_transform( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<NTransform> { - if let Ok(et) = world.get::<&mut EntityTransform>(entity) { - let result = et.propagate(world, entity); - Ok(result.into()) - } else { - Err(DropbearNativeError::MissingComponent) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "getEnabled" - ), - c -)] -fn on_rails_get_enabled( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<bool> { - let rails = world - .get::<&OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - Ok(rails.enabled) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "setEnabled" - ), - c -)] -fn on_rails_set_enabled( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - enabled: bool, -) -> DropbearNativeResult<()> { - let mut rails = world - .get::<&mut OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - rails.enabled = enabled; - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "existsForEntity" - ), - c -)] -fn on_rails_exists_for_entity( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<bool> { - Ok(world.get::<&OnRails>(entity).is_ok()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "getProgress" - ), - c -)] -fn on_rails_get_progress( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<f32> { - let rails = world - .get::<&OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - Ok(rails.progress) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "setProgress" - ), - c -)] -fn on_rails_set_progress( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - progress: f32, -) -> DropbearNativeResult<()> { - let mut rails = world - .get::<&mut OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - rails.progress = progress.clamp(0.0, 1.0); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "getPathLen" - ), - c -)] -fn on_rails_get_path_len( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<i32> { - let rails = world - .get::<&OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - Ok(rails.path.len() as i32) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "getPathPoint" - ), - c -)] -fn on_rails_get_path_point( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - index: i32, -) -> DropbearNativeResult<NVector3> { - let rails = world - .get::<&OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - let point = rails - .path - .get(index as usize) - .ok_or(DropbearNativeError::InvalidArgument)?; - Ok(NVector3::from(&point.position)) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "clearPath" - ), - c -)] -fn on_rails_clear_path( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<()> { - let mut rails = world - .get::<&mut OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - rails.path.clear(); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "pushPathPoint" - ), - c -)] -fn on_rails_push_path_point( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - point: &NVector3, -) -> DropbearNativeResult<()> { - let mut rails = world - .get::<&mut OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - rails.path.push(RailPoint { position: DVec3::from(point), rotation: None }); - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "getPathPointHasRotation" - ), - c -)] -fn on_rails_get_path_point_has_rotation( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - index: i32, -) -> DropbearNativeResult<bool> { - let rails = world - .get::<&OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - let point = rails - .path - .get(index as usize) - .ok_or(DropbearNativeError::InvalidArgument)?; - Ok(point.rotation.is_some()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "getPathPointRotation" - ), - c -)] -fn on_rails_get_path_point_rotation( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - index: i32, -) -> DropbearNativeResult<NQuaternion> { - let rails = world - .get::<&OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - let point = rails - .path - .get(index as usize) - .ok_or(DropbearNativeError::InvalidArgument)?; - Ok(NQuaternion::from(point.rotation.unwrap_or(DQuat::IDENTITY))) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "pushPathPointWithRotation" - ), - c -)] -fn on_rails_push_path_point_with_rotation( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - point: &NVector3, - rotation: &NQuaternion, -) -> DropbearNativeResult<()> { - let mut rails = world - .get::<&mut OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - rails.path.push(RailPoint { - position: DVec3::from(point), - rotation: Some(DQuat::from(*rotation)), - }); - Ok(()) -} - -// `0` = Automatic, `1` = FollowEntity, `2` = AxisDriven, `3` = Manual. -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "getDriveType" - ), - c -)] -fn on_rails_get_drive_type( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<i32> { - let rails = world - .get::<&OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - let tag = match &rails.drive { - RailDrive::Automatic { .. } => 0, - RailDrive::FollowEntity { .. } => 1, - RailDrive::AxisDriven { .. } => 2, - RailDrive::Manual => 3, - }; - Ok(tag) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "setDriveAutomatic" - ), - c -)] -fn on_rails_set_drive_automatic( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - speed: f32, - looping: bool, -) -> DropbearNativeResult<()> { - let mut rails = world - .get::<&mut OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - rails.drive = RailDrive::Automatic { speed, looping }; - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "setDriveFollowEntity" - ), - c -)] -fn on_rails_set_drive_follow_entity( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - target: u64, - monotonic: bool, -) -> DropbearNativeResult<()> { - let target_entity = - hecs::Entity::from_bits(target).ok_or(DropbearNativeError::InvalidEntity)?; - let mut rails = world - .get::<&mut OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - rails.drive = RailDrive::FollowEntity { - target: target_entity, - monotonic, - }; - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "setDriveAxisDriven" - ), - c -)] -fn on_rails_set_drive_axis_driven( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, - target: u64, - axis: &NVector3, - range_min: f32, - range_max: f32, -) -> DropbearNativeResult<()> { - let target_entity = - hecs::Entity::from_bits(target).ok_or(DropbearNativeError::InvalidEntity)?; - let mut rails = world - .get::<&mut OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - rails.drive = RailDrive::AxisDriven { - target: target_entity, - axis: Vec3::new(axis.x as f32, axis.y as f32, axis.z as f32), - range: (range_min, range_max), - }; - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "setDriveManual" - ), - c -)] -fn on_rails_set_drive_manual( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<()> { - let mut rails = world - .get::<&mut OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - rails.drive = RailDrive::Manual; - Ok(()) -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "getDriveAutomaticSpeed" - ), - c -)] -fn on_rails_get_drive_automatic_speed( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<f32> { - let rails = world - .get::<&OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::Automatic { speed, .. } = &rails.drive { - Ok(*speed) - } else { - Err(DropbearNativeError::InvalidArgument) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "getDriveAutomaticLooping" - ), - c -)] -fn on_rails_get_drive_automatic_looping( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<bool> { - let rails = world - .get::<&OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::Automatic { looping, .. } = &rails.drive { - Ok(*looping) - } else { - Err(DropbearNativeError::InvalidArgument) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "getDriveFollowEntityTarget" - ), - c -)] -fn on_rails_get_drive_follow_entity_target( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<u64> { - let rails = world - .get::<&OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::FollowEntity { target, .. } = &rails.drive { - Ok(target.to_bits().get()) - } else { - Err(DropbearNativeError::InvalidArgument) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "getDriveFollowEntityMonotonic" - ), - c -)] -fn on_rails_get_drive_follow_entity_monotonic( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<bool> { - let rails = world - .get::<&OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::FollowEntity { monotonic, .. } = &rails.drive { - Ok(*monotonic) - } else { - Err(DropbearNativeError::InvalidArgument) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "getDriveAxisDrivenTarget" - ), - c -)] -fn on_rails_get_drive_axis_driven_target( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<u64> { - let rails = world - .get::<&OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::AxisDriven { target, .. } = &rails.drive { - Ok(target.to_bits().get()) - } else { - Err(DropbearNativeError::InvalidArgument) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "getDriveAxisDrivenAxis" - ), - c -)] -fn on_rails_get_drive_axis_driven_axis( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<NVector3> { - let rails = world - .get::<&OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::AxisDriven { axis, .. } = &rails.drive { - Ok(NVector3::from(*axis)) - } else { - Err(DropbearNativeError::InvalidArgument) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "getDriveAxisDrivenRangeMin" - ), - c -)] -fn on_rails_get_drive_axis_driven_range_min( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<f32> { - let rails = world - .get::<&OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::AxisDriven { range, .. } = &rails.drive { - Ok(range.0) - } else { - Err(DropbearNativeError::InvalidArgument) - } -} - -#[dropbear_macro::export( - kotlin( - class = "com.dropbear.components.camera.OnRailsNative", - func = "getDriveAxisDrivenRangeMax" - ), - c -)] -fn on_rails_get_drive_axis_driven_range_max( - #[dropbear_macro::define(WorldPtr)] world: &hecs::World, - #[dropbear_macro::entity] entity: hecs::Entity, -) -> DropbearNativeResult<f32> { - let rails = world - .get::<&OnRails>(entity) - .map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::AxisDriven { range, .. } = &rails.drive { - Ok(range.1) - } else { - Err(DropbearNativeError::InvalidArgument) - } -} @@ -1,37 +1,11 @@ -//! FFI and C types of other library types, as used in the scripting module. use crate::physics::PhysicsState; -use crate::scripting::jni::utils::{FromJObject, ToJObject}; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; use dropbear_engine::entity::Transform; use glam::{DQuat, DVec3, Vec3}; -use hecs::Entity; -use jni::objects::{JObject, JValue}; -use jni::sys::jdouble; -use jni::{Env, jni_sig, jni_str}; use rapier3d::data::Index; -use rapier3d::parry::query::ShapeCastStatus; -use rapier3d::prelude::ColliderHandle; +use rapier3d::geometry::ColliderHandle; +use serde::{Deserialize, Serialize}; -#[repr(C)] -#[derive(Clone, Copy, Debug)] -pub struct NColour { - pub r: u8, - pub g: u8, - pub b: u8, - pub a: u8, -} - -impl NColour { - pub fn to_f32_array(self) -> [f32; 4] { - [ - self.r as f32 / 255.0, - self.g as f32 / 255.0, - self.b as f32 / 255.0, - self.a as f32 / 255.0, - ] - } -} +// --------------------------------------------------------------- math types -- #[repr(C)] #[derive(Clone, Copy, Debug)] @@ -51,42 +25,34 @@ impl From<glam::DVec2> for NVector2 { Self { x: v.x, y: v.y } } } - impl From<[f64; 2]> for NVector2 { - fn from(value: [f64; 2]) -> Self { - NVector2 { - x: value[0], - y: value[1], - } + fn from(v: [f64; 2]) -> Self { + Self { x: v[0], y: v[1] } } } - impl From<[f32; 2]> for NVector2 { - fn from(value: [f32; 2]) -> Self { - NVector2 { - x: value[0] as f64, - y: value[1] as f64, + fn from(v: [f32; 2]) -> Self { + Self { + x: v[0] as f64, + y: v[1] as f64, } } } - +impl From<(f64, f64)> for NVector2 { + fn from(v: (f64, f64)) -> Self { + Self { x: v.0, y: v.1 } + } +} impl From<NVector2> for glam::DVec2 { fn from(v: NVector2) -> Self { Self::new(v.x, v.y) } } -impl From<(f64, f64)> for NVector2 { - fn from(value: (f64, f64)) -> Self { - Self { - x: value.0, - y: value.1, - } - } -} +// ----------------------------------------------------------------------------- #[repr(C)] -#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize, PartialEq)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)] pub struct NVector3 { pub x: f64, pub y: f64, @@ -94,10 +60,9 @@ pub struct NVector3 { } impl NVector3 { - pub fn new(x: f64, y: f64, z: f64) -> NVector3 { - NVector3 { x, y, z } + pub fn new(x: f64, y: f64, z: f64) -> Self { + Self { x, y, z } } - pub fn to_array(&self) -> [f64; 3] { [self.x, self.y, self.z] } @@ -106,8 +71,8 @@ impl NVector3 { } } -impl From<glam::DVec3> for NVector3 { - fn from(v: glam::DVec3) -> Self { +impl From<DVec3> for NVector3 { + fn from(v: DVec3) -> Self { Self { x: v.x, y: v.y, @@ -115,9 +80,8 @@ impl From<glam::DVec3> for NVector3 { } } } - -impl From<&glam::DVec3> for NVector3 { - fn from(v: &glam::DVec3) -> Self { +impl From<&DVec3> for NVector3 { + fn from(v: &DVec3) -> Self { Self { x: v.x, y: v.y, @@ -125,8 +89,7 @@ impl From<&glam::DVec3> for NVector3 { } } } - -impl From<&NVector3> for glam::DVec3 { +impl From<&NVector3> for DVec3 { fn from(v: &NVector3) -> Self { Self { x: v.x, @@ -135,101 +98,46 @@ impl From<&NVector3> for glam::DVec3 { } } } - -impl From<[f64; 3]> for NVector3 { - fn from(value: [f64; 3]) -> Self { - NVector3 { - x: value[0], - y: value[1], - z: value[2], - } - } -} - -impl From<[f32; 3]> for NVector3 { - fn from(value: [f32; 3]) -> Self { - NVector3 { - x: value[0] as f64, - y: value[1] as f64, - z: value[2] as f64, - } +impl From<NVector3> for DVec3 { + fn from(v: NVector3) -> Self { + Self::new(v.x, v.y, v.z) } } - -impl From<glam::Vec3> for NVector3 { - fn from(value: Vec3) -> Self { +impl From<Vec3> for NVector3 { + fn from(v: Vec3) -> Self { Self { - x: value.x as f64, - y: value.y as f64, - z: value.z as f64, + x: v.x as f64, + y: v.y as f64, + z: v.z as f64, } } } - -impl From<NVector3> for glam::DVec3 { +impl From<NVector3> for Vec3 { fn from(v: NVector3) -> Self { - Self::new(v.x, v.y, v.z) + Self::new(v.x as f32, v.y as f32, v.z as f32) } } - -impl FromJObject for NVector3 { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> - where - Self: Sized, - { - let class = env - .load_class(jni_str!("com/dropbear/math/Vector3d")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - if !env - .is_instance_of(obj, &class) - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? - { - return Err(DropbearNativeError::InvalidArgument); +impl From<[f64; 3]> for NVector3 { + fn from(v: [f64; 3]) -> Self { + Self { + x: v[0], + y: v[1], + z: v[2], } - - let x = env - .get_field(obj, jni_str!("x"), jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let y = env - .get_field(obj, jni_str!("y"), jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let z = env - .get_field(obj, jni_str!("z"), jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - Ok(NVector3::new(x, y, z)) } } - -impl ToJObject for NVector3 { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com/dropbear/math/Vector3d")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let args = [ - jni::objects::JValue::Double(self.x), - jni::objects::JValue::Double(self.y), - jni::objects::JValue::Double(self.z), - ]; - - let obj = env - .new_object(&class, jni_sig!((double, double, double) -> void), &args) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) +impl From<[f32; 3]> for NVector3 { + fn from(v: [f32; 3]) -> Self { + Self { + x: v[0] as f64, + y: v[1] as f64, + z: v[2] as f64, + } } } +// ----------------------------------------------------------------------------- + #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct NVector4 { @@ -240,15 +148,11 @@ pub struct NVector4 { } impl NVector4 { - pub fn new(x: f64, y: f64, z: f64, w: f64) -> NVector4 { - NVector4 { x, y, z, w } - } - - pub fn to_array(&self) -> [f64; 3] { - [self.x, self.y, self.z] + pub fn new(x: f64, y: f64, z: f64, w: f64) -> Self { + Self { x, y, z, w } } - pub fn to_float_array(&self) -> [f32; 3] { - [self.x as f32, self.y as f32, self.z as f32] + pub fn to_array(&self) -> [f64; 4] { + [self.x, self.y, self.z, self.w] } } @@ -262,115 +166,44 @@ impl From<glam::DVec4> for NVector4 { } } } - -impl From<[f64; 4]> for NVector4 { - fn from(value: [f64; 4]) -> Self { - NVector4 { - x: value[0], - y: value[1], - z: value[2], - w: value[3], - } - } -} - -impl From<[f32; 4]> for NVector4 { - fn from(value: [f32; 4]) -> Self { - NVector4 { - x: value[0] as f64, - y: value[1] as f64, - z: value[2] as f64, - w: value[3] as f64, - } - } -} - impl From<NVector4> for glam::DVec4 { fn from(v: NVector4) -> Self { Self::new(v.x, v.y, v.z, v.w) } } - -impl FromJObject for NVector4 { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> - where - Self: Sized, - { - let class = env - .load_class(jni_str!("com/dropbear/math/Vector4d")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - if !env - .is_instance_of(obj, &class) - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? - { - return Err(DropbearNativeError::InvalidArgument); +impl From<[f64; 4]> for NVector4 { + fn from(v: [f64; 4]) -> Self { + Self { + x: v[0], + y: v[1], + z: v[2], + w: v[3], } - - let x = env - .get_field(obj, jni_str!("x"), jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let y = env - .get_field(obj, jni_str!("y"), jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let z = env - .get_field(obj, jni_str!("z"), jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let w = env - .get_field(obj, jni_str!("w"), jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - Ok(NVector4::new(x, y, z, w)) } } - -impl ToJObject for NVector4 { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com/dropbear/math/Vector3d")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let args = [ - jni::objects::JValue::Double(self.x), - jni::objects::JValue::Double(self.y), - jni::objects::JValue::Double(self.z), - jni::objects::JValue::Double(self.w), - ]; - - let obj = env - .new_object( - &class, - jni_sig!((double, double, double, double) -> void), - &args, - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) +impl From<[f32; 4]> for NVector4 { + fn from(v: [f32; 4]) -> Self { + Self { + x: v[0] as f64, + y: v[1] as f64, + z: v[2] as f64, + w: v[3] as f64, + } } } - impl From<NQuaternion> for NVector4 { - fn from(value: NQuaternion) -> Self { + fn from(q: NQuaternion) -> Self { Self { - x: value.x, - y: value.y, - z: value.z, - w: value.w, + x: q.x, + y: q.y, + z: q.z, + w: q.w, } } } +// ----------------------------------------------------------------------------- + #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct NQuaternion { @@ -381,125 +214,57 @@ pub struct NQuaternion { } impl From<DQuat> for NQuaternion { - fn from(value: DQuat) -> Self { + fn from(v: DQuat) -> Self { Self { - x: value.x, - y: value.y, - z: value.z, - w: value.w, + x: v.x, + y: v.y, + z: v.z, + w: v.w, } } } - -impl From<NQuaternion> for glam::DQuat { - fn from(value: NQuaternion) -> Self { +impl From<NQuaternion> for DQuat { + fn from(v: NQuaternion) -> Self { Self { - x: value.x, - y: value.y, - z: value.z, - w: value.w, + x: v.x, + y: v.y, + z: v.z, + w: v.w, } } } - impl From<[f64; 4]> for NQuaternion { - fn from(value: [f64; 4]) -> Self { + fn from(v: [f64; 4]) -> Self { Self { - x: value[0], - y: value[1], - z: value[2], - w: value[3], + x: v[0], + y: v[1], + z: v[2], + w: v[3], } } } - impl From<glam::Quat> for NQuaternion { - fn from(value: glam::Quat) -> Self { + fn from(v: glam::Quat) -> Self { Self { - x: value.x as f64, - y: value.y as f64, - z: value.z as f64, - w: value.w as f64, + x: v.x as f64, + y: v.y as f64, + z: v.z as f64, + w: v.w as f64, } } } - impl From<NVector4> for NQuaternion { - fn from(value: NVector4) -> Self { + fn from(v: NVector4) -> Self { Self { - x: value.x, - y: value.y, - z: value.z, - w: value.w, + x: v.x, + y: v.y, + z: v.z, + w: v.w, } } } -impl ToJObject for NQuaternion { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com/dropbear/math/Quaterniond")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let args = [ - JValue::Double(self.x), - JValue::Double(self.y), - JValue::Double(self.z), - JValue::Double(self.w), - ]; - - env.new_object( - &class, - jni_sig!((double, double, double, double) -> void), - &args, - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) - } -} - -impl FromJObject for NQuaternion { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> - where - Self: Sized, - { - let class = env - .load_class(jni_str!("com/dropbear/math/Quaterniond")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - if !env - .is_instance_of(obj, &class) - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? - { - return Err(DropbearNativeError::InvalidArgument); - } - - let x = env - .get_field(obj, jni_str!("x"), jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let y = env - .get_field(obj, jni_str!("y"), jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let z = env - .get_field(obj, jni_str!("z"), jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let w = env - .get_field(obj, jni_str!("w"), jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - Ok(NQuaternion { x, y, z, w }) - } -} +// ----------------------------------------------------------------------------- #[repr(C)] #[derive(Clone, Copy)] @@ -510,496 +275,122 @@ pub struct NTransform { } impl From<Transform> for NTransform { - fn from(value: Transform) -> Self { + fn from(v: Transform) -> Self { Self { - position: NVector3::from(value.position), - rotation: NQuaternion::from(value.rotation), - scale: NVector3::from(value.scale), + position: NVector3::from(v.position), + rotation: NQuaternion::from(v.rotation), + scale: NVector3::from(v.scale), } } } - impl From<NTransform> for Transform { - fn from(value: NTransform) -> Self { + fn from(v: NTransform) -> Self { Self { - position: DVec3::from(value.position), - rotation: DQuat::from(value.rotation), - scale: DVec3::from(value.scale), + position: DVec3::from(v.position), + rotation: DQuat::from(v.rotation), + scale: DVec3::from(v.scale), } } } -impl FromJObject for NTransform { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { - let pos_val = env - .get_field( - obj, - jni_str!("position"), - jni_sig!(com.dropbear.math.Vector3d), - ) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - - let pos_obj = pos_val - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let rot_val = env - .get_field( - obj, - jni_str!("rotation"), - jni_sig!(com.dropbear.math.Quaterniond), - ) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - - let rot_obj = rot_val - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let scale_val = env - .get_field(obj, jni_str!("scale"), jni_sig!(com.dropbear.math.Vector3d)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - - let scale_obj = scale_val - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let position: DVec3 = NVector3::from_jobject(env, &pos_obj)?.into(); - let scale: DVec3 = NVector3::from_jobject(env, &scale_obj)?.into(); - - let mut get_double = |field| -> DropbearNativeResult<f64> { - env.get_field(&rot_obj, field, jni_sig!(double)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed) - }; - - let rx = get_double(jni_str!("x"))?; - let ry = get_double(jni_str!("y"))?; - let rz = get_double(jni_str!("z"))?; - let rw = get_double(jni_str!("w"))?; - - let rotation = DQuat::from_xyzw(rx, ry, rz, rw); - - Ok(NTransform { - position: position.into(), - rotation: rotation.into(), - scale: scale.into(), - }) - } -} - -impl ToJObject for NTransform { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com/dropbear/math/Transform")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let args = [ - JValue::Double(self.position.x), - JValue::Double(self.position.y), - JValue::Double(self.position.z), - JValue::Double(self.rotation.x), - JValue::Double(self.rotation.y), - JValue::Double(self.rotation.z), - JValue::Double(self.rotation.w), - JValue::Double(self.scale.x), - JValue::Double(self.scale.y), - JValue::Double(self.scale.z), - ]; - - env.new_object(&class, jni_sig!((double, double, double, double, double, double, double, double, double, double) -> void), &args) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) - } -} +// ------------------------------------------------------------------- colour -- #[repr(C)] -#[derive(Clone, Copy)] -pub struct NCollider { - pub index: IndexNative, - pub entity_id: u64, - pub id: u32, +#[derive(Clone, Copy, Debug)] +pub struct NColour { + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8, } -impl ToJObject for NCollider { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let collider_cls = env - .load_class(jni_str!("com/dropbear/physics/Collider")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let index_cls = env - .load_class(jni_str!("com/dropbear/physics/Index")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let entity_cls = env - .load_class(jni_str!("com/dropbear/EntityId")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let entity_obj = env - .new_object( - &entity_cls, - jni_sig!((long) -> void), - &[JValue::Long(self.entity_id as i64)], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - let index_obj = env - .new_object( - &index_cls, - jni_sig!((int, int) -> void), - &[ - JValue::Int(self.index.index as i32), - JValue::Int(self.index.generation as i32), - ], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - let collider_obj = env - .new_object( - collider_cls, - jni_sig!((com.dropbear.physics.Index, com.dropbear.EntityId, int) -> void), - &[ - JValue::Object(&index_obj), - JValue::Object(&entity_obj), - JValue::Int(self.id as i32), - ], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(collider_obj) +impl NColour { + pub fn to_f32_array(self) -> [f32; 4] { + [ + self.r as f32 / 255.0, + self.g as f32 / 255.0, + self.b as f32 / 255.0, + self.a as f32 / 255.0, + ] } -} - -impl FromJObject for NCollider { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> - where - Self: Sized, - { - let index_obj = env - .get_field(obj, jni_str!("index"), jni_sig!(com.dropbear.physics.Index)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let entity_obj = env - .get_field(obj, jni_str!("entity"), jni_sig!(com.dropbear.EntityId)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let id_val = env - .get_field(obj, jni_str!("id"), jni_sig!(int)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .i() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let entity_raw = env - .get_field(&entity_obj, jni_str!("raw"), jni_sig!(long)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .j() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let idx_val = env - .get_field(&index_obj, jni_str!("index"), jni_sig!(int)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .i() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let gen_val = env - .get_field(&index_obj, jni_str!("generation"), jni_sig!(int)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .i() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + pub fn to_linear_rgb(self) -> DVec3 { + DVec3::new( + self.r as f64 / 255.0, + self.g as f64 / 255.0, + self.b as f64 / 255.0, + ) + } - Ok(NCollider { - index: IndexNative { - index: idx_val as u32, - generation: gen_val as u32, - }, - entity_id: entity_raw as u64, - id: id_val as u32, - }) + pub fn from_linear_rgb(rgb: DVec3) -> Self { + fn clamp_u8(x: f64) -> u8 { + (x * 255.0).round().clamp(0.0, 255.0) as u8 + } + Self { + r: clamp_u8(rgb.x), + g: clamp_u8(rgb.y), + b: clamp_u8(rgb.z), + a: 255, + } } } +// ----------------------------------------------------------- physics types --- + #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub struct IndexNative { pub index: u32, pub generation: u32, } impl From<Index> for IndexNative { - fn from(value: Index) -> Self { - let raw = value.into_raw_parts(); + fn from(v: Index) -> Self { + let raw = v.into_raw_parts(); Self { index: raw.0, generation: raw.1, } } } - impl From<IndexNative> for Index { - fn from(value: IndexNative) -> Self { - Self::from_raw_parts(value.index, value.generation) - } -} - -impl ToJObject for IndexNative { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let cls = env - .load_class(jni_str!("com/dropbear/physics/Index")) - .map_err(|e| { - eprintln!("[JNI Error] Could not find Index class: {:?}", e); - DropbearNativeError::GenericError - })?; - - let obj = env - .new_object( - cls, - jni_sig!((int, int) -> void), - &[ - JValue::Int(self.index as i32), - JValue::Int(self.generation as i32), - ], - ) - .map_err(|e| { - eprintln!("[JNI Error] Failed to create Index object: {:?}", e); - DropbearNativeError::GenericError - })?; - - Ok(obj) + fn from(v: IndexNative) -> Self { + Self::from_raw_parts(v.index, v.generation) } } -impl ToJObject for Option<IndexNative> { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - match self { - Some(value) => value.to_jobject(env), - None => Ok(JObject::null()), - } - } -} - -impl FromJObject for IndexNative { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> - where - Self: Sized, - { - let idx_val = env - .get_field(obj, jni_str!("index"), jni_sig!(int)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .i() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let gen_val = env - .get_field(obj, jni_str!("generation"), jni_sig!(int)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .i() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - Ok(IndexNative { - index: idx_val as u32, - generation: gen_val as u32, - }) - } -} +// ----------------------------------------------------------------------------- #[repr(C)] -#[derive(Clone, Copy)] -pub struct RigidBodyContext { +#[derive(Clone, Copy, Debug)] +pub struct NCollider { pub index: IndexNative, pub entity_id: u64, + pub id: u32, } -impl FromJObject for RigidBodyContext { - fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> - where - Self: Sized, - { - let index_obj = env - .get_field(obj, jni_str!("index"), jni_sig!(com.dropbear.physics.Index)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let idx_val = env - .get_field(&index_obj, jni_str!("index"), jni_sig!(int)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .i() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let gen_val = env - .get_field(&index_obj, jni_str!("generation"), jni_sig!(int)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .i() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let entity_obj = env - .get_field(obj, jni_str!("entity"), jni_sig!(com.dropbear.EntityId)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let entity_raw = env - .get_field(&entity_obj, jni_str!("raw"), jni_sig!(long)) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .j() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - Ok(RigidBodyContext { - index: IndexNative { - index: idx_val as u32, - generation: gen_val as u32, - }, - entity_id: entity_raw as u64, - }) - } -} - -impl ToJObject for RigidBodyContext { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let index_cls = env - .load_class(jni_str!("com/dropbear/physics/Index")) - .map_err(|e| { - eprintln!( - "[JNI Error] Class 'com/dropbear/physics/Index' not found: {:?}", - e - ); - DropbearNativeError::JNIClassNotFound - })?; - - let entity_cls = env - .load_class(jni_str!("com/dropbear/EntityId")) - .map_err(|e| { - eprintln!( - "[JNI Error] Class 'com/dropbear/EntityId' not found: {:?}", - e - ); - DropbearNativeError::JNIClassNotFound - })?; - - let rb_cls = env - .load_class(jni_str!("com/dropbear/physics/RigidBody")) - .map_err(|e| { - eprintln!( - "[JNI Error] Class 'com/dropbear/physics/RigidBody' not found: {:?}", - e - ); - DropbearNativeError::JNIClassNotFound - })?; - - let index_obj = env - .new_object( - &index_cls, - jni_sig!((int, int) -> void), - &[ - JValue::Int(self.index.index as i32), - JValue::Int(self.index.generation as i32), - ], - ) - .map_err(|e| { - eprintln!("[JNI Error] Failed to create Index object: {:?}", e); - DropbearNativeError::JNIFailedToCreateObject - })?; - - let entity_obj = env - .new_object( - &entity_cls, - jni_sig!((long) -> void), - &[JValue::Long(self.entity_id as i64)], - ) - .map_err(|e| { - eprintln!("[JNI Error] Failed to create EntityId object: {:?}", e); - DropbearNativeError::JNIFailedToCreateObject - })?; - - let rb_obj = env - .new_object( - rb_cls, - jni_sig!((com.dropbear.physics.Index, com.dropbear.EntityId) -> void), - &[JValue::Object(&index_obj), JValue::Object(&entity_obj)], - ) - .map_err(|e| { - eprintln!("[JNI Error] Failed to create RigidBody object: {:?}", e); - DropbearNativeError::JNIFailedToCreateObject - })?; - - Ok(rb_obj) - } -} +// ----------------------------------------------------------------------------- #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub struct RayHit { pub collider: NCollider, pub distance: f64, } -impl ToJObject for RayHit { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let collider = self.collider.to_jobject(env)?; - let distance = self.distance as jdouble; - - let class = env - .load_class(jni_str!("com/dropbear/physics/RayHit")) - .map_err(|e| { - eprintln!("[JNI Error] Failed to create RayHit object: {:?}", e); - DropbearNativeError::JNIClassNotFound - })?; - - let object = env - .new_object( - class, - jni_sig!((com.dropbear.physics.Collider, double) -> void), - &[JValue::Object(&collider), JValue::Double(distance)], - ) - .map_err(|e| { - eprintln!("[JNI Error] Failed to create RayHit object: {:?}", e); - DropbearNativeError::JNIFailedToCreateObject - })?; - - Ok(object) - } -} +// ----------------------------------------------------------------------------- #[repr(C)] -#[dropbear_macro::repr_c_enum] -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum NShapeCastStatus { - OutOfIterations, - Converged, - Failed, - PenetratingOrWithinTargetDist, -} - -impl Into<ShapeCastStatus> for NShapeCastStatus { - fn into(self) -> ShapeCastStatus { - match self { - NShapeCastStatus::OutOfIterations => ShapeCastStatus::OutOfIterations, - NShapeCastStatus::Converged => ShapeCastStatus::Converged, - NShapeCastStatus::Failed => ShapeCastStatus::Failed, - NShapeCastStatus::PenetratingOrWithinTargetDist => { - ShapeCastStatus::PenetratingOrWithinTargetDist - } - } - } +#[derive(Clone, Copy, Debug)] +pub struct RigidBodyContext { + pub index: IndexNative, + pub entity_id: u64, } -impl Into<NShapeCastStatus> for ShapeCastStatus { - fn into(self) -> NShapeCastStatus { - match self { - ShapeCastStatus::OutOfIterations => NShapeCastStatus::OutOfIterations, - ShapeCastStatus::Converged => NShapeCastStatus::Converged, - ShapeCastStatus::Failed => NShapeCastStatus::Failed, - ShapeCastStatus::PenetratingOrWithinTargetDist => { - NShapeCastStatus::PenetratingOrWithinTargetDist - } - } - } -} +// ----------------------------------------------------------------------------- #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub struct NShapeCastHit { pub collider: NCollider, pub distance: f64, @@ -1010,210 +401,108 @@ pub struct NShapeCastHit { pub status: NShapeCastStatus, } -impl ToJObject for NShapeCastHit { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - use jni::sys::jdouble; - - let collider = self.collider.to_jobject(env)?; - let witness1 = self.witness1.to_jobject(env)?; - let witness2 = self.witness2.to_jobject(env)?; - let normal1 = self.normal1.to_jobject(env)?; - let normal2 = self.normal2.to_jobject(env)?; - let status = self.status.to_jobject(env)?; - - let distance = self.distance as jdouble; - - let class = env - .load_class(jni_str!("com/dropbear/physics/ShapeCastHit")) - .map_err(|e| { - eprintln!("[JNI Error] Failed to find ShapeCastHit class: {:?}", e); - DropbearNativeError::JNIClassNotFound - })?; - - let object = env - .new_object( - class, - jni_sig!("(Lcom/dropbear/physics/Collider;DLcom/dropbear/math/Vector3d;Lcom/dropbear/math/Vector3d;Lcom/dropbear/math/Vector3d;Lcom/dropbear/math/Vector3d;Lcom/dropbear/physics/ShapeCastStatus;)V"), - &[ - JValue::Object(&collider), - JValue::Double(distance), - JValue::Object(&witness1), - JValue::Object(&witness2), - JValue::Object(&normal1), - JValue::Object(&normal2), - JValue::Object(&status), - ], - ) - .map_err(|e| { - eprintln!("[JNI Error] Failed to create ShapeCastHit object: {:?}", e); - DropbearNativeError::JNIFailedToCreateObject - })?; +#[repr(C)] +#[dropbear_macro::repr_c_enum] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum NShapeCastStatus { + OutOfIterations, + Converged, + Failed, + PenetratingOrWithinTargetDist, +} - Ok(object) +impl From<rapier3d::parry::query::ShapeCastStatus> for NShapeCastStatus { + fn from(v: rapier3d::parry::query::ShapeCastStatus) -> Self { + use rapier3d::parry::query::ShapeCastStatus; + match v { + ShapeCastStatus::OutOfIterations => Self::OutOfIterations, + ShapeCastStatus::Converged => Self::Converged, + ShapeCastStatus::Failed => Self::Failed, + ShapeCastStatus::PenetratingOrWithinTargetDist => Self::PenetratingOrWithinTargetDist, + } } } +// -------------------------------------------------------------- event types -- + #[repr(C)] -#[derive(Clone, Copy)] -/// Class: `com.dropbear.physics.CollisionEventType` +#[derive(Clone, Copy, Debug)] pub enum CollisionEventType { Started, Stopped, } -impl ToJObject for CollisionEventType { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com/dropbear/physics/CollisionEventType")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let name = match self { - CollisionEventType::Started => "Started", - CollisionEventType::Stopped => "Stopped", - }; - let name_jstring = env - .new_string(name) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - let value = env - .call_static_method( - class, - jni_str!("valueOf"), - jni_sig!((java.lang.String) -> com.dropbear.physics.CollisionEventType), - &[JValue::from(&name_jstring)], - ) - .map_err(|_| DropbearNativeError::JNIMethodNotFound)? - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - Ok(value) - } -} - #[repr(C)] #[derive(Clone, Copy)] pub struct CollisionEvent { - pub(crate) event_type: CollisionEventType, - pub(crate) collider1: NCollider, - pub(crate) collider2: NCollider, - pub(crate) flags: u64, + pub event_type: CollisionEventType, + pub collider1: NCollider, + pub collider2: NCollider, + pub flags: u64, } impl CollisionEvent { pub fn collider1_entity_id(&self) -> u64 { self.collider1.entity_id } - pub fn collider2_entity_id(&self) -> u64 { self.collider2.entity_id } -} -impl CollisionEvent { pub fn from_rapier3d( physics: &PhysicsState, value: rapier3d::geometry::CollisionEvent, - ) -> Option<CollisionEvent> { + ) -> Option<Self> { + fn resolve( + physics: &PhysicsState, + handle: ColliderHandle, + ) -> Option<hecs::Entity> { + let label = physics.colliders_entity_map.iter().find_map(|(l, s)| { + for (_, h) in s { + if *h == handle { + return Some(l.clone()); + } + } + None + })?; + physics + .entity_label_map + .iter() + .find_map(|(e, l)| if l == &label { Some(*e) } else { None }) + } + match value { rapier3d::prelude::CollisionEvent::Started(col1, col2, flags) => { - let collider1_info = physics - .colliders_entity_map - .iter() - .find_map(|(l, s)| { - for (_, h) in s { - if col1 == *h { - return Some(l.clone()); - } - } - None - }) - .and_then(|label| { - physics - .entity_label_map - .iter() - .find_map(|(e, l)| if l == &label { Some(*e) } else { None }) - })?; - - let collider2_info = physics - .colliders_entity_map - .iter() - .find_map(|(l, s)| { - for (_, h) in s { - if col2 == *h { - return Some(l.clone()); - } - } - None - }) - .and_then(|label| { - physics - .entity_label_map - .iter() - .find_map(|(e, l)| if l == &label { Some(*e) } else { None }) - })?; - + let e1 = resolve(physics, col1)?; + let e2 = resolve(physics, col2)?; Some(Self { event_type: CollisionEventType::Started, collider1: NCollider { index: IndexNative::from(col1.0), - entity_id: collider1_info.to_bits().get(), + entity_id: e1.to_bits().get(), id: col1.into_raw_parts().0, }, collider2: NCollider { index: IndexNative::from(col2.0), - entity_id: collider2_info.to_bits().get(), + entity_id: e2.to_bits().get(), id: col2.into_raw_parts().0, }, flags: flags.bits() as u64, }) } rapier3d::prelude::CollisionEvent::Stopped(col1, col2, flags) => { - let collider1_info = physics - .colliders_entity_map - .iter() - .find_map(|(l, s)| { - for (_, h) in s { - if col1 == *h { - return Some(l.clone()); - } - } - None - }) - .and_then(|label| { - physics - .entity_label_map - .iter() - .find_map(|(e, l)| if l == &label { Some(*e) } else { None }) - })?; - - let collider2_info = physics - .colliders_entity_map - .iter() - .find_map(|(l, s)| { - for (_, h) in s { - if col2 == *h { - return Some(l.clone()); - } - } - None - }) - .and_then(|label| { - physics - .entity_label_map - .iter() - .find_map(|(e, l)| if l == &label { Some(*e) } else { None }) - })?; - + let e1 = resolve(physics, col1)?; + let e2 = resolve(physics, col2)?; Some(Self { event_type: CollisionEventType::Stopped, collider1: NCollider { index: IndexNative::from(col1.0), - entity_id: collider1_info.to_bits().get(), + entity_id: e1.to_bits().get(), id: col1.into_raw_parts().0, }, collider2: NCollider { index: IndexNative::from(col2.0), - entity_id: collider2_info.to_bits().get(), + entity_id: e2.to_bits().get(), id: col2.into_raw_parts().0, }, flags: flags.bits() as u64, @@ -1223,80 +512,42 @@ impl CollisionEvent { } } -impl ToJObject for CollisionEvent { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com/dropbear/physics/CollisionEvent")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let event_type = self.event_type.to_jobject(env)?; - let collider1 = self.collider1.to_jobject(env)?; - let collider2 = self.collider2.to_jobject(env)?; - - let flags = self.flags as i32; - let obj = env - .new_object( - class, - jni_sig!("(Lcom/dropbear/physics/CollisionEventType;Lcom/dropbear/physics/Collider;Lcom/dropbear/physics/Collider;I)V"), - &[ - JValue::Object(&event_type), - JValue::Object(&collider1), - JValue::Object(&collider2), - JValue::Int(flags), - ], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } -} - #[repr(C)] #[derive(Clone, Copy)] pub struct ContactForceEvent { - pub(crate) collider1: NCollider, - pub(crate) collider2: NCollider, - pub(crate) total_force: NVector3, - pub(crate) total_force_magnitude: f64, - pub(crate) max_force_direction: NVector3, - pub(crate) max_force_magnitude: f64, + pub collider1: NCollider, + pub collider2: NCollider, + pub total_force: NVector3, + pub total_force_magnitude: f64, + pub max_force_direction: NVector3, + pub max_force_magnitude: f64, } impl ContactForceEvent { pub fn collider1_entity_id(&self) -> u64 { self.collider1.entity_id } - pub fn collider2_entity_id(&self) -> u64 { self.collider2.entity_id } -} -impl ContactForceEvent { pub fn from_rapier3d( physics: &PhysicsState, event: rapier3d::prelude::ContactForceEvent, - ) -> Option<ContactForceEvent> { - let find_entity = |collider_handle: ColliderHandle| -> Option<Entity> { - Some( - physics - .colliders_entity_map - .iter() - .find_map(|(l, s)| { - for (_, h) in s { - if collider_handle == *h { - return Some(l.clone()); - } - } - None - }) - .and_then(|label| { - physics - .entity_label_map - .iter() - .find_map(|(e, l)| if l == &label { Some(*e) } else { None }) - })?, - ) + ) -> Option<Self> { + let find_entity = |handle: ColliderHandle| -> Option<hecs::Entity> { + let label = physics.colliders_entity_map.iter().find_map(|(l, s)| { + for (_, h) in s { + if *h == handle { + return Some(l.clone()); + } + } + None + })?; + physics + .entity_label_map + .iter() + .find_map(|(e, l)| if l == &label { Some(*e) } else { None }) }; Some(Self { @@ -1311,47 +562,17 @@ impl ContactForceEvent { id: event.collider2.into_raw_parts().0, }, total_force: NVector3::new( - event.total_force.x.into(), - event.total_force.y.into(), - event.total_force.z.into(), + event.total_force.x as f64, + event.total_force.y as f64, + event.total_force.z as f64, ), total_force_magnitude: event.total_force_magnitude as f64, max_force_direction: NVector3::new( - event.max_force_direction.x.into(), - event.max_force_direction.y.into(), - event.max_force_direction.z.into(), + event.max_force_direction.x as f64, + event.max_force_direction.y as f64, + event.max_force_direction.z as f64, ), max_force_magnitude: event.max_force_magnitude as f64, }) } } - -impl ToJObject for ContactForceEvent { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com/dropbear/physics/ContactForceEvent")) - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - - let collider1 = self.collider1.to_jobject(env)?; - let collider2 = self.collider2.to_jobject(env)?; - let total_force = self.total_force.to_jobject(env)?; - let max_force_direction = self.max_force_direction.to_jobject(env)?; - - let obj = env - .new_object( - class, - jni_sig!("(Lcom/dropbear/physics/Collider;Lcom/dropbear/physics/Collider;Lcom/dropbear/math/Vector3d;DLcom/dropbear/math/Vector3d;D)V"), - &[ - JValue::Object(&collider1), - JValue::Object(&collider2), - JValue::Object(&total_force), - JValue::Double(self.total_force_magnitude), - JValue::Object(&max_force_direction), - JValue::Double(self.max_force_magnitude), - ], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } -} @@ -2,12 +2,9 @@ pub mod option; -use crate::scripting::result::DropbearNativeResult; use crate::ser::model::{EucalyptusMaterial, EucalyptusMesh, EucalyptusModel}; use crate::states::Node; use dropbear_engine::utils::ResourceReference; -use jni::objects::{JObject, JValue}; -use jni::{Env, jni_sig, jni_str}; use std::collections::hash_map::DefaultHasher; use std::fs; use std::hash::{Hash, Hasher}; @@ -850,9 +847,9 @@ pub fn start_deadlock_detector() { #[repr(C)] #[derive(Clone)] pub struct Progress { - pub(crate) current: usize, - pub(crate) total: usize, - pub(crate) message: String, + pub current: usize, + pub total: usize, + pub message: String, } impl Default for Progress { @@ -864,29 +861,3 @@ impl Default for Progress { } } } - -impl crate::scripting::jni::utils::ToJObject for crate::utils::Progress { - fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .load_class(jni_str!("com/dropbear/utils/Progress")) - .map_err(|_| crate::scripting::native::DropbearNativeError::JNIClassNotFound)?; - - let message_jstring = env - .new_string(&self.message) - .map_err(|_| crate::scripting::native::DropbearNativeError::JNIFailedToCreateObject)?; - - let obj = env - .new_object( - &class, - jni_sig!((double, double, java.lang.String) -> void), - &[ - JValue::Double(self.current as f64), - JValue::Double(self.total as f64), - JValue::from(&message_jstring), - ], - ) - .map_err(|_| crate::scripting::native::DropbearNativeError::JNIFailedToCreateObject)?; - - Ok(obj) - } -} @@ -66,7 +66,6 @@ use std::cmp::PartialEq; use std::collections::{HashSet, VecDeque}; use std::rc::Rc; use std::{collections::HashMap, path::PathBuf, sync::Arc, time::Instant}; -use arc_swap::ArcSwap; use tokio::sync::oneshot; use transform_gizmo_egui::{EnumSet, Gizmo, GizmoMode, GizmoOrientation}; use wgpu::{Color, Extent3d}; @@ -0,0 +1,26 @@ +[package] +name = "eucalyptus-exports" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +readme = "README.md" +authors.workspace = true +description = "Deals with exports of functions" + +[lib] +name = "eucalyptus_core" # keep the name for legacy reasons (it works as is, dont change please) +crate-type = ["cdylib"] + +[dependencies] +dropbear-engine = { path = "../dropbear-engine" } +eucalyptus-core = { path = "../eucalyptus-core" } +dropbear-macro = { path = "../dropbear-macro" } + +jni.workspace = true +hecs.workspace = true +glam.workspace = true +crossbeam-channel.workspace = true + +[build-dependencies] +goanna-gen = { path = "../goanna-gen" } @@ -0,0 +1,27 @@ +# eucalyptus-exports + +A shared library that absracts the exports from `eucalyptus-core` and presents them in as `libeucalyptus_core.so` +(or the equivalent for the related platform). + +## Language Support + +Despite the project using ECS, OOP-based paradigms are used heavily (in Kotlin) and can be used for your own bindings. + +Currently, this engine has support in these languages (priority in order) + +### JVM +Primarily JVM-based languages (Kotlin/JVM first-class, Java available). +#### Caveats +- Some issues with JVM is that the final executable that is shipped will need to be enabled +through an executable argument `{} --enable-jvm` (or enabled through a shipping setting within Project Settings). +Within the dropbear project, the JVM is used primarily for editor plugins and easy game prototyping. +C-Native-based languages can be used as an alternative for prototyping, however, it will require a longer build time +and will require the binding developer to create their own reflection methods. + +### C-Native +It is primarily supported through Kotlin/Native; however, the same exports *can* be used with +other languages, such as Python (no one has made such a thing). Follow the [Kotlin](../../scripting) class +structure for OOP based languages (idk about other paradigm based languages). + +There are headings created (albeit not always updated) under [dropbear.h](../../include/dropbear.h) that you can +use for reference or your own bindings. @@ -0,0 +1,13 @@ +fn main() { + // fuck you windows :( + #[cfg(target_os = "windows")] + { + println!("cargo:rustc-link-arg=/FORCE:MULTIPLE"); + println!("cargo:rustc-link-arg=/NODEFAULTLIB:libcmt.lib"); + } + + goanna_gen::generate_c_header().unwrap(); + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=src"); +} @@ -0,0 +1,400 @@ +use hecs::{Entity, World}; +use dropbear_engine::animation::{AnimationComponent, AnimationSettings}; +use dropbear_engine::asset::ASSET_REGISTRY; +use dropbear_engine::entity::MeshRenderer; +use eucalyptus_core::ptr::WorldPtr; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "animationComponentExistsForEntity" + ), + c +)] +fn exists_for_entity( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<bool> { + Ok(world.get::<&AnimationComponent>(entity).is_ok()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "getActiveAnimationIndex" + ), + c +)] +fn get_active_animation_index( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<Option<i32>> { + let component = world + .get::<&AnimationComponent>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + Ok(component.active_animation_index.map(|index| index as i32)) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "setActiveAnimationIndex" + ), + c +)] +fn set_active_animation_index( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + index: &Option<i32>, +) -> DropbearNativeResult<()> { + let mut component = world + .get::<&mut AnimationComponent>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + + let index = match index { + Some(value) if *value >= 0 => Some(*value as usize), + Some(_) => return Err(DropbearNativeError::InvalidArgument), + None => None, + }; + + if let Some(value) = index { + if !component.available_animations.is_empty() + && value >= component.available_animations.len() + { + return Err(DropbearNativeError::InvalidArgument); + } + } + + component.active_animation_index = index; + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "getTime" + ), + c +)] +fn get_time( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<f64> { + let component = world + .get::<&AnimationComponent>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + + if let Some(index) = component.active_animation_index { + if let Some(settings) = component.animation_settings.get(&index) { + return Ok(settings.time as f64); + } + } + + Ok(component.time as f64) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "setTime" + ), + c +)] +fn set_time( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + value: f64, +) -> DropbearNativeResult<()> { + let mut component = world + .get::<&mut AnimationComponent>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + + component.time = value as f32; + + if let Some(index) = component.active_animation_index { + let (time, speed, looping, is_playing) = ( + component.time, + component.speed, + component.looping, + component.is_playing, + ); + let settings = component + .animation_settings + .entry(index) + .or_insert_with(|| AnimationSettings { + time, + speed, + looping, + is_playing, + }); + settings.time = time; + } + + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "getSpeed" + ), + c +)] +fn get_speed( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<f64> { + let component = world + .get::<&AnimationComponent>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + + if let Some(index) = component.active_animation_index { + if let Some(settings) = component.animation_settings.get(&index) { + return Ok(settings.speed as f64); + } + } + + Ok(component.speed as f64) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "setSpeed" + ), + c +)] +fn set_speed( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + value: f64, +) -> DropbearNativeResult<()> { + let mut component = world + .get::<&mut AnimationComponent>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + + component.speed = value as f32; + + if let Some(index) = component.active_animation_index { + let (time, speed, looping, is_playing) = ( + component.time, + component.speed, + component.looping, + component.is_playing, + ); + let settings = component + .animation_settings + .entry(index) + .or_insert_with(|| AnimationSettings { + time, + speed, + looping, + is_playing, + }); + settings.speed = speed; + } + + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "getLooping" + ), + c +)] +fn get_looping( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<bool> { + let component = world + .get::<&AnimationComponent>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + + if let Some(index) = component.active_animation_index { + if let Some(settings) = component.animation_settings.get(&index) { + return Ok(settings.looping); + } + } + + Ok(component.looping) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "setLooping" + ), + c +)] +fn set_looping( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + value: bool, +) -> DropbearNativeResult<()> { + let mut component = world + .get::<&mut AnimationComponent>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + + component.looping = value; + + if let Some(index) = component.active_animation_index { + let (time, speed, looping, is_playing) = ( + component.time, + component.speed, + component.looping, + component.is_playing, + ); + let settings = component + .animation_settings + .entry(index) + .or_insert_with(|| AnimationSettings { + time, + speed, + looping, + is_playing, + }); + settings.looping = value; + } + + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "getIsPlaying" + ), + c +)] +fn get_is_playing( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<bool> { + let component = world + .get::<&AnimationComponent>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + + if let Some(index) = component.active_animation_index { + if let Some(settings) = component.animation_settings.get(&index) { + return Ok(settings.is_playing); + } + } + + Ok(component.is_playing) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "setIsPlaying" + ), + c +)] +fn set_is_playing( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + value: bool, +) -> DropbearNativeResult<()> { + let mut component = world + .get::<&mut AnimationComponent>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + + component.is_playing = value; + + if let Some(index) = component.active_animation_index { + let (time, speed, looping, is_playing) = ( + component.time, + component.speed, + component.looping, + component.is_playing, + ); + let settings = component + .animation_settings + .entry(index) + .or_insert_with(|| AnimationSettings { + time, + speed, + looping, + is_playing, + }); + settings.is_playing = value; + } + + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "getIndexFromString" + ), + c +)] +fn get_index_from_string( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, + name: String, +) -> DropbearNativeResult<Option<i32>> { + let component = world + .get::<&AnimationComponent>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + + Ok(component + .available_animations + .iter() + .enumerate() + .find_map(|(i, l)| if *l == name { Some(i as i32) } else { None })) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "getAvailableAnimations" + ), + c +)] +fn get_available_animations( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<Vec<String>> { + let component = world + .get::<&AnimationComponent>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + Ok(collect_available_animations(world, entity, &component)) +} + +// ---------------------- helpers ---------------------- + +fn collect_available_animations( + world: &World, + entity: Entity, + component: &AnimationComponent, +) -> Vec<String> { + if !component.available_animations.is_empty() { + return component.available_animations.clone(); + } + + let Ok(renderer) = world.get::<&MeshRenderer>(entity) else { + return Vec::new(); + }; + + let handle = renderer.model(); + if handle.is_null() { + return Vec::new(); + } + + let registry = ASSET_REGISTRY.read(); + let Some(model) = registry.get_model(handle) else { + return Vec::new(); + }; + + model + .animations + .iter() + .map(|animation| animation.name.clone()) + .collect() +} @@ -0,0 +1,58 @@ +pub mod model; +pub mod texture; + +use jni::{jni_sig, jni_str, Env}; +use jni::objects::JObject; +use dropbear_engine::asset::AssetKind; +use eucalyptus_core::ptr::{AssetRegistryPtr, AssetRegistryUnwrapped}; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use crate::FromJObject; + +impl FromJObject for AssetKind { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> + where + Self: Sized, + { + let ordinal = env + .call_method(obj, jni_str!("ordinal"), jni_sig!(() -> i32), &[])? + .i()?; + + match ordinal { + 0 => Ok(AssetKind::Texture), + 1 => Ok(AssetKind::Model), + _ => Err(DropbearNativeError::InvalidEnumOrdinal), + } + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.DropbearEngineNative", func = "getAsset"), + c(name = "dropbear_engine_get_asset") +)] +fn dropbear_asset_get_asset( + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, + label: String, + kind: &AssetKind, +) -> DropbearNativeResult<Option<u64>> { + let reader = asset.read(); + match kind { + AssetKind::Texture => { + let result = reader.get_texture_handle_from_label(&label); + if let Some(handle) = result { + Ok(Some(handle.id)) + } else { + Ok(None) + } + } + AssetKind::Model => { + let result = reader.get_model_handle_from_label(&label); + if let Some(handle) = result { + Ok(Some(handle.id)) + } else { + Ok(None) + } + } + } +} + @@ -0,0 +1,106 @@ +use dropbear_engine::asset::Handle; +use eucalyptus_core::ptr::{AssetRegistryPtr, AssetRegistryUnwrapped}; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use crate::asset::model::ty::{NAnimation, NMaterial, NMesh, NNode, NSkin}; + +mod ty; + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.asset.ModelNative", func = "getLabel"), + c(name = "dropbear_asset_model_get_label") +)] +fn dropbear_asset_model_get_label( + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, + model_handle: u64, +) -> DropbearNativeResult<String> { + let label = asset + .read() + .get_label_from_model_handle(Handle::new(model_handle)) + .ok_or_else(|| DropbearNativeError::InvalidHandle)?; + Ok(label) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.asset.ModelNative", func = "getMeshes"), + c(name = "dropbear_asset_model_get_meshes") +)] +fn dropbear_asset_model_get_meshes( + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, + model_handle: u64, +) -> DropbearNativeResult<Vec<NMesh>> { + let reader = asset.read(); + let model = reader + .get_model(Handle::new(model_handle)) + .ok_or(DropbearNativeError::InvalidHandle)?; + + Ok(model.meshes.iter().map(|v| v.into()).collect()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.asset.ModelNative", func = "getMaterials"), + c(name = "dropbear_asset_model_get_materials") +)] +fn dropbear_asset_model_get_materials( + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, + model_handle: u64, +) -> DropbearNativeResult<Vec<NMaterial>> { + let reader = asset.read(); + let model = reader + .get_model(Handle::new(model_handle)) + .ok_or(DropbearNativeError::InvalidHandle)?; + + Ok(model + .materials + .iter() + .map(|v| v.clone().into()) + .collect()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.asset.ModelNative", func = "getSkins"), + c(name = "dropbear_asset_model_get_skins") +)] +pub fn dropbear_asset_model_get_skins( + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, + model_handle: u64, +) -> DropbearNativeResult<Vec<NSkin>> { + let reader = asset.read(); + let model = reader + .get_model(Handle::new(model_handle)) + .ok_or(DropbearNativeError::InvalidHandle)?; + + Ok(model.skins.iter().map(|v| v.clone().into()).collect()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.asset.ModelNative", func = "getAnimations"), + c(name = "dropbear_asset_model_get_animations") +)] +pub fn dropbear_asset_model_get_animations( + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, + model_handle: u64, +) -> DropbearNativeResult<Vec<NAnimation>> { + let reader = asset.read(); + let model = reader + .get_model(Handle::new(model_handle)) + .ok_or(DropbearNativeError::InvalidHandle)?; + + Ok(model.animations.iter().map(|v| v.clone().into()).collect()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.asset.ModelNative", func = "getNodes"), + c(name = "dropbear_asset_model_get_nodes") +)] +pub fn dropbear_asset_model_get_nodes( + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, + model_handle: u64, +) -> DropbearNativeResult<Vec<NNode>> { + let reader = asset.read(); + let model = reader + .get_model(Handle::new(model_handle)) + .ok_or(DropbearNativeError::InvalidHandle)?; + + Ok(model.nodes.iter().map(|v| v.clone().into()).collect()) +} @@ -0,0 +1,652 @@ +use jni::{jni_sig, jni_str, Env, JValue}; +use jni::objects::JObject; +use dropbear_engine::model::{Animation, AnimationChannel, AnimationInterpolation, ChannelValues, Material, Mesh, ModelVertex, Node, NodeTransform, Skin}; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use crate::math::{NQuaternion, NVector2, NVector3, NVector4}; +use crate::ToJObject; + +#[repr(C)] +#[derive(Clone, Debug)] +pub struct NModelVertex { + pub position: NVector3, + pub normal: NVector3, + pub tangent: NVector4, + pub tex_coords0: NVector2, + pub tex_coords1: NVector2, + pub colour0: NVector4, + pub joints0: Vec<i32>, + pub weights0: NVector4, +} + +impl ToJObject for NModelVertex { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/asset/model/ModelVertex")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let position = self.position.to_jobject(env)?; + let normal = self.normal.to_jobject(env)?; + let tangent = self.tangent.to_jobject(env)?; + let tex_coords0 = self.tex_coords0.to_jobject(env)?; + let tex_coords1 = self.tex_coords1.to_jobject(env)?; + let colour0 = self.colour0.to_jobject(env)?; + let joints0 = self.joints0.as_slice().to_jobject(env)?; + let weights0 = self.weights0.to_jobject(env)?; + + let args = [ + JValue::Object(&position), + JValue::Object(&normal), + JValue::Object(&tangent), + JValue::Object(&tex_coords0), + JValue::Object(&tex_coords1), + JValue::Object(&colour0), + JValue::Object(&joints0), + JValue::Object(&weights0), + ]; + + let obj = env + .new_object( + &class, + jni_sig!((com.dropbear.math.Vector3f, com.dropbear.math.Vector3f, com.dropbear.math.Vector4f, com.dropbear.math.Vector2f, com.dropbear.math.Vector2f, com.dropbear.math.Vector4f, [int], com.dropbear.math.Vector4f) -> ()), + &args, + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + Ok(obj) + } +} + +impl Into<NModelVertex> for ModelVertex { + fn into(self) -> NModelVertex { + NModelVertex { + position: NVector3::from(self.position), + normal: NVector3::from(self.normal), + tangent: NVector4::from(self.tangent), + tex_coords0: NVector2::from(self.tex_coords0), + tex_coords1: NVector2::from(self.tex_coords1), + colour0: NVector4::from(self.colour0), + joints0: self.joints0.iter().map(|v| *v as i32).collect(), + weights0: NVector4::from(self.weights0), + } + } +} + + +#[repr(C)] +#[derive(Clone, Debug)] +pub struct NMesh { + pub name: String, + pub num_elements: i32, + pub material_index: i32, + pub vertices: Vec<NModelVertex>, +} + +impl ToJObject for NMesh { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com.dropbear.asset.model.Mesh")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let name = env + .new_string(&self.name) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + let vertices = self.vertices.to_jobject(env)?; + + let args = [ + JValue::Object(&name), + JValue::Int(self.num_elements), + JValue::Int(self.material_index), + JValue::Object(&vertices), + ]; + + let obj = env + .new_object( + &class, + jni_sig!((java.lang.String, int, int, java.util.List) -> ()), + &args, + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + Ok(obj) + } +} + +impl From<&Mesh> for NMesh { + fn from(mesh: &Mesh) -> NMesh { + NMesh { + name: mesh.name.clone(), + num_elements: mesh.num_elements as i32, + material_index: mesh.material as i32, + vertices: mesh.vertices.iter().map(|v| (*v).into()).collect(), + } + } +} + +#[repr(C)] +#[derive(Clone, Debug)] +pub struct NMaterial { + pub name: String, + pub diffuse_texture: u64, + pub normal_texture: Option<u64>, + pub emissive_texture: Option<u64>, + pub metallic_roughness_texture: Option<u64>, + pub occlusion_texture: Option<u64>, + pub tint: NVector4, + pub emissive_factor: NVector3, + pub metallic_factor: f32, + pub roughness_factor: f32, + pub alpha_cutoff: Option<f32>, + pub occlusion_strength: f32, + pub normal_scale: f32, + pub uv_tiling: NVector2, +} + +impl ToJObject for NMaterial { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com.dropbear.asset.model.Material")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let name = env + .new_string(&self.name) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + let diffuse_texture = new_texture(env, self.diffuse_texture)?; + let normal_texture = match self.normal_texture { + Some(id) => new_texture(env, id)?, + None => JObject::null(), + }; + let emissive_texture = match self.emissive_texture { + Some(id) => new_texture(env, id)?, + None => JObject::null(), + }; + let metallic_roughness_texture = match self.metallic_roughness_texture { + Some(id) => new_texture(env, id)?, + None => JObject::null(), + }; + let occlusion_texture = match self.occlusion_texture { + Some(id) => new_texture(env, id)?, + None => JObject::null(), + }; + let tint = self.tint.to_jobject(env)?; + let emissive_factor = self.emissive_factor.to_jobject(env)?; + let uv_tiling = self.uv_tiling.to_jobject(env)?; + let alpha_cutoff = self.alpha_cutoff.to_jobject(env)?; + + let args = [ + JValue::Object(&name), + JValue::Object(&diffuse_texture), + JValue::Object(&normal_texture), + JValue::Object(&tint), + JValue::Object(&emissive_factor), + JValue::Double(self.metallic_factor as f64), + JValue::Double(self.roughness_factor as f64), + JValue::Object(&alpha_cutoff), + JValue::Double(self.occlusion_strength as f64), + JValue::Double(self.normal_scale as f64), + JValue::Object(&uv_tiling), + JValue::Object(&emissive_texture), + JValue::Object(&metallic_roughness_texture), + JValue::Object(&occlusion_texture), + ]; + + let obj = env + .new_object( + &class, + jni_sig!(( + java.lang.String, + com.dropbear.asset.Texture, + com.dropbear.asset.Texture, + com.dropbear.math.Vector4d, + com.dropbear.math.Vector3d, + double, double, + java.lang.Double, + double, double, + com.dropbear.math.Vector2d, + com.dropbear.asset.Texture, + com.dropbear.asset.Texture, + com.dropbear.asset.Texture + ) -> ()), + &args, + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + Ok(obj) + } +} + +impl Into<NMaterial> for Material { + fn into(self) -> NMaterial { + NMaterial { + name: self.name.clone(), + diffuse_texture: self.diffuse_texture.id, + normal_texture: self.normal_texture.and_then(|v| Some(v.id)), + tint: NVector4::from(self.base_colour), + emissive_factor: NVector3::from(self.emissive_factor), + metallic_factor: self.metallic_factor, + roughness_factor: self.roughness_factor, + alpha_cutoff: self.alpha_cutoff, + occlusion_strength: self.occlusion_strength, + normal_scale: self.normal_scale, + uv_tiling: NVector2::from(self.uv_tiling), + emissive_texture: self.emissive_texture.and_then(|v| Some(v.id)), + metallic_roughness_texture: self.metallic_roughness_texture.and_then(|v| Some(v.id)), + occlusion_texture: self.occlusion_texture.and_then(|v| Some(v.id)), + } + } +} + +// todo: cant this just be represented as a `com.dropbear.math.Transform`? +#[repr(C)] +#[derive(Clone, Debug)] +pub struct NNodeTransform { + pub translation: NVector3, + pub rotation: NQuaternion, + pub scale: NVector3, +} + +impl ToJObject for NNodeTransform { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com.dropbear.asset.model.NodeTransform")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let translation = self.translation.to_jobject(env)?; + let rotation = self.rotation.to_jobject(env)?; + let scale = self.scale.to_jobject(env)?; + + let args = [ + JValue::Object(&translation), + JValue::Object(&rotation), + JValue::Object(&scale), + ]; + + let obj = env + .new_object( + &class, + jni_sig!("(Lcom/dropbear/math/Vector3d;Lcom/dropbear/math/Quaterniond;Lcom/dropbear/math/Vector3d;)V"), + &args, + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + Ok(obj) + } +} + +impl Into<NNodeTransform> for NodeTransform { + fn into(self) -> NNodeTransform { + NNodeTransform { + translation: NVector3::from(self.translation), + rotation: NQuaternion::from(self.rotation), + scale: NVector3::from(self.scale), + } + } +} + + +#[repr(C)] +#[derive(Clone, Debug)] +pub struct NNode { + pub name: String, + pub parent: Option<i32>, + pub children: Vec<i32>, + pub transform: NNodeTransform, +} + +impl ToJObject for NNode { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com.dropbear.asset.model.Node")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let name = env + .new_string(&self.name) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + let parent = self.parent.to_jobject(env)?; + let children = self.children.as_slice().to_jobject(env)?; + let transform = self.transform.to_jobject(env)?; + + let args = [ + JValue::Object(&name), + JValue::Object(&parent), + JValue::Object(&children), + JValue::Object(&transform), + ]; + + let obj = env + .new_object( + &class, + jni_sig!("(Ljava/lang/String;Ljava/lang/Integer;Ljava/util/List;Lcom/dropbear/asset/model/NodeTransform;)V"), + &args, + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + Ok(obj) + } +} + +impl Into<NNode> for Node { + fn into(self) -> NNode { + NNode { + name: self.name.clone(), + parent: self.parent.map(|v| v as i32), + children: self.children.iter().map(|v| *v as i32).collect(), + transform: self.transform.into(), + } + } +} + +#[repr(C)] +#[derive(Clone, Debug)] +pub struct NSkin { + pub name: String, + pub joints: Vec<i32>, + pub inverse_bind_matrices: Vec<Vec<f64>>, + pub skeleton_root: Option<i32>, +} + +impl ToJObject for NSkin { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com.dropbear.asset.model.Skin")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let name = env + .new_string(&self.name) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + let joints = self.joints.as_slice().to_jobject(env)?; + let inverse_bind_matrices = self.inverse_bind_matrices.as_slice().to_jobject(env)?; + let skeleton_root = self.skeleton_root.to_jobject(env)?; + + let args = [ + JValue::Object(&name), + JValue::Object(&joints), + JValue::Object(&inverse_bind_matrices), + JValue::Object(&skeleton_root), + ]; + + let obj = env + .new_object( + &class, + jni_sig!( + "(Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/lang/Integer;)V" + ), + &args, + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + Ok(obj) + } +} + +impl Into<NSkin> for Skin { + fn into(self) -> NSkin { + let inverse_bind_matrices = self + .inverse_bind_matrices + .iter() + .map(|matrix| matrix.to_cols_array().iter().map(|v| *v as f64).collect()) + .collect(); + + NSkin { + name: self.name.clone(), + joints: self.joints.iter().map(|v| *v as i32).collect(), + inverse_bind_matrices, + skeleton_root: self.skeleton_root.map(|v| v as i32), + } + } +} + +#[repr(C)] +#[derive(Clone, Debug)] +pub struct NAnimation { + pub name: String, + pub channels: Vec<NAnimationChannel>, + pub duration: f32, +} + +impl ToJObject for NAnimation { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com.dropbear.asset.model.Animation")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let name = env + .new_string(&self.name) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + let channels = self.channels.to_jobject(env)?; + + let args = [ + JValue::Object(&name), + JValue::Object(&channels), + JValue::Double(self.duration as f64), + ]; + + let obj = env + .new_object( + &class, + jni_sig!("(Ljava/lang/String;Ljava/util/List;D)V"), + &args, + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + Ok(obj) + } +} + +impl Into<NAnimation> for Animation { + fn into(self) -> NAnimation { + NAnimation { + name: self.name.clone(), + channels: self + .channels + .into_iter() + .map(|v| v.into()) + .collect(), + duration: self.duration, + } + } +} + +#[repr(C)] +#[derive(Clone, Debug)] +pub struct NAnimationChannel { + pub target_node: i32, + pub times: Vec<f64>, + pub values: NChannelValues, + pub interpolation: NAnimationInterpolation, +} + +impl ToJObject for NAnimationChannel { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com.dropbear.asset.model.AnimationChannel")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let times = self.times.as_slice().to_jobject(env)?; + let values = self.values.to_jobject(env)?; + let interpolation = self.interpolation.to_jobject(env)?; + + let args = [ + JValue::Int(self.target_node), + JValue::Object(×), + JValue::Object(&values), + JValue::Object(&interpolation), + ]; + + let obj = env + .new_object( + &class, + jni_sig!("(I[DLcom/dropbear/asset/model/ChannelValues;Lcom/dropbear/asset/model/AnimationInterpolation;)V"), + &args, + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + Ok(obj) + } +} + +impl Into<NAnimationChannel> for AnimationChannel { + fn into(self) -> NAnimationChannel { + NAnimationChannel { + target_node: self.target_node as i32, + times: self.times.iter().map(|v| *v as f64).collect(), + values: self.values.into(), + interpolation: self.interpolation.into(), + } + } +} + + +#[repr(C)] +#[derive(Clone, Debug)] +#[dropbear_macro::repr_c_enum] +pub enum NAnimationInterpolation { + Linear, + Step, + CubicSpline, +} + +impl ToJObject for NAnimationInterpolation { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com.dropbear.asset.model.AnimationInterpolation")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let field_name = match self { + NAnimationInterpolation::Linear => "LINEAR", + NAnimationInterpolation::Step => "STEP", + NAnimationInterpolation::CubicSpline => "CUBIC_SPLINE", + }; + + let value = env + .get_static_field( + class, + jni::strings::JNIString::from(field_name), + jni_sig!("Lcom/dropbear/asset/model/AnimationInterpolation;"), + ) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .l() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + Ok(value) + } +} + +impl Into<NAnimationInterpolation> for AnimationInterpolation { + fn into(self) -> NAnimationInterpolation { + match self { + AnimationInterpolation::Linear => NAnimationInterpolation::Linear, + AnimationInterpolation::Step => NAnimationInterpolation::Step, + AnimationInterpolation::CubicSpline => NAnimationInterpolation::CubicSpline, + } + } +} + +#[repr(C)] +#[derive(Clone, Debug)] +#[dropbear_macro::repr_c_enum] +pub enum NChannelValues { + Translations { values: Vec<NVector3> }, + Rotations { values: Vec<NQuaternion> }, + Scales { values: Vec<NVector3> }, + MorphWeights { values: Vec<Vec<f64>> }, +} + +impl ToJObject for NChannelValues { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + match self { + NChannelValues::Translations { values } => { + let class = env + .load_class(jni_str!( + "com.dropbear.asset.model.ChannelValues$Translations" + )) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + let list = values.to_jobject(env)?; + let obj = env + .new_object( + &class, + jni_sig!("(Ljava/util/List;)V"), + &[JValue::Object(&list)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + Ok(obj) + } + NChannelValues::Rotations { values } => { + let class = env + .load_class(jni_str!("com.dropbear.asset.model.ChannelValues$Rotations")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + let list = values.to_jobject(env)?; + let obj = env + .new_object( + &class, + jni_sig!("(Ljava/util/List;)V"), + &[JValue::Object(&list)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + Ok(obj) + } + NChannelValues::Scales { values } => { + let class = env + .load_class(jni_str!("com.dropbear.asset.model.ChannelValues$Scales")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + let list = values.to_jobject(env)?; + let obj = env + .new_object( + &class, + jni_sig!("(Ljava/util/List;)V"), + &[JValue::Object(&list)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + Ok(obj) + } + NChannelValues::MorphWeights { values } => { + let class = env + .load_class(jni_str!( + "com.dropbear.asset.model.ChannelValues$MorphWeights" + )) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + let list = values.to_jobject(env)?; + let obj = env + .new_object( + &class, + jni_sig!("(Ljava/util/List;)V"), + &[JValue::Object(&list)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + Ok(obj) + } + } + } +} + + +impl Into<NChannelValues> for ChannelValues { + fn into(self) -> NChannelValues { + match self { + ChannelValues::Translations(list) => NChannelValues::Translations { + values: list.iter().map(|v| NVector3::from(*v)).collect(), + }, + ChannelValues::Rotations(list) => NChannelValues::Rotations { + values: list.iter().map(|v| NQuaternion::from(*v)).collect(), + }, + ChannelValues::Scales(list) => NChannelValues::Scales { + values: list.iter().map(|v| NVector3::from(*v)).collect(), + }, + ChannelValues::MorphWeights(items) => NChannelValues::MorphWeights { + values: items + .iter() + .map(|weights| weights.iter().map(|v| *v as f64).collect()) + .collect(), + }, + } + } +} + +fn new_texture<'a>(env: &mut Env<'a>, texture_id: u64) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com.dropbear.asset.Texture")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + env.new_object(&class, jni_sig!("(J)V"), &[JValue::Long(texture_id as i64)]) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) +} @@ -0,0 +1,47 @@ +use dropbear_engine::asset::Handle; +use eucalyptus_core::ptr::{AssetRegistryPtr, AssetRegistryUnwrapped}; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.asset.TextureNative", func = "getLabel"), + c(name = "dropbear_asset_texture_get_label") +)] +fn get_texture_label( + #[dropbear_macro::define(AssetRegistryPtr)] asset_manager: &AssetRegistryUnwrapped, + texture_handle: u64, +) -> DropbearNativeResult<Option<String>> { + Ok(asset_manager + .read() + .get_label_from_texture_handle(Handle::new(texture_handle))) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.asset.TextureNative", func = "getWidth"), + c(name = "dropbear_asset_texture_get_width") +)] +fn get_texture_width( + #[dropbear_macro::define(AssetRegistryPtr)] asset_manager: &AssetRegistryUnwrapped, + texture_handle: u64, +) -> DropbearNativeResult<u32> { + asset_manager + .read() + .get_texture(Handle::new(texture_handle)) + .map(|v| v.size.width) + .ok_or(DropbearNativeError::AssetNotFound) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.asset.TextureNative", func = "getHeight"), + c(name = "dropbear_asset_texture_get_height") +)] +fn get_texture_height( + #[dropbear_macro::define(AssetRegistryPtr)] asset_manager: &AssetRegistryUnwrapped, + texture_handle: u64, +) -> DropbearNativeResult<u32> { + asset_manager + .read() + .get_texture(Handle::new(texture_handle)) + .map(|v| v.size.height) + .ok_or(DropbearNativeError::AssetNotFound) +} @@ -0,0 +1,349 @@ +use eucalyptus_core::ptr::WorldPtr; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use eucalyptus_core::types::NVector3; +use dropbear_engine::camera::Camera; + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "cameraExistsForEntity"), + c +)] +fn exists_for_entity( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<bool> { + Ok(world.get::<&Camera>(entity).is_ok()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraEye"), + c +)] +fn get_eye( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<NVector3> { + match world.get::<&Camera>(entity) { + Ok(camera) => Ok(camera.eye.into()), + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraEye"), + c +)] +fn set_eye( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + eye: &NVector3, +) -> DropbearNativeResult<()> { + match world.get::<&mut Camera>(entity) { + Ok(mut camera) => { + camera.eye = (*eye).into(); + Ok(()) + } + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraTarget"), + c +)] +fn get_target( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<NVector3> { + match world.get::<&Camera>(entity) { + Ok(camera) => Ok(camera.target.into()), + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraTarget"), + c +)] +fn set_target( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + target: &NVector3, +) -> DropbearNativeResult<()> { + match world.get::<&mut Camera>(entity) { + Ok(mut camera) => { + camera.target = target.into(); + Ok(()) + } + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraUp"), + c +)] +fn get_up( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<NVector3> { + match world.get::<&Camera>(entity) { + Ok(camera) => Ok(camera.up.into()), + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraUp"), + c +)] +fn set_up( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + up: &NVector3, +) -> DropbearNativeResult<()> { + match world.get::<&mut Camera>(entity) { + Ok(mut camera) => { + camera.up = up.into(); + Ok(()) + } + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraAspect"), + c +)] +fn get_aspect( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<f64> { + match world.get::<&Camera>(entity) { + Ok(camera) => Ok(camera.aspect.into()), + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraFovY"), + c +)] +fn get_fovy( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<f64> { + match world.get::<&Camera>(entity) { + Ok(camera) => Ok(camera.settings.fov_y.into()), + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraFovY"), + c +)] +fn set_fovy( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + fovy: f64, +) -> DropbearNativeResult<()> { + match world.get::<&mut Camera>(entity) { + Ok(mut camera) => { + camera.settings.fov_y = fovy.into(); + Ok(()) + } + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraZNear"), + c +)] +fn get_znear( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<f64> { + match world.get::<&Camera>(entity) { + Ok(camera) => Ok(camera.znear.into()), + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraZNear"), + c +)] +fn set_znear( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + znear: f64, +) -> DropbearNativeResult<()> { + match world.get::<&mut Camera>(entity) { + Ok(mut camera) => { + camera.znear = znear.into(); + Ok(()) + } + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraZFar"), + c +)] +fn get_zfar( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<f64> { + match world.get::<&Camera>(entity) { + Ok(camera) => Ok(camera.zfar.into()), + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraZFar"), + c +)] +fn set_zfar( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + zfar: f64, +) -> DropbearNativeResult<()> { + match world.get::<&mut Camera>(entity) { + Ok(mut camera) => { + camera.zfar = zfar.into(); + Ok(()) + } + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraYaw"), + c +)] +fn get_yaw( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<f64> { + match world.get::<&Camera>(entity) { + Ok(camera) => Ok(camera.yaw.into()), + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraYaw"), + c +)] +fn set_yaw( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + yaw: f64, +) -> DropbearNativeResult<()> { + match world.get::<&mut Camera>(entity) { + Ok(mut camera) => { + camera.yaw = yaw.into(); + Ok(()) + } + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraPitch"), + c +)] +fn get_pitch( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<f64> { + match world.get::<&Camera>(entity) { + Ok(camera) => Ok(camera.pitch.into()), + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraPitch"), + c +)] +fn set_pitch( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + pitch: f64, +) -> DropbearNativeResult<()> { + match world.get::<&mut Camera>(entity) { + Ok(mut camera) => { + camera.pitch = pitch.into(); + Ok(()) + } + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraSpeed"), + c +)] +fn get_speed( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<f64> { + match world.get::<&Camera>(entity) { + Ok(camera) => Ok(camera.settings.speed.into()), + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraSpeed"), + c +)] +fn set_speed( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + speed: f64, +) -> DropbearNativeResult<()> { + match world.get::<&mut Camera>(entity) { + Ok(mut camera) => { + camera.settings.speed = speed.into(); + Ok(()) + } + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraSensitivity"), + c +)] +fn get_sensitivity( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<f64> { + match world.get::<&Camera>(entity) { + Ok(camera) => Ok(camera.settings.sensitivity.into()), + Err(e) => Err(e.into()), + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraSensitivity"), + c +)] +fn set_sensitivity( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + sensitivity: f64, +) -> DropbearNativeResult<()> { + match world.get::<&mut Camera>(entity) { + Ok(mut camera) => { + camera.settings.sensitivity = sensitivity.into(); + Ok(()) + } + Err(e) => Err(e.into()), + } +} @@ -0,0 +1,40 @@ +use eucalyptus_core::scripting::result::DropbearNativeResult; +use eucalyptus_core::scripting::types::KotlinComponents; +use hecs::{Entity, World}; + +#[dropbear_macro::export(kotlin( + class = "com.dropbear.components.ComponentNative", + func = "hasKotlinComponent", +))] +fn has_kotlin_component( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, + fqcn: String, +) -> DropbearNativeResult<bool> { + if let Ok(kc) = world.get::<&KotlinComponents>(entity) { + Ok(kc.has(&fqcn)) + } else { + Ok(false) + } +} + +#[dropbear_macro::export(kotlin( + class = "com.dropbear.components.ComponentNative", + func = "registerKotlinComponent", +))] +fn register_kotlin_component_jni( + #[dropbear_macro::define(WorldPtr)] _world: &World, + fqcn: String, + type_name: String, + category: String, + description: String, +) -> DropbearNativeResult<()> { + use eucalyptus_core::component::{KOTLIN_COMPONENT_QUEUE, KotlinComponentDecl}; + KOTLIN_COMPONENT_QUEUE.lock().push(KotlinComponentDecl { + fqcn, + type_name, + category: if category.is_empty() { None } else { Some(category) }, + description: if description.is_empty() { None } else { Some(description) }, + }); + Ok(()) +} @@ -0,0 +1,213 @@ +use eucalyptus_core::ptr::GraphicsContextPtr; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use eucalyptus_core::types::{NColour, NVector3}; +use crate::math::NQuaternion; +use dropbear_engine::graphics::SharedGraphicsContext; +use glam::{Quat, Vec3}; + +macro_rules! with_debug { + ($graphics:expr, |$dd:ident| $body:expr) => { + if let Some($dd) = $graphics.debug_draw.lock().as_mut() { + $body + } + }; +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawLine"), + c +)] +fn draw_line( + #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, + start: &NVector3, + end: &NVector3, + colour: &NColour, +) -> DropbearNativeResult<()> { + let a = Vec3::new(start.x as f32, start.y as f32, start.z as f32); + let b = Vec3::new(end.x as f32, end.y as f32, end.z as f32); + with_debug!(graphics, |dd| dd.draw_line(a, b, colour.to_f32_array())); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawRay"), + c +)] +fn draw_ray( + #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, + origin: &NVector3, + dir: &NVector3, + colour: &NColour, +) -> DropbearNativeResult<()> { + let o = Vec3::new(origin.x as f32, origin.y as f32, origin.z as f32); + let d = Vec3::new(dir.x as f32, dir.y as f32, dir.z as f32); + with_debug!(graphics, |dd| dd.draw_ray(o, d, colour.to_f32_array())); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawArrow"), + c +)] +fn draw_arrow( + #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, + start: &NVector3, + end: &NVector3, + colour: &NColour, +) -> DropbearNativeResult<()> { + let a = Vec3::new(start.x as f32, start.y as f32, start.z as f32); + let b = Vec3::new(end.x as f32, end.y as f32, end.z as f32); + with_debug!(graphics, |dd| dd.draw_arrow(a, b, colour.to_f32_array())); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawPoint"), + c +)] +fn draw_point( + #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, + pos: &NVector3, + size: f32, + colour: &NColour, +) -> DropbearNativeResult<()> { + let p = Vec3::new(pos.x as f32, pos.y as f32, pos.z as f32); + with_debug!(graphics, |dd| dd.draw_point(p, size, colour.to_f32_array())); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawCircle"), + c +)] +fn draw_circle( + #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, + center: &NVector3, + radius: f32, + normal: &NVector3, + colour: &NColour, +) -> DropbearNativeResult<()> { + let c = Vec3::new(center.x as f32, center.y as f32, center.z as f32); + let n = Vec3::new(normal.x as f32, normal.y as f32, normal.z as f32); + with_debug!(graphics, |dd| dd.draw_circle(c, radius, n, colour.to_f32_array())); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawSphere"), + c +)] +fn draw_sphere( + #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, + center: &NVector3, + radius: f32, + colour: &NColour, +) -> DropbearNativeResult<()> { + let c = Vec3::new(center.x as f32, center.y as f32, center.z as f32); + with_debug!(graphics, |dd| dd.draw_sphere(c, radius, colour.to_f32_array())); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawGlobe"), + c +)] +fn draw_globe( + #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, + center: &NVector3, + radius: f32, + lat_lines: u32, + lon_lines: u32, + colour: &NColour, +) -> DropbearNativeResult<()> { + let c = Vec3::new(center.x as f32, center.y as f32, center.z as f32); + with_debug!(graphics, |dd| dd.draw_globe(c, radius, lat_lines, lon_lines, colour.to_f32_array())); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawAabb"), + c +)] +fn draw_aabb( + #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, + min: &NVector3, + max: &NVector3, + colour: &NColour, +) -> DropbearNativeResult<()> { + let mn = Vec3::new(min.x as f32, min.y as f32, min.z as f32); + let mx = Vec3::new(max.x as f32, max.y as f32, max.z as f32); + with_debug!(graphics, |dd| dd.draw_aabb(mn, mx, colour.to_f32_array())); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawObb"), + c +)] +fn draw_obb( + #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, + center: &NVector3, + half_extents: &NVector3, + rotation: &NQuaternion, + colour: &NColour, +) -> DropbearNativeResult<()> { + let c = Vec3::new(center.x as f32, center.y as f32, center.z as f32); + let he = Vec3::new(half_extents.x as f32, half_extents.y as f32, half_extents.z as f32); + let r = Quat::from_xyzw(rotation.x as f32, rotation.y as f32, rotation.z as f32, rotation.w as f32); + with_debug!(graphics, |dd| dd.draw_obb(c, he, r, colour.to_f32_array())); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawCapsule"), + c +)] +fn draw_capsule( + #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, + a: &NVector3, + b: &NVector3, + radius: f32, + colour: &NColour, +) -> DropbearNativeResult<()> { + let va = Vec3::new(a.x as f32, a.y as f32, a.z as f32); + let vb = Vec3::new(b.x as f32, b.y as f32, b.z as f32); + with_debug!(graphics, |dd| dd.draw_capsule(va, vb, radius, colour.to_f32_array())); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawCylinder"), + c +)] +fn draw_cylinder( + #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, + center: &NVector3, + half_height: f32, + radius: f32, + axis: &NVector3, + colour: &NColour, +) -> DropbearNativeResult<()> { + let c = Vec3::new(center.x as f32, center.y as f32, center.z as f32); + let ax = Vec3::new(axis.x as f32, axis.y as f32, axis.z as f32); + with_debug!(graphics, |dd| dd.draw_cylinder(c, half_height, radius, ax, colour.to_f32_array())); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawCone"), + c +)] +fn draw_cone( + #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, + apex: &NVector3, + dir: &NVector3, + angle: f32, + length: f32, + colour: &NColour, +) -> DropbearNativeResult<()> { + let a = Vec3::new(apex.x as f32, apex.y as f32, apex.z as f32); + let d = Vec3::new(dir.x as f32, dir.y as f32, dir.z as f32); + with_debug!(graphics, |dd| dd.draw_cone(a, d, angle, length, colour.to_f32_array())); + Ok(()) +} @@ -0,0 +1,45 @@ +use eucalyptus_core::ptr::{CommandBufferPtr, CommandBufferUnwrapped, WorldPtr}; +use eucalyptus_core::scripting::result::DropbearNativeResult; + +pub mod shared { + use hecs::{Entity, World}; + use eucalyptus_core::command::CommandBuffer; + use eucalyptus_core::scripting::native::DropbearNativeError; + use eucalyptus_core::scripting::result::DropbearNativeResult; + use eucalyptus_core::states::Label; + + pub fn get_entity(world: &World, label: &str) -> DropbearNativeResult<u64> { + for (id, entity_label) in world.query::<(Entity, &Label)>().iter() { + if entity_label.as_str() == label { + return Ok(id.to_bits().get()); + } + } + Err(DropbearNativeError::EntityNotFound) + } + + pub fn quit( + command_buffer: &crossbeam_channel::Sender<CommandBuffer>, + ) -> DropbearNativeResult<()> { + command_buffer + .send(CommandBuffer::Quit) + .map_err(|_| DropbearNativeError::SendError) + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.DropbearEngineNative", func = "getEntity",), + c +)] +fn get_entity( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + label: String, +) -> DropbearNativeResult<u64> { + shared::get_entity(&world, &label) +} + +#[dropbear_macro::export(kotlin(class = "com.dropbear.DropbearEngineNative", func = "quit",), c)] +fn quit( + #[dropbear_macro::define(CommandBufferPtr)] command_buffer: &CommandBufferUnwrapped, +) -> DropbearNativeResult<()> { + shared::quit(command_buffer) +} @@ -0,0 +1,85 @@ +use eucalyptus_core::hierarchy::{Children, Parent}; +use eucalyptus_core::ptr::WorldPtr; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use eucalyptus_core::states::Label; + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.LabelNative", func = "labelExistsForEntity"), + c +)] +fn label_exists_for_entity( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<bool> { + Ok(world.get::<&Label>(entity).is_ok()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.EntityRefNative", func = "getEntityLabel"), + c +)] +fn get_label( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<String> { + let label = world.get::<&Label>(entity)?.as_str().to_string(); + Ok(label) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.EntityRefNative", func = "getChildren"), + c +)] +fn get_children( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<Vec<u64>> { + if let Ok(children) = world.query_one::<&Children>(entity).get() { + let entity_bytes = children + .children() + .iter() + .map(|c| c.to_bits().get()) + .collect::<Vec<_>>(); + Ok(entity_bytes) + } else { + Ok(vec![]) + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.EntityRefNative", func = "getChildByLabel"), + c +)] +fn get_child_by_label( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + target: String, +) -> DropbearNativeResult<Option<u64>> { + if let Ok(children) = world.query_one::<&Children>(entity).get() { + for child in children.children() { + if let Ok(label) = world.get::<&Label>(*child) { + if label.as_str() == target { + let found = child.clone(); + return Ok(Some(found.to_bits().get())); + } + } else { + continue; + } + } + Ok(None) + } else { + Ok(None) + } +} + +#[dropbear_macro::export(kotlin(class = "com.dropbear.EntityRefNative", func = "getParent"), c)] +fn get_parent( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<Option<u64>> { + if let Ok(parent) = world.query_one::<&Parent>(entity).get() { + Ok(Some(parent.parent().to_bits().get())) + } else { + Ok(None) + } +} @@ -0,0 +1,361 @@ +use jni::objects::JObject; +use eucalyptus_core::input::InputState; +use eucalyptus_core::ptr::{CommandBufferPtr, CommandBufferUnwrapped, InputStatePtr}; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use crate::math::NVector2; + +pub mod shared { + use crossbeam_channel::Sender; + use dropbear_engine::winit::event::MouseButton; + use eucalyptus_core::command::{CommandBuffer, WindowCommand}; + use eucalyptus_core::input::InputState; + use eucalyptus_core::scripting::native::DropbearNativeError; + use eucalyptus_core::scripting::result::DropbearNativeResult; + use eucalyptus_core::utils::keycode_from_ordinal; + use crate::math::NVector2; + + fn map_int_to_gamepad_button(ordinal: i32) -> Option<dropbear_engine::gilrs::Button> { + match ordinal { + 0 => Some(dropbear_engine::gilrs::Button::Unknown), + 1 => Some(dropbear_engine::gilrs::Button::South), + 2 => Some(dropbear_engine::gilrs::Button::East), + 3 => Some(dropbear_engine::gilrs::Button::North), + 4 => Some(dropbear_engine::gilrs::Button::West), + 5 => Some(dropbear_engine::gilrs::Button::C), + 6 => Some(dropbear_engine::gilrs::Button::Z), + 7 => Some(dropbear_engine::gilrs::Button::LeftTrigger), + 8 => Some(dropbear_engine::gilrs::Button::RightTrigger), + 9 => Some(dropbear_engine::gilrs::Button::LeftTrigger2), + 10 => Some(dropbear_engine::gilrs::Button::RightTrigger2), + 11 => Some(dropbear_engine::gilrs::Button::Select), + 12 => Some(dropbear_engine::gilrs::Button::Start), + 13 => Some(dropbear_engine::gilrs::Button::Mode), + 14 => Some(dropbear_engine::gilrs::Button::LeftThumb), + 15 => Some(dropbear_engine::gilrs::Button::RightThumb), + 16 => Some(dropbear_engine::gilrs::Button::DPadUp), + 17 => Some(dropbear_engine::gilrs::Button::DPadDown), + 18 => Some(dropbear_engine::gilrs::Button::DPadLeft), + 19 => Some(dropbear_engine::gilrs::Button::DPadRight), + _ => None, + } + } + + pub fn get_gamepad_id( + input: &InputState, + target: usize, + ) -> Option<dropbear_engine::gilrs::GamepadId> { + input + .connected_gamepads + .iter() + .find(|g| usize::from(**g) == target) + .copied() + } + + pub fn is_gamepad_button_pressed( + input: &InputState, + gamepad_id: u64, + button_ordinal: i32, + ) -> bool { + let Some(id) = get_gamepad_id(input, gamepad_id as usize) else { + return false; + }; + + if let Some(btn) = map_int_to_gamepad_button(button_ordinal) { + input.is_button_pressed(id, btn) + } else { + false + } + } + + pub fn get_left_stick(input: &InputState, gamepad_id: u64) -> NVector2 { + let Some(id) = get_gamepad_id(input, gamepad_id as usize) else { + return NVector2 { x: 0.0, y: 0.0 }; + }; + let (x, y) = input.get_left_stick(id); + NVector2 { + x: x as f64, + y: y as f64, + } + } + + pub fn get_right_stick(input: &InputState, gamepad_id: u64) -> NVector2 { + let Some(id) = get_gamepad_id(input, gamepad_id as usize) else { + return NVector2 { x: 0.0, y: 0.0 }; + }; + let (x, y) = input.get_right_stick(id); + NVector2 { + x: x as f64, + y: y as f64, + } + } + + pub fn map_ordinal_to_mouse_button(ordinal: i32) -> Option<MouseButton> { + match ordinal { + 0 => Some(MouseButton::Left), + 1 => Some(MouseButton::Right), + 2 => Some(MouseButton::Middle), + 3 => Some(MouseButton::Back), + 4 => Some(MouseButton::Forward), + ordinal if ordinal >= 0 => Some(MouseButton::Other(ordinal as u16)), + _ => None, + } + } + + pub fn is_key_pressed(input: &InputState, key_ordinal: i32) -> bool { + if let Some(key) = keycode_from_ordinal(key_ordinal) { + input.is_key_pressed(key) + } else { + false + } + } + + pub fn is_mouse_button_pressed(input: &InputState, btn_ordinal: i32) -> bool { + if let Some(btn) = map_ordinal_to_mouse_button(btn_ordinal) { + input.mouse_button.contains(&btn) + } else { + false + } + } + + pub fn get_mouse_position(input: &InputState) -> NVector2 { + NVector2 { + x: input.mouse_pos.0, + y: input.mouse_pos.1, + } + } + + pub fn get_mouse_delta(input: &InputState) -> NVector2 { + input + .mouse_delta + .map(NVector2::from) + .unwrap_or(NVector2 { x: 0.0, y: 0.0 }) + } + + pub fn get_last_mouse_pos(input: &InputState) -> NVector2 { + input + .last_mouse_pos + .map(NVector2::from) + .unwrap_or(NVector2 { x: 0.0, y: 0.0 }) + } + + pub fn set_cursor_locked( + input: &mut InputState, + sender: &Sender<CommandBuffer>, + locked: bool, + ) -> DropbearNativeResult<()> { + input.is_cursor_locked = locked; + + sender + .send(CommandBuffer::WindowCommand(WindowCommand::WindowGrab( + locked, + ))) + .map_err(|_| DropbearNativeError::SendError)?; + + Ok(()) + } + + pub fn set_cursor_hidden( + input: &mut InputState, + sender: &Sender<CommandBuffer>, + hidden: bool, + ) -> DropbearNativeResult<()> { + input.is_cursor_hidden = hidden; + sender + .send(CommandBuffer::WindowCommand(WindowCommand::HideCursor( + hidden, + ))) + .map_err(|_| DropbearNativeError::SendError)?; + Ok(()) + } + + pub fn get_connected_gamepads(input: &InputState) -> Vec<u64> { + input + .connected_gamepads + .iter() + .map(|id| Into::<usize>::into(*id) as u64) + .collect() + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.input.InputStateNative", + func = "printInputState" + ), + c +)] +fn print_input_state( + #[dropbear_macro::define(InputStatePtr)] input: &InputState, +) -> DropbearNativeResult<()> { + println!("Input State: {:?}", input); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.input.InputStateNative", func = "isKeyPressed"), + c +)] +fn is_key_pressed( + #[dropbear_macro::define(InputStatePtr)] input: &InputState, + key_code: i32, +) -> DropbearNativeResult<bool> { + Ok(shared::is_key_pressed(input, key_code)) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.input.InputStateNative", + func = "getMousePosition" + ), + c +)] +fn get_mouse_position( + #[dropbear_macro::define(InputStatePtr)] input: &InputState, +) -> DropbearNativeResult<NVector2> { + Ok(shared::get_mouse_position(input)) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.input.InputStateNative", + func = "isMouseButtonPressed" + ), + c +)] +fn is_mouse_button_pressed( + #[dropbear_macro::define(InputStatePtr)] input: &InputState, + button_ordinal: i32, +) -> DropbearNativeResult<bool> { + Ok(shared::is_mouse_button_pressed(input, button_ordinal)) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.input.InputStateNative", func = "getMouseDelta"), + c +)] +fn get_mouse_delta( + #[dropbear_macro::define(InputStatePtr)] input: &InputState, +) -> DropbearNativeResult<NVector2> { + Ok(shared::get_mouse_delta(input)) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.input.InputStateNative", func = "isCursorLocked"), + c +)] +fn is_cursor_locked( + #[dropbear_macro::define(InputStatePtr)] input: &InputState, +) -> DropbearNativeResult<bool> { + Ok(input.is_cursor_locked) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.input.InputStateNative", + func = "setCursorLocked" + ), + c +)] +fn set_cursor_locked( + #[dropbear_macro::define(CommandBufferPtr)] + command_buffer: &CommandBufferUnwrapped, + #[dropbear_macro::define(InputStatePtr)] input: &mut InputState, + locked: bool, +) -> DropbearNativeResult<()> { + shared::set_cursor_locked(input, command_buffer, locked) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.input.InputStateNative", + func = "getLastMousePos" + ), + c +)] +fn get_last_mouse_pos( + #[dropbear_macro::define(InputStatePtr)] input: &InputState, +) -> DropbearNativeResult<NVector2> { + Ok(shared::get_last_mouse_pos(input)) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.input.InputStateNative", func = "isCursorHidden"), + c +)] +fn is_cursor_hidden( + #[dropbear_macro::define(InputStatePtr)] input: &InputState, +) -> DropbearNativeResult<bool> { + Ok(input.is_cursor_hidden) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.input.InputStateNative", + func = "setCursorHidden" + ), + c +)] +fn set_cursor_hidden( + #[dropbear_macro::define(CommandBufferPtr)] + command_buffer: &CommandBufferUnwrapped, + #[dropbear_macro::define(InputStatePtr)] input: &mut InputState, + hidden: bool, +) -> DropbearNativeResult<()> { + shared::set_cursor_hidden(input, command_buffer, hidden) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.input.InputStateNative", + func = "getConnectedGamepads" + ), + c +)] +fn get_connected_gamepads( + #[dropbear_macro::define(InputStatePtr)] input: &InputState, +) -> DropbearNativeResult<Vec<u64>> { + Ok(shared::get_connected_gamepads(input)) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.input.GamepadNative", + func = "isGamepadButtonPressed" + ), + c +)] +fn is_button_pressed( + #[dropbear_macro::define(InputStatePtr)] input: &InputState, + gamepad_id: u64, + button_ordinal: i32, +) -> DropbearNativeResult<bool> { + Ok(shared::is_gamepad_button_pressed(input, gamepad_id, button_ordinal)) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.input.GamepadNative", + func = "getLeftStickPosition" + ), + c +)] +fn get_left_stick_position( + #[dropbear_macro::define(InputStatePtr)] input: &InputState, + gamepad_id: u64, +) -> DropbearNativeResult<NVector2> { + Ok(shared::get_left_stick(input, gamepad_id)) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.input.GamepadNative", + func = "getRightStickPosition" + ), + c +)] +fn get_right_stick_position( + #[dropbear_macro::define(InputStatePtr)] input: &InputState, + gamepad_id: u64, +) -> DropbearNativeResult<NVector2> { + Ok(shared::get_right_stick(input, gamepad_id)) +} @@ -0,0 +1,44 @@ +use jni::Env; +use jni::objects::JObject; +use eucalyptus_core::scripting::result::DropbearNativeResult; + +// Re-export macros so the dropbear_macro::export expansion can find them at crate::convert_ptr! etc. +pub use eucalyptus_core::convert_ptr; +pub use eucalyptus_core::ffi_error_return; +pub use eucalyptus_core::convert_jstring; +pub use eucalyptus_core::convert_jlong_to_entity; + +pub mod animation; +pub mod asset; +pub mod camera; +pub mod component; +pub mod debug; +pub mod entity; +pub mod input; +pub mod lighting; +pub mod math; +pub mod mesh; +pub mod physics; +pub mod primitives; +pub mod properties; +pub mod scene; +pub mod scripting; +pub mod transform; +pub mod utils; +pub mod engine; + +pub mod ptr { + pub use eucalyptus_core::ptr::*; +} + +/// Trait that defines conversion from a Java object to a Rust struct. +pub trait FromJObject { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> + where + Self: Sized; +} + +/// Converts a Rust object (struct or enum) into a java [JObject]. +pub trait ToJObject { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>>; +} @@ -0,0 +1,525 @@ +use eucalyptus_core::ptr::WorldPtr; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use eucalyptus_core::types::{NColour, NVector3}; +use crate::{FromJObject, ToJObject}; +use dropbear_engine::entity::{EntityTransform, Transform}; +use dropbear_engine::lighting::{Light, LightType}; +use glam::{DQuat, DVec3}; +use hecs::{Entity, World}; +use jni::objects::{JObject, JValue}; +use jni::{Env, jni_sig, jni_str}; + +const LIGHT_FORWARD_AXIS: DVec3 = DVec3::new(0.0, -1.0, 0.0); + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct NRange { + pub start: f32, + pub end: f32, +} + +impl FromJObject for NRange { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { + let class = env + .load_class(jni_str!("com/dropbear/utils/Range")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + if !env + .is_instance_of(obj, &class) + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? + { + return Err(DropbearNativeError::InvalidArgument); + } + + let start = env + .get_field(obj, jni_str!("start"), jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32; + + let end = env + .get_field(obj, jni_str!("end"), jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32; + + Ok(Self { start, end }) + } +} + +impl ToJObject for NRange { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/utils/Range")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let args = [ + JValue::Double(self.start as f64), + JValue::Double(self.end as f64), + ]; + + env.new_object(&class, jni_sig!((double, double) -> void), &args) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct NAttenuation { + pub constant: f32, + pub linear: f32, + pub quadratic: f32, +} + +impl FromJObject for NAttenuation { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { + let class = env + .load_class(jni_str!("com/dropbear/lighting/Attenuation")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + if !env + .is_instance_of(obj, &class) + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? + { + return Err(DropbearNativeError::InvalidArgument); + } + + let constant = env + .call_method(obj, jni_str!("getConstant"), jni_sig!(() -> float), &[]) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let linear = env + .call_method(obj, jni_str!("getLinear"), jni_sig!(() -> float), &[]) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let quadratic = env + .call_method(obj, jni_str!("getQuadratic"), jni_sig!(() -> float), &[]) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + Ok(Self { constant, linear, quadratic }) + } +} + +impl ToJObject for NAttenuation { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/lighting/Attenuation")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let args = [ + JValue::Float(self.constant), + JValue::Float(self.linear), + JValue::Float(self.quadratic), + ]; + + env.new_object(&class, jni_sig!((float, float, float) -> void), &args) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +fn get_transform(world: &World, entity: Entity) -> DropbearNativeResult<Transform> { + if let Ok(et) = world.get::<&EntityTransform>(entity) { + Ok(et.sync()) + } else if let Ok(t) = world.get::<&Transform>(entity) { + Ok(*t) + } else { + Err(DropbearNativeError::MissingComponent) + } +} + +fn set_transform_position( + world: &mut World, + entity: Entity, + position: DVec3, +) -> DropbearNativeResult<()> { + if let Ok(mut et) = world.get::<&mut EntityTransform>(entity) { + et.local_mut().position = position; + Ok(()) + } else if let Ok(mut t) = world.get::<&mut Transform>(entity) { + t.position = position; + Ok(()) + } else { + Err(DropbearNativeError::MissingComponent) + } +} + +fn set_transform_rotation( + world: &mut World, + entity: Entity, + rotation: DQuat, +) -> DropbearNativeResult<()> { + if let Ok(mut et) = world.get::<&mut EntityTransform>(entity) { + et.local_mut().rotation = rotation; + Ok(()) + } else if let Ok(mut t) = world.get::<&mut Transform>(entity) { + t.rotation = rotation; + Ok(()) + } else { + Err(DropbearNativeError::MissingComponent) + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "lightExistsForEntity"), + c +)] +fn light_exists_for_entity( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<bool> { + Ok(world.get::<&dropbear_engine::lighting::LightComponent>(entity).is_ok()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "getPosition"), + c +)] +fn get_position( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<NVector3> { + let transform = get_transform(world, entity)?; + Ok(NVector3::from(transform.position)) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "setPosition"), + c +)] +fn set_position( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + position: &NVector3, +) -> DropbearNativeResult<()> { + set_transform_position(world, entity, (*position).into()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "getDirection"), + c +)] +fn get_direction( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<NVector3> { + let transform = get_transform(world, entity)?; + let forward = LIGHT_FORWARD_AXIS; + let dir = (transform.rotation * forward).normalize_or_zero(); + Ok(NVector3::from(dir)) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "setDirection"), + c +)] +fn set_direction( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + direction: &NVector3, +) -> DropbearNativeResult<()> { + let dir: DVec3 = (*direction).into(); + let desired = dir.normalize_or_zero(); + if desired.length_squared() < 1e-12 { + return Err(DropbearNativeError::InvalidArgument); + } + let forward = LIGHT_FORWARD_AXIS; + let rotation = DQuat::from_rotation_arc(forward, desired); + set_transform_rotation(world, entity, rotation) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "getColour"), + c +)] +fn get_colour( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<NColour> { + let light = world + .get::<&Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + Ok(NColour::from_linear_rgb(light.component.colour)) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "setColour"), + c +)] +fn set_colour( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + colour: &NColour, +) -> DropbearNativeResult<()> { + let mut light = world + .get::<&mut Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + light.component.colour = colour.to_linear_rgb(); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "getLightType"), + c +)] +fn get_light_type( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<i32> { + let light = world + .get::<&Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + Ok(light.component.light_type as i32) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "setLightType"), + c +)] +fn set_light_type( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + light_type: i32, +) -> DropbearNativeResult<()> { + let mut light = world + .get::<&mut Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + light.component.light_type = match light_type { + 0 => LightType::Directional, + 1 => LightType::Point, + 2 => LightType::Spot, + _ => return Err(DropbearNativeError::InvalidArgument), + }; + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "getIntensity"), + c +)] +fn get_intensity( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<f64> { + let light = world + .get::<&Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + Ok(light.component.intensity as f64) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "setIntensity"), + c +)] +fn set_intensity( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + intensity: f64, +) -> DropbearNativeResult<()> { + let mut light = world + .get::<&mut Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + light.component.intensity = intensity as f32; + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "getAttenuation"), + c +)] +fn get_attenuation( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<NAttenuation> { + let light = world + .get::<&Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + Ok(NAttenuation { + constant: light.component.attenuation.constant, + linear: light.component.attenuation.linear, + quadratic: light.component.attenuation.quadratic, + }) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "setAttenuation"), + c +)] +fn set_attenuation( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + attenuation: &NAttenuation, +) -> DropbearNativeResult<()> { + let mut light = world + .get::<&mut Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + light.component.attenuation.constant = attenuation.constant; + light.component.attenuation.linear = attenuation.linear; + light.component.attenuation.quadratic = attenuation.quadratic; + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "getEnabled"), + c +)] +fn get_enabled( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<bool> { + let light = world + .get::<&Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + Ok(light.component.enabled) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "setEnabled"), + c +)] +fn set_enabled( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + enabled: bool, +) -> DropbearNativeResult<()> { + let mut light = world + .get::<&mut Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + light.component.enabled = enabled; + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "getCutoffAngle"), + c +)] +fn get_cutoff_angle( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<f64> { + let light = world + .get::<&Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + Ok(light.component.cutoff_angle as f64) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "setCutoffAngle"), + c +)] +fn set_cutoff_angle( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + cutoff_angle: f64, +) -> DropbearNativeResult<()> { + let mut light = world + .get::<&mut Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + light.component.cutoff_angle = cutoff_angle as f32; + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "getOuterCutoffAngle"), + c +)] +fn get_outer_cutoff_angle( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<f64> { + let light = world + .get::<&Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + Ok(light.component.outer_cutoff_angle as f64) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "setOuterCutoffAngle"), + c +)] +fn set_outer_cutoff_angle( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + outer_cutoff_angle: f64, +) -> DropbearNativeResult<()> { + let mut light = world + .get::<&mut Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + light.component.outer_cutoff_angle = outer_cutoff_angle as f32; + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "getCastsShadows"), + c +)] +fn get_casts_shadows( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<bool> { + let light = world + .get::<&Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + Ok(light.component.cast_shadows) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "setCastsShadows"), + c +)] +fn set_casts_shadows( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + casts_shadows: bool, +) -> DropbearNativeResult<()> { + let mut light = world + .get::<&mut Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + light.component.cast_shadows = casts_shadows; + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "getDepth"), + c +)] +fn get_depth( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<NRange> { + let light = world + .get::<&Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + Ok(NRange { + start: light.component.depth.start, + end: light.component.depth.end, + }) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.lighting.LightNative", func = "setDepth"), + c +)] +fn set_depth( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + depth: &NRange, +) -> DropbearNativeResult<()> { + if !(depth.start.is_finite() && depth.end.is_finite()) { + return Err(DropbearNativeError::InvalidArgument); + } + if depth.end <= depth.start { + return Err(DropbearNativeError::InvalidArgument); + } + let mut light = world + .get::<&mut Light>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + light.component.depth = depth.start..depth.end; + Ok(()) +} @@ -0,0 +1,290 @@ +pub use eucalyptus_core::types::{NQuaternion, NTransform, NVector2, NVector3, NVector4}; + +use glam::{DQuat, DVec3}; +use jni::objects::JObject; +use jni::{jni_sig, jni_str, Env, JValue}; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use crate::{FromJObject, ToJObject}; + +// --------------------------------------------------------------- NVector2 --- + +impl ToJObject for NVector2 { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let cls = env + .load_class(jni_str!("com/dropbear/math/Vector2d")) + .map_err(|_| DropbearNativeError::GenericError)?; + + env.new_object( + cls, + jni_sig!((double, double) -> void), + &[JValue::Double(self.x), JValue::Double(self.y)], + ) + .map_err(|_| DropbearNativeError::GenericError) + } +} + +impl FromJObject for NVector2 { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { + let x = env + .get_field(obj, jni_str!("x"), jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + let y = env + .get_field(obj, jni_str!("y"), jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + Ok(NVector2 { x, y }) + } +} + +// --------------------------------------------------------------- NVector3 --- + +impl FromJObject for NVector3 { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { + let class = env + .load_class(jni_str!("com/dropbear/math/Vector3d")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + if !env + .is_instance_of(obj, &class) + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? + { + return Err(DropbearNativeError::InvalidArgument); + } + + let x = env + .get_field(obj, jni_str!("x"), jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + let y = env + .get_field(obj, jni_str!("y"), jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + let z = env + .get_field(obj, jni_str!("z"), jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + Ok(NVector3::new(x, y, z)) + } +} + +impl ToJObject for NVector3 { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/math/Vector3d")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + env.new_object( + &class, + jni_sig!((double, double, double) -> void), + &[ + JValue::Double(self.x), + JValue::Double(self.y), + JValue::Double(self.z), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +// --------------------------------------------------------------- NVector4 --- + +impl FromJObject for NVector4 { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { + let class = env + .load_class(jni_str!("com/dropbear/math/Vector4d")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + if !env + .is_instance_of(obj, &class) + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? + { + return Err(DropbearNativeError::InvalidArgument); + } + + let x = env + .get_field(obj, jni_str!("x"), jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + let y = env + .get_field(obj, jni_str!("y"), jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + let z = env + .get_field(obj, jni_str!("z"), jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + let w = env + .get_field(obj, jni_str!("w"), jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + Ok(NVector4::new(x, y, z, w)) + } +} + +impl ToJObject for NVector4 { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/math/Vector3d")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + env.new_object( + &class, + jni_sig!((double, double, double, double) -> void), + &[ + JValue::Double(self.x), + JValue::Double(self.y), + JValue::Double(self.z), + JValue::Double(self.w), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +// --------------------------------------------------------------- NQuaternion - + +impl ToJObject for NQuaternion { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/math/Quaterniond")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + env.new_object( + &class, + jni_sig!((double, double, double, double) -> void), + &[ + JValue::Double(self.x), + JValue::Double(self.y), + JValue::Double(self.z), + JValue::Double(self.w), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +impl FromJObject for NQuaternion { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { + let class = env + .load_class(jni_str!("com/dropbear/math/Quaterniond")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + if !env + .is_instance_of(obj, &class) + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? + { + return Err(DropbearNativeError::InvalidArgument); + } + + let x = env + .get_field(obj, jni_str!("x"), jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + let y = env + .get_field(obj, jni_str!("y"), jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + let z = env + .get_field(obj, jni_str!("z"), jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + let w = env + .get_field(obj, jni_str!("w"), jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + Ok(NQuaternion { x, y, z, w }) + } +} + +// --------------------------------------------------------------- NTransform -- + +impl FromJObject for NTransform { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { + let pos_obj = env + .get_field(obj, jni_str!("position"), jni_sig!(com.dropbear.math.Vector3d)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .l() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let rot_obj = env + .get_field(obj, jni_str!("rotation"), jni_sig!(com.dropbear.math.Quaterniond)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .l() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let scale_obj = env + .get_field(obj, jni_str!("scale"), jni_sig!(com.dropbear.math.Vector3d)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .l() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let position: DVec3 = NVector3::from_jobject(env, &pos_obj)?.into(); + let scale: DVec3 = NVector3::from_jobject(env, &scale_obj)?.into(); + + let mut get_double = |field| -> DropbearNativeResult<f64> { + env.get_field(&rot_obj, field, jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed) + }; + + let rx = get_double(jni_str!("x"))?; + let ry = get_double(jni_str!("y"))?; + let rz = get_double(jni_str!("z"))?; + let rw = get_double(jni_str!("w"))?; + + let rotation = DQuat::from_xyzw(rx, ry, rz, rw); + + Ok(NTransform { + position: position.into(), + rotation: rotation.into(), + scale: scale.into(), + }) + } +} + +impl ToJObject for NTransform { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/math/Transform")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let args = [ + JValue::Double(self.position.x), + JValue::Double(self.position.y), + JValue::Double(self.position.z), + JValue::Double(self.rotation.x), + JValue::Double(self.rotation.y), + JValue::Double(self.rotation.z), + JValue::Double(self.rotation.w), + JValue::Double(self.scale.x), + JValue::Double(self.scale.y), + JValue::Double(self.scale.z), + ]; + + env.new_object( + &class, + jni_sig!((double, double, double, double, double, double, double, double, double, double) -> void), + &args, + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} @@ -0,0 +1,224 @@ +use eucalyptus_core::ptr::{AssetRegistryPtr, AssetRegistryUnwrapped, GraphicsContextPtr, WorldPtr}; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use dropbear_engine::asset::{AssetRegistry, Handle}; +use dropbear_engine::entity::MeshRenderer; +use dropbear_engine::graphics::SharedGraphicsContext; +use dropbear_engine::model::Model; +use dropbear_engine::texture::Texture; +use std::collections::HashSet; +use std::sync::Arc; + +fn model_for_renderer(registry: &AssetRegistry, renderer: &MeshRenderer) -> Option<Arc<Model>> { + registry.get_model(renderer.model()) +} + +fn matches_material_label(material: &dropbear_engine::model::Material, target: &str) -> bool { + material.texture_tag.as_deref() == Some(target) || material.name == target +} + +fn resolve_target_material_index(model: &Model, target_identifier: &str) -> Option<usize> { + model + .materials + .iter() + .position(|mat| matches_material_label(mat, target_identifier)) +} + +fn resolve_target_material_name(model: &Model, target_identifier: &str) -> Option<String> { + model + .materials + .iter() + .find(|mat| matches_material_label(mat, target_identifier)) + .map(|mat| mat.name.clone()) +} + +#[dropbear_macro::export(kotlin( + class = "com.dropbear.components.MeshRendererNative", + func = "meshRendererExistsForEntity" +))] +fn mesh_renderer_exists_for_entity( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<bool> { + Ok(world.get::<&MeshRenderer>(entity).is_ok()) +} + +#[dropbear_macro::export(kotlin( + class = "com.dropbear.components.MeshRendererNative", + func = "getModel" +))] +fn get_model( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<u64> { + if let Ok(mesh) = world.get::<&MeshRenderer>(entity) { + Ok(mesh.model().id) + } else { + Err(DropbearNativeError::NoSuchComponent) + } +} + +#[dropbear_macro::export(kotlin( + class = "com.dropbear.components.MeshRendererNative", + func = "setModel" +))] +fn set_model( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, + #[dropbear_macro::entity] entity: hecs::Entity, + model_handle: u64, +) -> DropbearNativeResult<()> { + let handle = Handle::new(model_handle); + if asset.read().get_model(handle).is_none() { + return Err(DropbearNativeError::InvalidHandle); + } + + if let Ok(mut mesh) = world.get::<&mut MeshRenderer>(entity) { + mesh.set_model(handle); + Ok(()) + } else { + Err(DropbearNativeError::NoSuchComponent) + } +} + +#[dropbear_macro::export(kotlin( + class = "com.dropbear.components.MeshRendererNative", + func = "getAllTextureIds" +))] +fn get_all_texture_ids( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<Vec<u64>> { + let reader = asset.read(); + let renderer = world + .get::<&MeshRenderer>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + let model = + model_for_renderer(&reader, &renderer).ok_or(DropbearNativeError::AssetNotFound)?; + + let mut ids = HashSet::new(); + let mut push_handle = |handle: &Handle<Texture>| { + ids.insert(handle.id); + }; + + for material in &model.materials { + push_handle(&material.diffuse_texture); + if let Some(tex) = &material.normal_texture { + push_handle(tex); + } + if let Some(tex) = &material.emissive_texture { + push_handle(tex); + } + if let Some(tex) = &material.metallic_roughness_texture { + push_handle(tex); + } + if let Some(tex) = &material.occlusion_texture { + push_handle(tex); + } + } + + Ok(ids.into_iter().collect()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.MeshRendererNative", func = "getTexture"), + c +)] +fn get_texture( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, + #[dropbear_macro::entity] entity: hecs::Entity, + material_name: String, +) -> DropbearNativeResult<Option<u64>> { + let reader = asset.read(); + let renderer = world + .get::<&MeshRenderer>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + let model = + model_for_renderer(&reader, &renderer).ok_or(DropbearNativeError::AssetNotFound)?; + let idx = match resolve_target_material_index(&model, &material_name) { + Some(value) => value, + None => return Ok(None), + }; + let material = model + .materials + .get(idx) + .ok_or(DropbearNativeError::InvalidArgument)?; + + Ok(Some(material.diffuse_texture.id)) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.MeshRendererNative", + func = "setTextureOverride" + ), + c +)] +fn set_texture_override( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, + #[dropbear_macro::entity] entity: hecs::Entity, + material_name: String, + texture_handle: u64, +) -> DropbearNativeResult<()> { + let reader = asset.read(); + let renderer = world + .get::<&MeshRenderer>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + let model = + model_for_renderer(&reader, &renderer).ok_or(DropbearNativeError::AssetNotFound)?; + let _ = resolve_target_material_name(&model, &material_name) + .ok_or(DropbearNativeError::InvalidArgument)?; + + let handle = Handle::<Texture>::new(texture_handle); + if reader.get_texture(handle).is_none() { + return Err(DropbearNativeError::InvalidHandle); + } + + let mut renderer = world + .get::<&mut MeshRenderer>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + + if let Some(v) = renderer.material_snapshot.get_mut(&material_name) { + v.diffuse_texture = handle; + } else { + return Err(DropbearNativeError::InvalidArgument); + } + + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.MeshRendererNative", + func = "setMaterialTint" + ), + c +)] +fn set_material_tint( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, + #[dropbear_macro::define(GraphicsContextPtr)] graphics: &SharedGraphicsContext, + #[dropbear_macro::entity] entity: hecs::Entity, + material_name: String, + r: f32, + g: f32, + b: f32, + a: f32, +) -> DropbearNativeResult<()> { + let _ = asset; + let mut renderer = world + .get::<&mut MeshRenderer>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + + let material = renderer + .material_snapshot + .get_mut(&material_name) + .ok_or(DropbearNativeError::InvalidArgument)?; + + material.base_colour = [r, g, b, a]; + material.sync_uniform(graphics); + Ok(()) +} @@ -0,0 +1,676 @@ +use glam::{DQuat, Vec3}; +use jni::{jni_sig, jni_str, Env, JValue}; +use jni::objects::JObject; +use jni::sys::jdouble; +use eucalyptus_core::physics::collider::ColliderShape; +use eucalyptus_core::physics::PhysicsState; +use eucalyptus_core::ptr::PhysicsStatePtr; +use eucalyptus_core::rapier3d::geometry::{SharedShape, TypedShape}; +use eucalyptus_core::rapier3d::math::{Rotation, Vector}; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use crate::{FromJObject, ToJObject}; +use crate::physics::NCollider; +use crate::math::NVector3; +use crate::physics::collider::shared::{get_collider, get_collider_mut}; + +mod group { + use eucalyptus_core::physics::collider::ColliderGroup; + use eucalyptus_core::physics::PhysicsState; + use eucalyptus_core::ptr::{PhysicsStatePtr, WorldPtr}; + use eucalyptus_core::scripting::native::DropbearNativeError; + use eucalyptus_core::scripting::result::DropbearNativeResult; + use crate::physics::{IndexNative, NCollider}; + + #[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderGroupNative", + func = "colliderGroupExistsForEntity" + ), + c + )] + fn exists_for_entity( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + ) -> DropbearNativeResult<bool> { + Ok(world.get::<&ColliderGroup>(entity).is_ok()) + } + + #[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderGroupNative", + func = "getColliderGroupColliders" + ), + c + )] + fn get_colliders( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + #[dropbear_macro::entity] entity: hecs::Entity, + ) -> DropbearNativeResult<Vec<NCollider>> { + if world.get::<&ColliderGroup>(entity).is_ok() { + let handles_opt = physics + .entity_label_map + .get(&entity) + .and_then(|label| physics.colliders_entity_map.get(label)); + + let mut colliders: Vec<NCollider> = Vec::new(); + + if let Some(handles) = handles_opt { + for (_, handle) in handles { + let (idx, generation) = handle.into_raw_parts(); + + let col = NCollider { + index: IndexNative { + index: idx, + generation, + }, + entity_id: entity.to_bits().get(), + id: idx, + }; + colliders.push(col); + } + } + + Ok(colliders) + } else { + Err(DropbearNativeError::MissingComponent)? + } + } +} + +impl ToJObject for ColliderShape { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + match self { + ColliderShape::Box { half_extents } => { + let vec_cls = env + .load_class(jni_str!("com/dropbear/math/Vector3d")) + .map_err(|e| { + eprintln!("[JNI Error] Vector3d class not found: {:?}", e); + DropbearNativeError::JNIClassNotFound + })?; + + let vec_obj = env + .new_object( + &vec_cls, + jni_sig!("(DDD)V"), + &[ + JValue::Double(half_extents.x as jdouble), + JValue::Double(half_extents.y as jdouble), + JValue::Double(half_extents.z as jdouble), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + let cls = env + .load_class(jni_str!("com/dropbear/physics/ColliderShape$Box")) + .map_err(|e| { + eprintln!("[JNI Error] ColliderShape$Box class not found: {:?}", e); + DropbearNativeError::JNIClassNotFound + })?; + + let obj = env + .new_object( + &cls, + jni_sig!("(Lcom/dropbear/math/Vector3d;)V"), + &[JValue::Object(&vec_obj)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + Ok(obj) + } + ColliderShape::Sphere { radius } => { + let cls = env + .load_class(jni_str!("com/dropbear/physics/ColliderShape$Sphere")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let obj = env + .new_object(&cls, jni_sig!("(F)V"), &[JValue::Float(*radius)]) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + Ok(obj) + } + ColliderShape::Capsule { + half_height, + radius, + } => { + let cls = env + .load_class(jni_str!("com/dropbear/physics/ColliderShape$Capsule")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let obj = env + .new_object( + &cls, + jni_sig!("(FF)V"), + &[JValue::Float(*half_height), JValue::Float(*radius)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + Ok(obj) + } + ColliderShape::Cylinder { + half_height, + radius, + } => { + let cls = env + .load_class(jni_str!("com/dropbear/physics/ColliderShape$Cylinder")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + // let ctor = env.get_method_id(&cls, "<init>", "(FF)V") + // .map_err(|_| DropbearNativeError::JNIMethodNotFound)?; + + let obj = env + .new_object( + &cls, + jni_sig!("(FF)V"), + &[JValue::Float(*half_height), JValue::Float(*radius)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + Ok(obj) + } + ColliderShape::Cone { + half_height, + radius, + } => { + let cls = env + .load_class(jni_str!("com/dropbear/physics/ColliderShape$Cone")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let obj = env + .new_object( + &cls, + jni_sig!("(FF)V"), + &[JValue::Float(*half_height), JValue::Float(*radius)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + Ok(obj) + } + } + } +} + +impl FromJObject for ColliderShape { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> + where + Self: Sized, + { + let is_instance = |env: &mut Env, + obj: &JObject, + class_name: &jni::strings::JNIStr| + -> bool { env.is_instance_of(obj, class_name).unwrap_or(false) }; + + if is_instance(env, obj, jni_str!("com/dropbear/physics/ColliderShape$Box")) { + let vec_obj_val = env + .get_field( + obj, + jni_str!("halfExtents"), + jni_sig!("Lcom/dropbear/math/Vector3d;"), + ) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; + let vec_obj = vec_obj_val + .l() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let x = env + .get_field(&vec_obj, jni_str!("x"), jni_sig!("D")) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .unwrap_or(0.0); + let y = env + .get_field(&vec_obj, jni_str!("y"), jni_sig!("D")) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .unwrap_or(0.0); + let z = env + .get_field(&vec_obj, jni_str!("z"), jni_sig!("D")) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .unwrap_or(0.0); + + return Ok(ColliderShape::Box { + half_extents: Vec3::from([x as f32, y as f32, z as f32]), + }); + } + + if is_instance( + env, + obj, + jni_str!("com/dropbear/physics/ColliderShape$Sphere"), + ) { + let radius = env + .get_field(obj, jni_str!("radius"), jni_sig!("F")) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .unwrap_or(0.0); + + return Ok(ColliderShape::Sphere { radius }); + } + + if is_instance( + env, + obj, + jni_str!("com/dropbear/physics/ColliderShape$Capsule"), + ) { + let hh = env + .get_field(obj, jni_str!("halfHeight"), jni_sig!("F")) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .unwrap_or(0.0); + let r = env + .get_field(obj, jni_str!("radius"), jni_sig!("F")) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .unwrap_or(0.0); + + return Ok(ColliderShape::Capsule { + half_height: hh, + radius: r, + }); + } + + if is_instance( + env, + obj, + jni_str!("com/dropbear/physics/ColliderShape$Cylinder"), + ) { + let hh = env + .get_field(obj, jni_str!("halfHeight"), jni_sig!("F")) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .unwrap_or(0.0); + let r = env + .get_field(obj, jni_str!("radius"), jni_sig!("F")) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .unwrap_or(0.0); + + return Ok(ColliderShape::Cylinder { + half_height: hh, + radius: r, + }); + } + + if is_instance( + env, + obj, + jni_str!("com/dropbear/physics/ColliderShape$Cone"), + ) { + let hh = env + .get_field(obj, jni_str!("halfHeight"), jni_sig!("F")) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .unwrap_or(0.0); + let r = env + .get_field(obj, jni_str!("radius"), jni_sig!("F")) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .unwrap_or(0.0); + + return Ok(ColliderShape::Cone { + half_height: hh, + radius: r, + }); + } + + Err(DropbearNativeError::GenericError) + } +} + +// -------------------------------- collider ------------------------------------ + +pub mod shared { + use eucalyptus_core::physics::PhysicsState; + use eucalyptus_core::types::NCollider; + use eucalyptus_core::rapier3d::prelude::ColliderHandle; + + pub fn get_collider_mut<'a>( + physics: &'a mut PhysicsState, + ffi: &NCollider, + ) -> Option<&'a mut eucalyptus_core::rapier3d::prelude::Collider> { + let handle = ColliderHandle::from_raw_parts(ffi.index.index, ffi.index.generation); + physics.colliders.get_mut(handle) + } + + pub fn get_collider<'a>( + physics: &'a PhysicsState, + ffi: &NCollider, + ) -> Option<&'a eucalyptus_core::rapier3d::prelude::Collider> { + let handle = ColliderHandle::from_raw_parts(ffi.index.index, ffi.index.generation); + physics.colliders.get(handle) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderShape" + ), + c +)] +fn get_collider_shape( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + collider: &NCollider, +) -> DropbearNativeResult<ColliderShape> { + let collider = + get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + + let rapier_shape = collider.shape(); + let my_shape = match rapier_shape.as_typed_shape() { + TypedShape::Cuboid(c) => { + let he = c.half_extents; + ColliderShape::Box { + half_extents: glam::Vec3::from([he.x, he.y, he.z]), + } + } + TypedShape::Ball(b) => ColliderShape::Sphere { radius: b.radius }, + TypedShape::Capsule(c) => { + let height = c.segment.length(); + ColliderShape::Capsule { + half_height: height * 0.5, + radius: c.radius, + } + } + TypedShape::Cylinder(c) => ColliderShape::Cylinder { + half_height: c.half_height, + radius: c.radius, + }, + TypedShape::Cone(c) => ColliderShape::Cone { + half_height: c.half_height, + radius: c.radius, + }, + _ => return Err(DropbearNativeError::InvalidArgument), + }; + + Ok(my_shape) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderShape" + ), + c +)] +fn set_collider_shape( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + collider: &NCollider, + shape: &ColliderShape, +) -> DropbearNativeResult<()> { + let collider = + get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + + let new_shape = match shape { + ColliderShape::Box { half_extents } => SharedShape::cuboid( + half_extents.x as f32, + half_extents.y as f32, + half_extents.z as f32, + ), + ColliderShape::Sphere { radius } => SharedShape::ball(*radius), + ColliderShape::Capsule { + half_height, + radius, + } => SharedShape::capsule_y(*half_height, *radius), + ColliderShape::Cylinder { + half_height, + radius, + } => SharedShape::cylinder(*half_height, *radius), + ColliderShape::Cone { + half_height, + radius, + } => SharedShape::cone(*half_height, *radius), + }; + + collider.set_shape(new_shape); + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderDensity" + ), + c +)] +fn get_collider_density( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + collider: &NCollider, +) -> DropbearNativeResult<f64> { + let collider = + get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + Ok(collider.density() as f64) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderDensity" + ), + c +)] +fn set_collider_density( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + collider: &NCollider, + density: f64, +) -> DropbearNativeResult<()> { + let collider = + get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + collider.set_density(density as f32); + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderFriction" + ), + c +)] +fn get_collider_friction( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + collider: &NCollider, +) -> DropbearNativeResult<f64> { + let collider = + get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + Ok(collider.friction() as f64) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderFriction" + ), + c +)] +fn set_collider_friction( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + collider: &NCollider, + friction: f64, +) -> DropbearNativeResult<()> { + let collider = + get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + collider.set_friction(friction as f32); + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderRestitution" + ), + c +)] +fn get_collider_restitution( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + collider: &NCollider, +) -> DropbearNativeResult<f64> { + let collider = + get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + Ok(collider.restitution() as f64) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderRestitution" + ), + c +)] +fn set_collider_restitution( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + collider: &NCollider, + restitution: f64, +) -> DropbearNativeResult<()> { + let collider = + get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + collider.set_restitution(restitution as f32); + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderMass" + ), + c +)] +fn get_collider_mass( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + collider: &NCollider, +) -> DropbearNativeResult<f64> { + let collider = + get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + Ok(collider.mass() as f64) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderMass" + ), + c +)] +fn set_collider_mass( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + collider: &NCollider, + mass: f64, +) -> DropbearNativeResult<()> { + let collider = + get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + collider.set_mass(mass as f32); + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderIsSensor" + ), + c +)] +fn get_collider_is_sensor( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + collider: &NCollider, +) -> DropbearNativeResult<bool> { + let collider = + get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + Ok(collider.is_sensor()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderIsSensor" + ), + c +)] +fn set_collider_is_sensor( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + collider: &NCollider, + is_sensor: bool, +) -> DropbearNativeResult<()> { + let collider = + get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + collider.set_sensor(is_sensor); + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderTranslation" + ), + c +)] +fn get_collider_translation( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + collider: &NCollider, +) -> DropbearNativeResult<NVector3> { + let collider = + get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + let t: Vector = collider.translation(); + Ok(NVector3::new(t.x as f64, t.y as f64, t.z as f64)) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderTranslation" + ), + c +)] +fn set_collider_translation( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + collider: &NCollider, + translation: &NVector3, +) -> DropbearNativeResult<()> { + let collider = + get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + let t = Vector::new( + translation.x as f32, + translation.y as f32, + translation.z as f32, + ); + collider.set_translation(t); + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderRotation" + ), + c +)] +fn get_collider_rotation( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + collider: &NCollider, +) -> DropbearNativeResult<NVector3> { + let collider = + get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + let r: Rotation = collider.rotation(); + let q = DQuat::from_xyzw(r.x as f64, r.y as f64, r.z as f64, r.w as f64); + let euler = q.to_euler(glam::EulerRot::XYZ); + Ok(NVector3::new(euler.0, euler.1, euler.2)) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderRotation" + ), + c +)] +fn set_collider_rotation( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + collider: &NCollider, + rotation: &NVector3, +) -> DropbearNativeResult<()> { + let collider = + get_collider_mut(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + let q = DQuat::from_euler(glam::EulerRot::XYZ, rotation.x, rotation.y, rotation.z); + let r = Rotation::from_array([q.w as f32, q.x as f32, q.y as f32, q.z as f32]); + collider.set_rotation(r); + Ok(()) +} @@ -0,0 +1,569 @@ +use eucalyptus_core::physics::kcc::{CharacterMovementResult, KCC}; +use eucalyptus_core::physics::PhysicsState; +use eucalyptus_core::ptr::WorldPtr; +use eucalyptus_core::rapier3d; +use eucalyptus_core::rapier3d::dynamics::RigidBodyType; +use eucalyptus_core::rapier3d::math::Rotation; +use eucalyptus_core::rapier3d::pipeline::QueryFilter; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use eucalyptus_core::states::Label; +use crate::math::{NTransform, NVector3, NQuaternion}; +use crate::physics::{IndexNative, NCollider, NShapeCastStatus, CharacterCollisionArray}; + +pub mod shared { + use glam::{DQuat, DVec3}; + use hecs::{Entity, World}; + use eucalyptus_core::physics::collider::ColliderGroup; + use eucalyptus_core::physics::kcc::KCC; + use eucalyptus_core::rapier3d; + use eucalyptus_core::rapier3d::control::CharacterCollision; + use eucalyptus_core::rapier3d::na::Quaternion; + use eucalyptus_core::scripting::native::DropbearNativeError; + use crate::math::{NTransform, NVector3}; + use crate::physics::{IndexNative, NCollider, NShapeCastStatus}; + use super::*; + + fn get_collision_from_world( + world: &World, + entity: Entity, + collision_handle: &IndexNative, + ) -> DropbearNativeResult<CharacterCollision> { + let kcc = world + .get::<&KCC>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + + kcc.collisions + .iter() + .copied() + .find(|c| { + let (idx, generation) = c.handle.into_raw_parts(); + idx == collision_handle.index && generation == collision_handle.generation + }) + .ok_or(DropbearNativeError::NoSuchHandle) + } + + fn collider_ffi_from_handle( + world: &World, + handle: rapier3d::prelude::ColliderHandle, + ) -> Option<NCollider> { + let (idx, generation) = handle.into_raw_parts(); + + for (entity, group) in world.query::<(Entity, &ColliderGroup)>().iter() { + if group.colliders.iter().any(|c| c.id == idx) { + return Some(NCollider { + index: IndexNative { + index: idx, + generation, + }, + entity_id: entity.to_bits().get(), + id: idx, + }); + } + } + + None + } + + pub fn get_collider( + world: &World, + entity: Entity, + collision_handle: &IndexNative, + ) -> DropbearNativeResult<NCollider> { + let collision = get_collision_from_world(world, entity, collision_handle)?; + collider_ffi_from_handle(world, collision.handle) + .ok_or(DropbearNativeError::PhysicsObjectNotFound) + } + + pub fn get_character_position( + world: &World, + entity: Entity, + collision_handle: &IndexNative, + ) -> DropbearNativeResult<NTransform> { + let collision = get_collision_from_world(world, entity, collision_handle)?; + + let iso = collision.character_pos; + let t = iso.translation; + let rot = iso.rotation; + let q: Quaternion<f32> = Quaternion::from(rot); + + Ok(NTransform { + position: DVec3::new(t.x as f64, t.y as f64, t.z as f64).into(), + rotation: DQuat::from_xyzw(q.i as f64, q.j as f64, q.k as f64, q.w as f64).into(), + scale: DVec3::ONE.into(), + }) + } + + pub fn get_translation_applied( + world: &World, + entity: Entity, + collision_handle: &IndexNative, + ) -> DropbearNativeResult<NVector3> { + let collision = get_collision_from_world(world, entity, collision_handle)?; + let v = collision.translation_applied; + Ok(NVector3 { + x: v.x as f64, + y: v.y as f64, + z: v.z as f64, + }) + } + + pub fn get_translation_remaining( + world: &World, + entity: Entity, + collision_handle: &IndexNative, + ) -> DropbearNativeResult<NVector3> { + let collision = get_collision_from_world(world, entity, collision_handle)?; + let v = collision.translation_remaining; + Ok(NVector3 { + x: v.x as f64, + y: v.y as f64, + z: v.z as f64, + }) + } + + pub fn get_time_of_impact( + world: &World, + entity: Entity, + collision_handle: &IndexNative, + ) -> DropbearNativeResult<f64> { + let collision = get_collision_from_world(world, entity, collision_handle)?; + Ok(collision.hit.time_of_impact as f64) + } + + pub fn get_witness1( + world: &World, + entity: Entity, + collision_handle: &IndexNative, + ) -> DropbearNativeResult<NVector3> { + let collision = get_collision_from_world(world, entity, collision_handle)?; + let p = collision.hit.witness1; + Ok(NVector3 { + x: p.x as f64, + y: p.y as f64, + z: p.z as f64, + }) + } + + pub fn get_witness2( + world: &World, + entity: Entity, + collision_handle: &IndexNative, + ) -> DropbearNativeResult<NVector3> { + let collision = get_collision_from_world(world, entity, collision_handle)?; + let p = collision.hit.witness2; + Ok(NVector3 { + x: p.x as f64, + y: p.y as f64, + z: p.z as f64, + }) + } + + pub fn get_normal1( + world: &World, + entity: Entity, + collision_handle: &IndexNative, + ) -> DropbearNativeResult<NVector3> { + let collision = get_collision_from_world(world, entity, collision_handle)?; + let n = collision.hit.normal1; + Ok(NVector3 { + x: n.x as f64, + y: n.y as f64, + z: n.z as f64, + }) + } + + pub fn get_normal2( + world: &World, + entity: Entity, + collision_handle: &IndexNative, + ) -> DropbearNativeResult<NVector3> { + let collision = get_collision_from_world(world, entity, collision_handle)?; + let n = collision.hit.normal2; + Ok(NVector3 { + x: n.x as f64, + y: n.y as f64, + z: n.z as f64, + }) + } + + pub fn get_status( + world: &World, + entity: Entity, + collision_handle: &IndexNative, + ) -> DropbearNativeResult<NShapeCastStatus> { + let collision = get_collision_from_world(world, entity, collision_handle)?; + Ok(collision.hit.status.into()) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.CharacterCollisionNative", + func = "getCollider" + ), + c +)] +fn get_character_collision_collider( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + collision_handle: &IndexNative, +) -> DropbearNativeResult<NCollider> { + shared::get_collider(world, entity, collision_handle) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.CharacterCollisionNative", + func = "getCharacterPosition" + ), + c +)] +fn get_character_collision_position( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + collision_handle: &IndexNative, +) -> DropbearNativeResult<NTransform> { + shared::get_character_position(world, entity, collision_handle) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.CharacterCollisionNative", + func = "getTranslationApplied" + ), + c +)] +fn get_character_collision_translation_applied( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + collision_handle: &IndexNative, +) -> DropbearNativeResult<NVector3> { + shared::get_translation_applied(world, entity, collision_handle) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.CharacterCollisionNative", + func = "getTranslationRemaining" + ), + c +)] +fn get_character_collision_translation_remaining( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + collision_handle: &IndexNative, +) -> DropbearNativeResult<NVector3> { + shared::get_translation_remaining(world, entity, collision_handle) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.CharacterCollisionNative", + func = "getTimeOfImpact" + ), + c +)] +fn get_character_collision_time_of_impact( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + collision_handle: &IndexNative, +) -> DropbearNativeResult<f64> { + shared::get_time_of_impact(world, entity, collision_handle) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.CharacterCollisionNative", + func = "getWitness1" + ), + c +)] +fn get_character_collision_witness1( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + collision_handle: &IndexNative, +) -> DropbearNativeResult<NVector3> { + shared::get_witness1(world, entity, collision_handle) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.CharacterCollisionNative", + func = "getWitness2" + ), + c +)] +fn get_character_collision_witness2( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + collision_handle: &IndexNative, +) -> DropbearNativeResult<NVector3> { + shared::get_witness2(world, entity, collision_handle) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.CharacterCollisionNative", + func = "getNormal1" + ), + c +)] +fn get_character_collision_normal1( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + collision_handle: &IndexNative, +) -> DropbearNativeResult<NVector3> { + shared::get_normal1(world, entity, collision_handle) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.CharacterCollisionNative", + func = "getNormal2" + ), + c +)] +fn get_character_collision_normal2( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + collision_handle: &IndexNative, +) -> DropbearNativeResult<NVector3> { + shared::get_normal2(world, entity, collision_handle) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.CharacterCollisionNative", + func = "getStatus" + ), + c +)] +fn get_character_collision_status( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + collision_handle: &IndexNative, +) -> DropbearNativeResult<NShapeCastStatus> { + shared::get_status(world, entity, collision_handle) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.KinematicCharacterControllerNative", + func = "existsForEntity" + ), + c +)] +fn kcc_exists_for_entity( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<bool> { + Ok(world.get::<&KCC>(entity).is_ok()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.KinematicCharacterControllerNative", + func = "moveCharacter" + ), + c +)] +fn move_character( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(crate::ptr::PhysicsStatePtr)] physics_state: &mut PhysicsState, + #[dropbear_macro::entity] entity: hecs::Entity, + translation: &NVector3, + delta_time: f64, +) -> DropbearNativeResult<()> { + if let Ok((label, kcc)) = world.query_one::<(&Label, &mut KCC)>(entity).get() { + let rigid_body_handle = physics_state + .bodies_entity_map + .get(label) + .ok_or(DropbearNativeError::NoSuchHandle)?; + + let (body_type, body_pos) = { + let body = physics_state + .bodies + .get(*rigid_body_handle) + .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + (body.body_type(), *body.position()) + }; + + if body_type != RigidBodyType::KinematicPositionBased { + return Ok(()); // soft error, just tell the user + } + + let collider_handles = physics_state + .colliders_entity_map + .get(label) + .ok_or(DropbearNativeError::NoSuchHandle)?; + let (_, collider_handle) = collider_handles + .first() + .ok_or(DropbearNativeError::NoSuchHandle)?; + let collider = physics_state + .colliders + .get(*collider_handle) + .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + + let character_pos = if let Some(pos_wrt_parent) = collider.position_wrt_parent() { + body_pos * (*pos_wrt_parent) + } else { + *collider.position() + }; + + let filter = QueryFilter::default() + .exclude_rigid_body(*rigid_body_handle) + .exclude_sensors(); + let query_pipeline = physics_state.broad_phase.as_query_pipeline( + physics_state.narrow_phase.query_dispatcher(), + &physics_state.bodies, + &physics_state.colliders, + filter, + ); + + let movement = kcc.controller.move_shape( + delta_time as f32, + &query_pipeline, + collider.shape(), + &character_pos, + rapier3d::prelude::Vector::new( + translation.x as f32, + translation.y as f32, + translation.z as f32, + ), + |collision| { + if let Some(collisions) = + physics_state.collision_events_to_deal_with.get_mut(&entity) + { + collisions.push(collision) + } else { + physics_state + .collision_events_to_deal_with + .insert(entity, vec![collision]); + } + }, + ); + + if let Some(body) = physics_state.bodies.get_mut(*rigid_body_handle) { + let current_pos = body.translation(); + let new_pos = current_pos + movement.translation; + body.set_next_kinematic_translation(new_pos); + } + + kcc.movement = Some(CharacterMovementResult { + translation: movement.translation.into(), + grounded: movement.grounded, + is_sliding_down_slope: movement.is_sliding_down_slope, + }); + + Ok(()) + } else { + Err(DropbearNativeError::MissingComponent) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.KinematicCharacterControllerNative", + func = "setRotation" + ), + c +)] +fn set_rotation( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(crate::ptr::PhysicsStatePtr)] physics_state: &mut PhysicsState, + #[dropbear_macro::entity] entity: hecs::Entity, + rotation: &NQuaternion, +) -> DropbearNativeResult<()> { + if let Ok((label, _)) = world.query_one::<(&Label, &KCC)>(entity).get() { + let rigid_body_handle = physics_state + .bodies_entity_map + .get(label) + .ok_or(DropbearNativeError::NoSuchHandle)?; + + let body_type = { + let body = physics_state + .bodies + .get(*rigid_body_handle) + .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + body.body_type() + }; + + if body_type != RigidBodyType::KinematicPositionBased { + return Err(DropbearNativeError::InvalidArgument); + } + + let len = (rotation.x * rotation.x + + rotation.y * rotation.y + + rotation.z * rotation.z + + rotation.w * rotation.w) + .sqrt(); + let (x, y, z, w) = if len > 0.0 { + ( + rotation.x / len, + rotation.y / len, + rotation.z / len, + rotation.w / len, + ) + } else { + (0.0, 0.0, 0.0, 1.0) + }; + + if let Some(body) = physics_state.bodies.get_mut(*rigid_body_handle) { + let rot = Rotation::from_xyzw(x as f32, y as f32, z as f32, w as f32); + body.set_next_kinematic_rotation(rot); + } + + Ok(()) + } else { + Err(DropbearNativeError::MissingComponent) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.KinematicCharacterControllerNative", + func = "getHit" + ), + c +)] +fn get_hit( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<CharacterCollisionArray> { + let kcc = world + .get::<&KCC>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + + let mut collisions = Vec::with_capacity(kcc.collisions.len()); + for collision in &kcc.collisions { + let (idx, generation) = collision.handle.into_raw_parts(); + collisions.push(IndexNative { + index: idx, + generation, + }); + } + + Ok(CharacterCollisionArray { + entity_id: entity.to_bits().get(), + collisions, + }) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.KinematicCharacterControllerNative", + func = "getMovementResult" + ), + c +)] +fn get_movement_result( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<Option<CharacterMovementResult>> { + world + .get::<&KCC>(entity) + .map(|kcc| kcc.movement.clone()) + .map(Ok) + .unwrap_or(Ok(None)) +} @@ -0,0 +1,504 @@ +pub use eucalyptus_core::types::{ + CollisionEvent, CollisionEventType, ContactForceEvent, IndexNative, NCollider, NShapeCastHit, + NShapeCastStatus, RayHit, RigidBodyContext, +}; + +pub mod physics; + +use jni::objects::JObject; +use jni::sys::jdouble; +use jni::{jni_sig, jni_str, Env, JValue}; +use eucalyptus_core::physics::kcc::CharacterMovementResult; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use crate::{FromJObject, ToJObject}; +use crate::math::NVector3; + +pub mod collider; +pub mod kcc; +pub mod rigidbody; + +// ----------------------------------------------------------- CharacterCollision + +/// Returned by `getHit` — an entity's KCC collision handles packed for JNI transfer. +pub struct CharacterCollisionArray { + pub entity_id: u64, + pub collisions: Vec<IndexNative>, +} + +// ------------------------------------------------------- NCollider JNI impls -- + +impl FromJObject for NCollider { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { + let index_obj = env + .get_field(obj, jni_str!("index"), jni_sig!(com.dropbear.physics.Index)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .l() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let entity_obj = env + .get_field(obj, jni_str!("entity"), jni_sig!(com.dropbear.EntityId)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .l() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let id_val = env + .get_field(obj, jni_str!("id"), jni_sig!(int)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .i() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let entity_raw = env + .get_field(&entity_obj, jni_str!("raw"), jni_sig!(long)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .j() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let idx_val = env + .get_field(&index_obj, jni_str!("index"), jni_sig!(int)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .i() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let gen_val = env + .get_field(&index_obj, jni_str!("generation"), jni_sig!(int)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .i() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + Ok(NCollider { + index: IndexNative { + index: idx_val as u32, + generation: gen_val as u32, + }, + entity_id: entity_raw as u64, + id: id_val as u32, + }) + } +} + +impl ToJObject for NCollider { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let collider_cls = env + .load_class(jni_str!("com/dropbear/physics/Collider")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let index_cls = env + .load_class(jni_str!("com/dropbear/physics/Index")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let entity_cls = env + .load_class(jni_str!("com/dropbear/EntityId")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let entity_obj = env + .new_object( + &entity_cls, + jni_sig!((long) -> void), + &[JValue::Long(self.entity_id as i64)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + let index_obj = env + .new_object( + &index_cls, + jni_sig!((int, int) -> void), + &[ + JValue::Int(self.index.index as i32), + JValue::Int(self.index.generation as i32), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + let collider_obj = env + .new_object( + collider_cls, + jni_sig!((com.dropbear.physics.Index, com.dropbear.EntityId, int) -> void), + &[ + JValue::Object(&index_obj), + JValue::Object(&entity_obj), + JValue::Int(self.id as i32), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + Ok(collider_obj) + } +} + +// ----------------------------------------------- CollisionEventType JNI impl - + +impl ToJObject for CollisionEventType { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/physics/CollisionEventType")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let name = match self { + CollisionEventType::Started => "Started", + CollisionEventType::Stopped => "Stopped", + }; + let name_jstring = env + .new_string(name) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + env.call_static_method( + class, + jni_str!("valueOf"), + jni_sig!((java.lang.String) -> com.dropbear.physics.CollisionEventType), + &[JValue::from(&name_jstring)], + ) + .map_err(|_| DropbearNativeError::JNIMethodNotFound)? + .l() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed) + } +} + +// -------------------------------------------------- CollisionEvent JNI impl -- + +impl ToJObject for CollisionEvent { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/physics/CollisionEvent")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let event_type = self.event_type.to_jobject(env)?; + let collider1 = self.collider1.to_jobject(env)?; + let collider2 = self.collider2.to_jobject(env)?; + + let flags = self.flags as i32; + env.new_object( + class, + jni_sig!("(Lcom/dropbear/physics/CollisionEventType;Lcom/dropbear/physics/Collider;Lcom/dropbear/physics/Collider;I)V"), + &[ + JValue::Object(&event_type), + JValue::Object(&collider1), + JValue::Object(&collider2), + JValue::Int(flags), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +// ----------------------------------------------- ContactForceEvent JNI impl -- + +impl ToJObject for ContactForceEvent { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/physics/ContactForceEvent")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let collider1 = self.collider1.to_jobject(env)?; + let collider2 = self.collider2.to_jobject(env)?; + let total_force = self.total_force.to_jobject(env)?; + let max_force_direction = self.max_force_direction.to_jobject(env)?; + + env.new_object( + class, + jni_sig!("(Lcom/dropbear/physics/Collider;Lcom/dropbear/physics/Collider;Lcom/dropbear/math/Vector3d;DLcom/dropbear/math/Vector3d;D)V"), + &[ + JValue::Object(&collider1), + JValue::Object(&collider2), + JValue::Object(&total_force), + JValue::Double(self.total_force_magnitude), + JValue::Object(&max_force_direction), + JValue::Double(self.max_force_magnitude), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +// ------------------------------------------------ NShapeCastHit JNI impl ----- + +impl ToJObject for NShapeCastHit { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let collider = self.collider.to_jobject(env)?; + let witness1 = self.witness1.to_jobject(env)?; + let witness2 = self.witness2.to_jobject(env)?; + let normal1 = self.normal1.to_jobject(env)?; + let normal2 = self.normal2.to_jobject(env)?; + let status = self.status.to_jobject(env)?; + + let class = env + .load_class(jni_str!("com/dropbear/physics/ShapeCastHit")) + .map_err(|_| { + eprintln!("[JNI Error] Failed to find ShapeCastHit class"); + DropbearNativeError::JNIClassNotFound + })?; + + env.new_object( + class, + jni_sig!("(Lcom/dropbear/physics/Collider;DLcom/dropbear/math/Vector3d;Lcom/dropbear/math/Vector3d;Lcom/dropbear/math/Vector3d;Lcom/dropbear/math/Vector3d;Lcom/dropbear/physics/ShapeCastStatus;)V"), + &[ + JValue::Object(&collider), + JValue::Double(self.distance as jdouble), + JValue::Object(&witness1), + JValue::Object(&witness2), + JValue::Object(&normal1), + JValue::Object(&normal2), + JValue::Object(&status), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +// ------------------------------------------------ NShapeCastStatus JNI impl -- + +impl ToJObject for NShapeCastStatus { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/physics/ShapeCastStatus")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let name = match self { + NShapeCastStatus::OutOfIterations => "OutOfIterations", + NShapeCastStatus::Converged => "Converged", + NShapeCastStatus::Failed => "Failed", + NShapeCastStatus::PenetratingOrWithinTargetDist => "PenetratingOrWithinTargetDist", + }; + + let name_jstring = env + .new_string(name) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + env.call_static_method( + class, + jni_str!("valueOf"), + jni_sig!((java.lang.String) -> com.dropbear.physics.ShapeCastStatus), + &[JValue::from(&name_jstring)], + ) + .map_err(|_| DropbearNativeError::JNIMethodNotFound)? + .l() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed) + } +} + +// ------------------------------------------------ IndexNative JNI impls ------- + +impl FromJObject for IndexNative { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { + let idx_val = env + .get_field(obj, jni_str!("index"), jni_sig!(int)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .i() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let gen_val = env + .get_field(obj, jni_str!("generation"), jni_sig!(int)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .i() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + Ok(IndexNative { + index: idx_val as u32, + generation: gen_val as u32, + }) + } +} + +impl ToJObject for IndexNative { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let cls = env + .load_class(jni_str!("com/dropbear/physics/Index")) + .map_err(|_| DropbearNativeError::GenericError)?; + + env.new_object( + cls, + jni_sig!((int, int) -> void), + &[ + JValue::Int(self.index as i32), + JValue::Int(self.generation as i32), + ], + ) + .map_err(|_| DropbearNativeError::GenericError) + } +} + +impl ToJObject for Option<IndexNative> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + match self { + Some(v) => v.to_jobject(env), + None => Ok(JObject::null()), + } + } +} + +// ------------------------------------------------ RigidBodyContext JNI impls - + +impl FromJObject for RigidBodyContext { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { + let index_obj = env + .get_field(obj, jni_str!("index"), jni_sig!(com.dropbear.physics.Index)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .l() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let idx_val = env + .get_field(&index_obj, jni_str!("index"), jni_sig!(int)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .i() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let gen_val = env + .get_field(&index_obj, jni_str!("generation"), jni_sig!(int)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .i() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let entity_obj = env + .get_field(obj, jni_str!("entity"), jni_sig!(com.dropbear.EntityId)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .l() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let entity_raw = env + .get_field(&entity_obj, jni_str!("raw"), jni_sig!(long)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .j() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + Ok(RigidBodyContext { + index: IndexNative { + index: idx_val as u32, + generation: gen_val as u32, + }, + entity_id: entity_raw as u64, + }) + } +} + +impl ToJObject for RigidBodyContext { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let index_cls = env + .load_class(jni_str!("com/dropbear/physics/Index")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let entity_cls = env + .load_class(jni_str!("com/dropbear/EntityId")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let rb_cls = env + .load_class(jni_str!("com/dropbear/physics/RigidBody")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let index_obj = env + .new_object( + &index_cls, + jni_sig!((int, int) -> void), + &[ + JValue::Int(self.index.index as i32), + JValue::Int(self.index.generation as i32), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + let entity_obj = env + .new_object( + &entity_cls, + jni_sig!((long) -> void), + &[JValue::Long(self.entity_id as i64)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + env.new_object( + rb_cls, + jni_sig!((com.dropbear.physics.Index, com.dropbear.EntityId) -> void), + &[JValue::Object(&index_obj), JValue::Object(&entity_obj)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +// ---------------------------------------------------- RayHit JNI impl --------- + +impl ToJObject for RayHit { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let collider = self.collider.to_jobject(env)?; + + let class = env + .load_class(jni_str!("com/dropbear/physics/RayHit")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + env.new_object( + class, + jni_sig!((com.dropbear.physics.Collider, double) -> void), + &[ + JValue::Object(&collider), + JValue::Double(self.distance as jdouble), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +// ------------------------------------- CharacterMovementResult JNI impl ------- + +impl ToJObject for CharacterMovementResult { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/physics/CharacterMovementResult")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let translation_obj = NVector3::from(self.translation).to_jobject(env)?; + + env.new_object( + &class, + jni_sig!((com.dropbear.math.Vector3d, boolean, boolean) -> void), + &[ + JValue::Object(&translation_obj), + JValue::Bool(self.grounded), + JValue::Bool(self.is_sliding_down_slope), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +// ------------------------------------- CharacterCollisionArray JNI impl ------- + +impl ToJObject for CharacterCollisionArray { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let collision_cls = env + .load_class(jni_str!("com/dropbear/physics/CharacterCollision")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let entity_cls = env + .load_class(jni_str!("com/dropbear/EntityId")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let entity_obj = env + .new_object( + &entity_cls, + jni_sig!((long) -> void), + &[JValue::Long(self.entity_id as i64)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + let out = env + .new_object_array( + self.collisions.len() as i32, + &collision_cls, + JObject::null(), + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + for (i, handle) in self.collisions.iter().enumerate() { + let index_obj = handle.to_jobject(env)?; + let collision_obj = env + .new_object( + &collision_cls, + jni_sig!((com.dropbear.EntityId, com.dropbear.physics.Index) -> void), + &[JValue::Object(&entity_obj), JValue::Object(&index_obj)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + out.set_element(env, i, collision_obj) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + } + + Ok(JObject::from(out)) + } +} @@ -0,0 +1,335 @@ +use glam::Vec3; +use eucalyptus_core::ptr::PhysicsStatePtr; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use eucalyptus_core::types::{IndexNative, NCollider, NShapeCastHit, NVector3, RayHit}; +use eucalyptus_core::rapier3d::parry::query::{DefaultQueryDispatcher, ShapeCastOptions}; +use hecs::Entity; +use eucalyptus_core::physics::collider::ColliderShape; +use eucalyptus_core::physics::PhysicsState; +use eucalyptus_core::rapier3d::prelude::{nalgebra, point, vector, ColliderHandle, Pose3, QueryFilter, Ray, SharedShape}; + +pub mod shared { + use hecs::Entity; + use eucalyptus_core::physics::PhysicsState; + use eucalyptus_core::rapier3d::prelude::ColliderHandle; + use eucalyptus_core::types::{NCollider, NVector3}; + + pub fn get_gravity(physics: &PhysicsState) -> NVector3 { + NVector3::from(physics.gravity) + } + + pub fn set_gravity(physics: &mut PhysicsState, new: NVector3) { + physics.gravity = new.to_float_array(); + } + + fn collider_handle_from_ffi(collider: &NCollider) -> ColliderHandle { + ColliderHandle::from_raw_parts(collider.index.index, collider.index.generation) + } + + pub fn overlapping( + physics: &PhysicsState, + collider1: &NCollider, + collider2: &NCollider, + ) -> bool { + let h1 = collider_handle_from_ffi(collider1); + let h2 = collider_handle_from_ffi(collider2); + + if physics.colliders.get(h1).is_none() || physics.colliders.get(h2).is_none() { + return false; + } + + physics + .narrow_phase + .intersection_pair(h1, h2) + .unwrap_or(false) + } + + pub fn triggering( + physics: &PhysicsState, + collider1: &NCollider, + collider2: &NCollider, + ) -> bool { + let h1 = collider_handle_from_ffi(collider1); + let h2 = collider_handle_from_ffi(collider2); + + let is_sensor_1 = physics + .colliders + .get(h1) + .map(|c| c.is_sensor()) + .unwrap_or(false); + let is_sensor_2 = physics + .colliders + .get(h2) + .map(|c| c.is_sensor()) + .unwrap_or(false); + + (is_sensor_1 || is_sensor_2) && overlapping(physics, collider1, collider2) + } + + pub fn touching(physics: &PhysicsState, entity1: Entity, entity2: Entity) -> bool { + let Some(label1) = physics.entity_label_map.get(&entity1) else { + return false; + }; + let Some(label2) = physics.entity_label_map.get(&entity2) else { + return false; + }; + + let Some(handles1) = physics.colliders_entity_map.get(label1) else { + return false; + }; + let Some(handles2) = physics.colliders_entity_map.get(label2) else { + return false; + }; + + for (_, h1) in handles1 { + for (_, h2) in handles2 { + if let Some(pair) = physics.narrow_phase.contact_pair(*h1, *h2) { + if pair.has_any_active_contact() { + return true; + } + } + } + } + + false + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.physics.PhysicsNative", func = "getGravity"), + c +)] +fn get_gravity( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, +) -> DropbearNativeResult<NVector3> { + Ok(shared::get_gravity(physics)) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.physics.PhysicsNative", func = "setGravity"), + c +)] +fn set_gravity( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + gravity: &NVector3, +) -> DropbearNativeResult<()> { + Ok(shared::set_gravity(physics, *gravity)) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.physics.PhysicsNative", func = "raycast"), + c +)] +fn raycast( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + origin: &NVector3, + dir: &NVector3, + time_of_impact: f64, + solid: bool, +) -> DropbearNativeResult<Option<RayHit>> { + let qp = physics.broad_phase.as_query_pipeline( + &DefaultQueryDispatcher, + &physics.bodies, + &physics.colliders, + QueryFilter::new(), + ); + + let ray = Ray::new( + point![origin.x as f32, origin.y as f32, origin.z as f32].into(), + vector![dir.x as f32, dir.y as f32, dir.z as f32].into(), + ); + + if let Some((hit, distance)) = qp.cast_ray(&ray, time_of_impact as f32, solid) { + let raw = hit.0; + + let mut found = None; + for (l, colliders) in physics.colliders_entity_map.iter() { + for (_, c) in colliders { + if c.0 == hit.0 { + found = Some((l, c.0)); + } + } + } + + if let Some((label, _)) = found { + let entity = physics.entity_label_map.iter().find(|(_, l)| *l == label); + if let Some((e, _)) = entity { + let rayhit = RayHit { + collider: NCollider { + index: IndexNative::from(raw), + entity_id: e.to_bits().get(), + id: raw.into_raw_parts().0, + }, + distance: distance as f64, + }; + Ok(Some(rayhit)) + } else { + Ok(None) + } + } else { + eprintln!("Unknown collider, still returning value without entity_id"); + let rayhit = RayHit { + collider: NCollider { + index: IndexNative::from(raw), + entity_id: Entity::DANGLING.to_bits().get(), + id: raw.into_raw_parts().0, + }, + distance: distance as f64, + }; + Ok(Some(rayhit)) + } + } else { + Ok(None) + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.physics.PhysicsNative", func = "shapeCast"), + c +)] +fn shape_cast( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + origin: &NVector3, + direction: &NVector3, + shape: &ColliderShape, + time_of_impact: f64, + solid: bool, +) -> DropbearNativeResult<Option<NShapeCastHit>> { + let qp = physics.broad_phase.as_query_pipeline( + &DefaultQueryDispatcher, + &physics.bodies, + &physics.colliders, + QueryFilter::new(), + ); + + let dir_len = + ((direction.x * direction.x) + (direction.y * direction.y) + (direction.z * direction.z)) + .sqrt(); + if dir_len <= f64::EPSILON { + return Ok(None); + } + + let dir_unit = NVector3 { + x: direction.x / dir_len, + y: direction.y / dir_len, + z: direction.z / dir_len, + }; + + let cast_shape = match shape { + ColliderShape::Box { half_extents } => SharedShape::cuboid( + half_extents.x as f32, + half_extents.y as f32, + half_extents.z as f32, + ), + ColliderShape::Sphere { radius } => SharedShape::ball(*radius), + ColliderShape::Capsule { half_height, radius } => { + SharedShape::capsule_y(*half_height, *radius) + } + ColliderShape::Cylinder { half_height, radius } => { + SharedShape::cylinder(*half_height, *radius) + } + ColliderShape::Cone { half_height, radius } => { + SharedShape::cone(*half_height, *radius) + } + }; + + let iso: Pose3 = + nalgebra::Isometry3::translation(origin.x as f32, origin.y as f32, origin.z as f32) + .into(); + let vel: Vec3 = vector![dir_unit.x as f32, dir_unit.y as f32, dir_unit.z as f32].into(); + + let options = ShapeCastOptions { + max_time_of_impact: time_of_impact as f32, + target_distance: 0.0, + stop_at_penetration: solid, + compute_impact_geometry_on_penetration: true, + }; + + let Some((hit_handle, toi)) = qp.cast_shape(&iso, vel, cast_shape.as_ref(), options) else { + return Ok(None); + }; + + let collider = collider_ffi_from_handle(physics, hit_handle); + + let hit = NShapeCastHit { + collider, + distance: toi.time_of_impact as f64, + witness1: NVector3::from([toi.witness1.x, toi.witness1.y, toi.witness1.z]), + witness2: NVector3::from([toi.witness2.x, toi.witness2.y, toi.witness2.z]), + normal1: NVector3::from([toi.normal1.x, toi.normal1.y, toi.normal1.z]), + normal2: NVector3::from([toi.normal2.x, toi.normal2.y, toi.normal2.z]), + status: toi.status.into(), + }; + + Ok(Some(hit)) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.physics.PhysicsNative", func = "isOverlapping"), + c +)] +fn is_overlapping( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + collider1: &NCollider, + collider2: &NCollider, +) -> DropbearNativeResult<bool> { + Ok(shared::overlapping(physics, collider1, collider2)) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.physics.PhysicsNative", func = "isTriggering"), + c +)] +fn is_triggering( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + collider1: &NCollider, + collider2: &NCollider, +) -> DropbearNativeResult<bool> { + Ok(shared::triggering(physics, collider1, collider2)) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.physics.PhysicsNative", func = "isTouching"), + c +)] +fn is_touching( + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + #[dropbear_macro::entity] entity1: Entity, + #[dropbear_macro::entity] entity2: Entity, +) -> DropbearNativeResult<bool> { + Ok(shared::touching(physics, entity1, entity2)) +} + +fn collider_ffi_from_handle(physics: &PhysicsState, handle: ColliderHandle) -> NCollider { + let (idx, generation) = handle.into_raw_parts(); + + let mut found_label = None; + for (label, colliders) in physics.colliders_entity_map.iter() { + for (_, c) in colliders { + if c.0 == handle.0 { + found_label = Some(label); + break; + } + } + if found_label.is_some() { + break; + } + } + + let entity_id = if let Some(label) = found_label { + physics + .entity_label_map + .iter() + .find(|(_, l)| *l == label) + .map(|(e, _)| e.to_bits().get()) + .unwrap_or(Entity::DANGLING.to_bits().get()) + } else { + Entity::DANGLING.to_bits().get() + }; + + NCollider { + index: IndexNative { index: idx, generation }, + entity_id, + id: idx, + } +} @@ -0,0 +1,470 @@ +use eucalyptus_core::physics::PhysicsState; +use eucalyptus_core::physics::rigidbody::AxisLock; +use eucalyptus_core::physics::rigidbody::shared; +use eucalyptus_core::ptr::{PhysicsStatePtr, WorldPtr}; +use eucalyptus_core::rapier3d::prelude::RigidBodyType; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use eucalyptus_core::types::{IndexNative, NCollider, NVector3, RigidBodyContext}; +use jni::objects::{JObject, JValue}; +use jni::{Env, jni_sig, jni_str}; +use crate::{FromJObject, ToJObject}; + +// ---------------------------------------------------- AxisLock JNI impls -- + +impl FromJObject for AxisLock { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> + where + Self: Sized, + { + let class = env + .load_class(jni_str!("com/dropbear/physics/AxisLock")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + if !env + .is_instance_of(obj, &class) + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? + { + return Err(DropbearNativeError::InvalidArgument); + } + + let x = env + .get_field(obj, jni_str!("x"), jni_sig!(boolean)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .z() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let y = env + .get_field(obj, jni_str!("y"), jni_sig!(boolean)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .z() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let z = env + .get_field(obj, jni_str!("z"), jni_sig!(boolean)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .z() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + Ok(AxisLock { x, y, z }) + } +} + +impl ToJObject for AxisLock { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/physics/AxisLock")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let args = [ + JValue::Bool(self.x), + JValue::Bool(self.y), + JValue::Bool(self.z), + ]; + + let obj = env + .new_object(&class, jni_sig!((boolean, boolean, boolean) -> void), &args) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + Ok(obj) + } +} + +// ------------------------------------------------ RigidBodyNative exports -- + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "rigidBodyExistsForEntity" + ), + c +)] +fn exists_for_entity( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<Option<IndexNative>> { + Ok(shared::rigid_body_exists_for_entity(world, physics, entity)) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "getRigidBodyMode" + ), + c +)] +fn get_rigidbody_mode( + #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + rigidbody: &RigidBodyContext, +) -> DropbearNativeResult<i32> { + let body_type = shared::get_rigidbody_type(physics, rigidbody)?; + Ok(match body_type { + RigidBodyType::Dynamic => 0, + RigidBodyType::Fixed => 1, + RigidBodyType::KinematicPositionBased => 2, + RigidBodyType::KinematicVelocityBased => 3, + }) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "setRigidBodyMode" + ), + c +)] +fn set_rigidbody_mode( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + rigidbody: &RigidBodyContext, + mode: i32, +) -> DropbearNativeResult<()> { + shared::set_rigidbody_type(physics, world, rigidbody, mode as i64) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "getRigidBodyGravityScale" + ), + c +)] +fn get_rigidbody_gravity_scale( + #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + rigidbody: &RigidBodyContext, +) -> DropbearNativeResult<f64> { + shared::get_rigidbody_gravity_scale(physics, rigidbody) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "setRigidBodyGravityScale" + ), + c +)] +fn set_rigidbody_gravity_scale( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + rigidbody: &RigidBodyContext, + gravity_scale: f64, +) -> DropbearNativeResult<()> { + shared::set_rigidbody_gravity_scale(physics, world, rigidbody, gravity_scale) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "getRigidBodyLinearDamping" + ), + c +)] +fn get_rigidbody_linear_damping( + #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + rigidbody: &RigidBodyContext, +) -> DropbearNativeResult<f64> { + shared::get_rigidbody_linear_damping(physics, rigidbody) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "setRigidBodyLinearDamping" + ), + c +)] +fn set_rigidbody_linear_damping( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + rigidbody: &RigidBodyContext, + linear_damping: f64, +) -> DropbearNativeResult<()> { + shared::set_rigidbody_linear_damping(physics, world, rigidbody, linear_damping) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "getRigidBodyAngularDamping" + ), + c +)] +fn get_rigidbody_angular_damping( + #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + rigidbody: &RigidBodyContext, +) -> DropbearNativeResult<f64> { + shared::get_rigidbody_angular_damping(physics, rigidbody) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "setRigidBodyAngularDamping" + ), + c +)] +fn set_rigidbody_angular_damping( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + rigidbody: &RigidBodyContext, + angular_damping: f64, +) -> DropbearNativeResult<()> { + shared::set_rigidbody_angular_damping(physics, world, rigidbody, angular_damping) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "getRigidBodySleep" + ), + c +)] +fn get_rigidbody_sleep( + #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + rigidbody: &RigidBodyContext, +) -> DropbearNativeResult<bool> { + shared::get_rigidbody_sleep(physics, rigidbody) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "setRigidBodySleep" + ), + c +)] +fn set_rigidbody_sleep( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + rigidbody: &RigidBodyContext, + sleep: bool, +) -> DropbearNativeResult<()> { + shared::set_rigidbody_sleep(physics, world, rigidbody, sleep) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "getRigidBodyCcdEnabled" + ), + c +)] +fn get_rigidbody_ccd_enabled( + #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + rigidbody: &RigidBodyContext, +) -> DropbearNativeResult<bool> { + shared::get_rigidbody_ccd(physics, rigidbody) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "setRigidBodyCcdEnabled" + ), + c +)] +fn set_rigidbody_ccd_enabled( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + rigidbody: &RigidBodyContext, + ccd_enabled: bool, +) -> DropbearNativeResult<()> { + shared::set_rigidbody_ccd(physics, world, rigidbody, ccd_enabled) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "getRigidBodyLinearVelocity" + ), + c +)] +fn get_rigidbody_linear_velocity( + #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + rigidbody: &RigidBodyContext, +) -> DropbearNativeResult<NVector3> { + shared::get_rigidbody_linvel(physics, rigidbody) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "setRigidBodyLinearVelocity" + ), + c +)] +fn set_rigidbody_linear_velocity( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + rigidbody: &RigidBodyContext, + linear_velocity: &NVector3, +) -> DropbearNativeResult<()> { + shared::set_rigidbody_linvel(physics, world, rigidbody, *linear_velocity) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "getRigidBodyAngularVelocity" + ), + c +)] +fn get_rigidbody_angular_velocity( + #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + rigidbody: &RigidBodyContext, +) -> DropbearNativeResult<NVector3> { + shared::get_rigidbody_angvel(physics, rigidbody) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "setRigidBodyAngularVelocity" + ), + c +)] +fn set_rigidbody_angular_velocity( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + rigidbody: &RigidBodyContext, + angular_velocity: &NVector3, +) -> DropbearNativeResult<()> { + shared::set_rigidbody_angvel(physics, world, rigidbody, *angular_velocity) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "getRigidBodyLockTranslation" + ), + c +)] +fn get_rigidbody_lock_translation( + #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + rigidbody: &RigidBodyContext, +) -> DropbearNativeResult<AxisLock> { + shared::get_rigidbody_lock_translation(physics, rigidbody) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "setRigidBodyLockTranslation" + ), + c +)] +fn set_rigidbody_lock_translation( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + rigidbody: &RigidBodyContext, + lock_translation: &AxisLock, +) -> DropbearNativeResult<()> { + shared::set_rigidbody_lock_translation(physics, world, rigidbody, *lock_translation) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "getRigidBodyLockRotation" + ), + c +)] +fn get_rigidbody_lock_rotation( + #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + rigidbody: &RigidBodyContext, +) -> DropbearNativeResult<AxisLock> { + shared::get_rigidbody_lock_rotation(physics, rigidbody) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "setRigidBodyLockRotation" + ), + c +)] +fn set_rigidbody_lock_rotation( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + rigidbody: &RigidBodyContext, + lock_rotation: &AxisLock, +) -> DropbearNativeResult<()> { + shared::set_rigidbody_lock_rotation(physics, world, rigidbody, *lock_rotation) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "getRigidBodyChildren" + ), + c +)] +fn get_rigidbody_children( + #[dropbear_macro::define(WorldPtr)] _world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, + rigidbody: &RigidBodyContext, +) -> DropbearNativeResult<Vec<NCollider>> { + let children = shared::get_rigidbody_children(physics, rigidbody)?; + let colliders = children + .into_iter() + .map(|handle| { + let (idx, generation) = handle.into_raw_parts(); + NCollider { + index: IndexNative { + index: idx, + generation, + }, + entity_id: rigidbody.entity_id, + id: idx, + } + }) + .collect(); + + Ok(colliders) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "applyImpulse"), + c +)] +fn apply_impulse( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + rigidbody: &RigidBodyContext, + x: f64, + y: f64, + z: f64, +) -> DropbearNativeResult<()> { + let impulse = NVector3::new(x, y, z); + shared::apply_impulse(physics, world, rigidbody, impulse) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.physics.RigidBodyNative", + func = "applyTorqueImpulse" + ), + c +)] +fn apply_torque_impulse( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, + rigidbody: &RigidBodyContext, + x: f64, + y: f64, + z: f64, +) -> DropbearNativeResult<()> { + let torque = NVector3::new(x, y, z); + shared::apply_torque_impulse(physics, world, rigidbody, torque) +} @@ -0,0 +1,234 @@ +use crate::{FromJObject, ToJObject}; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use jni::objects::{JObject, JValue}; +use jni::sys::{jdouble, jint, jlong}; +use jni::{Env, jni_sig, jni_str}; + +impl ToJObject for Option<i32> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + match self { + Some(value) => { + let class = env + .load_class(jni_str!("java.lang.Integer")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + env.new_object(&class, jni_sig!((int) -> void), &[JValue::Int(*value)]) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } + None => Ok(JObject::null()), + } + } +} + +impl FromJObject for Option<i32> { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { + if obj.is_null() { + return Ok(None); + } + + let class = env + .load_class(jni_str!("java.lang.Integer")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + if !env + .is_instance_of(obj, &class) + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? + { + return Err(DropbearNativeError::InvalidArgument); + } + + let value = env + .call_method(obj, jni_str!("intValue"), jni_sig!(() -> i32), &[]) + .map_err(|_| DropbearNativeError::JNIMethodNotFound)? + .i() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + Ok(Some(value as i32)) + } +} + +impl ToJObject for Vec<i32> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + self.as_slice().to_jobject(env) + } +} + +impl ToJObject for &[i32] { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let array = env + .new_int_array(self.len()) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + let buf: Vec<jint> = self.iter().map(|v| *v as jint).collect(); + array + .set_region(env, 0, &buf) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + Ok(JObject::from(array)) + } +} + +impl ToJObject for &[Vec<i32>] { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let list = new_array_list(env)?; + for value in self.iter() { + let boxed = value.as_slice().to_jobject(env)?; + array_list_add(env, &list, &boxed)?; + } + Ok(list) + } +} + +impl ToJObject for Option<f32> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + match self { + Some(value) => { + let class = env + .load_class(jni_str!("java.lang.Float")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + env.new_object(&class, jni_sig!((f32) -> ()), &[JValue::Float(*value)]) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } + None => Ok(JObject::null()), + } + } +} + +impl ToJObject for Option<f64> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + match self { + Some(value) => { + let class = env + .load_class(jni_str!("java.lang.Double")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + env.new_object(&class, jni_sig!((f64) -> ()), &[JValue::Double(*value)]) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } + None => Ok(JObject::null()), + } + } +} + +impl ToJObject for Vec<f64> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + self.as_slice().to_jobject(env) + } +} + +impl ToJObject for &[f64] { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let array = env + .new_double_array(self.len()) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + let buf: Vec<jdouble> = self.iter().map(|v| *v as jdouble).collect(); + array + .set_region(env, 0, &buf) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + Ok(JObject::from(array)) + } +} + +impl ToJObject for &[Vec<f64>] { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let list = new_array_list(env)?; + for value in self.iter() { + let array = value.as_slice().to_jobject(env)?; + array_list_add(env, &list, &array)?; + } + Ok(list) + } +} + +fn new_array_list<'a>(env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("java.util.ArrayList")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + env.new_object(&class, jni_sig!(() -> ()), &[]) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) +} + +fn array_list_add(env: &mut Env, list: &JObject, item: &JObject) -> DropbearNativeResult<()> { + env.call_method( + list, + jni_str!("add"), + jni_sig!((java.lang.Object) -> boolean), + &[JValue::Object(item)], + ) + .map_err(|_| DropbearNativeError::JNIMethodNotFound)?; + Ok(()) +} + +impl ToJObject for Vec<u64> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + self.as_slice().to_jobject(env) + } +} + +impl ToJObject for &[u64] { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let array = env.new_long_array(self.len())?; + let buf: Vec<jlong> = self.iter().map(|v| *v as jlong).collect(); + array.set_region(env, 0, &buf)?; + Ok(JObject::from(array)) + } +} + +impl ToJObject for String { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let result = JObject::from(env.new_string(self)?); + Ok(result) + } +} + +impl<T> ToJObject for Vec<T> +where + T: ToJObject, +{ + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let list_class = env.load_class(jni_str!("java.util.ArrayList"))?; + let list_obj = env.new_object(&list_class, jni_sig!(()), &[])?; + + for item in self { + let obj = item.to_jobject(env)?; + let _ = env.call_method( + &list_obj, + jni_str!("add"), + jni_sig!((java.lang.Object) -> boolean), + &[JValue::Object(&obj)], + )?; + } + + Ok(list_obj) + } +} + +impl<T> FromJObject for Vec<T> +where + T: FromJObject, +{ + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> + where + Self: Sized, + { + let size = env + .call_method(obj, jni_str!("size"), jni_sig!(() -> int), &[])? + .i()? as jint; + let mut out = Vec::with_capacity(size as usize); + + for i in 0..size { + let item = env + .call_method( + obj, + jni_str!("get"), + jni_sig!((int) -> java.lang.Object), + &[JValue::Int(i)], + )? + .l()?; + let value = T::from_jobject(env, &item)?; + out.push(value); + } + + Ok(out) + } +} @@ -0,0 +1,314 @@ +use eucalyptus_core::properties::{CustomProperties, Value}; +use eucalyptus_core::ptr::WorldPtr; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use eucalyptus_core::types::NVector3; +use hecs::{Entity, World}; + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "customPropertiesExistsForEntity" + ), + c +)] +fn custom_properties_exists_for_entity( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, +) -> DropbearNativeResult<bool> { + Ok(world.get::<&CustomProperties>(entity).is_ok()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "getStringProperty" + ), + c +)] +fn get_string_property( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, + key: String, +) -> DropbearNativeResult<Option<String>> { + let props = world + .get::<&CustomProperties>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + + Ok(props.get_property(&key).and_then(|value| match value { + Value::String(s) => Some(s.clone()), + _ => None, + })) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "getIntProperty" + ), + c +)] +fn get_int_property( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, + key: String, +) -> DropbearNativeResult<Option<i32>> { + let props = world + .get::<&CustomProperties>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + + Ok(props.get_property(&key).and_then(|value| match value { + Value::Int(v) => Some(*v as i32), + _ => None, + })) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "getLongProperty" + ), + c +)] +fn get_long_property( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, + key: String, +) -> DropbearNativeResult<Option<i64>> { + let props = world + .get::<&CustomProperties>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + + Ok(props.get_property(&key).and_then(|value| match value { + Value::Int(v) => Some(*v), + _ => None, + })) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "getDoubleProperty" + ), + c +)] +fn get_double_property( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, + key: String, +) -> DropbearNativeResult<Option<f64>> { + let props = world + .get::<&CustomProperties>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + + Ok(props.get_property(&key).and_then(|value| match value { + Value::Double(v) => Some(*v), + _ => None, + })) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "getFloatProperty" + ), + c +)] +fn get_float_property( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, + key: String, +) -> DropbearNativeResult<Option<f32>> { + let props = world + .get::<&CustomProperties>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + + Ok(props.get_property(&key).and_then(|value| match value { + Value::Double(v) => Some(*v as f32), + _ => None, + })) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "getBoolProperty" + ), + c +)] +fn get_bool_property( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, + key: String, +) -> DropbearNativeResult<Option<bool>> { + let props = world + .get::<&CustomProperties>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + + Ok(props.get_property(&key).and_then(|value| match value { + Value::Bool(v) => Some(*v), + _ => None, + })) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "getVec3Property" + ), + c +)] +fn get_vec3_property( + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, + key: String, +) -> DropbearNativeResult<Option<NVector3>> { + let props = world + .get::<&CustomProperties>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + + Ok(props.get_property(&key).and_then(|value| match value { + Value::Vec3(v) => Some(NVector3::from(*v)), + _ => None, + })) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "setStringProperty" + ), + c +)] +fn set_string_property( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + key: String, + value: String, +) -> DropbearNativeResult<()> { + let mut props = world + .get::<&mut CustomProperties>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + props.set_property(key, Value::String(value)); + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "setIntProperty" + ), + c +)] +fn set_int_property( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + key: String, + value: i32, +) -> DropbearNativeResult<()> { + let mut props = world + .get::<&mut CustomProperties>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + props.set_property(key, Value::Int(value as i64)); + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "setLongProperty" + ), + c +)] +fn set_long_property( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + key: String, + value: i64, +) -> DropbearNativeResult<()> { + let mut props = world + .get::<&mut CustomProperties>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + props.set_property(key, Value::Int(value)); + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "setDoubleProperty" + ), + c +)] +fn set_double_property( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + key: String, + value: f64, +) -> DropbearNativeResult<()> { + let mut props = world + .get::<&mut CustomProperties>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + props.set_property(key, Value::Double(value)); + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "setFloatProperty" + ), + c +)] +fn set_float_property( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + key: String, + value: f64, +) -> DropbearNativeResult<()> { + let mut props = world + .get::<&mut CustomProperties>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + props.set_property(key, Value::Double(value)); + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "setBoolProperty" + ), + c +)] +fn set_bool_property( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + key: String, + value: bool, +) -> DropbearNativeResult<()> { + let mut props = world + .get::<&mut CustomProperties>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + props.set_property(key, Value::Bool(value)); + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "setVec3Property" + ), + c +)] +fn set_vec3_property( + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, + key: String, + value: &NVector3, +) -> DropbearNativeResult<()> { + let mut props = world + .get::<&mut CustomProperties>(entity) + .map_err(|_| DropbearNativeError::NoSuchComponent)?; + props.set_property(key, Value::Vec3(value.to_array())); + Ok(()) +} @@ -0,0 +1,113 @@ +use eucalyptus_core::ptr::{CommandBufferPtr, CommandBufferUnwrapped, SceneLoaderPtr, SceneLoaderUnwrapped}; +use eucalyptus_core::scene::scripting::shared; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use eucalyptus_core::utils::Progress; +use crate::ToJObject; +use jni::objects::{JObject, JValue}; +use jni::{Env, jni_sig, jni_str}; +use eucalyptus_core::scripting::native::DropbearNativeError; + +impl ToJObject for Progress { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/utils/Progress")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let message_obj = env + .new_string(&self.message) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + let args = [ + JValue::Double(self.current as f64), + JValue::Double(self.total as f64), + JValue::Object(&JObject::from(message_obj)), + ]; + + env.new_object(&class, jni_sig!((double, double, java.lang.String) -> void), &args) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.scene.SceneManagerNative", func = "loadSceneAsync"), + c +)] +fn load_scene_async( + #[dropbear_macro::define(CommandBufferPtr)] command_buffer: &CommandBufferUnwrapped, + #[dropbear_macro::define(SceneLoaderPtr)] scene_loader: &SceneLoaderUnwrapped, + scene_name: String, +) -> DropbearNativeResult<u64> { + Ok(shared::load_scene_async(command_buffer, scene_loader, scene_name, None)?) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.scene.SceneManagerNative", func = "loadSceneAsyncWithLoading"), + c +)] +fn load_scene_async_with_loading( + #[dropbear_macro::define(CommandBufferPtr)] command_buffer: &CommandBufferUnwrapped, + #[dropbear_macro::define(SceneLoaderPtr)] scene_loader: &SceneLoaderUnwrapped, + scene_name: String, + loading_scene: String, +) -> DropbearNativeResult<u64> { + Ok(shared::load_scene_async(command_buffer, scene_loader, scene_name, Some(loading_scene))?) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.scene.SceneManagerNative", func = "switchToSceneImmediate"), + c +)] +fn switch_to_scene_immediate( + #[dropbear_macro::define(CommandBufferPtr)] command_buffer: &CommandBufferUnwrapped, + scene_name: String, +) -> DropbearNativeResult<()> { + Ok(shared::switch_to_scene_immediate(command_buffer, scene_name)?) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.scene.SceneLoadHandleNative", + func = "getSceneLoadHandleSceneName" + ), + c +)] +fn get_scene_load_handle_scene_name( + #[dropbear_macro::define(SceneLoaderPtr)] scene_loader: &SceneLoaderUnwrapped, + scene_id: u64, +) -> DropbearNativeResult<String> { + Ok(shared::get_scene_load_handle_scene_name(scene_loader, scene_id)?) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.scene.SceneLoadHandleNative", func = "switchToSceneAsync"), + c +)] +fn switch_to_scene_async( + #[dropbear_macro::define(CommandBufferPtr)] command_buffer: &CommandBufferUnwrapped, + #[dropbear_macro::define(SceneLoaderPtr)] scene_loader: &SceneLoaderUnwrapped, + scene_id: u64, +) -> DropbearNativeResult<()> { + Ok(shared::switch_to_scene_async(command_buffer, scene_loader, scene_id)?) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.scene.SceneLoadHandleNative", func = "getSceneLoadProgress"), + c +)] +fn get_scene_load_progress( + #[dropbear_macro::define(SceneLoaderPtr)] scene_loader: &SceneLoaderUnwrapped, + scene_id: u64, +) -> DropbearNativeResult<Progress> { + Ok(shared::get_scene_load_progress(scene_loader, scene_id)?) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.scene.SceneLoadHandleNative", func = "getSceneLoadStatus"), + c +)] +fn get_scene_load_status( + #[dropbear_macro::define(SceneLoaderPtr)] scene_loader: &SceneLoaderUnwrapped, + scene_id: u64, +) -> DropbearNativeResult<u32> { + Ok(shared::get_scene_load_status(scene_loader, scene_id)?) +} @@ -0,0 +1,14 @@ +pub mod native { + pub use eucalyptus_core::scripting::native::DropbearNativeError; +} + +pub mod result { + pub use eucalyptus_core::scripting::result::DropbearNativeResult; +} + +pub mod jni { + pub mod utils { + pub use crate::FromJObject; + pub use crate::ToJObject; + } +} @@ -0,0 +1,706 @@ +use eucalyptus_core::hierarchy::EntityTransformExt; +use eucalyptus_core::ptr::WorldPtr; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use eucalyptus_core::transform::{OnRails, RailDrive, RailPoint}; +use crate::math::{NQuaternion, NTransform, NVector3}; +use crate::{FromJObject, ToJObject}; +use dropbear_engine::entity::{EntityTransform, Transform}; +use glam::{DQuat, DVec3, Vec3}; +use jni::objects::{JObject, JValue}; +use jni::{Env, jni_sig, jni_str}; + +impl FromJObject for Transform { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { + let pos_val = env + .get_field(obj, jni_str!("position"), jni_sig!(com.dropbear.math.Vector3d)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; + let pos_obj = pos_val.l().map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let rot_val = env + .get_field(obj, jni_str!("rotation"), jni_sig!(com.dropbear.math.Quaterniond)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; + let rot_obj = rot_val.l().map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let scale_val = env + .get_field(obj, jni_str!("scale"), jni_sig!(com.dropbear.math.Vector3d)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; + let scale_obj = scale_val.l().map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let position: DVec3 = NVector3::from_jobject(env, &pos_obj)?.into(); + let scale: DVec3 = NVector3::from_jobject(env, &scale_obj)?.into(); + + let mut get_double = |field| -> DropbearNativeResult<f64> { + env.get_field(&rot_obj, field, jni_sig!(double)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed) + }; + + let rx = get_double(jni_str!("x"))?; + let ry = get_double(jni_str!("y"))?; + let rz = get_double(jni_str!("z"))?; + let rw = get_double(jni_str!("w"))?; + let rotation = DQuat::from_xyzw(rx, ry, rz, rw); + + Ok(Transform { position, rotation, scale }) + } +} + +impl ToJObject for Transform { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let cls = env + .load_class(jni_str!("com/dropbear/math/Transform")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let p = self.position; + let r = self.rotation; + let s = self.scale; + + let args = [ + JValue::Double(p.x), + JValue::Double(p.y), + JValue::Double(p.z), + JValue::Double(r.x), + JValue::Double(r.y), + JValue::Double(r.z), + JValue::Double(r.w), + JValue::Double(s.x), + JValue::Double(s.y), + JValue::Double(s.z), + ]; + + env.new_object( + cls, + jni_sig!((double, double, double, double, double, double, double, double, double, double) -> void), + &args, + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +impl FromJObject for EntityTransform { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { + let local_val = env + .get_field(obj, jni_str!("local"), jni_sig!(com.dropbear.math.Transform)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; + let local_obj = local_val.l().map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let world_val = env + .get_field(obj, jni_str!("world"), jni_sig!(com.dropbear.math.Transform)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; + let world_obj = world_val.l().map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let local = Transform::from_jobject(env, &local_obj)?; + let world = Transform::from_jobject(env, &world_obj)?; + + Ok(EntityTransform::new(local, world)) + } +} + +impl ToJObject for EntityTransform { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let cls = env + .load_class(jni_str!("com/dropbear/components/EntityTransform")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + let local_obj = self.local().to_jobject(env)?; + let world_obj = self.world().to_jobject(env)?; + + let args = [JValue::Object(&local_obj), JValue::Object(&world_obj)]; + + env.new_object( + cls, + jni_sig!((com.dropbear.math.Transform, com.dropbear.math.Transform) -> void), + &args, + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.EntityTransformNative", + func = "entityTransformExistsForEntity" + ), + c +)] +fn exists_for_entity( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<bool> { + Ok(world.get::<&EntityTransform>(entity).is_ok()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.EntityTransformNative", + func = "getLocalTransform" + ), + c +)] +fn get_local_transform( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<NTransform> { + if let Ok(et) = world.get::<&EntityTransform>(entity) { + Ok((*et.local()).into()) + } else { + Err(DropbearNativeError::MissingComponent) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.EntityTransformNative", + func = "setLocalTransform" + ), + c +)] +fn set_local_transform( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + transform: &NTransform, +) -> DropbearNativeResult<()> { + if let Ok(mut et) = world.get::<&mut EntityTransform>(entity) { + *et.local_mut() = (*transform).into(); + Ok(()) + } else { + Err(DropbearNativeError::MissingComponent) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.EntityTransformNative", + func = "getWorldTransform" + ), + c +)] +fn get_world_transform( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<NTransform> { + if let Ok(et) = world.get::<&EntityTransform>(entity) { + Ok((*et.world()).into()) + } else { + Err(DropbearNativeError::MissingComponent) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.EntityTransformNative", + func = "setWorldTransform" + ), + c +)] +fn set_world_transform( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + transform: &NTransform, +) -> DropbearNativeResult<()> { + if let Ok(mut et) = world.get::<&mut EntityTransform>(entity) { + *et.world_mut() = (*transform).into(); + Ok(()) + } else { + Err(DropbearNativeError::MissingComponent) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.EntityTransformNative", + func = "propogateTransform" + ), + c +)] +fn propagate_transform( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<NTransform> { + if let Ok(et) = world.get::<&mut EntityTransform>(entity) { + let result = et.propagate(world, entity); + Ok(result.into()) + } else { + Err(DropbearNativeError::MissingComponent) + } +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getEnabled"), + c +)] +fn on_rails_get_enabled( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<bool> { + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + Ok(rails.enabled) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "setEnabled"), + c +)] +fn on_rails_set_enabled( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + enabled: bool, +) -> DropbearNativeResult<()> { + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + rails.enabled = enabled; + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "existsForEntity"), + c +)] +fn on_rails_exists_for_entity( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<bool> { + Ok(world.get::<&OnRails>(entity).is_ok()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getProgress"), + c +)] +fn on_rails_get_progress( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<f32> { + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + Ok(rails.progress) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "setProgress"), + c +)] +fn on_rails_set_progress( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + progress: f32, +) -> DropbearNativeResult<()> { + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + rails.progress = progress.clamp(0.0, 1.0); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getPathLen"), + c +)] +fn on_rails_get_path_len( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<i32> { + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + Ok(rails.path.len() as i32) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getPathPoint"), + c +)] +fn on_rails_get_path_point( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + index: i32, +) -> DropbearNativeResult<NVector3> { + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + let point = rails + .path + .get(index as usize) + .ok_or(DropbearNativeError::InvalidArgument)?; + Ok(NVector3::from(&point.position)) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "clearPath"), + c +)] +fn on_rails_clear_path( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<()> { + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + rails.path.clear(); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "pushPathPoint"), + c +)] +fn on_rails_push_path_point( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + point: &NVector3, +) -> DropbearNativeResult<()> { + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + rails.path.push(RailPoint { position: DVec3::from(point), rotation: None }); + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getPathPointHasRotation" + ), + c +)] +fn on_rails_get_path_point_has_rotation( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + index: i32, +) -> DropbearNativeResult<bool> { + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + let point = rails + .path + .get(index as usize) + .ok_or(DropbearNativeError::InvalidArgument)?; + Ok(point.rotation.is_some()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getPathPointRotation" + ), + c +)] +fn on_rails_get_path_point_rotation( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + index: i32, +) -> DropbearNativeResult<NQuaternion> { + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + let point = rails + .path + .get(index as usize) + .ok_or(DropbearNativeError::InvalidArgument)?; + Ok(NQuaternion::from(point.rotation.unwrap_or(DQuat::IDENTITY))) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "pushPathPointWithRotation" + ), + c +)] +fn on_rails_push_path_point_with_rotation( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + point: &NVector3, + rotation: &NQuaternion, +) -> DropbearNativeResult<()> { + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + rails.path.push(RailPoint { + position: DVec3::from(point), + rotation: Some(DQuat::from(*rotation)), + }); + Ok(()) +} + +#[dropbear_macro::export( + kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getDriveType"), + c +)] +fn on_rails_get_drive_type( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<i32> { + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + let tag = match &rails.drive { + RailDrive::Automatic { .. } => 0, + RailDrive::FollowEntity { .. } => 1, + RailDrive::AxisDriven { .. } => 2, + RailDrive::Manual => 3, + }; + Ok(tag) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "setDriveAutomatic" + ), + c +)] +fn on_rails_set_drive_automatic( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + speed: f32, + looping: bool, +) -> DropbearNativeResult<()> { + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + rails.drive = RailDrive::Automatic { speed, looping }; + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "setDriveFollowEntity" + ), + c +)] +fn on_rails_set_drive_follow_entity( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + target: u64, + monotonic: bool, +) -> DropbearNativeResult<()> { + let target_entity = + hecs::Entity::from_bits(target).ok_or(DropbearNativeError::InvalidEntity)?; + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + rails.drive = RailDrive::FollowEntity { target: target_entity, monotonic }; + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "setDriveAxisDriven" + ), + c +)] +fn on_rails_set_drive_axis_driven( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, + target: u64, + axis: &NVector3, + range_min: f32, + range_max: f32, +) -> DropbearNativeResult<()> { + let target_entity = + hecs::Entity::from_bits(target).ok_or(DropbearNativeError::InvalidEntity)?; + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + rails.drive = RailDrive::AxisDriven { + target: target_entity, + axis: Vec3::new(axis.x as f32, axis.y as f32, axis.z as f32), + range: (range_min, range_max), + }; + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "setDriveManual" + ), + c +)] +fn on_rails_set_drive_manual( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<()> { + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + rails.drive = RailDrive::Manual; + Ok(()) +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveAutomaticSpeed" + ), + c +)] +fn on_rails_get_drive_automatic_speed( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<f32> { + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::Automatic { speed, .. } = &rails.drive { + Ok(*speed) + } else { + Err(DropbearNativeError::InvalidArgument) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveAutomaticLooping" + ), + c +)] +fn on_rails_get_drive_automatic_looping( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<bool> { + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::Automatic { looping, .. } = &rails.drive { + Ok(*looping) + } else { + Err(DropbearNativeError::InvalidArgument) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveFollowEntityTarget" + ), + c +)] +fn on_rails_get_drive_follow_entity_target( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<u64> { + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::FollowEntity { target, .. } = &rails.drive { + Ok(target.to_bits().get()) + } else { + Err(DropbearNativeError::InvalidArgument) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveFollowEntityMonotonic" + ), + c +)] +fn on_rails_get_drive_follow_entity_monotonic( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<bool> { + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::FollowEntity { monotonic, .. } = &rails.drive { + Ok(*monotonic) + } else { + Err(DropbearNativeError::InvalidArgument) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveAxisDrivenTarget" + ), + c +)] +fn on_rails_get_drive_axis_driven_target( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<u64> { + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::AxisDriven { target, .. } = &rails.drive { + Ok(target.to_bits().get()) + } else { + Err(DropbearNativeError::InvalidArgument) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveAxisDrivenAxis" + ), + c +)] +fn on_rails_get_drive_axis_driven_axis( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<NVector3> { + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::AxisDriven { axis, .. } = &rails.drive { + Ok(NVector3::from(*axis)) + } else { + Err(DropbearNativeError::InvalidArgument) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveAxisDrivenRangeMin" + ), + c +)] +fn on_rails_get_drive_axis_driven_range_min( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<f32> { + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::AxisDriven { range, .. } = &rails.drive { + Ok(range.0) + } else { + Err(DropbearNativeError::InvalidArgument) + } +} + +#[dropbear_macro::export( + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveAxisDrivenRangeMax" + ), + c +)] +fn on_rails_get_drive_axis_driven_range_max( + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, +) -> DropbearNativeResult<f32> { + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::AxisDriven { range, .. } = &rails.drive { + Ok(range.1) + } else { + Err(DropbearNativeError::InvalidArgument) + } +} @@ -0,0 +1 @@ +pub mod ty; @@ -0,0 +1,58 @@ +pub use eucalyptus_core::types::NColour; + +use jni::objects::JObject; +use jni::{jni_sig, jni_str, Env, JValue}; +use eucalyptus_core::scripting::native::DropbearNativeError; +use eucalyptus_core::scripting::result::DropbearNativeResult; +use crate::{FromJObject, ToJObject}; + +impl FromJObject for NColour { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { + let class = env + .load_class(jni_str!("com/dropbear/utils/Colour")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + if !env + .is_instance_of(obj, &class) + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? + { + return Err(DropbearNativeError::InvalidArgument); + } + + let mut get_byte = |field| -> DropbearNativeResult<u8> { + let v = env + .get_field(obj, field, jni_sig!(byte)) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .b() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + Ok(v as u8) + }; + + Ok(Self { + r: get_byte(jni_str!("r"))?, + g: get_byte(jni_str!("g"))?, + b: get_byte(jni_str!("b"))?, + a: get_byte(jni_str!("a"))?, + }) + } +} + +impl ToJObject for NColour { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .load_class(jni_str!("com/dropbear/utils/Colour")) + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + env.new_object( + &class, + jni_sig!((byte, byte, byte, byte) -> void), + &[ + JValue::Byte(self.r as i8), + JValue::Byte(self.g as i8), + JValue::Byte(self.b as i8), + JValue::Byte(self.a as i8), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } +} @@ -16,58 +16,6 @@ typedef enum AssetKind { AssetKind_Model = 1, } AssetKind; -typedef struct NVector3 { - double x; - double y; - double z; -} NVector3; - -typedef enum ColliderShapeTag { - ColliderShapeTag_Box = 0, - ColliderShapeTag_Sphere = 1, - ColliderShapeTag_Capsule = 2, - ColliderShapeTag_Cylinder = 3, - ColliderShapeTag_Cone = 4, -} ColliderShapeTag; - -typedef struct ColliderShapeBox { - NVector3 half_extents; -} ColliderShapeBox; - -typedef struct ColliderShapeSphere { - float radius; -} ColliderShapeSphere; - -typedef struct ColliderShapeCapsule { - float half_height; - float radius; -} ColliderShapeCapsule; - -typedef struct ColliderShapeCylinder { - float half_height; - float radius; -} ColliderShapeCylinder; - -typedef struct ColliderShapeCone { - float half_height; - float radius; -} ColliderShapeCone; - -typedef union ColliderShapeData { - ColliderShapeBox Box; - ColliderShapeSphere Sphere; - ColliderShapeCapsule Capsule; - ColliderShapeCylinder Cylinder; - ColliderShapeCone Cone; -} ColliderShapeData; - -typedef struct ColliderShapeFfi { - ColliderShapeTag tag; - ColliderShapeData data; -} ColliderShapeFfi; - -typedef ColliderShapeFfi ColliderShape; - typedef enum NAnimationInterpolationTag { NAnimationInterpolationTag_Linear = 0, NAnimationInterpolationTag_Step = 1, @@ -96,18 +44,15 @@ typedef struct NAnimationInterpolationFfi { typedef NAnimationInterpolationFfi NAnimationInterpolation; +typedef struct NVector3 NVector3;// opaque + typedef struct NVector3Array { NVector3* values; size_t length; size_t capacity; } NVector3Array; -typedef struct NQuaternion { - double x; - double y; - double z; - double w; -} NQuaternion; +typedef struct NQuaternion NQuaternion;// opaque typedef struct NQuaternionArray { NQuaternion* values; @@ -158,51 +103,11 @@ typedef struct NChannelValuesFfi { typedef NChannelValuesFfi NChannelValues; -typedef enum NShapeCastStatusTag { - NShapeCastStatusTag_OutOfIterations = 0, - NShapeCastStatusTag_Converged = 1, - NShapeCastStatusTag_Failed = 2, - NShapeCastStatusTag_PenetratingOrWithinTargetDist = 3, -} NShapeCastStatusTag; - -typedef struct NShapeCastStatusOutOfIterations { -} NShapeCastStatusOutOfIterations; - -typedef struct NShapeCastStatusConverged { -} NShapeCastStatusConverged; - -typedef struct NShapeCastStatusFailed { -} NShapeCastStatusFailed; - -typedef struct NShapeCastStatusPenetratingOrWithinTargetDist { -} NShapeCastStatusPenetratingOrWithinTargetDist; - -typedef union NShapeCastStatusData { - NShapeCastStatusOutOfIterations OutOfIterations; - NShapeCastStatusConverged Converged; - NShapeCastStatusFailed Failed; - NShapeCastStatusPenetratingOrWithinTargetDist PenetratingOrWithinTargetDist; -} NShapeCastStatusData; - -typedef struct NShapeCastStatusFfi { - NShapeCastStatusTag tag; - NShapeCastStatusData data; -} NShapeCastStatusFfi; - -typedef NShapeCastStatusFfi NShapeCastStatus; - typedef void* AssetRegistryPtr; -typedef struct AxisLock { - bool x; - bool y; - bool z; -} AxisLock; +typedef struct AxisLock AxisLock;// opaque -typedef struct IndexNative { - uint32_t index; - uint32_t generation; -} IndexNative; +typedef struct IndexNative IndexNative;// opaque typedef struct IndexNativeArray { IndexNative* values; @@ -210,28 +115,17 @@ typedef struct IndexNativeArray { size_t capacity; } IndexNativeArray; +// NOTE: type is not #[repr(C)] in Rust; ensure C ABI safety. typedef struct CharacterCollisionArray { uint64_t entity_id; IndexNativeArray collisions; } CharacterCollisionArray; -typedef struct CharacterMovementResult { - NVector3 translation; - bool grounded; - bool is_sliding_down_slope; -} CharacterMovementResult; +typedef struct CharacterMovementResult CharacterMovementResult;// opaque -typedef void* CommandBufferPtr; +typedef struct ColliderShape ColliderShape;// opaque -typedef struct u64Array { - uint64_t* values; - size_t length; - size_t capacity; -} u64Array; - -typedef struct ConnectedGamepadIds { - u64Array ids; -} ConnectedGamepadIds; +typedef void* CommandBufferPtr; typedef void* GraphicsContextPtr; @@ -274,11 +168,7 @@ typedef struct NAttenuation { float quadratic; } NAttenuation; -typedef struct NCollider { - IndexNative index; - uint64_t entity_id; - uint32_t id; -} NCollider; +typedef struct NCollider NCollider;// opaque typedef struct NColliderArray { NCollider* values; @@ -286,24 +176,11 @@ typedef struct NColliderArray { size_t capacity; } NColliderArray; -typedef struct NColour { - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; -} NColour; - -typedef struct NVector4 { - double x; - double y; - double z; - double w; -} NVector4; - -typedef struct NVector2 { - double x; - double y; -} NVector2; +typedef struct NColour NColour;// opaque + +typedef struct NVector4 NVector4;// opaque + +typedef struct NVector2 NVector2;// opaque typedef struct NMaterial { const char* name; @@ -388,15 +265,9 @@ typedef struct NRange { float end; } NRange; -typedef struct NShapeCastHit { - NCollider collider; - double distance; - NVector3 witness1; - NVector3 witness2; - NVector3 normal1; - NVector3 normal2; - NShapeCastStatus status; -} NShapeCastHit; +typedef struct NShapeCastHit NShapeCastHit;// opaque + +typedef struct NShapeCastStatus NShapeCastStatus;// opaque typedef struct NSkin { const char* name; @@ -411,29 +282,15 @@ typedef struct NSkinArray { size_t capacity; } NSkinArray; -typedef struct NTransform { - NVector3 position; - NQuaternion rotation; - NVector3 scale; -} NTransform; +typedef struct NTransform NTransform;// opaque typedef void* PhysicsStatePtr; -typedef struct Progress { - size_t current; - size_t total; - const char* message; -} Progress; +typedef struct Progress Progress;// opaque -typedef struct RayHit { - NCollider collider; - double distance; -} RayHit; +typedef struct RayHit RayHit;// opaque -typedef struct RigidBodyContext { - IndexNative index; - uint64_t entity_id; -} RigidBodyContext; +typedef struct RigidBodyContext RigidBodyContext;// opaque typedef void* SceneLoaderPtr; @@ -447,6 +304,12 @@ typedef struct StringArray { typedef void* WorldPtr; +typedef struct u64Array { + uint64_t* values; + size_t length; + size_t capacity; +} u64Array; + int32_t dropbear_animation_exists_for_entity(WorldPtr world, uint64_t entity, bool* out0); int32_t dropbear_animation_get_active_animation_index(WorldPtr world, uint64_t entity, int32_t* out0, bool* out0_present); int32_t dropbear_animation_get_available_animations(WorldPtr world, uint64_t entity, StringArray* out0); @@ -491,16 +354,6 @@ int32_t dropbear_camera_set_up(WorldPtr world, uint64_t entity, const NVector3* int32_t dropbear_camera_set_yaw(WorldPtr world, uint64_t entity, double yaw); int32_t dropbear_camera_set_zfar(WorldPtr world, uint64_t entity, double zfar); int32_t dropbear_camera_set_znear(WorldPtr world, uint64_t entity, double znear); -int32_t dropbear_character_collision_get_character_collision_collider(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NCollider* out0); -int32_t dropbear_character_collision_get_character_collision_normal1(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0); -int32_t dropbear_character_collision_get_character_collision_normal2(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0); -int32_t dropbear_character_collision_get_character_collision_position(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NTransform* out0); -int32_t dropbear_character_collision_get_character_collision_status(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NShapeCastStatus* out0); -int32_t dropbear_character_collision_get_character_collision_time_of_impact(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, double* out0); -int32_t dropbear_character_collision_get_character_collision_translation_applied(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0); -int32_t dropbear_character_collision_get_character_collision_translation_remaining(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0); -int32_t dropbear_character_collision_get_character_collision_witness1(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0); -int32_t dropbear_character_collision_get_character_collision_witness2(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0); int32_t dropbear_collider_get_collider_density(PhysicsStatePtr physics, const NCollider* collider, double* out0); int32_t dropbear_collider_get_collider_friction(PhysicsStatePtr physics, const NCollider* collider, double* out0); int32_t dropbear_collider_get_collider_is_sensor(PhysicsStatePtr physics, const NCollider* collider, bool* out0); @@ -509,8 +362,6 @@ int32_t dropbear_collider_get_collider_restitution(PhysicsStatePtr physics, cons int32_t dropbear_collider_get_collider_rotation(PhysicsStatePtr physics, const NCollider* collider, NVector3* out0); int32_t dropbear_collider_get_collider_shape(PhysicsStatePtr physics, const NCollider* collider, ColliderShape* out0); int32_t dropbear_collider_get_collider_translation(PhysicsStatePtr physics, const NCollider* collider, NVector3* out0); -int32_t dropbear_collider_group_exists_for_entity(WorldPtr world, uint64_t entity, bool* out0); -int32_t dropbear_collider_group_get_colliders(WorldPtr world, PhysicsStatePtr physics, uint64_t entity, NColliderArray* out0); int32_t dropbear_collider_set_collider_density(PhysicsStatePtr physics, const NCollider* collider, double density); int32_t dropbear_collider_set_collider_friction(PhysicsStatePtr physics, const NCollider* collider, double friction); int32_t dropbear_collider_set_collider_is_sensor(PhysicsStatePtr physics, const NCollider* collider, bool is_sensor); @@ -539,13 +390,13 @@ int32_t dropbear_entity_get_children(WorldPtr world, uint64_t entity, u64Array* int32_t dropbear_entity_get_label(WorldPtr world, uint64_t entity, char** out0); int32_t dropbear_entity_get_parent(WorldPtr world, uint64_t entity, uint64_t* out0, bool* out0_present); int32_t dropbear_entity_label_exists_for_entity(WorldPtr world, uint64_t entity, bool* out0); -int32_t dropbear_gamepad_get_left_stick_position(InputStatePtr input, uint64_t gamepad_id, NVector2* out0); -int32_t dropbear_gamepad_get_right_stick_position(InputStatePtr input, uint64_t gamepad_id, NVector2* out0); -int32_t dropbear_gamepad_is_button_pressed(InputStatePtr input, uint64_t gamepad_id, int32_t button_ordinal, bool* out0); -int32_t dropbear_input_get_connected_gamepads(InputStatePtr input, ConnectedGamepadIds* out0); +int32_t dropbear_input_get_connected_gamepads(InputStatePtr input, u64Array* out0); int32_t dropbear_input_get_last_mouse_pos(InputStatePtr input, NVector2* out0); +int32_t dropbear_input_get_left_stick_position(InputStatePtr input, uint64_t gamepad_id, NVector2* out0); int32_t dropbear_input_get_mouse_delta(InputStatePtr input, NVector2* out0); int32_t dropbear_input_get_mouse_position(InputStatePtr input, NVector2* out0); +int32_t dropbear_input_get_right_stick_position(InputStatePtr input, uint64_t gamepad_id, NVector2* out0); +int32_t dropbear_input_is_button_pressed(InputStatePtr input, uint64_t gamepad_id, int32_t button_ordinal, bool* out0); int32_t dropbear_input_is_cursor_hidden(InputStatePtr input, bool* out0); int32_t dropbear_input_is_cursor_locked(InputStatePtr input, bool* out0); int32_t dropbear_input_is_key_pressed(InputStatePtr input, int32_t key_code, bool* out0); @@ -553,6 +404,16 @@ int32_t dropbear_input_is_mouse_button_pressed(InputStatePtr input, int32_t butt int32_t dropbear_input_print_input_state(InputStatePtr input); int32_t dropbear_input_set_cursor_hidden(CommandBufferPtr command_buffer, InputStatePtr input, bool hidden); int32_t dropbear_input_set_cursor_locked(CommandBufferPtr command_buffer, InputStatePtr input, bool locked); +int32_t dropbear_kcc_get_character_collision_collider(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NCollider* out0); +int32_t dropbear_kcc_get_character_collision_normal1(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0); +int32_t dropbear_kcc_get_character_collision_normal2(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0); +int32_t dropbear_kcc_get_character_collision_position(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NTransform* out0); +int32_t dropbear_kcc_get_character_collision_status(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NShapeCastStatus* out0); +int32_t dropbear_kcc_get_character_collision_time_of_impact(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, double* out0); +int32_t dropbear_kcc_get_character_collision_translation_applied(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0); +int32_t dropbear_kcc_get_character_collision_translation_remaining(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0); +int32_t dropbear_kcc_get_character_collision_witness1(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0); +int32_t dropbear_kcc_get_character_collision_witness2(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0); int32_t dropbear_kcc_get_hit(WorldPtr world, uint64_t entity, CharacterCollisionArray* out0); int32_t dropbear_kcc_get_movement_result(WorldPtr world, uint64_t entity, CharacterMovementResult* out0, bool* out0_present); int32_t dropbear_kcc_kcc_exists_for_entity(WorldPtr world, uint64_t entity, bool* out0); @@ -630,13 +491,13 @@ int32_t dropbear_rigidbody_set_rigidbody_lock_rotation(WorldPtr world, PhysicsSt int32_t dropbear_rigidbody_set_rigidbody_lock_translation(WorldPtr world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, const AxisLock* lock_translation); int32_t dropbear_rigidbody_set_rigidbody_mode(WorldPtr world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, int32_t mode); int32_t dropbear_rigidbody_set_rigidbody_sleep(WorldPtr world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, bool sleep); -int32_t dropbear_scripting_get_scene_load_handle_scene_name(SceneLoaderPtr scene_loader, uint64_t scene_id, char** out0); -int32_t dropbear_scripting_get_scene_load_progress(SceneLoaderPtr scene_loader, uint64_t scene_id, Progress* out0); -int32_t dropbear_scripting_get_scene_load_status(SceneLoaderPtr scene_loader, uint64_t scene_id, uint32_t* out0); -int32_t dropbear_scripting_load_scene_async(CommandBufferPtr command_buffer, SceneLoaderPtr scene_loader, const char* scene_name, uint64_t* out0); -int32_t dropbear_scripting_load_scene_async_with_loading(CommandBufferPtr command_buffer, SceneLoaderPtr scene_loader, const char* scene_name, const char* loading_scene, uint64_t* out0); -int32_t dropbear_scripting_switch_to_scene_async(CommandBufferPtr command_buffer, SceneLoaderPtr scene_loader, uint64_t scene_id); -int32_t dropbear_scripting_switch_to_scene_immediate(CommandBufferPtr command_buffer, const char* scene_name); +int32_t dropbear_scene_get_scene_load_handle_scene_name(SceneLoaderPtr scene_loader, uint64_t scene_id, char** out0); +int32_t dropbear_scene_get_scene_load_progress(SceneLoaderPtr scene_loader, uint64_t scene_id, Progress* out0); +int32_t dropbear_scene_get_scene_load_status(SceneLoaderPtr scene_loader, uint64_t scene_id, uint32_t* out0); +int32_t dropbear_scene_load_scene_async(CommandBufferPtr command_buffer, SceneLoaderPtr scene_loader, const char* scene_name, uint64_t* out0); +int32_t dropbear_scene_load_scene_async_with_loading(CommandBufferPtr command_buffer, SceneLoaderPtr scene_loader, const char* scene_name, const char* loading_scene, uint64_t* out0); +int32_t dropbear_scene_switch_to_scene_async(CommandBufferPtr command_buffer, SceneLoaderPtr scene_loader, uint64_t scene_id); +int32_t dropbear_scene_switch_to_scene_immediate(CommandBufferPtr command_buffer, const char* scene_name); int32_t dropbear_transform_exists_for_entity(WorldPtr world, uint64_t entity, bool* out0); int32_t dropbear_transform_get_local_transform(WorldPtr world, uint64_t entity, NTransform* out0); int32_t dropbear_transform_get_world_transform(WorldPtr world, uint64_t entity, NTransform* out0);