tirbofish/dropbear · diff
fmt: cargo fix + cargo fmt
Signature present but could not be verified.
Unverified
@@ -1,16 +1,20 @@ use slank::{SlangShaderBuilder, SlangTarget}; fn main() { - // to copy paste: + // to copy paste: // let shader = Shader::from_slang(graphics.clone(), &slank::compiled::CompiledSlangShader::from_bytes("light cube", include_slang!("light_cube"))); SlangShaderBuilder::new("light_cube") - .add_source_path("src/shaders/light.slang").unwrap() - .compile_to_out_dir(SlangTarget::SpirV).unwrap(); + .add_source_path("src/shaders/light.slang") + .unwrap() + .compile_to_out_dir(SlangTarget::SpirV) + .unwrap(); SlangShaderBuilder::new("blit_shader") - .add_source_path("src/shaders/blit.slang").unwrap() - .compile_to_out_dir(SlangTarget::SpirV).unwrap(); + .add_source_path("src/shaders/blit.slang") + .unwrap() + .compile_to_out_dir(SlangTarget::SpirV) + .unwrap(); println!("cargo:rerun-if-changed=src/shaders"); -} +} @@ -1,9 +1,9 @@ +use crate::graphics::SharedGraphicsContext; +use crate::model::{AnimationInterpolation, ChannelValues, Model, NodeTransform}; +use glam::Mat4; use std::collections::HashMap; use std::sync::Arc; -use glam::Mat4; use wgpu::util::DeviceExt; -use crate::graphics::SharedGraphicsContext; -use crate::model::{AnimationInterpolation, ChannelValues, Model, NodeTransform}; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct AnimationComponent { @@ -83,7 +83,11 @@ impl AnimationComponent { pub fn update(&mut self, dt: f32, model: &Model) { puffin::profile_function!(&model.label); - self.available_animations = model.animations.iter().map(|v| v.name.clone()).collect::<Vec<_>>(); + self.available_animations = model + .animations + .iter() + .map(|v| v.name.clone()) + .collect::<Vec<_>>(); let Some(anim_idx) = self.active_animation_index else { self.reset_to_bind_pose(model); @@ -95,15 +99,15 @@ impl AnimationComponent { return; } - let settings = self - .animation_settings - .entry(anim_idx) - .or_insert_with(|| AnimationSettings { - time: self.time, - speed: self.speed, - looping: self.looping, - is_playing: self.is_playing, - }); + let settings = + self.animation_settings + .entry(anim_idx) + .or_insert_with(|| AnimationSettings { + time: self.time, + speed: self.speed, + looping: self.looping, + is_playing: self.is_playing, + }); if !settings.is_playing { self.time = settings.time; @@ -139,7 +143,9 @@ impl AnimationComponent { for channel in &animation.channels { let count = channel.times.len(); - if count == 0 { continue; } + if count == 0 { + continue; + } if count == 1 || settings.time <= channel.times[0] { Self::apply_single_keyframe(channel, 0, &mut self.local_pose, model); @@ -149,7 +155,7 @@ impl AnimationComponent { Self::apply_single_keyframe(channel, count - 1, &mut self.local_pose, model); continue; } - + let next_idx = channel.times.partition_point(|&t| t <= settings.time); let prev_idx = next_idx.saturating_sub(1); @@ -163,16 +169,19 @@ impl AnimationComponent { 0.0 }; - let transform = self.local_pose + let transform = self + .local_pose .entry(channel.target_node) .or_insert_with(|| { - model.nodes.get(channel.target_node) + model + .nodes + .get(channel.target_node) .map(|n| n.transform.clone()) .unwrap_or_else(NodeTransform::identity) }); let dt = end_time - start_time; - + match &channel.values { ChannelValues::Translations(values) => { transform.translation = match channel.interpolation { @@ -181,15 +190,15 @@ impl AnimationComponent { let start = values[prev_idx]; let end = values[next_idx]; start.lerp(end, factor) - }, + } AnimationInterpolation::CubicSpline => { let t = factor; let t2 = t * t; let t3 = t2 * t; - + let idx0 = prev_idx * 3; let idx1 = next_idx * 3; - + if idx1 + 1 >= values.len() { values[idx0 + 1] } else { @@ -197,12 +206,12 @@ impl AnimationComponent { let m0 = values[idx0 + 2] * dt; let m1 = values[idx1 + 0] * dt; let p1 = values[idx1 + 1]; - + let h00 = 2.0 * t3 - 3.0 * t2 + 1.0; let h10 = t3 - 2.0 * t2 + t; let h01 = -2.0 * t3 + 3.0 * t2; let h11 = t3 - t2; - + p0 * h00 + m0 * h10 + p1 * h01 + m1 * h11 } } @@ -215,15 +224,15 @@ impl AnimationComponent { let start = values[prev_idx]; let end = values[next_idx]; start.slerp(end, factor).normalize() - }, + } AnimationInterpolation::CubicSpline => { let t = factor; let t2 = t * t; let t3 = t2 * t; - + let idx0 = prev_idx * 3; let idx1 = next_idx * 3; - + if idx1 + 1 >= values.len() { values[idx0 + 1] } else { @@ -231,12 +240,12 @@ impl AnimationComponent { let m0 = values[idx0 + 2] * dt; let m1 = values[idx1 + 0] * dt; let p1 = values[idx1 + 1]; - + let h00 = 2.0 * t3 - 3.0 * t2 + 1.0; let h10 = t3 - 2.0 * t2 + t; let h01 = -2.0 * t3 + 3.0 * t2; let h11 = t3 - t2; - + let res = p0 * h00 + m0 * h10 + p1 * h01 + m1 * h11; res.normalize() } @@ -250,15 +259,15 @@ impl AnimationComponent { let start = values[prev_idx]; let end = values[next_idx]; start.lerp(end, factor) - }, + } AnimationInterpolation::CubicSpline => { let t = factor; let t2 = t * t; let t3 = t2 * t; - + let idx0 = prev_idx * 3; let idx1 = next_idx * 3; - + if idx1 + 1 >= values.len() { values[idx0 + 1] } else { @@ -266,12 +275,12 @@ impl AnimationComponent { let m0 = values[idx0 + 2] * dt; let m1 = values[idx1 + 0] * dt; let p1 = values[idx1 + 1]; - + let h00 = 2.0 * t3 - 3.0 * t2 + 1.0; let h10 = t3 - 2.0 * t2 + t; let h01 = -2.0 * t3 + 3.0 * t2; let h11 = t3 - t2; - + p0 * h00 + m0 * h10 + p1 * h01 + m1 * h11 } } @@ -292,23 +301,31 @@ impl AnimationComponent { channel: &crate::model::AnimationChannel, index: usize, pose: &mut HashMap<usize, NodeTransform>, - model: &Model + model: &Model, ) { let transform = pose.entry(channel.target_node).or_insert_with(|| { - model.nodes.get(channel.target_node) + model + .nodes + .get(channel.target_node) .map(|n| n.transform.clone()) .unwrap_or_else(NodeTransform::identity) }); match &channel.values { ChannelValues::Translations(v) => { - if let Some(val) = v.get(index) { transform.translation = *val; } + if let Some(val) = v.get(index) { + transform.translation = *val; + } } ChannelValues::Rotations(v) => { - if let Some(val) = v.get(index) { transform.rotation = *val; } + if let Some(val) = v.get(index) { + transform.rotation = *val; + } } ChannelValues::Scales(v) => { - if let Some(val) = v.get(index) { transform.scale = *val; } + if let Some(val) = v.get(index) { + transform.scale = *val; + } } } } @@ -316,11 +333,12 @@ impl AnimationComponent { fn update_matrices(&mut self, model: &Model) { if let Some(skin) = model.skins.first() { if self.skinning_matrices.len() != skin.joints.len() { - self.skinning_matrices.resize(skin.joints.len(), Mat4::IDENTITY); + self.skinning_matrices + .resize(skin.joints.len(), Mat4::IDENTITY); } let mut global_transforms = HashMap::new(); - + for &joint_idx in &skin.joints { self.resolve_global_transform(joint_idx, model, &mut global_transforms); } @@ -345,7 +363,9 @@ impl AnimationComponent { } let node = &model.nodes[node_idx]; - let local_matrix = self.local_pose.get(&node_idx) + let local_matrix = self + .local_pose + .get(&node_idx) .map(|transform| transform.to_matrix()) .unwrap_or_else(|| node.transform.to_matrix()); @@ -361,30 +381,36 @@ impl AnimationComponent { } pub fn prepare_gpu_resources(&mut self, graphics: Arc<SharedGraphicsContext>) { - if self.skinning_matrices.is_empty() { return; } + if self.skinning_matrices.is_empty() { + return; + } let data = bytemuck::cast_slice(&self.skinning_matrices); if let Some(buffer) = &self.bone_buffer { graphics.queue.write_buffer(buffer, 0, data); } else { - let buffer = graphics.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("skinning buffer"), - contents: data, - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - }); - - let bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("skinning bind group"), - layout: &graphics.layouts.skinning_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: buffer.as_entire_binding(), - }], - }); + let buffer = graphics + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("skinning buffer"), + contents: data, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + }); + + let bind_group = graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("skinning bind group"), + layout: &graphics.layouts.skinning_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: buffer.as_entire_binding(), + }], + }); self.bone_buffer = Some(buffer); self.bind_group = Some(bind_group); } } -} +} @@ -1,22 +1,21 @@ +use crate::graphics::SharedGraphicsContext; +use crate::model::Model; +use crate::texture::Texture; +use crate::utils::ResourceReference; +use parking_lot::RwLock; use std::collections::HashMap; use std::hash::{DefaultHasher, Hash, Hasher}; use std::marker::PhantomData; use std::sync::{Arc, LazyLock}; -use parking_lot::RwLock; -use crate::{ - texture::Texture, -}; -use crate::graphics::SharedGraphicsContext; -use crate::utils::ResourceReference; -use crate::model::Model; -pub static ASSET_REGISTRY: LazyLock<Arc<RwLock<AssetRegistry>>> = LazyLock::new(|| Arc::new(RwLock::new(AssetRegistry::new()))); +pub static ASSET_REGISTRY: LazyLock<Arc<RwLock<AssetRegistry>>> = + LazyLock::new(|| Arc::new(RwLock::new(AssetRegistry::new()))); /// A handle with type [`T`] that provides an index to the [AssetRegistry] contents. #[derive(Hash, Eq, Debug)] pub struct Handle<T> { pub id: u64, - _phantom: PhantomData<T> + _phantom: PhantomData<T>, } impl<T> PartialEq for Handle<T> { @@ -47,11 +46,17 @@ impl<T> Handle<T> { /// where there already is a null handle item, it will be overwritten and data /// will not be saved. It is the reason why you will want to consider using the [Self::is_null] /// function to verify if the storage of the type has gone through correctly. - pub const NULL: Self = Self { id: 0, _phantom: PhantomData }; + pub const NULL: Self = Self { + id: 0, + _phantom: PhantomData, + }; /// Creates a new handle with the given ID. pub fn new(id: u64) -> Self { - Self { id, _phantom: Default::default() } + Self { + id, + _phantom: Default::default(), + } } /// Returns true if the handle is null. @@ -96,7 +101,7 @@ impl AssetRegistry { animations: vec![], nodes: vec![], }); - + result } @@ -136,14 +141,21 @@ impl AssetRegistry { /// This assumes a [Texture] has already been created by you. To create a new texture, /// you can use [`Texture::from_bytes`]. pub fn add_texture(&mut self, texture: Texture) -> Handle<Texture> { - let handle = texture.hash.map(|v| Handle::new(v)).unwrap_or_else(|| Handle::NULL); + let handle = texture + .hash + .map(|v| Handle::new(v)) + .unwrap_or_else(|| Handle::NULL); self.textures.entry(handle.id).or_insert(texture); handle } /// Adds a texture with a label. If the texture already exists (by hash), /// returns the existing handle and updates the label to point at it. - pub fn add_texture_with_label(&mut self, label: impl Into<String>, texture: Texture) -> Handle<Texture> { + pub fn add_texture_with_label( + &mut self, + label: impl Into<String>, + texture: Texture, + ) -> Handle<Texture> { let handle = self.add_texture(texture); self.texture_labels.insert(label.into(), handle.clone()); handle @@ -198,11 +210,7 @@ impl AssetRegistry { graphics: Arc<SharedGraphicsContext>, rgba: [u8; 4], ) -> Handle<Texture> { - self.solid_texture_rgba8_with_format( - graphics, - rgba, - Texture::TEXTURE_FORMAT, - ) + self.solid_texture_rgba8_with_format(graphics, rgba, Texture::TEXTURE_FORMAT) } pub fn solid_texture_rgba8_with_format( @@ -220,11 +228,7 @@ impl AssetRegistry { let label = format!( "Solid texture [{}, {}, {}, {}] {}", - rgba[0], - rgba[1], - rgba[2], - rgba[3], - format_tag, + rgba[0], rgba[1], rgba[2], rgba[3], format_tag, ); let texture = Texture::from_bytes_verbose_mipmapped_with_format( @@ -236,12 +240,18 @@ impl AssetRegistry { format, Some(label.as_str()), ); - + self.add_texture_with_label(label, texture) } pub fn get_label_from_texture_handle(&self, handle: Handle<Texture>) -> Option<String> { - self.texture_labels.iter().find_map(|(label, h)| if *h == handle { Some(label.clone()) } else { None }) + self.texture_labels.iter().find_map(|(label, h)| { + if *h == handle { + Some(label.clone()) + } else { + None + } + }) } } @@ -253,7 +263,11 @@ impl AssetRegistry { handle } - pub fn add_model_with_label(&mut self, label: impl Into<String>, model: Model) -> Handle<Model> { + pub fn add_model_with_label( + &mut self, + label: impl Into<String>, + model: Model, + ) -> Handle<Model> { let handle = self.add_model(model); self.model_labels.insert(label.into(), handle.clone()); handle @@ -295,11 +309,20 @@ impl AssetRegistry { pub fn list_models(&self) -> Vec<(Handle<Model>, String, ResourceReference)> { self.models .values() - .map(|model| (Handle::new(model.hash), model.label.clone(), model.path.clone())) + .map(|model| { + ( + Handle::new(model.hash), + model.label.clone(), + model.path.clone(), + ) + }) .collect() } - pub fn get_model_handle_by_reference(&self, reference: &ResourceReference) -> Option<Handle<Model>> { + pub fn get_model_handle_by_reference( + &self, + reference: &ResourceReference, + ) -> Option<Handle<Model>> { self.models .values() .find(|model| &model.path == reference) @@ -311,23 +334,36 @@ impl AssetRegistry { } pub fn get_label_from_model_handle(&self, handle: Handle<Model>) -> Option<String> { - self.model_labels.iter().find_map(|(label, h)| if *h == handle { Some(label.clone()) } else { None }) + self.model_labels.iter().find_map(|(label, h)| { + if *h == handle { + Some(label.clone()) + } else { + None + } + }) } /// Returns a list of all loaded textures with their handles, labels, and references. - pub fn list_textures(&self) -> Vec<(Handle<Texture>, Option<String>, Option<ResourceReference>)> { + pub fn list_textures( + &self, + ) -> Vec<(Handle<Texture>, Option<String>, Option<ResourceReference>)> { self.textures .values() - .map(|texture| ( - texture.hash.map(Handle::new).unwrap_or(Handle::NULL), - texture.label.clone(), - texture.reference.clone(), - )) + .map(|texture| { + ( + texture.hash.map(Handle::new).unwrap_or(Handle::NULL), + texture.label.clone(), + texture.reference.clone(), + ) + }) .collect() } /// Finds a texture handle by its resource reference. - pub fn get_texture_handle_by_reference(&self, reference: &ResourceReference) -> Option<Handle<Texture>> { + pub fn get_texture_handle_by_reference( + &self, + reference: &ResourceReference, + ) -> Option<Handle<Texture>> { self.textures .values() .find(|texture| texture.reference.as_ref() == Some(reference)) @@ -339,4 +375,4 @@ impl Default for AssetRegistry { fn default() -> Self { Self::new() } -} +} @@ -152,10 +152,14 @@ impl<T: bytemuck::Pod> ResizableBuffer<T> { if data.len() > self.capacity { self.capacity = data.len().max(self.capacity * 2); - + let new_size = (self.capacity * std::mem::size_of::<T>()) as wgpu::BufferAddress; - - log::debug!("Resizing buffer '{}' to hold {} items", self.label, self.capacity); + + log::debug!( + "Resizing buffer '{}' to hold {} items", + self.label, + self.capacity + ); self.buffer = device.create_buffer(&wgpu::BufferDescriptor { label: Some(&self.label), @@ -171,9 +175,9 @@ impl<T: bytemuck::Pod> ResizableBuffer<T> { pub fn buffer(&self) -> &wgpu::Buffer { &self.buffer } - + pub fn slice(&self, count: usize) -> wgpu::BufferSlice<'_> { let byte_count = (count * std::mem::size_of::<T>()) as wgpu::BufferAddress; self.buffer.slice(0..byte_count) } -} +} @@ -5,8 +5,8 @@ use std::sync::Arc; use glam::{DMat4, DQuat, DVec3, Mat4}; use serde::{Deserialize, Serialize}; use wgpu::{ - BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, - BindingType, BufferBindingType, ShaderStages + BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, + BindGroupLayoutEntry, BindingType, BufferBindingType, ShaderStages, }; use crate::{buffer::UniformBuffer, graphics::SharedGraphicsContext}; @@ -167,7 +167,14 @@ impl Camera { proj_mat, }; - log::debug!("Created new camera{}", if let Some(l) = label { format!(" with the label {}", l) } else { String::new() } ); + log::debug!( + "Created new camera{}", + if let Some(l) = label { + format!(" with the label {}", l) + } else { + String::new() + } + ); camera } @@ -179,7 +186,8 @@ impl Camera { eye: DVec3::new(0.0, 1.0, 2.0), target: DVec3::new(0.0, 0.0, 0.0), up: DVec3::Y, - aspect: (graphics.window.inner_size().width / graphics.window.inner_size().height).into(), + aspect: (graphics.window.inner_size().width / graphics.window.inner_size().height) + .into(), znear: 0.1, zfar: 100.0, settings: CameraSettings::default(), @@ -324,22 +332,16 @@ impl CameraUniform { pub fn update(&mut self, camera: &mut Camera) { self.view_position = camera.eye.as_vec3().extend(1.0).to_array(); - + let vp = camera.build_vp(); let view = camera.view_mat; let proj = camera.proj_mat; - + let wgpu_matrix = DMat4::from_cols_array_2d(&OPENGL_TO_WGPU_MATRIX); self.view = view.as_mat4().to_cols_array_2d(); self.view_proj = vp.as_mat4().to_cols_array_2d(); - - self.inv_proj = (wgpu_matrix * proj) - .inverse() - .as_mat4() - .to_cols_array_2d(); - self.inv_view = view - .inverse() - .as_mat4() - .to_cols_array_2d(); + + self.inv_proj = (wgpu_matrix * proj).inverse().as_mat4().to_cols_array_2d(); + self.inv_view = view.inverse().as_mat4().to_cols_array_2d(); } } @@ -1,20 +1,17 @@ use glam::{DMat4, DQuat, DVec3, Mat4, Quat, Vec3}; use serde::{Deserialize, Serialize}; -use std::{ - collections::{HashMap}, - path::Path, - sync::{Arc}, -}; +use std::{collections::HashMap, path::Path, sync::Arc}; +use crate::asset::Handle; +use crate::model::Material; use crate::{ asset::ASSET_REGISTRY, graphics::{Instance, SharedGraphicsContext}, model::Model, - texture::Texture, utils::ResourceReference, + texture::Texture, + utils::ResourceReference, }; -use egui::{Ui}; -use crate::asset::Handle; -use crate::model::Material; +use egui::Ui; /// A type of transform that is attached to all entities. It contains the local and world transforms. #[derive(Default, Debug, Deserialize, Serialize, Copy, PartialEq, Clone)] @@ -104,16 +101,12 @@ impl Transform { Self::default() } - /// Applies an offset, typically used for physics based calculations where [self.scale] - /// is not required. + /// Applies an offset, typically used for physics based calculations where [self.scale] + /// is not required. pub fn with_offset(&self, translation: [f32; 3], rotation: [f32; 3]) -> Self { let offset_pos = Vec3::from(translation).as_dvec3(); - let offset_rot = Quat::from_euler( - glam::EulerRot::XYZ, - rotation[0], - rotation[1], - rotation[2] - ).as_dquat(); + let offset_rot = + Quat::from_euler(glam::EulerRot::XYZ, rotation[0], rotation[1], rotation[2]).as_dquat(); Transform { position: self.position + self.rotation * offset_pos, @@ -163,19 +156,25 @@ impl Transform { }); ui.horizontal(|ui| { ui.colored_label(egui::Color32::from_rgb(200, 80, 80), "X:"); - ui.add(egui::DragValue::new(&mut self.position.x) - .speed(0.1) - .fixed_decimals(2)); + ui.add( + egui::DragValue::new(&mut self.position.x) + .speed(0.1) + .fixed_decimals(2), + ); ui.colored_label(egui::Color32::from_rgb(80, 200, 80), "Y:"); - ui.add(egui::DragValue::new(&mut self.position.y) - .speed(0.1) - .fixed_decimals(2)); + ui.add( + egui::DragValue::new(&mut self.position.y) + .speed(0.1) + .fixed_decimals(2), + ); ui.colored_label(egui::Color32::from_rgb(80, 120, 220), "Z:"); - ui.add(egui::DragValue::new(&mut self.position.z) - .speed(0.1) - .fixed_decimals(2)); + ui.add( + egui::DragValue::new(&mut self.position.z) + .speed(0.1) + .fixed_decimals(2), + ); }); ui.add_space(4.0); @@ -189,34 +188,48 @@ impl Transform { y = y.to_degrees(); z = z.to_degrees(); - let changed = ui.horizontal(|ui| { - ui.colored_label(egui::Color32::from_rgb(200, 80, 80), "X:"); - let cx = ui.add(egui::DragValue::new(&mut x) - .speed(1.0) - .suffix("°") - .fixed_decimals(1)).changed(); - - ui.colored_label(egui::Color32::from_rgb(80, 200, 80), "Y:"); - let cy = ui.add(egui::DragValue::new(&mut y) - .speed(1.0) - .suffix("°") - .fixed_decimals(1)).changed(); - - ui.colored_label(egui::Color32::from_rgb(80, 120, 220), "Z:"); - let cz = ui.add(egui::DragValue::new(&mut z) - .speed(1.0) - .suffix("°") - .fixed_decimals(1)).changed(); - - cx || cy || cz - }).inner; + let changed = ui + .horizontal(|ui| { + ui.colored_label(egui::Color32::from_rgb(200, 80, 80), "X:"); + let cx = ui + .add( + egui::DragValue::new(&mut x) + .speed(1.0) + .suffix("°") + .fixed_decimals(1), + ) + .changed(); + + ui.colored_label(egui::Color32::from_rgb(80, 200, 80), "Y:"); + let cy = ui + .add( + egui::DragValue::new(&mut y) + .speed(1.0) + .suffix("°") + .fixed_decimals(1), + ) + .changed(); + + ui.colored_label(egui::Color32::from_rgb(80, 120, 220), "Z:"); + let cz = ui + .add( + egui::DragValue::new(&mut z) + .speed(1.0) + .suffix("°") + .fixed_decimals(1), + ) + .changed(); + + cx || cy || cz + }) + .inner; if changed { self.rotation = DQuat::from_euler( glam::EulerRot::XYZ, x.to_radians(), y.to_radians(), - z.to_radians() + z.to_radians(), ); } @@ -229,19 +242,25 @@ impl Transform { ui.horizontal(|ui| { ui.colored_label(egui::Color32::from_rgb(200, 80, 80), "X:"); - ui.add(egui::DragValue::new(&mut self.scale.x) - .speed(0.01) - .fixed_decimals(3)); + ui.add( + egui::DragValue::new(&mut self.scale.x) + .speed(0.01) + .fixed_decimals(3), + ); ui.colored_label(egui::Color32::from_rgb(80, 200, 80), "Y:"); - ui.add(egui::DragValue::new(&mut self.scale.y) - .speed(0.01) - .fixed_decimals(3)); + ui.add( + egui::DragValue::new(&mut self.scale.y) + .speed(0.01) + .fixed_decimals(3), + ); ui.colored_label(egui::Color32::from_rgb(80, 120, 220), "Z:"); - ui.add(egui::DragValue::new(&mut self.scale.z) - .speed(0.01) - .fixed_decimals(3)); + ui.add( + egui::DragValue::new(&mut self.scale.z) + .speed(0.01) + .fixed_decimals(3), + ); }); } } @@ -258,7 +277,7 @@ pub struct MeshRenderer { handle: Handle<Model>, pub instance: Instance, previous_matrix: DMat4, - pub material_snapshot: HashMap<String, Material> + pub material_snapshot: HashMap<String, Material>, } impl MeshRenderer { @@ -267,9 +286,7 @@ impl MeshRenderer { let material_snapshot = ASSET_REGISTRY .read() .get_model(model) - .map(|m| { - m.materials.clone() - }) + .map(|m| m.materials.clone()) .unwrap_or_default(); for m in material_snapshot { hm.insert(m.name.clone(), m); @@ -283,7 +300,7 @@ impl MeshRenderer { material_snapshot: hm, } } - + pub async fn from_path( graphics: Arc<SharedGraphicsContext>, path: impl AsRef<Path>, @@ -296,7 +313,8 @@ impl MeshRenderer { Some(ResourceReference::from_path(&path)?), label, ASSET_REGISTRY.clone(), - ).await?; + ) + .await?; Ok(Self { handle, instance: Instance::default(), @@ -310,11 +328,8 @@ impl MeshRenderer { pub fn update(&mut self, transform: &Transform) { puffin::profile_function!(); let scale = transform.scale * glam::DVec3::splat(self.import_scale as f64); - let current_matrix = DMat4::from_scale_rotation_translation( - scale, - transform.rotation, - transform.position, - ); + let current_matrix = + DMat4::from_scale_rotation_translation(scale, transform.rotation, transform.position); if self.previous_matrix != current_matrix { self.instance = Instance::from_matrix(current_matrix); self.previous_matrix = current_matrix; @@ -336,14 +351,16 @@ impl MeshRenderer { pub fn model(&self) -> Handle<Model> { self.handle } - + pub fn mutate_material(&mut self, material_name: &str, f: impl FnOnce(&mut Material)) { - self.material_snapshot.entry(material_name.to_string()).and_modify(f); + self.material_snapshot + .entry(material_name.to_string()) + .and_modify(f); } - + pub fn is_texture_attached(&self, texture: Handle<Texture>) -> bool { let registry = ASSET_REGISTRY.read(); - + if let Some(model) = registry.get_model(self.handle) { for material in &model.materials { if material.diffuse_texture.hash == Some(texture.id) { @@ -369,7 +386,7 @@ impl MeshRenderer { } } } - + false } @@ -378,9 +395,7 @@ impl MeshRenderer { let material_snapshot = ASSET_REGISTRY .read() .get_model(self.handle) - .map(|m| { - m.materials.clone() - }) + .map(|m| m.materials.clone()) .unwrap_or_default(); for m in material_snapshot { hm.insert(m.name.clone(), m); @@ -299,4 +299,4 @@ mod tests { assert_eq!(false, f::is_enabled(f::Cool)); assert_eq!(false, f::is_enabled(f::Beans)); } -} +} @@ -1,13 +1,10 @@ -use std::ops::{Deref, DerefMut}; use crate::{BindGroupLayouts, texture}; -use crate::{ - State, - egui_renderer::EguiRenderer, -}; +use crate::{State, egui_renderer::EguiRenderer}; use dropbear_future_queue::FutureQueue; use egui::{Context, TextureId}; use glam::{DMat4, DQuat, DVec3, Mat3}; use parking_lot::{Mutex, RwLock}; +use std::ops::{Deref, DerefMut}; use std::sync::Arc; use wgpu::*; use winit::window::Window; @@ -38,20 +35,20 @@ pub struct SharedGraphicsContext { } impl SharedGraphicsContext { - pub const MODEL_UNIFORM_BIND_GROUP_LAYOUT: wgpu::BindGroupLayoutDescriptor<'_> = + pub const MODEL_UNIFORM_BIND_GROUP_LAYOUT: wgpu::BindGroupLayoutDescriptor<'_> = wgpu::BindGroupLayoutDescriptor { - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - label: Some("model_uniform_bind_group_layout"), - }; + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + label: Some("model_uniform_bind_group_layout"), + }; pub fn get_egui_context(&self) -> Context { self.egui_renderer.lock().context().clone() @@ -59,9 +56,7 @@ impl SharedGraphicsContext { } impl SharedGraphicsContext { - pub(crate) fn from_state( - state: &State, - ) -> Self { + pub(crate) fn from_state(state: &State) -> Self { SharedGraphicsContext { future_queue: state.future_queue.clone(), device: state.device.clone(), @@ -169,21 +164,18 @@ impl InstanceRaw { shader_location: 11, format: wgpu::VertexFormat::Float32x4, }, - // normal_matrix_0 wgpu::VertexAttribute { offset: size_of::<[f32; 16]>() as wgpu::BufferAddress, shader_location: 12, format: wgpu::VertexFormat::Float32x3, }, - // normal_matrix_1 wgpu::VertexAttribute { offset: size_of::<[f32; 19]>() as wgpu::BufferAddress, shader_location: 13, format: wgpu::VertexFormat::Float32x3, }, - // normal_matrix_2 wgpu::VertexAttribute { offset: size_of::<[f32; 22]>() as wgpu::BufferAddress, @@ -195,7 +187,6 @@ impl InstanceRaw { } } - /// A wrapper to the [wgpu::CommandEncoder] pub struct CommandEncoder { queue: Arc<Queue>, @@ -217,11 +208,13 @@ impl DerefMut for CommandEncoder { } impl CommandEncoder { - /// Creates a new instance of a command encoder. + /// Creates a new instance of a command encoder. pub fn new(graphics: Arc<SharedGraphicsContext>, label: Option<&str>) -> Self { Self { queue: graphics.queue.clone(), - inner: graphics.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label }), + inner: graphics + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label }), } } @@ -235,11 +228,11 @@ impl CommandEncoder { match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { self.queue.submit(std::iter::once(command_buffer)); })) { - Ok(_) => {Ok(())} + Ok(_) => Ok(()), Err(_) => { log::error!("Failed to submit command buffer, device may be lost"); return Err(anyhow::anyhow!("Command buffer submission failed")); } } } -} +} @@ -1,3 +1,4 @@ +pub mod animation; pub mod asset; pub mod attenuation; pub mod buffer; @@ -5,22 +6,21 @@ pub mod camera; pub mod colour; pub mod egui_renderer; pub mod entity; +pub mod features; pub mod graphics; pub mod input; pub mod lighting; +pub mod mipmap; pub mod model; pub mod panic; +pub mod pipelines; pub mod procedural; pub mod resources; pub mod scene; pub mod shader; -pub mod utils; -pub mod texture; -pub mod pipelines; -pub mod mipmap; pub mod sky; -pub mod features; -pub mod animation; +pub mod texture; +pub mod utils; features! { pub mod feature_list { @@ -44,12 +44,20 @@ use gilrs::{Gilrs, GilrsBuilder}; use log::LevelFilter; use parking_lot::{Mutex, RwLock}; use spin_sleep::SpinSleeper; -use std::fs::OpenOptions; -use std::sync::OnceLock; -use std::{fs, sync::Arc, time::{Duration, Instant}}; use std::collections::HashMap; +use std::fs::OpenOptions; use std::rc::Rc; -use wgpu::{BindGroupLayout, BindGroupLayoutEntry, BindingType, BufferBindingType, Device, ExperimentalFeatures, Instance, Queue, ShaderStages, Surface, SurfaceConfiguration, SurfaceError, TextureFormat}; +use std::sync::OnceLock; +use std::{ + fs, + sync::Arc, + time::{Duration, Instant}, +}; +use wgpu::{ + BindGroupLayout, BindGroupLayoutEntry, BindingType, BufferBindingType, Device, + ExperimentalFeatures, Instance, Queue, ShaderStages, Surface, SurfaceConfiguration, + SurfaceError, TextureFormat, +}; use winit::event::{DeviceEvent, DeviceId}; use winit::{ application::ApplicationHandler, @@ -60,19 +68,19 @@ use winit::{ }; use crate::camera::Camera; +use crate::egui_renderer::EguiRenderer; use crate::graphics::{CommandEncoder, SharedGraphicsContext}; -use crate::lighting::{Light}; -use crate::texture::Texture; -use crate::{egui_renderer::EguiRenderer}; +use crate::lighting::Light; use crate::mipmap::MipMapper; +use crate::texture::Texture; +use crate::pipelines::hdr::HdrPipeline; +use crate::scene::Scene; pub use dropbear_future_queue as future; pub use gilrs; pub use wgpu; pub use winit; use winit::window::{WindowAttributes, WindowId}; -use crate::pipelines::hdr::HdrPipeline; -use crate::scene::Scene; pub struct BindGroupLayouts { pub scene_globals_bind_group_layout: BindGroupLayout, @@ -117,7 +125,11 @@ pub struct State { impl State { /// Asynchronously initialised the state and sets up the backend and surface for wgpu to render to. - pub async fn new(window: Arc<Window>, instance: Arc<Instance>, future_queue: Arc<FutureQueue>) -> anyhow::Result<Self> { + pub async fn new( + window: Arc<Window>, + instance: Arc<Instance>, + future_queue: Arc<FutureQueue>, + ) -> anyhow::Result<Self> { let title = window.title(); let size = window.inner_size(); @@ -160,14 +172,21 @@ impl State { .flags .contains(wgpu::DownlevelFlags::VERTEX_STORAGE) && device.limits().max_storage_buffers_per_shader_stage > 0; - - log::debug!("graphics device {} support storage resources", if !supports_storage_resources { "DOES NOT" } else { "does" }); + + log::debug!( + "graphics device {} support storage resources", + if !supports_storage_resources { + "DOES NOT" + } else { + "does" + } + ); if WGPU_BACKEND.get().is_none() { let info = adapter.get_info(); let os_info = os_info::get(); log::info!( - "\n==================== BACKEND INFO ==================== + "\n==================== BACKEND INFO ==================== Backend: {} Software: @@ -215,7 +234,7 @@ Hardware: .find(|f| f.is_srgb()) .copied() .unwrap_or(TextureFormat::Rgba16Float); - + let config = SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: surface_format, @@ -233,8 +252,7 @@ Hardware: } let depth_texture = Texture::depth_texture(&config, &device, Some("depth texture")); - let viewport_texture = - Texture::viewport(&config, &device, Some("viewport texture")); + let viewport_texture = Texture::viewport(&config, &device, Some("viewport texture")); let mipmapper = Arc::new(MipMapper::new(&device)); @@ -253,14 +271,16 @@ Hardware: .register_native_texture(&device, &viewport_texture.view, wgpu::FilterMode::Linear); // shaders/shader.wgsl - @group(1) - let camera_bind_group_layout = device.create_bind_group_layout(&Camera::CAMERA_BIND_GROUP_LAYOUT); - + let camera_bind_group_layout = + device.create_bind_group_layout(&Camera::CAMERA_BIND_GROUP_LAYOUT); + // shader/light.wgsl - @group(1) - let light_bind_group_layout = device.create_bind_group_layout(&Light::LIGHT_BIND_GROUP_LAYOUT); + let light_bind_group_layout = + device.create_bind_group_layout(&Light::LIGHT_BIND_GROUP_LAYOUT); // shaders/light.wgsl - @group(1) - let light_cube_bind_group_layout = device - .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + let light_cube_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { entries: &[wgpu::BindGroupLayoutEntry { // @binding(0) binding: 0, @@ -274,147 +294,149 @@ Hardware: }], label: Some("Per-Light Layout"), }); - + // shaders/shader.wgsl - @group(0) - let scene_globals_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("scene globals bind group layout"), - entries: &[ - // u_globals - BindGroupLayoutEntry { - binding: 0, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Buffer { - ty: BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, + let scene_globals_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("scene globals bind group layout"), + entries: &[ + // u_globals + BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, }, - count: None, - }, - // u_camera - BindGroupLayoutEntry { - binding: 1, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Buffer { - ty: BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, + // u_camera + BindGroupLayoutEntry { + binding: 1, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, }, - count: None, - }, - ], - }); + ], + }); // shaders/shader.wgsl - @group(1) - let material_bind_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("material_bind_layout"), - entries: &[ - // u_material - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, + let material_bind_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("material_bind_layout"), + entries: &[ + // u_material + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, }, - count: None, - }, - // t_diffuse - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, + // t_diffuse + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, }, - count: None, - }, - // s_diffuse - wgpu::BindGroupLayoutEntry { - binding: 2, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - // t_normal - wgpu::BindGroupLayoutEntry { - binding: 3, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, + // s_diffuse + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, }, - count: None, - }, - // s_normal - wgpu::BindGroupLayoutEntry { - binding: 4, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - // t_emissive - wgpu::BindGroupLayoutEntry { - binding: 5, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, + // t_normal + wgpu::BindGroupLayoutEntry { + binding: 3, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, }, - count: None, - }, - // s_emissive - wgpu::BindGroupLayoutEntry { - binding: 6, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - // t_metallic - wgpu::BindGroupLayoutEntry { - binding: 7, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, + // s_normal + wgpu::BindGroupLayoutEntry { + binding: 4, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, }, - count: None, - }, - // s_metallic - wgpu::BindGroupLayoutEntry { - binding: 8, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - // t_occlusion - wgpu::BindGroupLayoutEntry { - binding: 9, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, + // t_emissive + wgpu::BindGroupLayoutEntry { + binding: 5, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, }, - count: None, - }, - // s_occlusion - wgpu::BindGroupLayoutEntry { - binding: 10, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }); + // s_emissive + wgpu::BindGroupLayoutEntry { + binding: 6, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + // t_metallic + wgpu::BindGroupLayoutEntry { + binding: 7, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, + }, + // s_metallic + wgpu::BindGroupLayoutEntry { + binding: 8, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + // t_occlusion + wgpu::BindGroupLayoutEntry { + binding: 9, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, + }, + // s_occlusion + wgpu::BindGroupLayoutEntry { + binding: 10, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + }); // shaders/light.wgsl - @group(1) - let light_array_bind_group_layout = device.create_bind_group_layout( - &wgpu::BindGroupLayoutDescriptor { + let light_array_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { entries: &[ // @binding(0) wgpu::BindGroupLayoutEntry { @@ -429,43 +451,43 @@ Hardware: }, ], label: Some("Light Array Layout"), - } - ); + }); // shaders/shader.wgsl - @group(2) - let scene_light_skin_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("scene light+skinning bind group layout"), - entries: &[ - // s_light_array - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Storage { read_only: true }, - has_dynamic_offset: false, - min_binding_size: None, + let scene_light_skin_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("scene light+skinning bind group layout"), + entries: &[ + // s_light_array + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, }, - count: None, - }, - // s_skinning - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Storage { read_only: true }, - has_dynamic_offset: false, - min_binding_size: None, + // s_skinning + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, }, - count: None, - }, - ], - }); + ], + }); // shaders/shader.wgsl - legacy globals layout - let shader_globals_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("shader.wgsl globals bind group layout"), - entries: &[ - BindGroupLayoutEntry { + let shader_globals_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("shader.wgsl globals bind group layout"), + entries: &[BindGroupLayoutEntry { // @binding(0) binding: 0, visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, @@ -475,9 +497,8 @@ Hardware: min_binding_size: None, }, count: None, - } - ], - }); + }], + }); let device = Arc::new(device); let queue = Arc::new(queue); @@ -524,19 +545,20 @@ Hardware: ], }); - let skinning_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("skinning bind group layout"), - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Storage { read_only: true }, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - }); + let skinning_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("skinning bind group layout"), + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + }); let result = Self { surface: Arc::new(surface), @@ -609,7 +631,8 @@ Hardware: return; } - if self.viewport_texture.size.width == width && self.viewport_texture.size.height == height { + if self.viewport_texture.size.width == width && self.viewport_texture.size.height == height + { return; } @@ -617,10 +640,8 @@ Hardware: config.width = width; config.height = height; - self.depth_texture = - Texture::depth_texture(&config, &self.device, Some("depth texture")); - self.viewport_texture = - Texture::viewport(&config, &self.device, Some("viewport texture")); + self.depth_texture = Texture::depth_texture(&config, &self.device, Some("depth texture")); + self.viewport_texture = Texture::viewport(&config, &self.device, Some("viewport texture")); self.hdr.write().resize(&self.device, width, height); self.egui_renderer .lock() @@ -645,9 +666,9 @@ Hardware: if !self.is_surface_configured { return Ok(Vec::new()); } - + let config = self.config.read().clone(); - + let output = match self.surface.get_current_texture() { Ok(val) => val, Err(e) => { @@ -682,17 +703,17 @@ Hardware: pixels_per_point: self.window.scale_factor() as f32, }; - let view = output - .texture - .create_view(&wgpu::TextureViewDescriptor { - label: Some("surface texture view descriptor"), - format: Some(self.config.read().format.add_srgb_suffix()), - ..Default::default() - }); + let view = output.texture.create_view(&wgpu::TextureViewDescriptor { + label: Some("surface texture view descriptor"), + format: Some(self.config.read().format.add_srgb_suffix()), + ..Default::default() + }); - { // ensures clearing of the encoder is done correctly. + { + // ensures clearing of the encoder is done correctly. puffin::profile_scope!("surface clear"); - let mut encoder = CommandEncoder::new(graphics.clone(), Some("surface clear render encoder")); + let mut encoder = + CommandEncoder::new(graphics.clone(), Some("surface clear render encoder")); { let hdr = self.hdr.read(); @@ -718,7 +739,7 @@ Hardware: log_once::error_once!("{}", e); } } - + self.egui_renderer.lock().begin_frame(&self.window); let mut scene_manager = std::mem::replace(&mut self.scene_manager, scene::Manager::new()); @@ -758,7 +779,7 @@ Hardware: let hdr = self.hdr.read(); hdr.process(&mut encoder, &self.viewport_texture.view); } - + self.egui_renderer.lock().end_frame_and_draw( &self.device, &self.queue, @@ -814,7 +835,10 @@ Hardware: let window_count = Arc::strong_count(&window); if window_count > 1 { - log::warn!("Window still has {} strong references after cleanup", window_count); + log::warn!( + "Window still has {} strong references after cleanup", + window_count + ); } drop(window); @@ -850,7 +874,10 @@ impl DropbearAppBuilder { windows_to_create: vec![], future_queue: None, max_fps: u32::MAX, - app_data: AppInfo { name: "unknown_dropbear_app", author: "unknown" }, + app_data: AppInfo { + name: "unknown_dropbear_app", + author: "unknown", + }, } } @@ -893,10 +920,9 @@ impl DropbearAppBuilder { pub async fn run(self) -> anyhow::Result<()> { #[cfg(not(target_os = "android"))] { - let log_dir = - app_dirs2::app_root(AppDataType::UserData, &self.app_data) - .expect("Failed to get app data directory") - .join("logs"); + let log_dir = app_dirs2::app_root(AppDataType::UserData, &self.app_data) + .expect("Failed to get app data directory") + .join("logs"); fs::create_dir_all(&log_dir).expect("Failed to create log dir"); let datetime_str = Local::now().format("%Y-%m-%d_%H-%M-%S"); @@ -935,7 +961,7 @@ impl DropbearAppBuilder { record.file().unwrap_or("unknown"), record.line().unwrap_or(0) ) - .bright_black(); + .bright_black(); let console_line = format!( "{} {} [{}] - {}\n", @@ -1005,10 +1031,7 @@ impl DropbearAppBuilder { pub trait SceneWithInput: Scene + input::Keyboard + input::Mouse + input::Controller {} -impl<T> SceneWithInput for T -where - T: Scene + input::Keyboard + input::Mouse + input::Controller -{} +impl<T> SceneWithInput for T where T: Scene + input::Keyboard + input::Mouse + input::Controller {} #[derive(Clone)] pub struct WindowData { @@ -1037,12 +1060,17 @@ impl DropbearWindowBuilder { self } - pub fn add_scene_with_input<S>(mut self, scene: Rc<RwLock<S>>, scene_name: impl ToString) -> Self + pub fn add_scene_with_input<S>( + mut self, + scene: Rc<RwLock<S>>, + scene_name: impl ToString, + ) -> Self where - S: 'static + Scene + input::Keyboard + input::Mouse + input::Controller + S: 'static + Scene + input::Keyboard + input::Mouse + input::Controller, { let scene_name = scene_name.to_string(); - self.scenes.insert(scene_name, scene as Rc<RwLock<dyn SceneWithInput>>); + self.scenes + .insert(scene_name, scene as Rc<RwLock<dyn SceneWithInput>>); self } @@ -1090,8 +1118,7 @@ pub struct App { root_window_id: Option<WindowId>, windows_to_create: Vec<WindowData>, - - #[allow(dead_code)] + #[allow(dead_code)] puffin_server: Option<Arc<puffin_http::Server>>, } @@ -1100,7 +1127,7 @@ impl App { /// window config. fn new(app_data: AppInfo, future_queue: Option<Arc<FutureQueue>>) -> Self { let mut puffin_server: Option<Arc<puffin_http::Server>> = None; - + if feature_list::is_enabled(feature_list::EnablePuffinTracer) { log::info!("Enabling puffin profiler"); puffin::set_scopes_on(true); @@ -1110,10 +1137,10 @@ impl App { log::info!("Started puffin http server at \"127.0.0.1:8585\""); puffin_server = Some(Arc::new(v)); // need to keep as Arc to keep it alive - }, + } Err(e) => { log::error!("Unable to start puffin http server: {}", e); - }, + } } } @@ -1152,16 +1179,22 @@ impl App { } /// Creates a new window and adds it to its internal window manager (its really just a hashmap). - pub fn create_window(&mut self, event_loop: &ActiveEventLoop, attribs: WindowAttributes) -> anyhow::Result<WindowId> { + pub fn create_window( + &mut self, + event_loop: &ActiveEventLoop, + attribs: WindowAttributes, + ) -> anyhow::Result<WindowId> { puffin::profile_function!("load wgpu state"); - let window = Arc::new( - event_loop.create_window(attribs)? - ); + let window = Arc::new(event_loop.create_window(attribs)?); let window_id = window.id(); - let mut win_state = match pollster::block_on(State::new(window, self.instance.clone(), self.future_queue.clone())) { + let mut win_state = match pollster::block_on(State::new( + window, + self.instance.clone(), + self.future_queue.clone(), + )) { Ok(v) => v, Err(e) => { // force a panic because pollster doesnt panic on error @@ -1216,17 +1249,25 @@ impl ApplicationHandler for App { let mouse_name = format!("{}_mouse", scene_name); let controller_name = format!("{}_controller", scene_name); - let keyboard_handler: Rc<RwLock<dyn input::Keyboard>> = scene.clone(); + let keyboard_handler: Rc<RwLock<dyn input::Keyboard>> = + scene.clone(); let mouse_handler: Rc<RwLock<dyn input::Mouse>> = scene.clone(); - let controller_handler: Rc<RwLock<dyn input::Controller>> = scene.clone(); + let controller_handler: Rc<RwLock<dyn input::Controller>> = + scene.clone(); - self.input_manager.add_keyboard(&keyboard_name, keyboard_handler); + self.input_manager + .add_keyboard(&keyboard_name, keyboard_handler); self.input_manager.add_mouse(&mouse_name, mouse_handler); - self.input_manager.add_controller(&controller_name, controller_handler); + self.input_manager + .add_controller(&controller_name, controller_handler); - state.scene_manager.attach_input(&scene_name, &keyboard_name); + state + .scene_manager + .attach_input(&scene_name, &keyboard_name); state.scene_manager.attach_input(&scene_name, &mouse_name); - state.scene_manager.attach_input(&scene_name, &controller_name); + state + .scene_manager + .attach_input(&scene_name, &controller_name); } if let Some(initial_scene) = window_data.first_scene { @@ -1304,8 +1345,7 @@ impl ApplicationHandler for App { self.input_manager.update(&mut self.gilrs); - let render_result = - state.render(self.delta_time, event_loop, graphics.clone()); + let render_result = state.render(self.delta_time, event_loop, graphics.clone()); window_commands = render_result.unwrap_or_else(|e| { log::error!("Render failed: {:?}", e); @@ -1331,64 +1371,73 @@ impl ApplicationHandler for App { for command in window_commands { match command { - scene::SceneCommand::RequestWindow(window_data) => { - log::info!("Scene requested new window creation"); - match self.create_window(event_loop, window_data.attributes) { - Ok(new_window_id) => { - if let Some((new_state, _)) = self.windows.get_mut(&new_window_id) { - for (scene_name, scene) in window_data.scenes { - new_state.scene_manager.add(&scene_name, scene.clone()); - - let keyboard_name = format!("{}_keyboard", scene_name); - let mouse_name = format!("{}_mouse", scene_name); - let controller_name = format!("{}_controller", scene_name); - - let keyboard_handler: Rc<RwLock<dyn input::Keyboard>> = scene.clone(); - let mouse_handler: Rc<RwLock<dyn input::Mouse>> = scene.clone(); - let controller_handler: Rc<RwLock<dyn input::Controller>> = scene.clone(); - - self.input_manager.add_keyboard(&keyboard_name, keyboard_handler); - self.input_manager.add_mouse(&mouse_name, mouse_handler); - self.input_manager.add_controller(&controller_name, controller_handler); - - new_state.scene_manager.attach_input(&scene_name, &keyboard_name); - new_state.scene_manager.attach_input(&scene_name, &mouse_name); - new_state.scene_manager.attach_input(&scene_name, &controller_name); - } - - if let Some(initial_scene) = window_data.first_scene { - new_state.scene_manager.switch(&initial_scene); - } - } + scene::SceneCommand::RequestWindow(window_data) => { + log::info!("Scene requested new window creation"); + match self.create_window(event_loop, window_data.attributes) { + Ok(new_window_id) => { + if let Some((new_state, _)) = self.windows.get_mut(&new_window_id) { + for (scene_name, scene) in window_data.scenes { + new_state.scene_manager.add(&scene_name, scene.clone()); + + let keyboard_name = format!("{}_keyboard", scene_name); + let mouse_name = format!("{}_mouse", scene_name); + let controller_name = format!("{}_controller", scene_name); + + let keyboard_handler: Rc<RwLock<dyn input::Keyboard>> = + scene.clone(); + let mouse_handler: Rc<RwLock<dyn input::Mouse>> = scene.clone(); + let controller_handler: Rc<RwLock<dyn input::Controller>> = + scene.clone(); + + self.input_manager + .add_keyboard(&keyboard_name, keyboard_handler); + self.input_manager.add_mouse(&mouse_name, mouse_handler); + self.input_manager + .add_controller(&controller_name, controller_handler); + + new_state + .scene_manager + .attach_input(&scene_name, &keyboard_name); + new_state + .scene_manager + .attach_input(&scene_name, &mouse_name); + new_state + .scene_manager + .attach_input(&scene_name, &controller_name); } - Err(e) => { - log::error!("Failed to create requested window: {}", e); + + if let Some(initial_scene) = window_data.first_scene { + new_state.scene_manager.switch(&initial_scene); } } } - scene::SceneCommand::CloseWindow(target_window_id) => { - log::info!("Scene requested closing window: {:?}", target_window_id); - if Some(target_window_id) == self.root_window_id { - self.quit(event_loop, None); - } else { - self.windows.remove(&target_window_id); - } - } - scene::SceneCommand::Quit(hook) => { - log::debug!("Caught SceneCommand::Quit command!"); - self.quit(event_loop, hook); - } - scene::SceneCommand::SetFPS(new_fps) => { - self.set_target_fps(new_fps); - } - scene::SceneCommand::ResizeViewport((width, height)) => { - if let Some((state, graphics)) = self.windows.get_mut(&window_id) { - state.resize_viewport_texture(width, height); - *graphics = - Arc::new(graphics::SharedGraphicsContext::from_state(state)); - } + Err(e) => { + log::error!("Failed to create requested window: {}", e); } - _ => {} + } + } + scene::SceneCommand::CloseWindow(target_window_id) => { + log::info!("Scene requested closing window: {:?}", target_window_id); + if Some(target_window_id) == self.root_window_id { + self.quit(event_loop, None); + } else { + self.windows.remove(&target_window_id); + } + } + scene::SceneCommand::Quit(hook) => { + log::debug!("Caught SceneCommand::Quit command!"); + self.quit(event_loop, hook); + } + scene::SceneCommand::SetFPS(new_fps) => { + self.set_target_fps(new_fps); + } + scene::SceneCommand::ResizeViewport((width, height)) => { + if let Some((state, graphics)) = self.windows.get_mut(&window_id) { + state.resize_viewport_texture(width, height); + *graphics = Arc::new(graphics::SharedGraphicsContext::from_state(state)); + } + } + _ => {} } } @@ -1448,4 +1497,4 @@ impl ApplicationHandler for App { window_state.window.request_redraw(); } } -} +} @@ -1,17 +1,14 @@ +use crate::asset::{ASSET_REGISTRY, Handle}; use crate::attenuation::{Attenuation, RANGE_50}; use crate::buffer::{ResizableBuffer, UniformBuffer}; use crate::graphics::SharedGraphicsContext; use crate::pipelines::light_cube::InstanceInput; -use crate::{ - entity::Transform, - model::Model, -}; +use crate::procedural::ProcedurallyGeneratedObject; +use crate::{entity::Transform, model::Model}; use glam::{DMat4, DQuat, DVec3}; use std::fmt::{Display, Formatter}; use std::sync::Arc; -use wgpu::{BindGroup}; -use crate::asset::{Handle, ASSET_REGISTRY}; -use crate::procedural::{ProcedurallyGeneratedObject}; +use wgpu::BindGroup; pub const MAX_LIGHTS: usize = 10; @@ -86,7 +83,9 @@ impl Default for LightArrayUniform { } } -#[derive(Default, Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)] +#[derive( + Default, Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, +)] pub enum LightType { #[default] // Example: Sunlight @@ -120,34 +119,34 @@ impl From<LightType> for u32 { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct LightComponent { #[serde(default)] - pub position: DVec3, // point, spot + pub position: DVec3, // point, spot #[serde(default)] - pub direction: DVec3, // directional, spot + pub direction: DVec3, // directional, spot #[serde(default)] - pub colour: DVec3, // all + pub colour: DVec3, // all #[serde(default)] - pub light_type: LightType, // all + pub light_type: LightType, // all #[serde(default)] - pub intensity: f32, // all + pub intensity: f32, // all #[serde(default)] pub attenuation: Attenuation, // point, spot #[serde(default)] - pub enabled: bool, // all - light + pub enabled: bool, // all - light #[serde(default)] - pub visible: bool, // all - cube + pub visible: bool, // all - cube #[serde(default)] - pub cutoff_angle: f32, // spot + pub cutoff_angle: f32, // spot #[serde(default)] - pub outer_cutoff_angle: f32, // spot + pub outer_cutoff_angle: f32, // spot #[serde(default)] pub cast_shadows: bool, @@ -279,7 +278,7 @@ pub struct Light { } impl Light { - pub const LIGHT_BIND_GROUP_LAYOUT: wgpu::BindGroupLayoutDescriptor<'_> = + pub const LIGHT_BIND_GROUP_LAYOUT: wgpu::BindGroupLayoutDescriptor<'_> = wgpu::BindGroupLayoutDescriptor { // @binding(0) entries: &[wgpu::BindGroupLayoutEntry { @@ -317,13 +316,12 @@ impl Light { log::trace!("Created new light uniform"); - let cube_model = ProcedurallyGeneratedObject::cuboid(DVec3::ONE) - .build_model( - graphics.clone(), - None, - Some("light cube"), - ASSET_REGISTRY.clone() - ); + let cube_model = ProcedurallyGeneratedObject::cuboid(DVec3::ONE).build_model( + graphics.clone(), + None, + Some("light cube"), + ASSET_REGISTRY.clone(), + ); let label_str = label.unwrap_or("Light").to_string(); @@ -344,14 +342,15 @@ impl Light { let instance: InstanceInput = DMat4::from_scale_rotation_translation( transform.scale, transform.rotation, - transform.position - ).into(); + transform.position, + ) + .into(); let mut instance_buffer = ResizableBuffer::new( &graphics.device, 1, wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - "Light Instance Buffer" + "Light Instance Buffer", ); instance_buffer.write(&graphics.device, &graphics.queue, &[instance]); @@ -401,4 +400,4 @@ impl Light { pub fn label(&self) -> &str { &self.label } -} +} @@ -1,6 +1,6 @@ use slank::{include_slang, utils::WgpuUtils}; -use crate::{texture::Texture}; +use crate::texture::Texture; pub struct MipMapper { blit_mipmap: wgpu::RenderPipeline, @@ -13,10 +13,13 @@ pub struct MipMapper { impl MipMapper { pub fn new(device: &wgpu::Device) -> Self { puffin::profile_function!(); - let blit_shader = device.create_shader_module(slank::CompiledSlangShader::from_bytes( - "mipmap blit_shader", - include_slang!("blit_shader") - ).create_wgpu_shader()); + let blit_shader = device.create_shader_module( + slank::CompiledSlangShader::from_bytes( + "mipmap blit_shader", + include_slang!("blit_shader"), + ) + .create_wgpu_shader(), + ); // Keep this SRGB so we can render directly into the SRGB textures we create for materials. let blit_format = Texture::TEXTURE_FORMAT; @@ -33,13 +36,11 @@ impl MipMapper { module: &blit_shader, entry_point: None, compilation_options: Default::default(), - targets: &[ - Some(wgpu::ColorTargetState { - format: blit_format, - blend: None, - write_mask: wgpu::ColorWrites::ALL, - }) - ], + targets: &[Some(wgpu::ColorTargetState { + format: blit_format, + blend: None, + write_mask: wgpu::ColorWrites::ALL, + })], }), primitive: wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::TriangleList, @@ -131,7 +132,7 @@ impl MipMapper { let texture = &texture.texture; match texture.format() { - wgpu::TextureFormat::Rgba8Unorm | wgpu::TextureFormat::Rgba8UnormSrgb => {}, + wgpu::TextureFormat::Rgba8Unorm | wgpu::TextureFormat::Rgba8UnormSrgb => {} _ => anyhow::bail!("Unsupported format {:?}", texture.format()), } @@ -145,7 +146,10 @@ impl MipMapper { // We need to render to this texture, so if the supplied texture // isn't setup for rendering, we need to create a temporary one. - let (mut src_view, maybe_temp) = if texture.usage().contains(wgpu::TextureUsages::RENDER_ATTACHMENT) { + let (mut src_view, maybe_temp) = if texture + .usage() + .contains(wgpu::TextureUsages::RENDER_ATTACHMENT) + { ( texture.create_view(&wgpu::TextureViewDescriptor { base_mip_level: 0, @@ -394,4 +398,4 @@ impl MipMapper { Ok(()) } -} +} @@ -1,20 +1,20 @@ use crate::asset::{AssetRegistry, Handle}; use crate::buffer::UniformBuffer; use crate::{ - graphics::{SharedGraphicsContext}, - utils::{ResourceReference}, - texture::{Texture, TextureWrapMode} + graphics::SharedGraphicsContext, + texture::{Texture, TextureWrapMode}, + utils::ResourceReference, }; -use parking_lot::{RwLock}; +use gltf::image::Format; +use gltf::texture::MinFilter; +use parking_lot::RwLock; +use puffin::profile_scope; use rayon::prelude::*; use serde::{Deserialize, Serialize}; use std::hash::{DefaultHasher, Hash, Hasher}; -use std::sync::{Arc}; +use std::sync::Arc; use std::{mem, ops::Range}; -use gltf::image::Format; -use gltf::texture::MinFilter; -use puffin::profile_scope; -use wgpu::{BufferAddress, VertexAttribute, VertexBufferLayout, util::DeviceExt, BindGroup}; +use wgpu::{BindGroup, BufferAddress, VertexAttribute, VertexBufferLayout, util::DeviceExt}; // do not derive clone otherwise it wil take too much memory // #[derive(Clone)] @@ -192,7 +192,7 @@ impl Material { let tint_buffer = UniformBuffer::new(&graphics.device, "material_tint_uniform"); tint_buffer.write(&graphics.queue, &uniform); - + let bind_group = Self::create_bind_group( &graphics, &diffuse_texture, @@ -240,56 +240,58 @@ impl Material { name: &str, ) -> BindGroup { puffin::profile_function!(); - graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some(format!("{} texture bind group", name).as_str()), - layout: &graphics.layouts.material_bind_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: uniform_buffer.buffer().as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::TextureView(&diffuse.view), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::Sampler(&diffuse.sampler), - }, - wgpu::BindGroupEntry { - binding: 3, - resource: wgpu::BindingResource::TextureView(&normal.view), - }, - wgpu::BindGroupEntry { - binding: 4, - resource: wgpu::BindingResource::Sampler(&normal.sampler), - }, - wgpu::BindGroupEntry { - binding: 5, - resource: wgpu::BindingResource::TextureView(&emissive.view), - }, - wgpu::BindGroupEntry { - binding: 6, - resource: wgpu::BindingResource::Sampler(&emissive.sampler), - }, - wgpu::BindGroupEntry { - binding: 7, - resource: wgpu::BindingResource::TextureView(&metallic_roughness.view), - }, - wgpu::BindGroupEntry { - binding: 8, - resource: wgpu::BindingResource::Sampler(&metallic_roughness.sampler), - }, - wgpu::BindGroupEntry { - binding: 9, - resource: wgpu::BindingResource::TextureView(&occlusion.view), - }, - wgpu::BindGroupEntry { - binding: 10, - resource: wgpu::BindingResource::Sampler(&occlusion.sampler), - }, - ], - }) + graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some(format!("{} texture bind group", name).as_str()), + layout: &graphics.layouts.material_bind_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: uniform_buffer.buffer().as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(&diffuse.view), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::Sampler(&diffuse.sampler), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::TextureView(&normal.view), + }, + wgpu::BindGroupEntry { + binding: 4, + resource: wgpu::BindingResource::Sampler(&normal.sampler), + }, + wgpu::BindGroupEntry { + binding: 5, + resource: wgpu::BindingResource::TextureView(&emissive.view), + }, + wgpu::BindGroupEntry { + binding: 6, + resource: wgpu::BindingResource::Sampler(&emissive.sampler), + }, + wgpu::BindGroupEntry { + binding: 7, + resource: wgpu::BindingResource::TextureView(&metallic_roughness.view), + }, + wgpu::BindGroupEntry { + binding: 8, + resource: wgpu::BindingResource::Sampler(&metallic_roughness.sampler), + }, + wgpu::BindGroupEntry { + binding: 9, + resource: wgpu::BindingResource::TextureView(&occlusion.view), + }, + wgpu::BindGroupEntry { + binding: 10, + resource: wgpu::BindingResource::Sampler(&occlusion.sampler), + }, + ], + }) } pub fn sync_uniform(&self, graphics: &SharedGraphicsContext) { @@ -348,10 +350,18 @@ impl GLTFTextureInformation { let (min_filter, mipmap_filter) = match sampler.min_filter() { Some(MinFilter::Nearest) => (wgpu::FilterMode::Nearest, wgpu::FilterMode::Nearest), Some(MinFilter::Linear) => (wgpu::FilterMode::Linear, wgpu::FilterMode::Nearest), - Some(MinFilter::NearestMipmapNearest) => (wgpu::FilterMode::Nearest, wgpu::FilterMode::Nearest), - Some(MinFilter::LinearMipmapNearest) => (wgpu::FilterMode::Linear, wgpu::FilterMode::Nearest), - Some(MinFilter::NearestMipmapLinear) => (wgpu::FilterMode::Nearest, wgpu::FilterMode::Linear), - Some(MinFilter::LinearMipmapLinear) => (wgpu::FilterMode::Linear, wgpu::FilterMode::Linear), + Some(MinFilter::NearestMipmapNearest) => { + (wgpu::FilterMode::Nearest, wgpu::FilterMode::Nearest) + } + Some(MinFilter::LinearMipmapNearest) => { + (wgpu::FilterMode::Linear, wgpu::FilterMode::Nearest) + } + Some(MinFilter::NearestMipmapLinear) => { + (wgpu::FilterMode::Nearest, wgpu::FilterMode::Linear) + } + Some(MinFilter::LinearMipmapLinear) => { + (wgpu::FilterMode::Linear, wgpu::FilterMode::Linear) + } None => (wgpu::FilterMode::Linear, wgpu::FilterMode::Linear), }; @@ -396,7 +406,7 @@ impl GLTFTextureInformation { rgba.push(255); } (rgba, wgpu::TextureFormat::Rgba8Unorm) - }, + } Format::R8G8B8A8 => (image_data.pixels.clone(), wgpu::TextureFormat::Rgba8Unorm), _ => panic!("Unsupported format"), }; @@ -477,7 +487,10 @@ impl Model { ) -> Vec<GLTFMaterialInformation> { puffin::profile_function!(); let process_texture = |texture: gltf::Texture<'_>| -> Option<GLTFTextureInformation> { - puffin::profile_scope!("reading texture bytes", texture.name().unwrap_or("Unnamed Texture")); + puffin::profile_scope!( + "reading texture bytes", + texture.name().unwrap_or("Unnamed Texture") + ); Some(GLTFTextureInformation::fetch(&texture, images)) }; @@ -580,8 +593,11 @@ impl Model { puffin::profile_function!(&mesh_name); for (primitive_index, primitive) in mesh.primitives().enumerate() { - puffin::profile_scope!("reading primitive", &format!("{}[{}]", &mesh_name, primitive_index)); - + puffin::profile_scope!( + "reading primitive", + &format!("{}[{}]", &mesh_name, primitive_index) + ); + let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()])); let positions: Vec<[f32; 3]> = reader @@ -685,17 +701,17 @@ impl Model { fn load_nodes(gltf: &gltf::Document) -> Vec<Node> { puffin::profile_function!("loading nodes"); let mut nodes = Vec::new(); - + for node in gltf.nodes() { profile_scope!("reading node", node.name().unwrap_or("Unnamed Node")); let (translation, rotation, scale) = node.transform().decomposed(); - + let transform = NodeTransform { translation: glam::Vec3::from(translation), rotation: glam::Quat::from_array(rotation), scale: glam::Vec3::from(scale), }; - + nodes.push(Node { name: node.name().unwrap_or("Unnamed Node").to_string(), parent: None, @@ -703,50 +719,53 @@ impl Model { transform, }); } - + for (node_index, node) in gltf.nodes().enumerate() { - profile_scope!("second pass enumerating children", node.name().unwrap_or("Unnamed Node")); + profile_scope!( + "second pass enumerating children", + node.name().unwrap_or("Unnamed Node") + ); for child in node.children() { if let Some(child_node) = nodes.get_mut(child.index()) { child_node.parent = Some(node_index); } } } - + nodes } fn load_skins(gltf: &gltf::Document, buffers: &[gltf::buffer::Data]) -> Vec<Skin> { puffin::profile_function!("loading skins"); let mut skins = Vec::new(); - + for skin in gltf.skins() { puffin::profile_scope!("reading skin", skin.name().unwrap_or("Unnamed Skin")); let joints: Vec<usize> = skin.joints().map(|j| j.index()).collect(); - + let inverse_bind_matrices = if let Some(accessor) = skin.inverse_bind_matrices() { let view = accessor.view().expect("Accessor must have a buffer view"); let buffer_data = &buffers[view.buffer().index()]; let start = view.offset() + accessor.offset(); let stride = view.stride().unwrap_or(accessor.size()); - + let mut matrices = Vec::with_capacity(accessor.count()); for i in 0..accessor.count() { let offset = start + i * stride; let matrix_bytes = &buffer_data[offset..offset + 64]; - + let mut floats = [0f32; 16]; for (j, chunk) in matrix_bytes.chunks_exact(4).enumerate() { floats[j] = f32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); } - + matrices.push(glam::Mat4::from_cols_array(&floats)); } matrices } else { vec![glam::Mat4::IDENTITY; joints.len()] }; - + skins.push(Skin { name: skin.name().unwrap_or("Unnamed Skin").to_string(), joints, @@ -754,43 +773,45 @@ impl Model { skeleton_root: skin.skeleton().map(|n| n.index()), }); } - + skins } fn load_animations(gltf: &gltf::Document, buffers: &[gltf::buffer::Data]) -> Vec<Animation> { puffin::profile_function!("loading animations"); let mut animations = Vec::new(); - + for animation in gltf.animations() { - puffin::profile_scope!("reading animation", animation.name().unwrap_or("Unnamed Animation")); + puffin::profile_scope!( + "reading animation", + animation.name().unwrap_or("Unnamed Animation") + ); let mut channels = Vec::new(); let mut max_time = 0.0f32; - + for channel in animation.channels() { let target = channel.target(); let reader = channel.reader(|buffer| Some(&buffers[buffer.index()])); let interpolation_mode = channel.sampler().interpolation(); - + let times: Vec<f32> = if let Some(inputs) = reader.read_inputs() { inputs.collect() } else { continue; }; - + if let Some(&last_time) = times.last() { max_time = max_time.max(last_time); } - + let values = match target.property() { gltf::animation::Property::Translation => { puffin::profile_scope!("reading translation values"); if let Some(outputs) = reader.read_outputs() { match outputs { gltf::animation::util::ReadOutputs::Translations(iter) => { - let translations: Vec<glam::Vec3> = iter - .map(|t| glam::Vec3::from(t)) - .collect(); + let translations: Vec<glam::Vec3> = + iter.map(|t| glam::Vec3::from(t)).collect(); ChannelValues::Translations(translations) } _ => continue, @@ -804,16 +825,14 @@ impl Model { if let Some(outputs) = reader.read_outputs() { match outputs { gltf::animation::util::ReadOutputs::Rotations(iter) => { - let rotations: Vec<glam::Quat> = if interpolation_mode == gltf::animation::Interpolation::CubicSpline { + let rotations: Vec<glam::Quat> = if interpolation_mode + == gltf::animation::Interpolation::CubicSpline + { iter.into_f32() .enumerate() .map(|(i, r)| { let q = glam::Quat::from_array(r); - if i % 3 == 1 { - q.normalize() - } else { - q - } + if i % 3 == 1 { q.normalize() } else { q } }) .collect() } else { @@ -834,9 +853,8 @@ impl Model { if let Some(outputs) = reader.read_outputs() { match outputs { gltf::animation::util::ReadOutputs::Scales(iter) => { - let scales: Vec<glam::Vec3> = iter - .map(|s| glam::Vec3::from(s)) - .collect(); + let scales: Vec<glam::Vec3> = + iter.map(|s| glam::Vec3::from(s)).collect(); ChannelValues::Scales(scales) } _ => continue, @@ -851,13 +869,15 @@ impl Model { continue; } }; - + let interpolation = match channel.sampler().interpolation() { gltf::animation::Interpolation::Linear => AnimationInterpolation::Linear, gltf::animation::Interpolation::Step => AnimationInterpolation::Step, - gltf::animation::Interpolation::CubicSpline => AnimationInterpolation::CubicSpline, + gltf::animation::Interpolation::CubicSpline => { + AnimationInterpolation::CubicSpline + } }; - + channels.push(AnimationChannel { target_node: target.node().index(), times, @@ -865,14 +885,14 @@ impl Model { interpolation, }); } - + animations.push(Animation { name: animation.name().unwrap_or("Unnamed Animation").to_string(), channels, duration: max_time, }); } - + animations } @@ -913,11 +933,11 @@ impl Model { } let nodes = Self::load_nodes(&gltf); - + let skins = Self::load_skins(&gltf, &buffers); - + let animations = Self::load_animations(&gltf, &buffers); - + log::debug!( "Loaded {} nodes, {} skins, {} animations for model [{:?}]", nodes.len(), @@ -934,9 +954,15 @@ impl Model { puffin::profile_scope!("processing material textures"); let material_name = material_info.name; - let extract = |info: Option<GLTFTextureInformation>| -> (Option<(Vec<u8>, (u32, u32))>, Option<wgpu::SamplerDescriptor<'static>>) { + let extract = |info: Option<GLTFTextureInformation>| -> ( + Option<(Vec<u8>, (u32, u32))>, + Option<wgpu::SamplerDescriptor<'static>>, + ) { if let Some(info) = info { - (Some((info.pixels, (info.width, info.height))), Some(info.sampler)) + ( + Some((info.pixels, (info.width, info.height))), + Some(info.sampler), + ) } else { (None, None) } @@ -944,9 +970,12 @@ impl Model { let (processed_diffuse, diffuse_sampler) = extract(material_info.diffuse_texture); let (processed_normal, normal_sampler) = extract(material_info.normal_texture); - let (processed_emissive, emissive_sampler) = extract(material_info.emissive_texture); - let (processed_metallic_roughness, metallic_roughness_sampler) = extract(material_info.metallic_roughness_texture); - let (processed_occlusion, occlusion_sampler) = extract(material_info.occlusion_texture); + let (processed_emissive, emissive_sampler) = + extract(material_info.emissive_texture); + let (processed_metallic_roughness, metallic_roughness_sampler) = + extract(material_info.metallic_roughness_texture); + let (processed_occlusion, occlusion_sampler) = + extract(material_info.occlusion_texture); let tint = material_info.tint; let emissive_factor = material_info.emissive_factor; @@ -1029,7 +1058,10 @@ impl Model { } else if let Some(white) = registry.get_texture(white_srgb_texture) { (*white).clone() } else { - anyhow::bail!("Unable to find processed diffuse or fetch fallback texture for model {:?}", label); + anyhow::bail!( + "Unable to find processed diffuse or fetch fallback texture for model {:?}", + label + ); }; let has_normal_texture = processed_normal.is_some(); @@ -1046,7 +1078,10 @@ impl Model { } else if let Some(tex) = registry.get_texture(flat_normal_texture) { (*tex).clone() } else { - anyhow::bail!("Unable to find processed normal or fetch fallback texture for model {:?}", label); + anyhow::bail!( + "Unable to find processed normal or fetch fallback texture for model {:?}", + label + ); }; let emissive_texture = processed_emissive.map(|(rgba_data, dimensions)| { @@ -1060,17 +1095,18 @@ impl Model { Some(material_name.as_str()), ) }); - let metallic_roughness_texture = processed_metallic_roughness.map(|(rgba_data, dimensions)| { - Texture::from_bytes_verbose_mipmapped_with_format( - graphics.clone(), - &rgba_data, - Some(dimensions), - None, - metallic_roughness_sampler.clone(), - Texture::TEXTURE_FORMAT_BASE, - Some(material_name.as_str()), - ) - }); + let metallic_roughness_texture = + processed_metallic_roughness.map(|(rgba_data, dimensions)| { + Texture::from_bytes_verbose_mipmapped_with_format( + graphics.clone(), + &rgba_data, + Some(dimensions), + None, + metallic_roughness_sampler.clone(), + Texture::TEXTURE_FORMAT_BASE, + Some(material_name.as_str()), + ) + }); let occlusion_texture = processed_occlusion.map(|(rgba_data, dimensions)| { Texture::from_bytes_verbose_mipmapped_with_format( graphics.clone(), @@ -1086,15 +1122,30 @@ impl Model { let emissive_texture_bound = emissive_texture .clone() .or_else(|| registry.get_texture(white_srgb_texture).cloned()) - .ok_or_else(|| anyhow::anyhow!("Unable to resolve emissive fallback texture for model {:?}", label))?; + .ok_or_else(|| { + anyhow::anyhow!( + "Unable to resolve emissive fallback texture for model {:?}", + label + ) + })?; let metallic_roughness_texture_bound = metallic_roughness_texture .clone() .or_else(|| registry.get_texture(white_linear_texture).cloned()) - .ok_or_else(|| anyhow::anyhow!("Unable to resolve metallic fallback texture for model {:?}", label))?; + .ok_or_else(|| { + anyhow::anyhow!( + "Unable to resolve metallic fallback texture for model {:?}", + label + ) + })?; let occlusion_texture_bound = occlusion_texture .clone() .or_else(|| registry.get_texture(white_linear_texture).cloned()) - .ok_or_else(|| anyhow::anyhow!("Unable to resolve occlusion fallback texture for model {:?}", label))?; + .ok_or_else(|| { + anyhow::anyhow!( + "Unable to resolve occlusion fallback texture for model {:?}", + label + ) + })?; let texture_tag = Some(material_name.clone()); let mut material = Material::new( @@ -1273,7 +1324,13 @@ where globals_camera_bind_group: &'b wgpu::BindGroup, light_skin_bind_group: &'a wgpu::BindGroup, ) { - self.draw_mesh_instanced(mesh, material, 0..1, globals_camera_bind_group, light_skin_bind_group); + self.draw_mesh_instanced( + mesh, + material, + 0..1, + globals_camera_bind_group, + light_skin_bind_group, + ); } fn draw_mesh_instanced( @@ -1299,7 +1356,12 @@ where globals_camera_bind_group: &'b wgpu::BindGroup, light_skin_bind_group: &'a wgpu::BindGroup, ) { - self.draw_model_instanced(model, 0..1, globals_camera_bind_group, light_skin_bind_group); + self.draw_model_instanced( + model, + 0..1, + globals_camera_bind_group, + light_skin_bind_group, + ); } fn draw_model_instanced( @@ -1430,10 +1492,10 @@ pub trait Vertex { pub struct ModelVertex { pub position: [f32; 3], pub normal: [f32; 3], - pub tangent: [f32; 4], // xyz + handedness (w) + pub tangent: [f32; 4], // xyz + handedness (w) pub tex_coords0: [f32; 2], - pub tex_coords1: [f32; 2], // optional, can be zeroed if missing - pub colour0: [f32; 4], // optional, default to white + pub tex_coords1: [f32; 2], // optional, can be zeroed if missing + pub colour0: [f32; 4], // optional, default to white pub joints0: [u16; 4], pub weights0: [f32; 4], } @@ -1516,14 +1578,28 @@ impl Eq for ModelVertex {} impl Hash for ModelVertex { fn hash<H: Hasher>(&self, state: &mut H) { - for v in &self.position { v.to_bits().hash(state); } - for v in &self.normal { v.to_bits().hash(state); } - for v in &self.tangent { v.to_bits().hash(state); } - for v in &self.tex_coords0{ v.to_bits().hash(state); } - for v in &self.tex_coords1{ v.to_bits().hash(state); } - for v in &self.colour0 { v.to_bits().hash(state); } + for v in &self.position { + v.to_bits().hash(state); + } + for v in &self.normal { + v.to_bits().hash(state); + } + for v in &self.tangent { + v.to_bits().hash(state); + } + for v in &self.tex_coords0 { + v.to_bits().hash(state); + } + for v in &self.tex_coords1 { + v.to_bits().hash(state); + } + for v in &self.colour0 { + v.to_bits().hash(state); + } self.joints0.hash(state); - for v in &self.weights0 { v.to_bits().hash(state); } + for v in &self.weights0 { + v.to_bits().hash(state); + } } } @@ -1544,4 +1620,4 @@ pub struct MaterialUniform { pub has_metallic_texture: u32, pub has_occlusion_texture: u32, pub pad: u32, -} +} @@ -39,14 +39,16 @@ impl GlobalsUniform { let buffer = UniformBuffer::new(&graphics.device, label); - let bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &graphics.layouts.shader_globals_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: buffer.buffer().as_entire_binding(), - }], - label: Some(label), - }); + let bind_group = graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + layout: &graphics.layouts.shader_globals_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: buffer.buffer().as_entire_binding(), + }], + label: Some(label), + }); let data = Globals::default(); buffer.write(&graphics.queue, &data); @@ -1,6 +1,6 @@ -use wgpu::Operations; use crate::pipelines::create_render_pipeline; use crate::texture::Texture; +use wgpu::Operations; pub struct HdrPipeline { pipeline: wgpu::RenderPipeline, @@ -165,4 +165,4 @@ impl HdrPipeline { pass.set_bind_group(0, &self.bind_group, &[]); pass.draw(0..3, 0..1); } -} +} @@ -1,9 +1,4 @@ -use std::sync::Arc; -use std::mem::size_of; -use glam::DMat4; -use slank::include_slang; -use wgpu::{BufferAddress, CompareFunction, DepthBiasState, StencilState}; -use crate::buffer::{StorageBuffer}; +use crate::buffer::StorageBuffer; use crate::entity::{EntityTransform, Transform}; use crate::graphics::SharedGraphicsContext; use crate::lighting::{Light, LightArrayUniform, MAX_LIGHTS}; @@ -11,6 +6,11 @@ use crate::model::{ModelVertex, Vertex}; use crate::pipelines::DropbearShaderPipeline; use crate::shader::Shader; use crate::texture::Texture; +use glam::DMat4; +use slank::include_slang; +use std::mem::size_of; +use std::sync::Arc; +use wgpu::{BufferAddress, CompareFunction, DepthBiasState, StencilState}; pub struct LightCubePipeline { shader: Shader, @@ -23,85 +23,93 @@ pub struct LightCubePipeline { impl DropbearShaderPipeline for LightCubePipeline { fn new(graphics: Arc<SharedGraphicsContext>) -> Self { - let shader = Shader::from_slang(graphics.clone(), &slank::CompiledSlangShader::from_bytes("light cube", include_slang!("light_cube"))); + let shader = Shader::from_slang( + graphics.clone(), + &slank::CompiledSlangShader::from_bytes("light cube", include_slang!("light_cube")), + ); - let pipeline_layout = graphics.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("light cube pipeline layout"), - bind_group_layouts: &[ - &graphics.layouts.camera_bind_group_layout, - &graphics.layouts.light_cube_bind_group_layout, - ], - push_constant_ranges: &[], - }); + let pipeline_layout = + graphics + .device + .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("light cube pipeline layout"), + bind_group_layouts: &[ + &graphics.layouts.camera_bind_group_layout, + &graphics.layouts.light_cube_bind_group_layout, + ], + push_constant_ranges: &[], + }); let hdr_format = graphics.hdr.read().format(); - let pipeline = graphics.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("light cube pipeline"), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: Some("vs_main"), - compilation_options: Default::default(), - buffers: &[ - // model - LightCubeVertex::desc(), - // instance - InstanceInput::desc(), - ], - }, - fragment: Some(wgpu::FragmentState { - module: &shader.module, - entry_point: Some("fs_main"), - targets: &[Some(wgpu::ColorTargetState { - format: hdr_format, - blend: Some(wgpu::BlendState { - alpha: wgpu::BlendComponent::REPLACE, - color: wgpu::BlendComponent::REPLACE, - }), - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: Default::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Cw, - cull_mode: Some(wgpu::Face::Back), - polygon_mode: wgpu::PolygonMode::Fill, - unclipped_depth: false, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: Texture::DEPTH_FORMAT, - depth_write_enabled: true, - depth_compare: CompareFunction::Greater, - stencil: StencilState::default(), - bias: DepthBiasState::default(), - }), - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - cache: None, - }); + let pipeline = graphics + .device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("light cube pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: Some("vs_main"), + compilation_options: Default::default(), + buffers: &[ + // model + LightCubeVertex::desc(), + // instance + InstanceInput::desc(), + ], + }, + fragment: Some(wgpu::FragmentState { + module: &shader.module, + entry_point: Some("fs_main"), + targets: &[Some(wgpu::ColorTargetState { + format: hdr_format, + blend: Some(wgpu::BlendState { + alpha: wgpu::BlendComponent::REPLACE, + color: wgpu::BlendComponent::REPLACE, + }), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Cw, + cull_mode: Some(wgpu::Face::Back), + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: Texture::DEPTH_FORMAT, + depth_write_enabled: true, + depth_compare: CompareFunction::Greater, + stencil: StencilState::default(), + bias: DepthBiasState::default(), + }), + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + cache: None, + }); - let storage_buffer = StorageBuffer::new( - &graphics.device, - "light cube pipeline storage buffer", - ); + let storage_buffer = + StorageBuffer::new(&graphics.device, "light cube pipeline storage buffer"); let light_buffer: &wgpu::Buffer = storage_buffer.buffer(); - let light_bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &graphics.layouts.light_array_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: light_buffer.as_entire_binding(), - }], - label: Some("light array bind group"), - }); + let light_bind_group = graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + layout: &graphics.layouts.light_array_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: light_buffer.as_entire_binding(), + }], + label: Some("light array bind group"), + }); Self { shader, @@ -157,7 +165,9 @@ impl LightCubePipeline { panic!("No Transform or EntityTransform available for this light cube"); }; - light.instance_buffer.write(&graphics.device, &graphics.queue, &[instance]); + light + .instance_buffer + .write(&graphics.device, &graphics.queue, &[instance]); if light.component.enabled && light_index < MAX_LIGHTS { let uniform = *light.uniform(); @@ -194,13 +204,11 @@ impl LightCubeVertex { wgpu::VertexBufferLayout { array_stride: size_of::<ModelVertex>() as BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[ - wgpu::VertexAttribute { - offset: 0, - shader_location: 0, - format: wgpu::VertexFormat::Float32x3, - }, - ], + attributes: &[wgpu::VertexAttribute { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float32x3, + }], } } } @@ -1,19 +1,19 @@ -use std::sync::Arc; use crate::graphics::SharedGraphicsContext; use crate::shader::Shader; +use std::sync::Arc; -pub mod shader; -pub mod light_cube; pub mod globals; pub mod hdr; +pub mod light_cube; +pub mod shader; pub use globals::{Globals, GlobalsUniform}; -/// A helper in defining a pipelines required information, as well as getters. -/// -/// This contains the bare minimum for any pipeline. +/// A helper in defining a pipelines required information, as well as getters. +/// +/// This contains the bare minimum for any pipeline. pub trait DropbearShaderPipeline { - /// Creates a new instance of a pipeline. + /// Creates a new instance of a pipeline. fn new(graphics: Arc<SharedGraphicsContext>) -> Self; /// Fetches the shader property fn shader(&self) -> &Shader; @@ -1,11 +1,11 @@ -use std::sync::Arc; -use wgpu::{CompareFunction, DepthBiasState, StencilState}; use crate::graphics::{InstanceRaw, SharedGraphicsContext}; use crate::model; use crate::model::Vertex; use crate::pipelines::DropbearShaderPipeline; use crate::shader::Shader; use crate::texture::Texture; +use std::sync::Arc; +use wgpu::{CompareFunction, DepthBiasState, StencilState}; /// As defined in `shaders/shader.wgsl` pub struct MainRenderPipeline { @@ -24,12 +24,13 @@ impl DropbearShaderPipeline for MainRenderPipeline { let bind_group_layouts = vec![ &graphics.layouts.scene_globals_bind_group_layout, // @group(0) - &graphics.layouts.material_bind_layout, // @group(1) + &graphics.layouts.material_bind_layout, // @group(1) &graphics.layouts.scene_light_skin_bind_group_layout, // @group(2) ]; let pipeline_layout = - graphics.device + graphics + .device .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("main render pipeline layout"), bind_group_layouts: bind_group_layouts.as_slice(), @@ -37,51 +38,51 @@ impl DropbearShaderPipeline for MainRenderPipeline { }); let hdr_format = graphics.hdr.read().format(); - let pipeline = - graphics.device - .create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("main render pipeline"), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: &shader.module, - entry_point: Some("vs_main"), - buffers: &[model::ModelVertex::desc(), InstanceRaw::desc()], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &shader.module, - entry_point: Some("s_fs_main"), - targets: &[Some(wgpu::ColorTargetState { - format: hdr_format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Cw, - cull_mode: Some(wgpu::Face::Back), - polygon_mode: wgpu::PolygonMode::Fill, - unclipped_depth: false, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: Texture::DEPTH_FORMAT, - depth_write_enabled: true, - depth_compare: CompareFunction::Greater, - stencil: StencilState::default(), - bias: DepthBiasState::default(), - }), - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - cache: None, - }); + let pipeline = graphics + .device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("main render pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader.module, + entry_point: Some("vs_main"), + buffers: &[model::ModelVertex::desc(), InstanceRaw::desc()], + compilation_options: wgpu::PipelineCompilationOptions::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &shader.module, + entry_point: Some("s_fs_main"), + targets: &[Some(wgpu::ColorTargetState { + format: hdr_format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: wgpu::PipelineCompilationOptions::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Cw, + cull_mode: Some(wgpu::Face::Back), + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: Texture::DEPTH_FORMAT, + depth_write_enabled: true, + depth_compare: CompareFunction::Greater, + stencil: StencilState::default(), + bias: DepthBiasState::default(), + }), + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + cache: None, + }); log::debug!("Created main render pipeline"); @@ -12,8 +12,8 @@ impl ProcedurallyGeneratedObject { let uv_y = 1.0_f32; let uv_z = 1.0_f32; - let make_vertex = |position: [f32; 3], normal: [f32; 3], tangent: [f32; 3], uv: [f32; 2]| { - ModelVertex { + let make_vertex = + |position: [f32; 3], normal: [f32; 3], tangent: [f32; 3], uv: [f32; 2]| ModelVertex { position, normal, tangent: [tangent[0], tangent[1], tangent[2], 1.0], @@ -22,56 +22,174 @@ impl ProcedurallyGeneratedObject { colour0: [1.0, 1.0, 1.0, 1.0], joints0: [0, 0, 0, 0], weights0: [1.0, 0.0, 0.0, 0.0], - } - }; + }; let vertices = vec![ // Front Face (Normal: 0, 0, 1) - make_vertex([-half.x, -half.y, half.z], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0], [0.0, uv_y]), - make_vertex([ half.x, -half.y, half.z], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0], [uv_x, uv_y]), - make_vertex([ half.x, half.y, half.z], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0], [uv_x, 0.0]), - make_vertex([-half.x, half.y, half.z], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0], [0.0, 0.0]), - + make_vertex( + [-half.x, -half.y, half.z], + [0.0, 0.0, 1.0], + [1.0, 0.0, 0.0], + [0.0, uv_y], + ), + make_vertex( + [half.x, -half.y, half.z], + [0.0, 0.0, 1.0], + [1.0, 0.0, 0.0], + [uv_x, uv_y], + ), + make_vertex( + [half.x, half.y, half.z], + [0.0, 0.0, 1.0], + [1.0, 0.0, 0.0], + [uv_x, 0.0], + ), + make_vertex( + [-half.x, half.y, half.z], + [0.0, 0.0, 1.0], + [1.0, 0.0, 0.0], + [0.0, 0.0], + ), // Back Face (Normal: 0, 0, -1) - make_vertex([ half.x, -half.y, -half.z], [0.0, 0.0, -1.0], [-1.0, 0.0, 0.0], [0.0, uv_y]), - make_vertex([-half.x, -half.y, -half.z], [0.0, 0.0, -1.0], [-1.0, 0.0, 0.0], [uv_x, uv_y]), - make_vertex([-half.x, half.y, -half.z], [0.0, 0.0, -1.0], [-1.0, 0.0, 0.0], [uv_x, 0.0]), - make_vertex([ half.x, half.y, -half.z], [0.0, 0.0, -1.0], [-1.0, 0.0, 0.0], [0.0, 0.0]), - + make_vertex( + [half.x, -half.y, -half.z], + [0.0, 0.0, -1.0], + [-1.0, 0.0, 0.0], + [0.0, uv_y], + ), + make_vertex( + [-half.x, -half.y, -half.z], + [0.0, 0.0, -1.0], + [-1.0, 0.0, 0.0], + [uv_x, uv_y], + ), + make_vertex( + [-half.x, half.y, -half.z], + [0.0, 0.0, -1.0], + [-1.0, 0.0, 0.0], + [uv_x, 0.0], + ), + make_vertex( + [half.x, half.y, -half.z], + [0.0, 0.0, -1.0], + [-1.0, 0.0, 0.0], + [0.0, 0.0], + ), // Top Face (Normal: 0, 1, 0) - make_vertex([-half.x, half.y, half.z], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0], [0.0, uv_z]), - make_vertex([ half.x, half.y, half.z], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0], [uv_x, uv_z]), - make_vertex([ half.x, half.y, -half.z], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0], [uv_x, 0.0]), - make_vertex([-half.x, half.y, -half.z], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0]), - + make_vertex( + [-half.x, half.y, half.z], + [0.0, 1.0, 0.0], + [1.0, 0.0, 0.0], + [0.0, uv_z], + ), + make_vertex( + [half.x, half.y, half.z], + [0.0, 1.0, 0.0], + [1.0, 0.0, 0.0], + [uv_x, uv_z], + ), + make_vertex( + [half.x, half.y, -half.z], + [0.0, 1.0, 0.0], + [1.0, 0.0, 0.0], + [uv_x, 0.0], + ), + make_vertex( + [-half.x, half.y, -half.z], + [0.0, 1.0, 0.0], + [1.0, 0.0, 0.0], + [0.0, 0.0], + ), // Bottom Face (Normal: 0, -1, 0) - make_vertex([-half.x, -half.y, -half.z], [0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [0.0, uv_z]), - make_vertex([ half.x, -half.y, -half.z], [0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [uv_x, uv_z]), - make_vertex([ half.x, -half.y, half.z], [0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [uv_x, 0.0]), - make_vertex([-half.x, -half.y, half.z], [0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0]), - + make_vertex( + [-half.x, -half.y, -half.z], + [0.0, -1.0, 0.0], + [1.0, 0.0, 0.0], + [0.0, uv_z], + ), + make_vertex( + [half.x, -half.y, -half.z], + [0.0, -1.0, 0.0], + [1.0, 0.0, 0.0], + [uv_x, uv_z], + ), + make_vertex( + [half.x, -half.y, half.z], + [0.0, -1.0, 0.0], + [1.0, 0.0, 0.0], + [uv_x, 0.0], + ), + make_vertex( + [-half.x, -half.y, half.z], + [0.0, -1.0, 0.0], + [1.0, 0.0, 0.0], + [0.0, 0.0], + ), // Right Face (Normal: 1, 0, 0) - make_vertex([ half.x, -half.y, half.z], [1.0, 0.0, 0.0], [0.0, 0.0, -1.0], [0.0, uv_y]), - make_vertex([ half.x, -half.y, -half.z], [1.0, 0.0, 0.0], [0.0, 0.0, -1.0], [uv_z, uv_y]), - make_vertex([ half.x, half.y, -half.z], [1.0, 0.0, 0.0], [0.0, 0.0, -1.0], [uv_z, 0.0]), - make_vertex([ half.x, half.y, half.z], [1.0, 0.0, 0.0], [0.0, 0.0, -1.0], [0.0, 0.0]), - + make_vertex( + [half.x, -half.y, half.z], + [1.0, 0.0, 0.0], + [0.0, 0.0, -1.0], + [0.0, uv_y], + ), + make_vertex( + [half.x, -half.y, -half.z], + [1.0, 0.0, 0.0], + [0.0, 0.0, -1.0], + [uv_z, uv_y], + ), + make_vertex( + [half.x, half.y, -half.z], + [1.0, 0.0, 0.0], + [0.0, 0.0, -1.0], + [uv_z, 0.0], + ), + make_vertex( + [half.x, half.y, half.z], + [1.0, 0.0, 0.0], + [0.0, 0.0, -1.0], + [0.0, 0.0], + ), // Left Face (Normal: -1, 0, 0) - make_vertex([-half.x, -half.y, -half.z], [-1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, uv_y]), - make_vertex([-half.x, -half.y, half.z], [-1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [uv_z, uv_y]), - make_vertex([-half.x, half.y, half.z], [-1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [uv_z, 0.0]), - make_vertex([-half.x, half.y, -half.z], [-1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, 0.0]), + make_vertex( + [-half.x, -half.y, -half.z], + [-1.0, 0.0, 0.0], + [0.0, 0.0, 1.0], + [0.0, uv_y], + ), + make_vertex( + [-half.x, -half.y, half.z], + [-1.0, 0.0, 0.0], + [0.0, 0.0, 1.0], + [uv_z, uv_y], + ), + make_vertex( + [-half.x, half.y, half.z], + [-1.0, 0.0, 0.0], + [0.0, 0.0, 1.0], + [uv_z, 0.0], + ), + make_vertex( + [-half.x, half.y, -half.z], + [-1.0, 0.0, 0.0], + [0.0, 0.0, 1.0], + [0.0, 0.0], + ), ]; let indices: Vec<u32> = vec![ - 0, 1, 2, 2, 3, 0, // front - 4, 5, 6, 6, 7, 4, // back - 8, 9, 10, 10, 11, 8, // top + 0, 1, 2, 2, 3, 0, // front + 4, 5, 6, 6, 7, 4, // back + 8, 9, 10, 10, 11, 8, // top 12, 13, 14, 14, 15, 12, // bottom 16, 17, 18, 18, 19, 16, // right 20, 21, 22, 22, 23, 20, // left ]; - Self { vertices, indices, ty: crate::procedural::ProcObjType::Cuboid } + Self { + vertices, + indices, + ty: crate::procedural::ProcObjType::Cuboid, + } } -} +} @@ -3,29 +3,25 @@ use crate::asset::{AssetRegistry, Handle}; use crate::graphics::SharedGraphicsContext; +use crate::model::ModelVertex; use crate::model::{Material, Mesh, Model}; use crate::texture::Texture; use crate::utils::ResourceReference; -use crate::model::ModelVertex; -use std::hash::{DefaultHasher, Hasher}; -use std::sync::Arc; use parking_lot::RwLock; use serde::{Deserialize, Serialize}; +use std::hash::{DefaultHasher, Hasher}; +use std::sync::Arc; use wgpu::util::DeviceExt; pub mod cube; -#[derive( - Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize -)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum ProcObjType { Cuboid, } -/// An object that comes with a template, and is generated through parameter input. -#[derive( - Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize -)] +/// An object that comes with a template, and is generated through parameter input. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ProcedurallyGeneratedObject { pub vertices: Vec<ModelVertex>, pub indices: Vec<u32>, @@ -33,7 +29,7 @@ pub struct ProcedurallyGeneratedObject { } impl ProcedurallyGeneratedObject { - /// Constructs a [`Model`] and returns the model itself instead of adding to the registry. + /// Constructs a [`Model`] and returns the model itself instead of adding to the registry. pub fn construct( &self, graphics: Arc<SharedGraphicsContext>, @@ -133,7 +129,9 @@ impl ProcedurallyGeneratedObject { let model = Model { label: label.clone(), hash, - path: ResourceReference::from_reference(crate::utils::ResourceReferenceType::ProcObj(self.clone())), + path: ResourceReference::from_reference(crate::utils::ResourceReferenceType::ProcObj( + self.clone(), + )), meshes: vec![mesh], materials: vec![material], skins: Vec::new(), @@ -173,11 +171,18 @@ impl ProcedurallyGeneratedObject { } } - let model = Self::construct(&self, graphics, material, label, Some(hash), registry.clone()); + let model = Self::construct( + &self, + graphics, + material, + label, + Some(hash), + registry.clone(), + ); { let mut _rguard = registry.write(); _rguard.add_model_with_label(label_str, model) } } -} +} @@ -2,11 +2,11 @@ // logically, it wouldn't be possible to deadlock #![allow(clippy::await_holding_lock)] +use winit::event::WindowEvent; use winit::event_loop::ActiveEventLoop; use winit::window::WindowId; -use winit::event::WindowEvent; -use crate::{WindowData, graphics::{SharedGraphicsContext}, input}; +use crate::{WindowData, graphics::SharedGraphicsContext, input}; use parking_lot::RwLock; use std::{collections::HashMap, rc::Rc, sync::Arc}; @@ -29,7 +29,7 @@ pub trait Scene { #[derive(Clone)] pub enum SceneCommand { None, - Quit(Option<fn ()>), + Quit(Option<fn()>), SwitchScene(String), DebugMessage(String), RequestWindow(WindowData), @@ -147,7 +147,10 @@ impl Manager { } SceneCommand::None => {} SceneCommand::DebugMessage(msg) => log::debug!("{}", msg), - SceneCommand::RequestWindow(_) | SceneCommand::CloseWindow(_) | SceneCommand::SetFPS(_) | SceneCommand::ResizeViewport(_) => { + SceneCommand::RequestWindow(_) + | SceneCommand::CloseWindow(_) + | SceneCommand::SetFPS(_) + | SceneCommand::ResizeViewport(_) => { return vec![command]; } } @@ -156,11 +159,7 @@ impl Manager { Vec::new() } - pub fn physics_update( - &mut self, - dt: f32, - graphics: Arc<SharedGraphicsContext>, - ) { + pub fn physics_update(&mut self, dt: f32, graphics: Arc<SharedGraphicsContext>) { puffin::profile_function!(); if let Some(scene_name) = &self.current_scene && let Some(scene) = self.scenes.get_mut(scene_name) @@ -1,26 +1,26 @@ -//! Deals with shaders, primarily around the [Shader] struct. +//! Deals with shaders, primarily around the [Shader] struct. -use std::ops::Deref; use crate::graphics::SharedGraphicsContext; -use std::sync::Arc; use slank::{CompiledSlangShader, utils::WgpuUtils}; +use std::ops::Deref; +use std::sync::Arc; use wgpu::ShaderModule; /// A nice little struct that stored basic information about a WGPU shaders. pub struct Shader { - /// The label of the shader. - /// - /// If it is not set in [Shader::new], the default is "shader". + /// The label of the shader. + /// + /// If it is not set in [Shader::new], the default is "shader". pub label: String, /// The compiled content of the WGSL shader. - /// - /// When [Shader] is dereferenced (such as that in `&shader`), it will automatically reference + /// + /// When [Shader] is dereferenced (such as that in `&shader`), it will automatically reference /// this module. pub module: ShaderModule, /// The content of the shader as a readable string content, in the case you need to look - /// at the original source. + /// at the original source. pub content: String, } @@ -67,7 +67,10 @@ impl Shader { .device .create_shader_module(shader.create_wgpu_shader()); - log::debug!("Created new shaders [slang] under the label: {}", shader.label()); + log::debug!( + "Created new shaders [slang] under the label: {}", + shader.label() + ); Self { label: shader.label().clone(), @@ -1,11 +1,12 @@ -use std::io::Cursor; -use std::sync::Arc; +use crate::graphics::SharedGraphicsContext; +use crate::pipelines::create_render_pipeline_ex; use crate::texture::Texture; use image::codecs::hdr::HdrDecoder; -use crate::graphics::SharedGraphicsContext; -use crate::pipelines::{create_render_pipeline_ex}; +use std::io::Cursor; +use std::sync::Arc; -pub const DEFAULT_SKY_TEXTURE: &[u8] = include_bytes!("../../../resources/textures/kloofendal_48d_partly_cloudy_puresky_4k.hdr"); +pub const DEFAULT_SKY_TEXTURE: &[u8] = + include_bytes!("../../../resources/textures/kloofendal_48d_partly_cloudy_puresky_4k.hdr"); pub struct CubeTexture { texture: wgpu::Texture, @@ -66,12 +67,17 @@ impl CubeTexture { } } - pub fn texture(&self) -> &wgpu::Texture { &self.texture } - - pub fn view(&self) -> &wgpu::TextureView { &self.view } + pub fn texture(&self) -> &wgpu::Texture { + &self.texture + } - pub fn sampler(&self) -> &wgpu::Sampler { &self.sampler } + pub fn view(&self) -> &wgpu::TextureView { + &self.view + } + pub fn sampler(&self) -> &wgpu::Sampler { + &self.sampler + } } pub struct HdrLoader { @@ -83,7 +89,8 @@ pub struct HdrLoader { impl HdrLoader { pub fn new(device: &wgpu::Device) -> Self { puffin::profile_function!(); - let module = device.create_shader_module(wgpu::include_wgsl!("shaders/equirectangular.wgsl")); + let module = + device.create_shader_module(wgpu::include_wgsl!("shaders/equirectangular.wgsl")); let texture_format = wgpu::TextureFormat::Rgba32Float; let equirect_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: Some("HdrLoader::equirect_layout"), @@ -147,7 +154,7 @@ impl HdrLoader { let hdr_decoder = HdrDecoder::new(Cursor::new(data))?; let meta = hdr_decoder.metadata(); - #[cfg(not(target_arch="wasm32"))] + #[cfg(not(target_arch = "wasm32"))] let pixels = { let mut pixels = vec![[0.0, 0.0, 0.0, 0.0]; meta.width as usize * meta.height as usize]; hdr_decoder.read_image_transform( @@ -159,8 +166,9 @@ impl HdrLoader { )?; pixels }; - #[cfg(target_arch="wasm32")] - let pixels = hdr_decoder.read_image_native()? + #[cfg(target_arch = "wasm32")] + let pixels = hdr_decoder + .read_image_native()? .into_iter() .map(|pix| { let rgb = pix.to_hdr(); @@ -202,8 +210,7 @@ impl HdrLoader { 1, // We are going to write to `dst` texture so we // need to use a `STORAGE_BINDING`. - wgpu::TextureUsages::STORAGE_BINDING - | wgpu::TextureUsages::TEXTURE_BINDING, + wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING, wgpu::FilterMode::Nearest, label, ); @@ -235,7 +242,10 @@ impl HdrLoader { }); let mut encoder = device.create_command_encoder(&Default::default()); - let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { label, timestamp_writes: None }); + let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { + label, + timestamp_writes: None, + }); let num_workgroups = (dst_size + 15) / 16; pass.set_pipeline(&loader.equirect_to_cubemap); @@ -259,27 +269,35 @@ pub struct SkyPipeline { impl SkyPipeline { pub fn new(graphics: Arc<SharedGraphicsContext>, sky_texture: CubeTexture) -> Self { puffin::profile_function!(); - let environment_bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("environment_bind_group"), - layout: &graphics.layouts.environment_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&sky_texture.view()), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(sky_texture.sampler()), - }, - ], - }); + let environment_bind_group = + graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("environment_bind_group"), + layout: &graphics.layouts.environment_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&sky_texture.view()), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(sky_texture.sampler()), + }, + ], + }); let sky_pipeline = { - let layout = graphics.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Sky Pipeline Layout"), - bind_group_layouts: &[&graphics.layouts.camera_bind_group_layout, &graphics.layouts.environment_bind_group_layout], - push_constant_ranges: &[], - }); + let layout = graphics + .device + .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Sky Pipeline Layout"), + bind_group_layouts: &[ + &graphics.layouts.camera_bind_group_layout, + &graphics.layouts.environment_bind_group_layout, + ], + push_constant_ranges: &[], + }); let shader = wgpu::include_wgsl!("shaders/sky.wgsl"); create_render_pipeline_ex( Some("sky render pipeline"), @@ -294,11 +312,11 @@ impl SkyPipeline { wgpu::CompareFunction::GreaterEqual, ) }; - + Self { texture: sky_texture, pipeline: sky_pipeline, bind_group: environment_bind_group, } } -} +} @@ -1,10 +1,10 @@ use std::{fs, path::PathBuf, sync::Arc}; -use image::GenericImageView; -use serde::{Deserialize, Serialize}; use crate::asset::AssetRegistry; use crate::graphics::SharedGraphicsContext; use crate::utils::{ResourceReference, ToPotentialString}; +use image::GenericImageView; +use serde::{Deserialize, Serialize}; /// As defined in `shaders.wgsl` as /// ``` @@ -17,7 +17,7 @@ use crate::utils::{ResourceReference, ToPotentialString}; /// @group(0) @binding(3) /// var s_normal: sampler; /// ``` -pub const TEXTURE_BIND_GROUP_LAYOUT: wgpu::BindGroupLayoutDescriptor<'_> = +pub const TEXTURE_BIND_GROUP_LAYOUT: wgpu::BindGroupLayoutDescriptor<'_> = wgpu::BindGroupLayoutDescriptor { entries: &[ // t_diffuse @@ -69,7 +69,7 @@ pub struct Texture { pub size: wgpu::Extent3d, pub view: wgpu::TextureView, pub hash: Option<u64>, - pub reference: Option<ResourceReference> + pub reference: Option<ResourceReference>, } impl Texture { @@ -200,7 +200,7 @@ impl Texture { /// Creates a viewport texture. /// - /// This is an internal function. + /// This is an internal function. pub fn viewport( config: &wgpu::SurfaceConfiguration, device: &wgpu::Device, @@ -239,7 +239,7 @@ impl Texture { } } - /// Loads the texture from a file. + /// Loads the texture from a file. pub async fn from_file( graphics: Arc<SharedGraphicsContext>, path: &PathBuf, @@ -253,9 +253,13 @@ impl Texture { } /// Loads the texture from bytes. - /// + /// /// If you want more customisability in the texture being generated, you can use [Self::from_bytes_verbose] - pub fn from_bytes(graphics: Arc<SharedGraphicsContext>, bytes: &[u8], label: Option<&str>) -> Self { + pub fn from_bytes( + graphics: Arc<SharedGraphicsContext>, + bytes: &[u8], + label: Option<&str>, + ) -> Self { puffin::profile_function!(label.unwrap_or("")); Self::from_bytes_verbose_mipmapped(graphics, bytes, None, None, None, label) } @@ -282,9 +286,10 @@ impl Texture { label, ); - if let Err(err) = graphics - .mipmapper - .compute_mipmaps(&graphics.device, &graphics.queue, &texture) + if let Err(err) = + graphics + .mipmapper + .compute_mipmaps(&graphics.device, &graphics.queue, &texture) { log_once::warn_once!("Failed to generate mipmaps: {}", err); } @@ -313,9 +318,10 @@ impl Texture { label, ); - if let Err(err) = graphics - .mipmapper - .compute_mipmaps(&graphics.device, &graphics.queue, &texture) + if let Err(err) = + graphics + .mipmapper + .compute_mipmaps(&graphics.device, &graphics.queue, &texture) { log_once::warn_once!("Failed to generate mipmaps: {}", err); } @@ -323,8 +329,8 @@ impl Texture { texture } - /// Loads the texture from bytes, with options for more arguments. - /// + /// Loads the texture from bytes, with options for more arguments. + /// /// Requires more arguments. For a simpler usage, you should use [Self::from_bytes] pub fn from_bytes_verbose( graphics: Arc<SharedGraphicsContext>, @@ -361,7 +367,7 @@ impl Texture { } let hash = AssetRegistry::hash_bytes(bytes); - + let (diffuse_rgba, dimensions) = { puffin::profile_scope!("load from memory image"); match image::load_from_memory(bytes) { @@ -379,22 +385,22 @@ impl Texture { (bytes.to_vec(), dims) } else { log::error!( - "Texture [{:?}] decode failed ({:?}); expected {} bytes for raw RGBA ({}x{}), got {}. Falling back.", - label, - err, - expected_len, - dims.0, - dims.1, - bytes.len() - ); + "Texture [{:?}] decode failed ({:?}); expected {} bytes for raw RGBA ({}x{}), got {}. Falling back.", + label, + err, + expected_len, + dims.0, + dims.1, + bytes.len() + ); (vec![255, 0, 255, 255], (1, 1)) } } else { log::error!( - "Texture [{:?}] decode failed ({:?}) and no dimensions were provided; falling back to 1x1 magenta.", - label, - err - ); + "Texture [{:?}] decode failed ({:?}) and no dimensions were provided; falling back to 1x1 magenta.", + label, + err + ); (vec![255, 0, 255, 255], (1, 1)) } } @@ -426,7 +432,9 @@ impl Texture { }); let unpadded_bytes_per_row = 4 * size.width; - let padded_bytes_per_row = (unpadded_bytes_per_row + wgpu::COPY_BYTES_PER_ROW_ALIGNMENT - 1) & !(wgpu::COPY_BYTES_PER_ROW_ALIGNMENT - 1); + let padded_bytes_per_row = (unpadded_bytes_per_row + wgpu::COPY_BYTES_PER_ROW_ALIGNMENT + - 1) + & !(wgpu::COPY_BYTES_PER_ROW_ALIGNMENT - 1); debug_assert!(diffuse_rgba.len() >= (unpadded_bytes_per_row * size.height) as usize); if padded_bytes_per_row == unpadded_bytes_per_row { @@ -517,9 +525,7 @@ impl Texture { } } -#[derive( - Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize -)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum TextureWrapMode { Repeat, Clamp, @@ -544,11 +550,12 @@ pub struct DropbearEngineLogo; impl DropbearEngineLogo { /// Note: image size is 256x256 - pub const DROPBEAR_ENGINE_LOGO: &[u8] = include_bytes!("../../../resources/eucalyptus-editor.png"); + pub const DROPBEAR_ENGINE_LOGO: &[u8] = + include_bytes!("../../../resources/eucalyptus-editor.png"); - /// Generates the dropbear engine logo in a form that [winit::window::Icon] can accept. - /// - /// Returns (the bytes, width, height) in resp order. + /// Generates the dropbear engine logo in a form that [winit::window::Icon] can accept. + /// + /// Returns (the bytes, width, height) in resp order. pub fn generate() -> anyhow::Result<(Vec<u8>, u32, u32)> { puffin::profile_function!("generate dropbear engine logo"); let image = image::load_from_memory(Self::DROPBEAR_ENGINE_LOGO)?.into_rgba8(); @@ -556,4 +563,4 @@ impl DropbearEngineLogo { let rgba = image.into_raw(); Ok((rgba, width, height)) } -} +} @@ -1,9 +1,9 @@ //! Utilities and helper functions for the dropbear renderer. +use crate::procedural::ProcedurallyGeneratedObject; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; use std::path::Path; -use crate::procedural::{ProcedurallyGeneratedObject}; pub const EUCA_SCHEME: &str = "euca://"; @@ -86,9 +86,7 @@ pub fn relative_path_from_euca<'a>(uri: &'a str) -> anyhow::Result<&'a str> { /// ); /// assert_eq!(resource_ref.as_path().unwrap(), "models/cube.obj"); /// ``` -#[derive( - Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize -)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum ResourceReferenceType { /// The default type; Specifies there being no resource reference type. /// Typically creates errors, so watch out! @@ -100,9 +98,9 @@ pub enum ResourceReferenceType { /// The content in bytes. Sometimes, there is a model that is loaded into memory through the /// [`include_bytes!`] macro, this type stores it. Bytes(Vec<u8>), - + /// An object that can be generated at runtime with the usage of vertices and indices, as well - /// as a solid grey mesh. + /// as a solid grey mesh. ProcObj(ProcedurallyGeneratedObject), } @@ -256,19 +254,20 @@ macro_rules! resource { }; } -/// Helper trait for converting `Option<T: ToString>` to [`Option<String>`] without looking into its contents. +/// Helper trait for converting `Option<T: ToString>` to [`Option<String>`] without looking into its contents. pub trait ToPotentialString { /// Converts an [`Option<T>`], where [`T`] can be converted to a [`String`], into an [`Option<String>`]. fn to_potential_string(&self) -> Option<String>; } -impl<T> ToPotentialString for Option<T> -where T: ToString +impl<T> ToPotentialString for Option<T> +where + T: ToString, { fn to_potential_string(&self) -> Option<String> { match self { None => None, - Some(v) => Some(v.to_string()) + Some(v) => Some(v.to_string()), } } -} +} @@ -1,13 +1,13 @@ use proc_macro::TokenStream; use quote::quote; +use std::path::Path; use syn::{ + FnArg, GenericArgument, Ident, ItemEnum, ItemFn, LitStr, PathArguments, ReturnType, Token, + Type, parse::{Parse, ParseStream}, parse_macro_input, parse_quote, spanned::Spanned, - FnArg, GenericArgument, Ident, ItemFn, ItemEnum, LitStr, PathArguments, - ReturnType, Token, Type, }; -use std::path::Path; struct ExportArgs { c: Option<CArgs>, @@ -33,7 +33,10 @@ enum ExportItem { impl Parse for ExportArgs { fn parse(input: ParseStream) -> syn::Result<Self> { let items = syn::punctuated::Punctuated::<ExportItem, Token![,]>::parse_terminated(input)?; - let mut args = ExportArgs { c: None, kotlin: None }; + let mut args = ExportArgs { + c: None, + kotlin: None, + }; for item in items { match item { @@ -98,9 +101,15 @@ impl Parse for ExportItem { } } - let class = class.ok_or_else(|| syn::Error::new(ident.span(), "kotlin(class = ...) is required"))?; - let func = func.ok_or_else(|| syn::Error::new(ident.span(), "kotlin(func = ...) is required"))?; - return Ok(ExportItem::Kotlin(KotlinArgs { class, func, jni_path })); + let class = class + .ok_or_else(|| syn::Error::new(ident.span(), "kotlin(class = ...) is required"))?; + let func = func + .ok_or_else(|| syn::Error::new(ident.span(), "kotlin(func = ...) is required"))?; + return Ok(ExportItem::Kotlin(KotlinArgs { + class, + func, + jni_path, + })); } Err(syn::Error::new(ident.span(), "Expected c or kotlin(...)")) @@ -132,7 +141,10 @@ pub fn export(attr: TokenStream, item: TokenStream) -> TokenStream { let mut func = parse_macro_input!(item as ItemFn); let original_name = func.sig.ident.clone(); - let inner_name = Ident::new(&format!("__dropbear_export_inner_{}", original_name), original_name.span()); + let inner_name = Ident::new( + &format!("__dropbear_export_inner_{}", original_name), + original_name.span(), + ); let return_type = func.sig.output.clone(); let result_inner = match extract_dropbear_result_inner_type(&return_type) { @@ -148,21 +160,31 @@ pub fn export(attr: TokenStream, item: TokenStream) -> TokenStream { for input in func.sig.inputs.iter_mut() { match input { FnArg::Receiver(_) => { - return syn::Error::new(input.span(), "export does not support methods").to_compile_error().into(); + return syn::Error::new(input.span(), "export does not support methods") + .to_compile_error() + .into(); } FnArg::Typed(pat_ty) => { let (define_ty, is_entity) = extract_arg_markers(&mut pat_ty.attrs); let (arg_name, arg_ty) = match &*pat_ty.pat { syn::Pat::Ident(ident) => (ident.ident.clone(), (*pat_ty.ty).clone()), _ => { - return syn::Error::new(pat_ty.span(), "export only supports identifier arguments") - .to_compile_error() - .into(); + return syn::Error::new( + pat_ty.span(), + "export only supports identifier arguments", + ) + .to_compile_error() + .into(); } }; cleaned_inputs.push(FnArg::Typed(pat_ty.clone())); - arg_specs.push(ArgSpec { name: arg_name, ty: arg_ty, define_ty, is_entity }); + arg_specs.push(ArgSpec { + name: arg_name, + ty: arg_ty, + define_ty, + is_entity, + }); } } } @@ -171,12 +193,28 @@ pub fn export(attr: TokenStream, item: TokenStream) -> TokenStream { func.sig.inputs = syn::punctuated::Punctuated::from_iter(cleaned_inputs); let c_wrapper = match args.c { - Some(c_args) => build_c_wrapper(&original_name, &inner_name, &arg_specs, &result_inner, is_option, option_inner.as_ref(), c_args), + Some(c_args) => build_c_wrapper( + &original_name, + &inner_name, + &arg_specs, + &result_inner, + is_option, + option_inner.as_ref(), + c_args, + ), None => quote! {}, }; let kotlin_wrapper = match args.kotlin { - Some(k_args) => build_kotlin_wrapper(&original_name, &inner_name, &arg_specs, &result_inner, is_option, option_inner.as_ref(), k_args), + Some(k_args) => build_kotlin_wrapper( + &original_name, + &inner_name, + &arg_specs, + &result_inner, + is_option, + option_inner.as_ref(), + k_args, + ), None => quote! {}, }; @@ -194,7 +232,10 @@ pub fn repr_c_enum(_attr: TokenStream, item: TokenStream) -> TokenStream { let input = parse_macro_input!(item as ItemEnum); let enum_ident = input.ident.clone(); let enum_name = enum_ident.to_string(); - let mod_ident = Ident::new(&format!("{}_ffi", to_snake_case(&enum_name)), enum_ident.span()); + let mod_ident = Ident::new( + &format!("{}_ffi", to_snake_case(&enum_name)), + enum_ident.span(), + ); let tag_ident = Ident::new(&format!("{}Tag", enum_name), enum_ident.span()); let data_ident = Ident::new(&format!("{}Data", enum_name), enum_ident.span()); let ffi_ident = Ident::new(&format!("{}Ffi", enum_name), enum_ident.span()); @@ -208,7 +249,10 @@ pub fn repr_c_enum(_attr: TokenStream, item: TokenStream) -> TokenStream { for (index, variant) in input.variants.iter().enumerate() { let variant_ident = &variant.ident; let variant_name = variant_ident.to_string(); - let variant_struct_ident = Ident::new(&format!("{}{}", enum_name, variant_name), variant_ident.span()); + let variant_struct_ident = Ident::new( + &format!("{}{}", enum_name, variant_name), + variant_ident.span(), + ); let index = index as u32; tag_variants.push(quote! { #variant_ident = #index }); @@ -216,11 +260,8 @@ pub fn repr_c_enum(_attr: TokenStream, item: TokenStream) -> TokenStream { pub #variant_ident: ::std::mem::ManuallyDrop<#variant_struct_ident> }); - let (fields, field_inits, match_pattern) = build_variant_fields( - &enum_ident, - variant, - &mut array_structs, - ); + let (fields, field_inits, match_pattern) = + build_variant_fields(&enum_ident, variant, &mut array_structs); variant_structs.push(quote! { #[repr(C)] @@ -288,7 +329,11 @@ fn build_variant_fields( enum_ident: &Ident, variant: &syn::Variant, array_structs: &mut std::collections::BTreeMap<String, proc_macro2::TokenStream>, -) -> (Vec<proc_macro2::TokenStream>, Vec<proc_macro2::TokenStream>, proc_macro2::TokenStream) { +) -> ( + Vec<proc_macro2::TokenStream>, + Vec<proc_macro2::TokenStream>, + proc_macro2::TokenStream, +) { let mut fields = Vec::new(); let mut field_inits = Vec::new(); @@ -297,7 +342,8 @@ fn build_variant_fields( let mut pat_fields = Vec::new(); for field in &named.named { let field_ident = field.ident.as_ref().expect("named field"); - let (field_ty, init_expr) = map_field_type(enum_ident, &field.ty, field_ident, array_structs); + let (field_ty, init_expr) = + map_field_type(enum_ident, &field.ty, field_ident, array_structs); fields.push(quote! { pub #field_ident: #field_ty, }); field_inits.push(quote! { #field_ident: #init_expr, }); pat_fields.push(quote! { #field_ident }); @@ -310,7 +356,8 @@ fn build_variant_fields( let mut pat_fields = Vec::new(); for (idx, field) in unnamed.unnamed.iter().enumerate() { let field_ident = Ident::new(&format!("_{}", idx), field.span()); - let (field_ty, init_expr) = map_field_type(enum_ident, &field.ty, &field_ident, array_structs); + let (field_ty, init_expr) = + map_field_type(enum_ident, &field.ty, &field_ident, array_structs); fields.push(quote! { pub #field_ident: #field_ty, }); field_inits.push(quote! { #field_ident: #init_expr, }); pat_fields.push(quote! { #field_ident }); @@ -344,9 +391,12 @@ fn map_field_type( pub len: usize, } }; - array_structs.entry(array_ident.to_string()).or_insert(array_struct); + array_structs + .entry(array_ident.to_string()) + .or_insert(array_struct); let array_ty: Type = parse_quote!(#array_ident); - let init_expr = quote! { #array_ident { ptr: #field_ident.as_ptr(), len: #field_ident.len() } }; + let init_expr = + quote! { #array_ident { ptr: #field_ident.as_ptr(), len: #field_ident.len() } }; return (array_ty, init_expr); } @@ -407,14 +457,26 @@ fn extract_arg_markers(attrs: &mut Vec<syn::Attribute>) -> (Option<Type>, bool) let path = &attr.path(); let ident = path.get_ident().map(|v| v.to_string()).unwrap_or_default(); - if ident == "define" || path.segments.last().map(|s| s.ident == "define").unwrap_or(false) { + if ident == "define" + || path + .segments + .last() + .map(|s| s.ident == "define") + .unwrap_or(false) + { if let Ok(ty) = attr.parse_args::<Type>() { define_ty = Some(ty); } return false; } - if ident == "entity" || path.segments.last().map(|s| s.ident == "entity").unwrap_or(false) { + if ident == "entity" + || path + .segments + .last() + .map(|s| s.ident == "entity") + .unwrap_or(false) + { is_entity = true; return false; } @@ -439,9 +501,15 @@ fn extract_dropbear_result_inner_type(output: &ReturnType) -> syn::Result<Type> } } } - Err(syn::Error::new(output.span(), "export requires DropbearNativeResult<T> return type")) + Err(syn::Error::new( + output.span(), + "export requires DropbearNativeResult<T> return type", + )) } - ReturnType::Default => Err(syn::Error::new(output.span(), "export requires DropbearNativeResult<T> return type")), + ReturnType::Default => Err(syn::Error::new( + output.span(), + "export requires DropbearNativeResult<T> return type", + )), } } @@ -498,8 +566,11 @@ fn build_c_wrapper( let (target_ty, is_mut_ref) = match &arg.ty { Type::Reference(reference) => (&*reference.elem, reference.mutability.is_some()), _ => { - return syn::Error::new(arg.ty.span(), "define(...) requires a reference argument") - .to_compile_error(); + return syn::Error::new( + arg.ty.span(), + "define(...) requires a reference argument", + ) + .to_compile_error(); } }; @@ -580,7 +651,7 @@ fn build_c_wrapper( if out0_present.is_null() { return crate::scripting::native::DropbearNativeError::NullPointer.code(); } - } + }, ) } else { ( @@ -589,7 +660,7 @@ fn build_c_wrapper( if out0.is_null() { return crate::scripting::native::DropbearNativeError::NullPointer.code(); } - } + }, ) }; @@ -686,8 +757,11 @@ fn build_kotlin_wrapper( let (target_ty, is_mut_ref) = match &arg.ty { Type::Reference(reference) => (&*reference.elem, reference.mutability.is_some()), _ => { - return syn::Error::new(arg.ty.span(), "define(...) requires a reference argument") - .to_compile_error(); + return syn::Error::new( + arg.ty.span(), + "define(...) requires a reference argument", + ) + .to_compile_error(); } }; let convert = if is_mut_ref { @@ -1021,7 +1095,11 @@ fn jni_arg_cast(ty: &Type, name: &proc_macro2::TokenStream) -> proc_macro2::Toke quote! { #name } } -fn jni_value_cast(ty: &Type, name: proc_macro2::TokenStream, jni_path: &syn::Path) -> proc_macro2::TokenStream { +fn jni_value_cast( + ty: &Type, + name: proc_macro2::TokenStream, + jni_path: &syn::Path, +) -> proc_macro2::TokenStream { if is_bool_type(ty) { return quote! { if #name { 1 } else { 0 } }; } @@ -1050,7 +1128,11 @@ fn jni_value_cast(ty: &Type, name: proc_macro2::TokenStream, jni_path: &syn::Pat fn jni_boxing_info( ty: &Type, jni_path: &syn::Path, -) -> (proc_macro2::TokenStream, proc_macro2::TokenStream, proc_macro2::TokenStream) { +) -> ( + proc_macro2::TokenStream, + proc_macro2::TokenStream, + proc_macro2::TokenStream, +) { if is_i32_type(ty) || is_u32_type(ty) { return ( quote! { "(I)Ljava/lang/Integer;" }, @@ -2,19 +2,19 @@ use app_dirs2::AppInfo; use dropbear_engine::future::FutureQueue; +use dropbear_engine::{DropbearAppBuilder, DropbearWindowBuilder}; use eucalyptus_core::runtime::RuntimeProjectConfig; +use eucalyptus_core::scripting::jni::{RUNTIME_MODE, RuntimeMode}; use parking_lot::RwLock; use redback_runtime::PlayMode; +use ron::ser::PrettyConfig; +use serde::{Deserialize, Serialize}; use std::env::current_exe; use std::fs; use std::rc::Rc; use std::sync::Arc; -use ron::ser::PrettyConfig; -use winit::window::{Fullscreen, WindowAttributes}; -use dropbear_engine::{DropbearAppBuilder, DropbearWindowBuilder}; -use serde::{Deserialize, Serialize}; use winit::dpi::PhysicalSize; -use eucalyptus_core::scripting::jni::{RuntimeMode, RUNTIME_MODE}; +use winit::window::{Fullscreen, WindowAttributes}; #[tokio::main] async fn main() { @@ -69,7 +69,7 @@ async fn main() { record.file().unwrap_or("unknown"), record.line().unwrap_or(0) ) - .bright_black(); + .bright_black(); let console_line = format!( "{} {} [{}] - {}\n", @@ -97,10 +97,7 @@ async fn main() { }) .filter_level(LevelFilter::Warn) .filter(Some("dropbear_engine"), LevelFilter::Trace) - .filter( - Some("eucalyptus_editor"), - LevelFilter::Debug, - ) + .filter(Some("eucalyptus_editor"), LevelFilter::Debug) .filter(Some("eucalyptus_core"), LevelFilter::Debug) .filter(Some("dropbear_traits"), LevelFilter::Debug) .filter(Some("euca_runner"), LevelFilter::Debug) @@ -108,17 +105,21 @@ async fn main() { log::info!("Initialised logger"); } - dropbear_engine::panic::set_hook(); log::debug!("Set panic hook"); - let window_config_file = current_exe().unwrap() + let window_config_file = current_exe() + .unwrap() .parent() .ok_or(anyhow::anyhow!( "Unable to get parent of current executable" - )).unwrap() + )) + .unwrap() .join("config.eucfg"); - log::debug!("Fetched window config file path: {}", window_config_file.display()); + log::debug!( + "Fetched window config file path: {}", + window_config_file.display() + ); log::debug!("Reading from window config file"); let value = fs::read(&window_config_file); @@ -146,16 +147,22 @@ async fn main() { } }; - let path = current_exe().unwrap() + let path = current_exe() + .unwrap() .parent() .ok_or(anyhow::anyhow!( - "Unable to locate parent folder for current executable" - )).unwrap() + "Unable to locate parent folder for current executable" + )) + .unwrap() .join("data.eupak"); log::debug!("scene config (potential) file path: {}", path.display()); let scene_config = fs::read(&path).unwrap(); - log::debug!("Located scene config file: [{}] ({} bytes)", path.display(), scene_config.len()); + log::debug!( + "Located scene config file: [{}] ({} bytes)", + path.display(), + scene_config.len() + ); let scene_config: RuntimeProjectConfig = postcard::from_bytes(&scene_config).unwrap(); scene_config.populate().unwrap(); @@ -164,7 +171,9 @@ async fn main() { let _ = RUNTIME_MODE.set(RuntimeMode::Runtime); - let runtime_scene = Rc::new(RwLock::new(PlayMode::new(Some(scene_config.initial_scene)).unwrap())); + let runtime_scene = Rc::new(RwLock::new( + PlayMode::new(Some(scene_config.initial_scene)).unwrap(), + )); let future_queue = Arc::new(FutureQueue::new()); let authors = scene_config.authors.developer.clone(); @@ -178,9 +187,9 @@ async fn main() { let attributes = WindowAttributes::default(); let attributes = match config.target_resolution { - WindowModes::Windowed(x, y) => {attributes.with_inner_size(PhysicalSize::new(x, y))} - WindowModes::Maximised => {attributes.with_maximized(true)} - WindowModes::Fullscreen => {attributes.with_fullscreen(Some(Fullscreen::Borderless(None)))} + WindowModes::Windowed(x, y) => attributes.with_inner_size(PhysicalSize::new(x, y)), + WindowModes::Maximised => attributes.with_maximized(true), + WindowModes::Fullscreen => attributes.with_fullscreen(Some(Fullscreen::Borderless(None))), }; let window = DropbearWindowBuilder::new() @@ -194,19 +203,18 @@ async fn main() { DropbearAppBuilder::new() .add_window(window) .max_fps(config.max_fps) - .app_data(AppInfo { - name, - author, - }) + .app_data(AppInfo { name, author }) .with_future_queue(future_queue) - .run().await.unwrap(); + .run() + .await + .unwrap(); } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct ConfigFile { pub jvm_args: Option<String>, pub max_fps: u32, - pub target_resolution: WindowModes + pub target_resolution: WindowModes, } #[derive(Debug, Clone, Deserialize, Serialize)] @@ -214,4 +222,4 @@ pub enum WindowModes { Windowed(u32, u32), Maximised, Fullscreen, -} +} @@ -11,4 +11,4 @@ fn main() -> anyhow::Result<()> { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=src"); Ok(()) -} +} @@ -1,24 +1,21 @@ -use std::sync::Arc; -use egui::{CollapsingHeader, ComboBox, Ui}; -use hecs::{Entity, World}; -use dropbear_engine::animation::{AnimationComponent, AnimationSettings}; -use dropbear_engine::asset::ASSET_REGISTRY; -use dropbear_engine::entity::MeshRenderer; -use dropbear_engine::graphics::SharedGraphicsContext; -use jni::objects::JObject; -use jni::JNIEnv; use crate::component::{Component, ComponentDescriptor, InspectableComponent, SerializedComponent}; use crate::ptr::WorldPtr; -use crate::scripting::jni::utils::ToJObject; use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; +use dropbear_engine::animation::{AnimationComponent, AnimationSettings}; +use dropbear_engine::asset::ASSET_REGISTRY; +use dropbear_engine::entity::MeshRenderer; +use dropbear_engine::graphics::SharedGraphicsContext; +use egui::{CollapsingHeader, ComboBox, Ui}; +use hecs::{Entity, World}; +use std::sync::Arc; #[typetag::serde] impl SerializedComponent for AnimationComponent {} impl Component for AnimationComponent { type SerializedForm = Self; - type RequiredComponentTypes = (Self, ); + type RequiredComponentTypes = (Self,); fn descriptor() -> ComponentDescriptor { ComponentDescriptor { @@ -33,10 +30,17 @@ impl Component for AnimationComponent { ser: &'a Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, ) -> crate::component::ComponentInitFuture<'a, Self> { - Box::pin(async move { Ok((ser.clone(), )) }) + Box::pin(async move { Ok((ser.clone(),)) }) } - fn update_component(&mut self, world: &World, _physics: &mut crate::physics::PhysicsState, entity: Entity, dt: f32, graphics: Arc<SharedGraphicsContext>) { + fn update_component( + &mut self, + world: &World, + _physics: &mut crate::physics::PhysicsState, + entity: Entity, + dt: f32, + graphics: Arc<SharedGraphicsContext>, + ) { let Ok(renderer) = world.get::<&MeshRenderer>(entity) else { return; }; @@ -63,94 +67,107 @@ impl Component for AnimationComponent { impl InspectableComponent for AnimationComponent { fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { - CollapsingHeader::new("Animation").default_open(true).show(ui, |ui| { - let has_animations = !self.available_animations.is_empty(); - let mut enabled = self.active_animation_index.is_some() && has_animations; - - let mut selected_index = self - .active_animation_index - .unwrap_or(0) - .min(self.available_animations.len().saturating_sub(1)); - - let selected_label = if has_animations { - self.available_animations - .get(selected_index) - .map(String::as_str) - .unwrap_or("Unnamed Animation") - } else { - "No Animations" - }; - - let mut selection_changed = false; - ComboBox::from_label("Animation") - .selected_text(selected_label) - .show_ui(ui, |ui| { - for (index, name) in self.available_animations.iter().enumerate() { - if ui.selectable_value(&mut selected_index, index, name).changed() { - selection_changed = true; + CollapsingHeader::new("Animation") + .default_open(true) + .show(ui, |ui| { + let has_animations = !self.available_animations.is_empty(); + let mut enabled = self.active_animation_index.is_some() && has_animations; + + let mut selected_index = self + .active_animation_index + .unwrap_or(0) + .min(self.available_animations.len().saturating_sub(1)); + + let selected_label = if has_animations { + self.available_animations + .get(selected_index) + .map(String::as_str) + .unwrap_or("Unnamed Animation") + } else { + "No Animations" + }; + + let mut selection_changed = false; + ComboBox::from_label("Animation") + .selected_text(selected_label) + .show_ui(ui, |ui| { + for (index, name) in self.available_animations.iter().enumerate() { + if ui + .selectable_value(&mut selected_index, index, name) + .changed() + { + selection_changed = true; + } } - } - }); - - if selection_changed && has_animations { - self.active_animation_index = Some(selected_index); - enabled = true; - } - - ui.horizontal(|ui| { - ui.label("Active"); + }); - if ui.checkbox(&mut enabled, "Enable").changed() { - self.active_animation_index = if enabled && has_animations { - Some(selected_index) - } else { - None - }; + if selection_changed && has_animations { + self.active_animation_index = Some(selected_index); + enabled = true; } - }); - - if has_animations { - let settings = self - .animation_settings - .entry(selected_index) - .or_insert_with(|| AnimationSettings { - time: self.time, - speed: self.speed, - looping: self.looping, - is_playing: self.is_playing, - }); ui.horizontal(|ui| { - ui.label("Playing"); - ui.checkbox(&mut settings.is_playing, ""); + ui.label("Active"); + + if ui.checkbox(&mut enabled, "Enable").changed() { + self.active_animation_index = if enabled && has_animations { + Some(selected_index) + } else { + None + }; + } }); - ui.horizontal(|ui| { - ui.label("Looping"); - ui.checkbox(&mut settings.looping, ""); - }); + if has_animations { + let settings = self + .animation_settings + .entry(selected_index) + .or_insert_with(|| AnimationSettings { + time: self.time, + speed: self.speed, + looping: self.looping, + is_playing: self.is_playing, + }); + + ui.horizontal(|ui| { + ui.label("Playing"); + ui.checkbox(&mut settings.is_playing, ""); + }); - ui.horizontal(|ui| { - ui.label("Speed"); - ui.add(egui::DragValue::new(&mut settings.speed).speed(0.01).range(0.0..=10.0)); - }); + ui.horizontal(|ui| { + ui.label("Looping"); + ui.checkbox(&mut settings.looping, ""); + }); - ui.horizontal(|ui| { - ui.label("Start Time"); - ui.add(egui::DragValue::new(&mut settings.time).speed(0.01).range(0.0..=1_000_000.0)); - if ui.button("Reset").clicked() { - settings.time = 0.0; - } - }); + ui.horizontal(|ui| { + ui.label("Speed"); + ui.add( + egui::DragValue::new(&mut settings.speed) + .speed(0.01) + .range(0.0..=10.0), + ); + }); + + ui.horizontal(|ui| { + ui.label("Start Time"); + ui.add( + egui::DragValue::new(&mut settings.time) + .speed(0.01) + .range(0.0..=1_000_000.0), + ); + if ui.button("Reset").clicked() { + settings.time = 0.0; + } + }); - if self.active_animation_index == Some(selected_index) { - self.time = settings.time; - self.speed = settings.speed; - self.looping = settings.looping; - self.is_playing = settings.is_playing; + if self.active_animation_index == Some(selected_index) { + self.time = settings.time; + self.speed = settings.speed; + self.looping = settings.looping; + self.is_playing = settings.is_playing; + } } - } - }); + }); } } @@ -185,27 +202,29 @@ fn collect_available_animations( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "animationComponentExistsForEntity"), + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "animationComponentExistsForEntity" + ), c )] fn animation_component_exists_for_entity( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: 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"), + 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, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<Option<i32>> { let component = world .get::<&AnimationComponent>(entity) @@ -214,14 +233,15 @@ fn get_active_animation_index( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "setActiveAnimationIndex"), + 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, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, index: &Option<i32>, ) -> DropbearNativeResult<()> { let mut component = world @@ -247,14 +267,15 @@ fn set_active_animation_index( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "getTime"), + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "getTime" + ), c )] fn get_time( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<f64> { let component = world .get::<&AnimationComponent>(entity) @@ -270,14 +291,15 @@ fn get_time( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "setTime"), + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "setTime" + ), c )] fn set_time( - #[dropbear_macro::define(WorldPtr)] - world: &mut World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, value: f64, ) -> DropbearNativeResult<()> { let mut component = world @@ -309,14 +331,15 @@ fn set_time( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "getSpeed"), + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "getSpeed" + ), c )] fn get_speed( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<f64> { let component = world .get::<&AnimationComponent>(entity) @@ -332,14 +355,15 @@ fn get_speed( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "setSpeed"), + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "setSpeed" + ), c )] fn set_speed( - #[dropbear_macro::define(WorldPtr)] - world: &mut World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, value: f64, ) -> DropbearNativeResult<()> { let mut component = world @@ -371,14 +395,15 @@ fn set_speed( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "getLooping"), + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "getLooping" + ), c )] fn get_looping( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<bool> { let component = world .get::<&AnimationComponent>(entity) @@ -394,14 +419,15 @@ fn get_looping( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "setLooping"), + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "setLooping" + ), c )] fn set_looping( - #[dropbear_macro::define(WorldPtr)] - world: &mut World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, value: bool, ) -> DropbearNativeResult<()> { let mut component = world @@ -433,14 +459,15 @@ fn set_looping( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "getIsPlaying"), + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "getIsPlaying" + ), c )] fn get_is_playing( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<bool> { let component = world .get::<&AnimationComponent>(entity) @@ -456,14 +483,15 @@ fn get_is_playing( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "setIsPlaying"), + 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, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, value: bool, ) -> DropbearNativeResult<()> { let mut component = world @@ -495,41 +523,41 @@ fn set_is_playing( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "getIndexFromString"), + 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, + #[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 - } - })) + 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"), + kotlin( + class = "com.dropbear.animation.AnimationComponentNative", + func = "getAvailableAnimations" + ), c )] fn get_available_animations( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[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,21 +1,20 @@ -pub mod texture; pub mod model; +pub mod texture; -use jni::JNIEnv; -use jni::objects::JObject; -use dropbear_engine::asset::AssetKind; use crate::ptr::{AssetRegistryPtr, AssetRegistryUnwrapped}; -use crate::scripting::jni::utils::{FromJObject}; +use crate::scripting::jni::utils::FromJObject; use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; +use dropbear_engine::asset::AssetKind; +use jni::JNIEnv; +use jni::objects::JObject; #[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, + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, label: String, kind: &AssetKind, ) -> DropbearNativeResult<Option<u64>> { @@ -43,16 +42,14 @@ fn dropbear_asset_get_asset( impl FromJObject for AssetKind { fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> where - Self: Sized + Self: Sized, { - let ordinal = env - .call_method(obj, "ordinal", "()I", &[])? - .i()?; + let ordinal = env.call_method(obj, "ordinal", "()I", &[])?.i()?; match ordinal { 0 => Ok(AssetKind::Texture), 1 => Ok(AssetKind::Model), - _ => Err(DropbearNativeError::InvalidEnumOrdinal) + _ => Err(DropbearNativeError::InvalidEnumOrdinal), } } -} +} @@ -1,14 +1,16 @@ -use jni::JNIEnv; -use jni::objects::{JObject, JValue}; -use jni::sys::{jdouble, jint}; +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 dropbear_engine::model::{ + Animation, AnimationChannel, AnimationInterpolation, ChannelValues, Material, Mesh, + ModelVertex, Node, NodeTransform, Skin, +}; use dropbear_engine::texture::Texture; -use crate::ptr::{AssetRegistryPtr, AssetRegistryUnwrapped}; -use crate::scripting::jni::utils::ToJObject; +use jni::JNIEnv; +use jni::objects::{JObject, JValue}; #[repr(C)] #[derive(Clone, Debug)] @@ -25,7 +27,8 @@ pub struct NModelVertex { impl ToJObject for NModelVertex { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/asset/model/ModelVertex") + let class = env + .find_class("com/dropbear/asset/model/ModelVertex") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let position = self.position.to_jobject(env)?; @@ -71,10 +74,12 @@ pub struct NMesh { impl ToJObject for NMesh { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/asset/model/Mesh") + let class = env + .find_class("com/dropbear/asset/model/Mesh") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let name = env.new_string(&self.name) + let name = env + .new_string(&self.name) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; let vertices = self.vertices.to_jobject(env)?; @@ -86,11 +91,7 @@ impl ToJObject for NMesh { ]; let obj = env - .new_object( - &class, - "(Ljava/lang/String;IILjava/util/List;)V", - &args, - ) + .new_object(&class, "(Ljava/lang/String;IILjava/util/List;)V", &args) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) @@ -119,10 +120,12 @@ pub struct NMaterial { impl ToJObject for NMaterial { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/asset/model/Material") + let class = env + .find_class("com/dropbear/asset/model/Material") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let name = env.new_string(&self.name) + let name = env + .new_string(&self.name) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; let diffuse_texture = new_texture(env, self.diffuse_texture)?; let normal_texture = new_texture(env, self.normal_texture)?; @@ -183,7 +186,8 @@ pub struct NNodeTransform { impl ToJObject for NNodeTransform { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/asset/model/NodeTransform") + let class = env + .find_class("com/dropbear/asset/model/NodeTransform") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let translation = self.translation.to_jobject(env)?; @@ -219,10 +223,12 @@ pub struct NNode { impl ToJObject for NNode { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/asset/model/Node") + let class = env + .find_class("com/dropbear/asset/model/Node") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let name = env.new_string(&self.name) + 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)?; @@ -258,10 +264,12 @@ pub struct NSkin { impl ToJObject for NSkin { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/asset/model/Skin") + let class = env + .find_class("com/dropbear/asset/model/Skin") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let name = env.new_string(&self.name) + 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)?; @@ -296,10 +304,12 @@ pub struct NAnimation { impl ToJObject for NAnimation { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/asset/model/Animation") + let class = env + .find_class("com/dropbear/asset/model/Animation") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let name = env.new_string(&self.name) + let name = env + .new_string(&self.name) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; let channels = self.channels.to_jobject(env)?; @@ -310,11 +320,7 @@ impl ToJObject for NAnimation { ]; let obj = env - .new_object( - &class, - "(Ljava/lang/String;Ljava/util/List;D)V", - &args, - ) + .new_object(&class, "(Ljava/lang/String;Ljava/util/List;D)V", &args) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) @@ -332,7 +338,8 @@ pub struct NAnimationChannel { impl ToJObject for NAnimationChannel { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/asset/model/AnimationChannel") + let class = env + .find_class("com/dropbear/asset/model/AnimationChannel") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let times = self.times.as_slice().to_jobject(env)?; @@ -440,21 +447,15 @@ impl ToJObject for NChannelValues { } fn new_texture<'a>(env: &mut JNIEnv<'a>, texture_id: u64) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/asset/Texture") + let class = env + .find_class("com/dropbear/asset/Texture") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - env.new_object( - &class, - "(J)V", - &[JValue::Long(texture_id as i64)], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + env.new_object(&class, "(J)V", &[JValue::Long(texture_id as i64)]) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) } -fn texture_handle_id( - registry: &dropbear_engine::asset::AssetRegistry, - texture: &Texture, -) -> u64 { +fn texture_handle_id(registry: &dropbear_engine::asset::AssetRegistry, texture: &Texture) -> u64 { texture .hash .and_then(|hash| registry.texture_handle_by_hash(hash).map(|h| h.id)) @@ -584,7 +585,11 @@ fn map_animation_channel(channel: &AnimationChannel) -> NAnimationChannel { fn map_animation(animation: &Animation) -> NAnimation { NAnimation { name: animation.name.clone(), - channels: animation.channels.iter().map(map_animation_channel).collect(), + channels: animation + .channels + .iter() + .map(map_animation_channel) + .collect(), duration: animation.duration, } } @@ -594,8 +599,7 @@ fn map_animation(animation: &Animation) -> NAnimation { c(name = "dropbear_asset_model_get_label") )] fn dropbear_asset_model_get_label( - #[dropbear_macro::define(AssetRegistryPtr)] - asset: &AssetRegistryUnwrapped, + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, model_handle: u64, ) -> DropbearNativeResult<String> { let label = asset @@ -610,8 +614,7 @@ fn dropbear_asset_model_get_label( c(name = "dropbear_asset_model_get_meshes") )] fn dropbear_asset_model_get_meshes( - #[dropbear_macro::define(AssetRegistryPtr)] - asset: &AssetRegistryUnwrapped, + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, model_handle: u64, ) -> DropbearNativeResult<Vec<NMesh>> { let reader = asset.read(); @@ -627,8 +630,7 @@ fn dropbear_asset_model_get_meshes( c(name = "dropbear_asset_model_get_materials") )] fn dropbear_asset_model_get_materials( - #[dropbear_macro::define(AssetRegistryPtr)] - asset: &AssetRegistryUnwrapped, + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, model_handle: u64, ) -> DropbearNativeResult<Vec<NMaterial>> { let reader = asset.read(); @@ -648,8 +650,7 @@ fn dropbear_asset_model_get_materials( c(name = "dropbear_asset_model_get_skins") )] pub fn dropbear_asset_model_get_skins( - #[dropbear_macro::define(AssetRegistryPtr)] - asset: &AssetRegistryUnwrapped, + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, model_handle: u64, ) -> DropbearNativeResult<Vec<NSkin>> { let reader = asset.read(); @@ -665,8 +666,7 @@ pub fn dropbear_asset_model_get_skins( c(name = "dropbear_asset_model_get_animations") )] pub fn dropbear_asset_model_get_animations( - #[dropbear_macro::define(AssetRegistryPtr)] - asset: &AssetRegistryUnwrapped, + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, model_handle: u64, ) -> DropbearNativeResult<Vec<NAnimation>> { let reader = asset.read(); @@ -682,8 +682,7 @@ pub fn dropbear_asset_model_get_animations( c(name = "dropbear_asset_model_get_nodes") )] pub fn dropbear_asset_model_get_nodes( - #[dropbear_macro::define(AssetRegistryPtr)] - asset: &AssetRegistryUnwrapped, + #[dropbear_macro::define(AssetRegistryPtr)] asset: &AssetRegistryUnwrapped, model_handle: u64, ) -> DropbearNativeResult<Vec<NNode>> { let reader = asset.read(); @@ -692,4 +691,4 @@ pub fn dropbear_asset_model_get_nodes( .ok_or(DropbearNativeError::InvalidHandle)?; Ok(model.nodes.iter().map(map_node).collect()) -} +} @@ -1,18 +1,19 @@ -use dropbear_engine::asset::Handle; 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, + #[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))) + Ok(asset_manager + .read() + .get_label_from_texture_handle(Handle::new(texture_handle))) } #[dropbear_macro::export( @@ -20,8 +21,7 @@ fn get_texture_label( c(name = "dropbear_asset_texture_get_width") )] fn get_texture_width( - #[dropbear_macro::define(AssetRegistryPtr)] - asset_manager: &AssetRegistryUnwrapped, + #[dropbear_macro::define(AssetRegistryPtr)] asset_manager: &AssetRegistryUnwrapped, texture_handle: u64, ) -> DropbearNativeResult<u32> { asset_manager @@ -36,8 +36,7 @@ fn get_texture_width( c(name = "dropbear_asset_texture_get_height") )] fn get_texture_height( - #[dropbear_macro::define(AssetRegistryPtr)] - asset_manager: &AssetRegistryUnwrapped, + #[dropbear_macro::define(AssetRegistryPtr)] asset_manager: &AssetRegistryUnwrapped, texture_handle: u64, ) -> DropbearNativeResult<u32> { asset_manager @@ -45,4 +44,4 @@ fn get_texture_height( .get_texture(Handle::new(texture_handle)) .map(|v| v.size.height) .ok_or(DropbearNativeError::AssetNotFound) -} +} @@ -1,17 +1,19 @@ //! Additional information and context for cameras from the [`dropbear_engine::camera`] +use crate::component::{ + Component, ComponentDescriptor, ComponentInitFuture, InspectableComponent, SerializedComponent, +}; +use crate::ptr::WorldPtr; +use crate::scripting::result::DropbearNativeResult; use crate::states::SerializableCamera; +use crate::types::NVector3; use dropbear_engine::camera::{Camera, CameraBuilder, CameraSettings}; +use dropbear_engine::graphics::SharedGraphicsContext; +use egui::{CollapsingHeader, Ui}; use glam::DVec3; +use hecs::{Entity, World}; use serde::{Deserialize, Serialize}; use std::any::Any; use std::sync::Arc; -use egui::{CollapsingHeader, Ui}; -use hecs::{Entity, World}; -use dropbear_engine::graphics::SharedGraphicsContext; -use crate::component::{Component, ComponentDescriptor, ComponentInitFuture, InspectableComponent, SerializedComponent}; -use crate::ptr::WorldPtr; -use crate::scripting::result::DropbearNativeResult; -use crate::types::NVector3; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CameraComponent { @@ -32,7 +34,9 @@ impl Component for Camera { fqtn: "dropbear_engine::camera::Camera".to_string(), type_name: "Camera3D".to_string(), category: Some("Camera".to_string()), - description: Some("Allows you to view the scene through the eyes of the component".to_string()), + description: Some( + "Allows you to view the scene through the eyes of the component".to_string(), + ), } } @@ -50,7 +54,14 @@ impl Component for Camera { }) } - fn update_component(&mut self, _world: &World, _physics: &mut crate::physics::PhysicsState, _entity: Entity, _dt: f32, graphics: Arc<SharedGraphicsContext>) { + fn update_component( + &mut self, + _world: &World, + _physics: &mut crate::physics::PhysicsState, + _entity: Entity, + _dt: f32, + graphics: Arc<SharedGraphicsContext>, + ) { self.update(graphics.clone()) } @@ -71,43 +82,73 @@ impl InspectableComponent for Camera { ui.horizontal(|ui| { ui.label("Eye"); - changed |= ui.add(egui::DragValue::new(&mut self.eye.x).speed(0.1)).changed(); - changed |= ui.add(egui::DragValue::new(&mut self.eye.y).speed(0.1)).changed(); - changed |= ui.add(egui::DragValue::new(&mut self.eye.z).speed(0.1)).changed(); + changed |= ui + .add(egui::DragValue::new(&mut self.eye.x).speed(0.1)) + .changed(); + changed |= ui + .add(egui::DragValue::new(&mut self.eye.y).speed(0.1)) + .changed(); + changed |= ui + .add(egui::DragValue::new(&mut self.eye.z).speed(0.1)) + .changed(); }); ui.horizontal(|ui| { ui.label("Target"); - changed |= ui.add(egui::DragValue::new(&mut self.target.x).speed(0.1)).changed(); - changed |= ui.add(egui::DragValue::new(&mut self.target.y).speed(0.1)).changed(); - changed |= ui.add(egui::DragValue::new(&mut self.target.z).speed(0.1)).changed(); + changed |= ui + .add(egui::DragValue::new(&mut self.target.x).speed(0.1)) + .changed(); + changed |= ui + .add(egui::DragValue::new(&mut self.target.y).speed(0.1)) + .changed(); + changed |= ui + .add(egui::DragValue::new(&mut self.target.z).speed(0.1)) + .changed(); }); ui.horizontal(|ui| { ui.label("Up"); - changed |= ui.add(egui::DragValue::new(&mut self.up.x).speed(0.1)).changed(); - changed |= ui.add(egui::DragValue::new(&mut self.up.y).speed(0.1)).changed(); - changed |= ui.add(egui::DragValue::new(&mut self.up.z).speed(0.1)).changed(); + changed |= ui + .add(egui::DragValue::new(&mut self.up.x).speed(0.1)) + .changed(); + changed |= ui + .add(egui::DragValue::new(&mut self.up.y).speed(0.1)) + .changed(); + changed |= ui + .add(egui::DragValue::new(&mut self.up.z).speed(0.1)) + .changed(); }); ui.horizontal(|ui| { ui.label("Aspect"); changed |= ui - .add(egui::DragValue::new(&mut self.aspect).speed(0.01).range(0.1..=10.0)) + .add( + egui::DragValue::new(&mut self.aspect) + .speed(0.01) + .range(0.1..=10.0), + ) .changed(); }); ui.horizontal(|ui| { ui.label("Near Plane"); changed |= ui - .add(egui::DragValue::new(&mut self.znear).speed(0.01).range(0.01..=1000.0)) + .add( + egui::DragValue::new(&mut self.znear) + .speed(0.01) + .range(0.01..=1000.0), + ) .changed(); }); ui.horizontal(|ui| { ui.label("Far Plane"); changed |= ui - .add(egui::DragValue::new(&mut self.zfar).speed(1.0).range(0.1..=10000.0)) + .add( + egui::DragValue::new(&mut self.zfar) + .speed(1.0) + .range(0.1..=10000.0), + ) .changed(); }); @@ -120,7 +161,9 @@ impl InspectableComponent for Camera { ui.horizontal(|ui| { ui.label("Speed"); - changed |= ui.add(egui::DragValue::new(&mut self.settings.speed).speed(0.1)).changed(); + changed |= ui + .add(egui::DragValue::new(&mut self.settings.speed).speed(0.1)) + .changed(); }); ui.horizontal(|ui| { @@ -243,19 +286,22 @@ pub enum CameraAction { pub mod shared { pub fn camera_exists_for_entity(world: &hecs::World, entity: hecs::Entity) -> bool { - world.get::<&dropbear_engine::camera::Camera>(entity).is_ok() + world + .get::<&dropbear_engine::camera::Camera>(entity) + .is_ok() } } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "cameraExistsForEntity"), + kotlin( + class = "com.dropbear.components.CameraNative", + func = "cameraExistsForEntity" + ), c )] fn exists_for_entity( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<bool> { Ok(shared::camera_exists_for_entity(world, entity)) } @@ -265,10 +311,8 @@ fn exists_for_entity( c )] fn get_eye( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<NVector3> { match world.get::<&Camera>(entity) { Ok(camera) => Ok(camera.eye.into()), @@ -281,30 +325,29 @@ fn get_eye( c )] fn set_eye( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_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"), + kotlin( + class = "com.dropbear.components.CameraNative", + func = "getCameraTarget" + ), c )] fn get_target( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<NVector3> { match world.get::<&Camera>(entity) { Ok(camera) => Ok(camera.target.into()), @@ -313,21 +356,22 @@ fn get_target( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraTarget"), + kotlin( + class = "com.dropbear.components.CameraNative", + func = "setCameraTarget" + ), c )] fn set_target( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_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()), } } @@ -337,10 +381,8 @@ fn set_target( c )] fn get_up( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<NVector3> { match world.get::<&Camera>(entity) { Ok(camera) => Ok(camera.up.into()), @@ -353,30 +395,29 @@ fn get_up( c )] fn set_up( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_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"), + kotlin( + class = "com.dropbear.components.CameraNative", + func = "getCameraAspect" + ), c )] fn get_aspect( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<f64> { match world.get::<&Camera>(entity) { Ok(camera) => Ok(camera.aspect.into()), @@ -389,10 +430,8 @@ fn get_aspect( c )] fn get_fovy( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<f64> { match world.get::<&Camera>(entity) { Ok(camera) => Ok(camera.settings.fov_y.into()), @@ -405,30 +444,29 @@ fn get_fovy( c )] fn set_fovy( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_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"), + kotlin( + class = "com.dropbear.components.CameraNative", + func = "getCameraZNear" + ), c )] fn get_znear( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<f64> { match world.get::<&Camera>(entity) { Ok(camera) => Ok(camera.znear.into()), @@ -437,21 +475,22 @@ fn get_znear( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraZNear"), + kotlin( + class = "com.dropbear.components.CameraNative", + func = "setCameraZNear" + ), c )] fn set_znear( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_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()), } } @@ -461,10 +500,8 @@ fn set_znear( c )] fn get_zfar( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<f64> { match world.get::<&Camera>(entity) { Ok(camera) => Ok(camera.zfar.into()), @@ -477,17 +514,15 @@ fn get_zfar( c )] fn set_zfar( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_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()), } } @@ -497,10 +532,8 @@ fn set_zfar( c )] fn get_yaw( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<f64> { match world.get::<&Camera>(entity) { Ok(camera) => Ok(camera.yaw.into()), @@ -513,30 +546,29 @@ fn get_yaw( c )] fn set_yaw( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_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"), + kotlin( + class = "com.dropbear.components.CameraNative", + func = "getCameraPitch" + ), c )] fn get_pitch( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<f64> { match world.get::<&Camera>(entity) { Ok(camera) => Ok(camera.pitch.into()), @@ -545,34 +577,36 @@ fn get_pitch( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraPitch"), + kotlin( + class = "com.dropbear.components.CameraNative", + func = "setCameraPitch" + ), c )] fn set_pitch( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_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"), + kotlin( + class = "com.dropbear.components.CameraNative", + func = "getCameraSpeed" + ), c )] fn get_speed( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<f64> { match world.get::<&Camera>(entity) { Ok(camera) => Ok(camera.settings.speed.into()), @@ -581,34 +615,36 @@ fn get_speed( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraSpeed"), + kotlin( + class = "com.dropbear.components.CameraNative", + func = "setCameraSpeed" + ), c )] fn set_speed( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_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"), + kotlin( + class = "com.dropbear.components.CameraNative", + func = "getCameraSensitivity" + ), c )] fn get_sensitivity( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropebear_macro::entity] - entity: hecs::Entity, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropebear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<f64> { match world.get::<&Camera>(entity) { Ok(camera) => Ok(camera.settings.sensitivity.into()), @@ -617,21 +653,22 @@ fn get_sensitivity( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraSensitivity"), + 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, + #[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,10 +1,10 @@ //! One way command buffers between the scripting module and the editor/runtime. +use crate::scene::loading::SceneLoadHandle; use crossbeam_channel::{Receiver, Sender, unbounded}; use dropbear_engine::graphics::SharedGraphicsContext; use once_cell::sync::Lazy; use parking_lot::RwLock; use std::sync::{Arc, OnceLock}; -use crate::scene::loading::SceneLoadHandle; pub static COMMAND_BUFFER: Lazy<(Box<Sender<CommandBuffer>>, Receiver<CommandBuffer>)> = Lazy::new(|| { @@ -49,4 +49,4 @@ pub enum WindowCommand { /// Command buffer that is used for oneway communication between Kotlin to Rust. pub trait CommandBufferPoller { fn poll(&mut self, graphics: Arc<SharedGraphicsContext>); -} +} @@ -1,26 +1,26 @@ +use crate::hierarchy::EntityTransformExt; +use crate::physics::PhysicsState; +use crate::states::{SerializedMaterialCustomisation, SerializedMeshRenderer}; +use crate::utils::ResolveReference; +use downcast_rs::{Downcast, impl_downcast}; +use dropbear_engine::asset::{ASSET_REGISTRY, Handle}; +use dropbear_engine::entity::{EntityTransform, MeshRenderer}; +use dropbear_engine::graphics::SharedGraphicsContext; +use dropbear_engine::model::Model; +use dropbear_engine::procedural::{ProcObjType, ProcedurallyGeneratedObject}; +use dropbear_engine::texture::Texture; +use dropbear_engine::utils::{ResourceReference, ResourceReferenceType}; +use egui::{CollapsingHeader, ComboBox, DragValue, RichText, UiBuilder}; +use hecs::{Entity, World}; +pub use serde::{Deserialize, Serialize}; use std::any::TypeId; use std::collections::HashMap; use std::collections::hash_map::DefaultHasher; -use std::hash::{Hash, Hasher}; -use std::time::{SystemTime, UNIX_EPOCH}; use std::future::Future; +use std::hash::{Hash, Hasher}; use std::pin::Pin; use std::sync::Arc; -use egui::{CollapsingHeader, ComboBox, DragValue, RichText, UiBuilder}; -use hecs::{Entity, World}; -pub use serde::{Deserialize, Serialize}; -use dropbear_engine::asset::{Handle, ASSET_REGISTRY}; -use dropbear_engine::entity::{EntityTransform, MeshRenderer}; -use dropbear_engine::graphics::SharedGraphicsContext; -use dropbear_engine::model::{Model}; -use dropbear_engine::procedural::{ProcObjType, ProcedurallyGeneratedObject}; -use dropbear_engine::texture::Texture; -use dropbear_engine::utils::{ResourceReference, ResourceReferenceType}; -use crate::hierarchy::EntityTransformExt; -use crate::physics::PhysicsState; -use crate::states::{SerializedMaterialCustomisation, SerializedMeshRenderer}; -use crate::utils::ResolveReference; -use downcast_rs::{Downcast, impl_downcast}; +use std::time::{SystemTime, UNIX_EPOCH}; pub use typetag::*; @@ -39,7 +39,7 @@ pub struct ComponentRegistry { extractors: HashMap<TypeId, ExtractorFn>, /// Functions that allow for the entity to load. loaders: HashMap<TypeId, LoaderFn>, - /// Functions that update the contents of the component. + /// Functions that update the contents of the component. updaters: HashMap<TypeId, UpdateFn>, /// Functions that create default serialized components. defaults: HashMap<TypeId, DefaultFn>, @@ -47,29 +47,48 @@ pub struct ComponentRegistry { removers: HashMap<TypeId, RemoveFn>, /// Functions that find entities with a component. finders: HashMap<TypeId, FindFn>, - /// Allows for inspecting the component in the Resource Inspector dock. + /// Allows for inspecting the component in the Resource Inspector dock. inspectors: HashMap<TypeId, InspectFn>, } -/// Describes a handy little future for [`Component::init`], which deals with initialising a component from its serialized form. -/// +/// Describes a handy little future for [`Component::init`], which deals with initialising a component from its serialized form. +/// /// Typically thrown in as a return parameter as `-> ComponentInitFuture<'a, Self>` -pub type ComponentInitFuture<'a, T: Component> = std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<T::RequiredComponentTypes>> + Send + Sync + 'a>>; +pub type ComponentInitFuture<'a, T: Component> = std::pin::Pin< + Box< + dyn std::future::Future<Output = anyhow::Result<T::RequiredComponentTypes>> + + Send + + Sync + + 'a, + >, +>; -type LoaderFuture<'a> = Pin<Box< - dyn Future<Output = anyhow::Result<Box<dyn for<'b> FnOnce(&'b mut hecs::EntityBuilder) + Send + Sync>>> + Send + Sync + 'a ->>; +type LoaderFuture<'a> = Pin< + Box< + dyn Future< + Output = anyhow::Result< + Box<dyn for<'b> FnOnce(&'b mut hecs::EntityBuilder) + Send + Sync>, + >, + > + Send + + Sync + + 'a, + >, +>; type LoaderFn = Box< dyn for<'a> Fn(&'a dyn SerializedComponent, Arc<SharedGraphicsContext>) -> LoaderFuture<'a> + Send - + Sync + + Sync, >; -type ExtractorFn = Box<dyn Fn(&hecs::World, hecs::Entity) -> Option<Box<dyn SerializedComponent>> + Send + Sync>; -type UpdateFn = Box<dyn Fn(&mut hecs::World, &mut PhysicsState, f32, Arc<SharedGraphicsContext>) + Send + Sync>; +type ExtractorFn = + Box<dyn Fn(&hecs::World, hecs::Entity) -> Option<Box<dyn SerializedComponent>> + Send + Sync>; +type UpdateFn = + Box<dyn Fn(&mut hecs::World, &mut PhysicsState, f32, Arc<SharedGraphicsContext>) + Send + Sync>; type DefaultFn = Box<dyn Fn() -> Box<dyn SerializedComponent> + Send + Sync>; type RemoveFn = Box<dyn Fn(&mut hecs::World, hecs::Entity) + Send + Sync>; type FindFn = Box<dyn Fn(&hecs::World) -> Vec<hecs::Entity> + Send + Sync>; -type InspectFn = Box<dyn Fn(&mut hecs::World, hecs::Entity, &mut egui::Ui, Arc<SharedGraphicsContext>) + Send + Sync>; +type InspectFn = Box< + dyn Fn(&mut hecs::World, hecs::Entity, &mut egui::Ui, Arc<SharedGraphicsContext>) + Send + Sync, +>; // fn inspect(&mut self, ui: &mut egui::Ui); @@ -103,63 +122,85 @@ impl ComponentRegistry { self.fqtn_to_type.insert(desc.fqtn.clone(), type_id); if let Some(ref cat) = desc.category { - self.categories.entry(cat.clone()).or_default().push(type_id); + self.categories + .entry(cat.clone()) + .or_default() + .push(type_id); } self.descriptors.insert(type_id, desc); self.serialized_to_component .insert(serialized_type_id, type_id); - self.extractors.insert(type_id, Box::new(|world, entity| { - let Ok(c) = world.get::<&T>(entity) else { return None }; - Some(c.save(world, entity)) - })); - - self.loaders.insert(serialized_type_id, Box::new(|serialized, graphics| { - let serialized = serialized - .as_any() - .downcast_ref::<T::SerializedForm>() - .expect("type mismatch in loader — registry bug"); - - Box::pin(async move { - let bundle = T::init(serialized, graphics).await?; - let applier: Box<dyn FnOnce(&mut hecs::EntityBuilder) + Send + Sync> = - Box::new(move |builder: &mut hecs::EntityBuilder| { - builder.add_bundle(bundle); - }); - Ok(applier) - }) - })); - - self.defaults.insert(type_id, Box::new(|| { - Box::new(T::SerializedForm::default()) - })); - - self.removers.insert(type_id, Box::new(|world, entity| { - let _ = world.remove_one::<T>(entity); - })); - - self.finders.insert(type_id, Box::new(|world| { - world - .query::<(hecs::Entity, &T)>() - .iter() - .map(|(entity, _)| entity) - .collect() - })); - - self.updaters.insert(type_id, Box::new(|world, physics, dt, graphics| { - let world_ptr = world as *mut hecs::World; // safe assuming world is kept at the DropbearAppBuilder application level (lifetime) - let mut query = world.query::<(hecs::Entity, &mut T)>(); - for (entity, component) in query.iter() { - let world_ref = unsafe { &*world_ptr }; - component.update_component(world_ref, physics, entity, dt, graphics.clone()); - } - })); - - self.inspectors.insert(type_id, Box::new(|world, entity, ui, graphics| { - if let Ok(mut comp) = world.get::<&mut T>(entity) { - comp.inspect(ui, graphics); - } - })); + self.extractors.insert( + type_id, + Box::new(|world, entity| { + let Ok(c) = world.get::<&T>(entity) else { + return None; + }; + Some(c.save(world, entity)) + }), + ); + + self.loaders.insert( + serialized_type_id, + Box::new(|serialized, graphics| { + let serialized = serialized + .as_any() + .downcast_ref::<T::SerializedForm>() + .expect("type mismatch in loader — registry bug"); + + Box::pin(async move { + let bundle = T::init(serialized, graphics).await?; + let applier: Box<dyn FnOnce(&mut hecs::EntityBuilder) + Send + Sync> = + Box::new(move |builder: &mut hecs::EntityBuilder| { + builder.add_bundle(bundle); + }); + Ok(applier) + }) + }), + ); + + self.defaults + .insert(type_id, Box::new(|| Box::new(T::SerializedForm::default()))); + + self.removers.insert( + type_id, + Box::new(|world, entity| { + let _ = world.remove_one::<T>(entity); + }), + ); + + self.finders.insert( + type_id, + Box::new(|world| { + world + .query::<(hecs::Entity, &T)>() + .iter() + .map(|(entity, _)| entity) + .collect() + }), + ); + + self.updaters.insert( + type_id, + Box::new(|world, physics, dt, graphics| { + let world_ptr = world as *mut hecs::World; // safe assuming world is kept at the DropbearAppBuilder application level (lifetime) + let mut query = world.query::<(hecs::Entity, &mut T)>(); + for (entity, component) in query.iter() { + let world_ref = unsafe { &*world_ptr }; + component.update_component(world_ref, physics, entity, dt, graphics.clone()); + } + }), + ); + + self.inspectors.insert( + type_id, + Box::new(|world, entity, ui, graphics| { + if let Ok(mut comp) = world.get::<&mut T>(entity) { + comp.inspect(ui, graphics); + } + }), + ); } /// Get descriptor for a specific component type @@ -282,12 +323,7 @@ impl ComponentRegistry { } /// Removes a component by numeric id from an entity. - pub fn remove_component_by_id( - &self, - world: &mut hecs::World, - entity: hecs::Entity, - id: u64, - ) { + pub fn remove_component_by_id(&self, world: &mut hecs::World, entity: hecs::Entity, id: u64) { if let Some(type_id) = self.type_id_from_numeric_id(id) { if let Some(remover) = self.removers.get(&type_id) { remover(world, entity); @@ -296,11 +332,7 @@ impl ComponentRegistry { } /// Finds entities with the component matching a numeric id. - pub fn find_entities_by_numeric_id( - &self, - world: &hecs::World, - id: u64, - ) -> Vec<hecs::Entity> { + pub fn find_entities_by_numeric_id(&self, world: &hecs::World, id: u64) -> Vec<hecs::Entity> { self.type_id_from_numeric_id(id) .and_then(|type_id| self.finders.get(&type_id)) .map(|finder| finder(world)) @@ -442,7 +474,14 @@ pub trait Component: Sync + Send { ) -> ComponentInitFuture<'a, Self>; /// Called every frame to update the component's state. - fn update_component(&mut self, world: &hecs::World, physics: &mut PhysicsState, entity: hecs::Entity, dt: f32, graphics: Arc<SharedGraphicsContext>); + fn update_component( + &mut self, + world: &hecs::World, + physics: &mut PhysicsState, + entity: hecs::Entity, + dt: f32, + graphics: Arc<SharedGraphicsContext>, + ); /// Called when saving the scene to disk. Returns the [`Self::SerializedForm`] of the component that can be /// saved to disk. @@ -460,7 +499,7 @@ impl SerializedComponent for SerializedMeshRenderer {} // sample for MeshRenderer impl Component for MeshRenderer { type SerializedForm = SerializedMeshRenderer; - type RequiredComponentTypes = (Self, ); + type RequiredComponentTypes = (Self,); fn descriptor() -> ComponentDescriptor { ComponentDescriptor { @@ -509,7 +548,7 @@ impl Component for MeshRenderer { } ResourceReferenceType::ProcObj(obj) => { obj.build_model(graphics.clone(), None, None, ASSET_REGISTRY.clone()) - }, + } }; let mut renderer = MeshRenderer::from_handle(handle); @@ -576,11 +615,18 @@ impl Component for MeshRenderer { } } - Ok((renderer, )) + Ok((renderer,)) }) } - fn update_component(&mut self, world: &World, _physics: &mut PhysicsState, entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) { + fn update_component( + &mut self, + world: &World, + _physics: &mut PhysicsState, + entity: Entity, + _dt: f32, + _graphics: Arc<SharedGraphicsContext>, + ) { if let Ok(transform) = world.query_one::<&EntityTransform>(entity).get() { self.update(&transform.propagate(&world, entity)); } @@ -593,7 +639,10 @@ impl Component for MeshRenderer { (model.label.clone(), model.path.clone()) } else { if !self.model().is_null() { - log::warn!("MeshRenderer save: missing model handle {} in registry", self.model().id); + log::warn!( + "MeshRenderer save: missing model handle {} in registry", + self.model().id + ); } ("None".to_string(), ResourceReference::default()) }; @@ -602,30 +651,42 @@ impl Component for MeshRenderer { for (label, mat) in &self.material_snapshot { let diffuse_texture = mat.diffuse_texture.reference.clone(); let normal_texture = mat.normal_texture.reference.clone(); - let emissive_texture = mat.emissive_texture.as_ref().and_then(|t| t.reference.clone()); - let occlusion_texture = mat.occlusion_texture.as_ref().and_then(|t| t.reference.clone()); - let metallic_roughness_texture = mat.metallic_roughness_texture.as_ref().and_then(|t| t.reference.clone()); - - texture_override.insert(label.to_string(), SerializedMaterialCustomisation { - label: label.clone(), - diffuse_texture, - tint: mat.tint, - emissive_factor: mat.emissive_factor, - metallic_factor: mat.metallic_factor, - roughness_factor: mat.roughness_factor, - alpha_mode: mat.alpha_mode, - alpha_cutoff: mat.alpha_cutoff, - double_sided: mat.double_sided, - occlusion_strength: mat.occlusion_strength, - normal_scale: mat.normal_scale, - uv_tiling: mat.uv_tiling, - texture_tag: mat.texture_tag.clone(), - wrap_mode: mat.wrap_mode, - emissive_texture, - normal_texture, - occlusion_texture, - metallic_roughness_texture, - }); + let emissive_texture = mat + .emissive_texture + .as_ref() + .and_then(|t| t.reference.clone()); + let occlusion_texture = mat + .occlusion_texture + .as_ref() + .and_then(|t| t.reference.clone()); + let metallic_roughness_texture = mat + .metallic_roughness_texture + .as_ref() + .and_then(|t| t.reference.clone()); + + texture_override.insert( + label.to_string(), + SerializedMaterialCustomisation { + label: label.clone(), + diffuse_texture, + tint: mat.tint, + emissive_factor: mat.emissive_factor, + metallic_factor: mat.metallic_factor, + roughness_factor: mat.roughness_factor, + alpha_mode: mat.alpha_mode, + alpha_cutoff: mat.alpha_cutoff, + double_sided: mat.double_sided, + occlusion_strength: mat.occlusion_strength, + normal_scale: mat.normal_scale, + uv_tiling: mat.uv_tiling, + texture_tag: mat.texture_tag.clone(), + wrap_mode: mat.wrap_mode, + emissive_texture, + normal_texture, + occlusion_texture, + metallic_roughness_texture, + }, + ); } Box::new(SerializedMeshRenderer { @@ -685,13 +746,8 @@ impl InspectableComponent for MeshRenderer { let proc_obj = ProcedurallyGeneratedObject::cuboid(size_vec); if force_new { - let mut model = proc_obj.construct( - graphics.clone(), - None, - None, - None, - ASSET_REGISTRY.clone(), - ); + let mut model = + proc_obj.construct(graphics.clone(), None, None, None, ASSET_REGISTRY.clone()); let mut hasher = DefaultHasher::new(); model.hash.hash(&mut hasher); if let Ok(duration) = SystemTime::now().duration_since(UNIX_EPOCH) { @@ -707,17 +763,15 @@ impl InspectableComponent for MeshRenderer { }; renderer.set_model(handle); } else if current_model.is_null() { - let handle = proc_obj.build_model( - graphics.clone(), - None, - None, - ASSET_REGISTRY.clone(), - ); + let handle = + proc_obj.build_model(graphics.clone(), None, None, ASSET_REGISTRY.clone()); renderer.set_model(handle); } else { let existing_label = { let asset = ASSET_REGISTRY.read(); - asset.get_model(current_model).map(|model| model.label.clone()) + asset + .get_model(current_model) + .map(|model| model.label.clone()) }; if let Some(existing_label) = existing_label { @@ -734,16 +788,12 @@ impl InspectableComponent for MeshRenderer { let mut asset = ASSET_REGISTRY.write(); asset.update_model(current_model, model); } else { - let handle = proc_obj.build_model( - graphics.clone(), - None, - None, - ASSET_REGISTRY.clone(), - ); + let handle = + proc_obj.build_model(graphics.clone(), None, None, ASSET_REGISTRY.clone()); renderer.set_model(handle); } } - + renderer.reset_texture_override(); }; @@ -837,7 +887,10 @@ impl InspectableComponent for MeshRenderer { .show_ui(ui, |ui| { if ui .selectable_label( - matches!(model_reference.ref_type, ResourceReferenceType::None), + matches!( + model_reference.ref_type, + ResourceReferenceType::None + ), "None", ) .clicked() @@ -881,14 +934,16 @@ impl InspectableComponent for MeshRenderer { let pointer_released = ui.input(|i| i.pointer.any_released()); if pointer_released && response.hovered() { let drag_id = egui::Id::new(DRAGGED_ASSET_ID); - let dragged_reference = ui - .ctx() - .data_mut(|d| d.get_temp::<Option<ResourceReference>>(drag_id).unwrap_or(None)); + let dragged_reference = ui.ctx().data_mut(|d| { + d.get_temp::<Option<ResourceReference>>(drag_id) + .unwrap_or(None) + }); if let Some(reference) = dragged_reference { if let Some(uri) = reference.as_uri() { if is_probably_model_uri(uri) { - if let Some(handle) = - ASSET_REGISTRY.read().get_model_handle_by_reference(&reference) + if let Some(handle) = ASSET_REGISTRY + .read() + .get_model_handle_by_reference(&reference) { if let Some(model) = ASSET_REGISTRY.read().get_model(handle) { if model.label.eq_ignore_ascii_case("light cube") { @@ -957,46 +1012,58 @@ impl InspectableComponent for MeshRenderer { if let Some(mut size) = proc_obj_size(obj) { ui.label(RichText::new("Cuboid").strong()); ui.horizontal(|ui| { - ui.label("Extents:"); - let mut changed = false; - ui.label("X"); - changed |= ui - .add(DragValue::new(&mut size[0]).speed(0.05).range(0.01..=10_000.0)) - .changed(); - ui.label("Y"); - changed |= ui - .add(DragValue::new(&mut size[1]).speed(0.05).range(0.01..=10_000.0)) - .changed(); - ui.label("Z"); - changed |= ui - .add(DragValue::new(&mut size[2]).speed(0.05).range(0.01..=10_000.0)) - .changed(); - - if changed { - // Preserve material customizations across cuboid size change - let saved_materials = self.material_snapshot.clone(); - apply_cuboid(self, size, false); - // Re-apply material customizations to the new model - for (name, saved_mat) in saved_materials { - if let Some(mat) = self.material_snapshot.get_mut(&name) { - mat.tint = saved_mat.tint; - mat.emissive_factor = saved_mat.emissive_factor; - mat.metallic_factor = saved_mat.metallic_factor; - mat.roughness_factor = saved_mat.roughness_factor; - mat.alpha_mode = saved_mat.alpha_mode; - mat.alpha_cutoff = saved_mat.alpha_cutoff; - mat.double_sided = saved_mat.double_sided; - mat.occlusion_strength = saved_mat.occlusion_strength; - mat.normal_scale = saved_mat.normal_scale; - mat.uv_tiling = saved_mat.uv_tiling; - mat.wrap_mode = saved_mat.wrap_mode; - mat.texture_tag = saved_mat.texture_tag.clone(); + ui.label("Extents:"); + let mut changed = false; + ui.label("X"); + changed |= ui + .add( + DragValue::new(&mut size[0]) + .speed(0.05) + .range(0.01..=10_000.0), + ) + .changed(); + ui.label("Y"); + changed |= ui + .add( + DragValue::new(&mut size[1]) + .speed(0.05) + .range(0.01..=10_000.0), + ) + .changed(); + ui.label("Z"); + changed |= ui + .add( + DragValue::new(&mut size[2]) + .speed(0.05) + .range(0.01..=10_000.0), + ) + .changed(); + + if changed { + // Preserve material customizations across cuboid size change + let saved_materials = self.material_snapshot.clone(); + apply_cuboid(self, size, false); + // Re-apply material customizations to the new model + for (name, saved_mat) in saved_materials { + if let Some(mat) = self.material_snapshot.get_mut(&name) { + mat.tint = saved_mat.tint; + mat.emissive_factor = saved_mat.emissive_factor; + mat.metallic_factor = saved_mat.metallic_factor; + mat.roughness_factor = saved_mat.roughness_factor; + mat.alpha_mode = saved_mat.alpha_mode; + mat.alpha_cutoff = saved_mat.alpha_cutoff; + mat.double_sided = saved_mat.double_sided; + mat.occlusion_strength = saved_mat.occlusion_strength; + mat.normal_scale = saved_mat.normal_scale; + mat.uv_tiling = saved_mat.uv_tiling; + mat.wrap_mode = saved_mat.wrap_mode; + mat.texture_tag = saved_mat.texture_tag.clone(); + } } } - } - }); + }); - ui.separator(); + ui.separator(); } } @@ -1018,7 +1085,9 @@ impl InspectableComponent for MeshRenderer { material.emissive_factor[1], material.emissive_factor[2], ]; - if egui::color_picker::color_edit_button_rgb(ui, &mut emissive_rgb).changed() { + if egui::color_picker::color_edit_button_rgb(ui, &mut emissive_rgb) + .changed() + { material.emissive_factor[0] = emissive_rgb[0]; material.emissive_factor[1] = emissive_rgb[1]; material.emissive_factor[2] = emissive_rgb[2]; @@ -1026,30 +1095,38 @@ impl InspectableComponent for MeshRenderer { ui.horizontal(|ui| { ui.label("Metallic"); - ui.add(DragValue::new(&mut material.metallic_factor) - .speed(0.01) - .range(0.0..=1.0)); + ui.add( + DragValue::new(&mut material.metallic_factor) + .speed(0.01) + .range(0.0..=1.0), + ); }); ui.horizontal(|ui| { ui.label("Roughness"); - ui.add(DragValue::new(&mut material.roughness_factor) - .speed(0.01) - .range(0.0..=1.0)); + ui.add( + DragValue::new(&mut material.roughness_factor) + .speed(0.01) + .range(0.0..=1.0), + ); }); ui.horizontal(|ui| { ui.label("Occlusion Strength"); - ui.add(DragValue::new(&mut material.occlusion_strength) - .speed(0.01) - .range(0.0..=1.0)); + ui.add( + DragValue::new(&mut material.occlusion_strength) + .speed(0.01) + .range(0.0..=1.0), + ); }); ui.horizontal(|ui| { ui.label("Normal Scale"); - ui.add(DragValue::new(&mut material.normal_scale) - .speed(0.01) - .range(0.0..=10.0)); + ui.add( + DragValue::new(&mut material.normal_scale) + .speed(0.01) + .range(0.0..=10.0), + ); }); ui.horizontal(|ui| { @@ -1082,9 +1159,8 @@ impl InspectableComponent for MeshRenderer { let mut cutoff = material.alpha_cutoff.unwrap_or(0.5); ui.horizontal(|ui| { ui.label("Alpha Cutoff"); - if ui.add(DragValue::new(&mut cutoff) - .speed(0.01) - .range(0.0..=1.0)) + if ui + .add(DragValue::new(&mut cutoff).speed(0.01).range(0.0..=1.0)) .changed() { material.alpha_cutoff = Some(cutoff); @@ -1156,18 +1232,21 @@ impl InspectableComponent for MeshRenderer { // Helper to create texture slot UI let texture_slot = |ui: &mut egui::Ui, - label: &str, - _id_salt: &str, - current_texture: &Texture, - _graphics: Arc<SharedGraphicsContext>, - dragging_valid: bool| -> Option<Texture> { + label: &str, + _id_salt: &str, + current_texture: &Texture, + _graphics: Arc<SharedGraphicsContext>, + dragging_valid: bool| + -> Option<Texture> { let mut result = None; ui.horizontal(|ui| { ui.label(label); - - let texture_label = current_texture.label.clone() + + let texture_label = current_texture + .label + .clone() .unwrap_or_else(|| "Default".to_string()); - + let (rect, response) = ui.allocate_exact_size( egui::vec2(ui.available_width().min(150.0), 24.0), egui::Sense::click(), @@ -1184,7 +1263,8 @@ impl InspectableComponent for MeshRenderer { }; ui.painter().rect_filled(rect, 2.0, fill); ui.painter().rect_stroke( - rect, 2.0, + rect, + 2.0, ui.visuals().widgets.inactive.bg_stroke, egui::StrokeKind::Inside, ); @@ -1200,17 +1280,20 @@ impl InspectableComponent for MeshRenderer { let pointer_released = ui.input(|i| i.pointer.any_released()); if pointer_released && response.hovered() { let drag_id = egui::Id::new(DRAGGED_ASSET_ID); - let dragged_reference = ui - .ctx() - .data_mut(|d| d.get_temp::<Option<ResourceReference>>(drag_id).unwrap_or(None)); + let dragged_reference = ui.ctx().data_mut(|d| { + d.get_temp::<Option<ResourceReference>>(drag_id) + .unwrap_or(None) + }); if let Some(reference) = dragged_reference { if let Some(uri) = reference.as_uri() { if is_probably_texture_uri(uri) { // Try to find the texture in the registry - if let Some(handle) = ASSET_REGISTRY.read() + if let Some(handle) = ASSET_REGISTRY + .read() .get_texture_handle_by_reference(&reference) { - if let Some(tex) = ASSET_REGISTRY.read() + if let Some(tex) = ASSET_REGISTRY + .read() .get_texture(handle) .cloned() { @@ -1218,7 +1301,10 @@ impl InspectableComponent for MeshRenderer { } } ui.ctx().data_mut(|d| { - d.insert_temp(drag_id, None::<ResourceReference>) + d.insert_temp( + drag_id, + None::<ResourceReference>, + ) }); } } @@ -1234,7 +1320,8 @@ impl InspectableComponent for MeshRenderer { // Diffuse texture slot if let Some(new_tex) = texture_slot( - ui, "Diffuse", + ui, + "Diffuse", &format!("diffuse_tex_{}", material_name), &material.diffuse_texture, graphics.clone(), @@ -1245,7 +1332,8 @@ impl InspectableComponent for MeshRenderer { // Normal texture slot if let Some(new_tex) = texture_slot( - ui, "Normal", + ui, + "Normal", &format!("normal_tex_{}", material_name), &material.normal_texture, graphics.clone(), @@ -1257,12 +1345,13 @@ impl InspectableComponent for MeshRenderer { // Emissive texture slot (optional) ui.horizontal(|ui| { ui.label("Emissive"); - - let texture_label = material.emissive_texture + + let texture_label = material + .emissive_texture .as_ref() .and_then(|t| t.label.clone()) .unwrap_or_else(|| "None".to_string()); - + let (rect, response) = ui.allocate_exact_size( egui::vec2(ui.available_width().min(150.0), 24.0), egui::Sense::click(), @@ -1279,7 +1368,8 @@ impl InspectableComponent for MeshRenderer { }; ui.painter().rect_filled(rect, 2.0, fill); ui.painter().rect_stroke( - rect, 2.0, + rect, + 2.0, ui.visuals().widgets.inactive.bg_stroke, egui::StrokeKind::Inside, ); @@ -1294,16 +1384,19 @@ impl InspectableComponent for MeshRenderer { let pointer_released = ui.input(|i| i.pointer.any_released()); if pointer_released && response.hovered() { let drag_id = egui::Id::new(DRAGGED_ASSET_ID); - let dragged_reference = ui - .ctx() - .data_mut(|d| d.get_temp::<Option<ResourceReference>>(drag_id).unwrap_or(None)); + let dragged_reference = ui.ctx().data_mut(|d| { + d.get_temp::<Option<ResourceReference>>(drag_id) + .unwrap_or(None) + }); if let Some(reference) = dragged_reference { if let Some(uri) = reference.as_uri() { if is_probably_texture_uri(uri) { - if let Some(handle) = ASSET_REGISTRY.read() + if let Some(handle) = ASSET_REGISTRY + .read() .get_texture_handle_by_reference(&reference) { - if let Some(tex) = ASSET_REGISTRY.read() + if let Some(tex) = ASSET_REGISTRY + .read() .get_texture(handle) .cloned() { @@ -1323,7 +1416,8 @@ impl InspectableComponent for MeshRenderer { } // Clear button for optional texture - if material.emissive_texture.is_some() && ui.small_button("X").clicked() { + if material.emissive_texture.is_some() && ui.small_button("X").clicked() + { material.emissive_texture = None; } }); @@ -1331,12 +1425,13 @@ impl InspectableComponent for MeshRenderer { // Metallic/Roughness texture slot (optional) ui.horizontal(|ui| { ui.label("Metal/Rough"); - - let texture_label = material.metallic_roughness_texture + + let texture_label = material + .metallic_roughness_texture .as_ref() .and_then(|t| t.label.clone()) .unwrap_or_else(|| "None".to_string()); - + let (rect, response) = ui.allocate_exact_size( egui::vec2(ui.available_width().min(150.0), 24.0), egui::Sense::click(), @@ -1353,7 +1448,8 @@ impl InspectableComponent for MeshRenderer { }; ui.painter().rect_filled(rect, 2.0, fill); ui.painter().rect_stroke( - rect, 2.0, + rect, + 2.0, ui.visuals().widgets.inactive.bg_stroke, egui::StrokeKind::Inside, ); @@ -1368,16 +1464,19 @@ impl InspectableComponent for MeshRenderer { let pointer_released = ui.input(|i| i.pointer.any_released()); if pointer_released && response.hovered() { let drag_id = egui::Id::new(DRAGGED_ASSET_ID); - let dragged_reference = ui - .ctx() - .data_mut(|d| d.get_temp::<Option<ResourceReference>>(drag_id).unwrap_or(None)); + let dragged_reference = ui.ctx().data_mut(|d| { + d.get_temp::<Option<ResourceReference>>(drag_id) + .unwrap_or(None) + }); if let Some(reference) = dragged_reference { if let Some(uri) = reference.as_uri() { if is_probably_texture_uri(uri) { - if let Some(handle) = ASSET_REGISTRY.read() + if let Some(handle) = ASSET_REGISTRY + .read() .get_texture_handle_by_reference(&reference) { - if let Some(tex) = ASSET_REGISTRY.read() + if let Some(tex) = ASSET_REGISTRY + .read() .get_texture(handle) .cloned() { @@ -1396,7 +1495,9 @@ impl InspectableComponent for MeshRenderer { response.on_hover_text("Drop a texture here"); } - if material.metallic_roughness_texture.is_some() && ui.small_button("X").clicked() { + if material.metallic_roughness_texture.is_some() + && ui.small_button("X").clicked() + { material.metallic_roughness_texture = None; } }); @@ -1404,12 +1505,13 @@ impl InspectableComponent for MeshRenderer { // Occlusion texture slot (optional) ui.horizontal(|ui| { ui.label("Occlusion"); - - let texture_label = material.occlusion_texture + + let texture_label = material + .occlusion_texture .as_ref() .and_then(|t| t.label.clone()) .unwrap_or_else(|| "None".to_string()); - + let (rect, response) = ui.allocate_exact_size( egui::vec2(ui.available_width().min(150.0), 24.0), egui::Sense::click(), @@ -1426,7 +1528,8 @@ impl InspectableComponent for MeshRenderer { }; ui.painter().rect_filled(rect, 2.0, fill); ui.painter().rect_stroke( - rect, 2.0, + rect, + 2.0, ui.visuals().widgets.inactive.bg_stroke, egui::StrokeKind::Inside, ); @@ -1441,16 +1544,19 @@ impl InspectableComponent for MeshRenderer { let pointer_released = ui.input(|i| i.pointer.any_released()); if pointer_released && response.hovered() { let drag_id = egui::Id::new(DRAGGED_ASSET_ID); - let dragged_reference = ui - .ctx() - .data_mut(|d| d.get_temp::<Option<ResourceReference>>(drag_id).unwrap_or(None)); + let dragged_reference = ui.ctx().data_mut(|d| { + d.get_temp::<Option<ResourceReference>>(drag_id) + .unwrap_or(None) + }); if let Some(reference) = dragged_reference { if let Some(uri) = reference.as_uri() { if is_probably_texture_uri(uri) { - if let Some(handle) = ASSET_REGISTRY.read() + if let Some(handle) = ASSET_REGISTRY + .read() .get_texture_handle_by_reference(&reference) { - if let Some(tex) = ASSET_REGISTRY.read() + if let Some(tex) = ASSET_REGISTRY + .read() .get_texture(handle) .cloned() { @@ -1469,7 +1575,9 @@ impl InspectableComponent for MeshRenderer { response.on_hover_text("Drop a texture here"); } - if material.occlusion_texture.is_some() && ui.small_button("X").clicked() { + if material.occlusion_texture.is_some() + && ui.small_button("X").clicked() + { material.occlusion_texture = None; } }); @@ -1479,4 +1587,3 @@ impl InspectableComponent for MeshRenderer { }); } } - @@ -1,9 +1,7 @@ -//! The eucalyptus configuration files and its metadata. +//! The eucalyptus configuration files and its metadata. use crate::runtime::{Authoring, RuntimeSettings}; use crate::scene::SceneConfig; -use crate::states::{ - File, Folder, Node, RESOURCES, ResourceType, SCENES, SOURCE, -}; +use crate::states::{File, Folder, Node, RESOURCES, ResourceType, SCENES, SOURCE}; use chrono::Utc; use ron::ser::PrettyConfig; use serde::{Deserialize, Serialize}; @@ -251,7 +249,10 @@ impl ProjectConfig { let scene_entry = scene_entry?; let path = scene_entry.path(); - if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("eucs") && path.extension().and_then(|s| s.to_str()) != Some("bak") { + if path.is_file() + && path.extension().and_then(|s| s.to_str()) == Some("eucs") + && path.extension().and_then(|s| s.to_str()) != Some("bak") + { match SceneConfig::read_from(&path) { Ok(scene) => { log::debug!("Loaded scene config: {}", scene.scene_name); @@ -263,16 +264,28 @@ impl ProjectConfig { log::warn!("Scene file {:?} not found", path); } else { if let Some(first) = scene_configs.first() { - log::warn!("Unable to load scene {}: [{:?}], loading the first available scene [{}]", path.display(), &e, first.scene_name); + log::warn!( + "Unable to load scene {}: [{:?}], loading the first available scene [{}]", + path.display(), + &e, + first.scene_name + ); } else { - if let Some(scene) = deal_with_bad_scene(&path, &e, &project_root) { + if let Some(scene) = + deal_with_bad_scene(&path, &e, &project_root) + { scene_configs.push(scene); } } } } else { if let Some(first) = scene_configs.first() { - log::warn!("Unable to load scene {}: [{:?}], loading the first available scene [{}]", path.display(), &e, first.scene_name); + log::warn!( + "Unable to load scene {}: [{:?}], loading the first available scene [{}]", + path.display(), + &e, + first.scene_name + ); } else { if let Some(scene) = deal_with_bad_scene(&path, &e, &project_root) { scene_configs.push(scene); @@ -17,37 +17,29 @@ pub mod shared { Err(DropbearNativeError::EntityNotFound) } - pub fn quit(command_buffer: &crossbeam_channel::Sender<CommandBuffer>) -> DropbearNativeResult<()> { - command_buffer.send(CommandBuffer::Quit) + 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", - ), + kotlin(class = "com.dropbear.DropbearEngineNative", func = "getEntity",), c )] fn get_entity( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, + #[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 -)] +#[dropbear_macro::export(kotlin(class = "com.dropbear.DropbearEngineNative", func = "quit",), c)] fn quit( - #[dropbear_macro::define(CommandBufferPtr)] - command_buffer: &CommandBufferUnwrapped, + #[dropbear_macro::define(CommandBufferPtr)] command_buffer: &CommandBufferUnwrapped, ) -> DropbearNativeResult<()> { shared::quit(command_buffer) -} +} @@ -4,45 +4,51 @@ use crate::scripting::result::DropbearNativeResult; use crate::states::Label; #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.LabelNative", func = "labelExistsForEntity"), + 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 + #[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.components.EntityRefNative", func = "getEntityLabel"), + kotlin( + class = "com.dropbear.components.EntityRefNative", + func = "getEntityLabel" + ), c )] fn get_label( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropbear_macro::entity] - entity: hecs::Entity + #[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.components.EntityRefNative", func = "getChildren"), + kotlin( + class = "com.dropbear.components.EntityRefNative", + func = "getChildren" + ), c )] fn get_children( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropbear_macro::entity] - entity: hecs::Entity + #[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<_>>(); + 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 @@ -51,18 +57,18 @@ fn get_children( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.EntityRefNative", func = "getChildByLabel"), + kotlin( + class = "com.dropbear.components.EntityRefNative", + func = "getChildByLabel" + ), c )] fn get_child_by_label( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropbear_macro::entity] - entity: hecs::Entity, + #[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() - { + if let Ok(children) = world.query_one::<&Children>(entity).get() { for child in children.children() { if let Ok(label) = world.get::<&Label>(entity) { if label.as_str() == target { @@ -85,14 +91,12 @@ fn get_child_by_label( c )] fn get_parent( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropbear_macro::entity] - entity: hecs::Entity, + #[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,4 +1,4 @@ -//! The hierarchy of an entity and a scene. +//! The hierarchy of an entity and a scene. use crate::states::Label; use dropbear_engine::entity::{EntityTransform, Transform}; @@ -2,21 +2,21 @@ pub mod gamepad; +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::JNIEnv; +use jni::objects::JObject; +use jni::sys::jlong; use std::sync::Arc; use std::{ collections::{HashMap, HashSet}, time::{Duration, Instant}, }; -use glam::Vec2; use winit::window::Window; use winit::{event::MouseButton, keyboard::KeyCode}; -use crate::scripting::jni::utils::ToJObject; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; -use jni::objects::JObject; -use jni::sys::jlong; -use jni::JNIEnv; #[allow(dead_code)] #[derive(Clone, Debug)] @@ -26,7 +26,7 @@ pub struct Gamepad { right_stick_pos: Vec2, } -/// Shows the information about the input at that current time. +/// Shows the information about the input at that current time. #[derive(Clone, Debug)] pub struct InputState { pub window: Option<Arc<Window>>, @@ -116,10 +116,10 @@ impl InputState { } pub mod shared { - use crossbeam_channel::Sender; + use super::*; use crate::command::{CommandBuffer, WindowCommand}; use crate::types::NVector2; - use super::*; + use crossbeam_channel::Sender; pub fn map_ordinal_to_mouse_button(ordinal: i32) -> Option<MouseButton> { match ordinal { @@ -157,21 +157,30 @@ pub mod shared { } pub fn get_mouse_delta(input: &InputState) -> NVector2 { - input.mouse_delta.map(NVector2::from).unwrap_or(NVector2 { x: 0.0, y: 0.0 }) + 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 }) + 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 + locked: bool, ) -> DropbearNativeResult<()> { input.is_cursor_locked = locked; - sender.send(CommandBuffer::WindowCommand(WindowCommand::WindowGrab(locked))) + sender + .send(CommandBuffer::WindowCommand(WindowCommand::WindowGrab( + locked, + ))) .map_err(|_| DropbearNativeError::SendError)?; Ok(()) @@ -180,16 +189,21 @@ pub mod shared { pub fn set_cursor_hidden( input: &mut InputState, sender: &Sender<CommandBuffer>, - hidden: bool + hidden: bool, ) -> DropbearNativeResult<()> { input.is_cursor_hidden = hidden; - sender.send(CommandBuffer::WindowCommand(WindowCommand::HideCursor(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() + input + .connected_gamepads + .iter() .map(|id| Into::<usize>::into(*id) as u64) .collect() } @@ -214,12 +228,14 @@ impl ToJObject for ConnectedGamepadIds { } #[dropbear_macro::export( - kotlin(class = "com.dropbear.input.InputStateNative", func = "printInputState"), + kotlin( + class = "com.dropbear.input.InputStateNative", + func = "printInputState" + ), c )] fn print_input_state( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] - input: &InputState, + #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &InputState, ) -> DropbearNativeResult<()> { println!("Input State: {:?}", input); Ok(()) @@ -230,31 +246,34 @@ fn print_input_state( c )] fn is_key_pressed( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] - input: &InputState, + #[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"), + kotlin( + class = "com.dropbear.input.InputStateNative", + func = "getMousePosition" + ), c )] fn get_mouse_position( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] - input: &InputState, + #[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"), + kotlin( + class = "com.dropbear.input.InputStateNative", + func = "isMouseButtonPressed" + ), c )] fn is_mouse_button_pressed( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] - input: &InputState, + #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &InputState, button_ordinal: i32, ) -> DropbearNativeResult<bool> { Ok(shared::is_mouse_button_pressed(input, button_ordinal)) @@ -265,8 +284,7 @@ fn is_mouse_button_pressed( c )] fn get_mouse_delta( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] - input: &InputState, + #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &InputState, ) -> DropbearNativeResult<crate::types::NVector2> { Ok(shared::get_mouse_delta(input)) } @@ -276,33 +294,36 @@ fn get_mouse_delta( c )] fn is_cursor_locked( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] - input: &InputState, + #[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"), + 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, + #[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"), + kotlin( + class = "com.dropbear.input.InputStateNative", + func = "getLastMousePos" + ), c )] fn get_last_mouse_pos( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] - input: &InputState, + #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &InputState, ) -> DropbearNativeResult<crate::types::NVector2> { Ok(shared::get_last_mouse_pos(input)) } @@ -312,35 +333,38 @@ fn get_last_mouse_pos( c )] fn is_cursor_hidden( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] - input: &InputState, + #[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"), + 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, + #[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"), + kotlin( + class = "com.dropbear.input.InputStateNative", + func = "getConnectedGamepads" + ), c )] fn get_connected_gamepads( - #[dropbear_macro::define(crate::ptr::InputStatePtr)] - input: &InputState, + #[dropbear_macro::define(crate::ptr::InputStatePtr)] input: &InputState, ) -> DropbearNativeResult<ConnectedGamepadIds> { Ok(ConnectedGamepadIds { ids: shared::get_connected_gamepads(input), }) -} +} @@ -4,13 +4,13 @@ use crate::scripting::result::DropbearNativeResult; use crate::types::NVector2; pub mod shared { - use jni::JNIEnv; - use jni::objects::{JObject, JValue}; 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::JNIEnv; + use jni::objects::{JObject, JValue}; fn map_int_to_gamepad_button(ordinal: i32) -> Option<dropbear_engine::gilrs::Button> { match ordinal { @@ -38,11 +38,22 @@ pub mod shared { } } - 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 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 { + 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; }; @@ -56,46 +67,44 @@ pub mod shared { 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 - } + 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 } + 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 - } + 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 } + NVector2 { + x: x as f64, + y: y as f64, + } } impl ToJObject for NVector2 { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let cls = env.find_class("com/dropbear/math/Vector2d") + let cls = env.find_class("com/dropbear/math/Vector2d").map_err(|e| { + eprintln!("Could not find Vector2d class: {:?}", e); + DropbearNativeError::GenericError + })?; + + let obj = env + .new_object( + cls, + "(DD)V", + &[JValue::Double(self.x), JValue::Double(self.y)], + ) .map_err(|e| { - eprintln!("Could not find Vector2d class: {:?}", e); + eprintln!("Failed to create Vector2d object: {:?}", e); DropbearNativeError::GenericError })?; - let obj = env.new_object( - cls, - "(DD)V", - &[ - JValue::Double(self.x), - JValue::Double(self.y) - ] - ).map_err(|e| { - eprintln!("Failed to create Vector2d object: {:?}", e); - DropbearNativeError::GenericError - })?; - Ok(obj) } } @@ -103,7 +112,7 @@ pub mod shared { impl FromJObject for NVector2 { fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> where - Self: Sized + Self: Sized, { let x_val = env .get_field(obj, "x", "D") @@ -117,47 +126,54 @@ pub mod shared { .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - Ok(NVector2 { - x: x_val, - y: y_val, - }) + Ok(NVector2 { x: x_val, y: y_val }) } } } #[dropbear_macro::export( - kotlin(class = "com.dropbear.input.GamepadNative", func = "isGamepadButtonPressed"), + kotlin( + class = "com.dropbear.input.GamepadNative", + func = "isGamepadButtonPressed" + ), c )] fn is_button_pressed( - #[dropbear_macro::define(InputStatePtr)] - input: &InputState, + #[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)) + Ok(shared::is_gamepad_button_pressed( + &input, + gamepad_id, + button_ordinal, + )) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.input.GamepadNative", func = "getLeftStickPosition"), + kotlin( + class = "com.dropbear.input.GamepadNative", + func = "getLeftStickPosition" + ), c )] fn get_left_stick_position( - #[dropbear_macro::define(InputStatePtr)] - input: &InputState, + #[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"), + kotlin( + class = "com.dropbear.input.GamepadNative", + func = "getRightStickPosition" + ), c )] fn get_right_stick_position( - #[dropbear_macro::define(InputStatePtr)] - input: &InputState, + #[dropbear_macro::define(InputStatePtr)] input: &InputState, gamepad_id: u64, ) -> DropbearNativeResult<NVector2> { Ok(shared::get_right_stick(&input, gamepad_id)) -} +} @@ -1,41 +1,41 @@ -pub mod lighting; +pub mod animation; +pub mod asset; pub mod camera; +pub mod command; +pub mod component; pub mod config; +pub mod engine; +pub mod entity; pub mod hierarchy; pub mod input; +pub mod lighting; pub mod logging; +pub mod mesh; +pub mod physics; +pub mod properties; pub mod ptr; pub mod runtime; pub mod scene; pub mod scripting; pub mod states; -pub mod utils; -pub mod command; -pub mod physics; -pub mod types; -pub mod properties; -pub mod mesh; -pub mod entity; -pub mod engine; pub mod transform; -pub mod asset; -pub mod component; -pub mod animation; +pub mod types; +pub mod utils; pub use dropbear_macro as macros; -pub use egui; -pub use rapier3d; +use crate::component::ComponentRegistry; +use crate::physics::collider::ColliderGroup; +use crate::physics::kcc::KCC; +use crate::physics::rigidbody::RigidBody; +use crate::states::Script; use dropbear_engine::animation::AnimationComponent; use dropbear_engine::camera::Camera; use dropbear_engine::entity::{EntityTransform, MeshRenderer}; use dropbear_engine::lighting::Light; +pub use egui; use properties::CustomProperties; -use crate::component::ComponentRegistry; -use crate::physics::collider::ColliderGroup; -use crate::physics::kcc::KCC; -use crate::physics::rigidbody::RigidBody; -use crate::states::{Script}; +pub use rapier3d; /// The appdata directory for storing any information. /// @@ -46,9 +46,7 @@ pub const APP_INFO: app_dirs2::AppInfo = app_dirs2::AppInfo { }; /// Registers all available and potential serializers and deserializers of an entity. -pub fn register_components( - component_registry: &mut ComponentRegistry, -) { +pub fn register_components(component_registry: &mut ComponentRegistry) { component_registry.register::<EntityTransform>(); component_registry.register::<CustomProperties>(); component_registry.register::<Light>(); @@ -59,4 +57,4 @@ pub fn register_components( component_registry.register::<ColliderGroup>(); component_registry.register::<KCC>(); component_registry.register::<AnimationComponent>(); -} +} @@ -1,20 +1,22 @@ -use std::sync::Arc; -use egui::{CollapsingHeader, ComboBox, DragValue, Ui}; +use crate::component::{ + Component, ComponentDescriptor, ComponentInitFuture, InspectableComponent, 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::NVector3; use dropbear_engine::attenuation::ATTENUATION_PRESETS; use dropbear_engine::entity::{EntityTransform, Transform}; +use dropbear_engine::graphics::SharedGraphicsContext; 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::JNIEnv; -use dropbear_engine::graphics::SharedGraphicsContext; -use crate::component::{Component, ComponentDescriptor, ComponentInitFuture, InspectableComponent, SerializedComponent}; -use crate::states::SerializedLight; +use jni::objects::{JObject, JValue}; +use std::sync::Arc; #[typetag::serde] impl SerializedComponent for SerializedLight {} @@ -41,23 +43,33 @@ impl Component for Light { let light = Light::new( graphics.clone(), light_component.clone(), - Some(ser.label.as_str()) - ).await; + Some(ser.label.as_str()), + ) + .await; let transform = light_component.to_transform(); Ok((light, transform)) }) } - fn update_component(&mut self, world: &World, _physics: &mut crate::physics::PhysicsState, entity: Entity, _dt: f32, graphics: Arc<SharedGraphicsContext>) { + fn update_component( + &mut self, + world: &World, + _physics: &mut crate::physics::PhysicsState, + entity: Entity, + _dt: f32, + graphics: Arc<SharedGraphicsContext>, + ) { let synced = &mut self.component; if let Ok(entity_transform) = world.query_one::<&EntityTransform>(entity).get() { let transform = entity_transform.sync(); synced.position = transform.position; - synced.direction = (transform.rotation * DVec3::new(0.0, 0.0, -1.0)).normalize_or_zero(); + synced.direction = + (transform.rotation * DVec3::new(0.0, 0.0, -1.0)).normalize_or_zero(); } else if let Ok(transform) = world.query_one::<&Transform>(entity).get() { synced.position = transform.position; - synced.direction = (transform.rotation * DVec3::new(0.0, 0.0, -1.0)).normalize_or_zero(); + synced.direction = + (transform.rotation * DVec3::new(0.0, 0.0, -1.0)).normalize_or_zero(); } self.update(&graphics); @@ -74,314 +86,339 @@ impl Component for Light { impl InspectableComponent for Light { fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { - CollapsingHeader::new("Light").default_open(true).show(ui, |ui| { - ui.add_space(6.0); - ui.label("Uniform"); - - ui.label("Light Type"); - ComboBox::from_id_salt("Light Type").selected_text(format!("{}", self.component.light_type)).show_ui(ui, |ui| { - ui.selectable_value(&mut self.component.light_type, LightType::Directional, "Directional"); - ui.selectable_value(&mut self.component.light_type, LightType::Point, "Point"); - ui.selectable_value(&mut self.component.light_type, LightType::Spot, "Spot"); - }); + CollapsingHeader::new("Light") + .default_open(true) + .show(ui, |ui| { + ui.add_space(6.0); + ui.label("Uniform"); + + ui.label("Light Type"); + ComboBox::from_id_salt("Light Type") + .selected_text(format!("{}", self.component.light_type)) + .show_ui(ui, |ui| { + ui.selectable_value( + &mut self.component.light_type, + LightType::Directional, + "Directional", + ); + ui.selectable_value( + &mut self.component.light_type, + LightType::Point, + "Point", + ); + ui.selectable_value( + &mut self.component.light_type, + LightType::Spot, + "Spot", + ); + }); + + let mut display_pos = |yueye: &mut Ui| { + yueye.horizontal(|yueye| { + yueye.label("Position"); + yueye.add(DragValue::new(&mut self.component.position.x).speed(0.01)); + yueye.add(DragValue::new(&mut self.component.position.y).speed(0.01)); + yueye.add(DragValue::new(&mut self.component.position.z).speed(0.01)); + }); + }; + + let mut display_dir = |yueye: &mut Ui| { + yueye.horizontal(|yueye| { + yueye.label("Direction"); + yueye.add(DragValue::new(&mut self.component.direction.x).speed(0.01)); + yueye.add(DragValue::new(&mut self.component.direction.y).speed(0.01)); + yueye.add(DragValue::new(&mut self.component.direction.z).speed(0.01)); + }); + }; + + match self.component.light_type { + LightType::Directional => { + display_dir(ui); + } + LightType::Point => { + display_pos(ui); + } + LightType::Spot => { + display_pos(ui); + display_dir(ui); + } + } - let mut display_pos = |yueye: &mut Ui| { - yueye.horizontal(|yueye| { - yueye.label("Position"); - yueye.add(DragValue::new(&mut self.component.position.x).speed(0.01)); - yueye.add(DragValue::new(&mut self.component.position.y).speed(0.01)); - yueye.add(DragValue::new(&mut self.component.position.z).speed(0.01)); - }); - }; - - let mut display_dir = |yueye: &mut Ui| { - yueye.horizontal(|yueye| { - yueye.label("Direction"); - yueye.add(DragValue::new(&mut self.component.direction.x).speed(0.01)); - yueye.add(DragValue::new(&mut self.component.direction.y).speed(0.01)); - yueye.add(DragValue::new(&mut self.component.direction.z).speed(0.01)); - }); - }; - - match self.component.light_type { - LightType::Directional => { - display_dir(ui); - }, - LightType::Point => { - display_pos(ui); - }, - LightType::Spot => { - display_pos(ui); - display_dir(ui); - }, - } - - let mut colour_rgb = [ - self.component.colour[0] as f32, - self.component.colour[1] as f32, - self.component.colour[2] as f32, - ]; - egui::color_picker::color_edit_button_rgb(ui, &mut colour_rgb); - self.component.colour = Vec3::from_array(colour_rgb).as_dvec3(); - - ui.horizontal(|ui| { - ui.label("Intensity"); - ui.add(DragValue::new(&mut self.component.intensity).speed(0.05)); - }); + let mut colour_rgb = [ + self.component.colour[0] as f32, + self.component.colour[1] as f32, + self.component.colour[2] as f32, + ]; + egui::color_picker::color_edit_button_rgb(ui, &mut colour_rgb); + self.component.colour = Vec3::from_array(colour_rgb).as_dvec3(); - if matches!(self.component.light_type, LightType::Point | LightType::Spot) { ui.horizontal(|ui| { - ComboBox::from_id_salt("Attenuation Range") - .selected_text(format!("Range {}", self.component.attenuation.range)) - .show_ui(ui, |ui| { - for (preset, label) in ATTENUATION_PRESETS { - ui.selectable_value(&mut self.component.attenuation, *preset, *label); - } - }); + ui.label("Intensity"); + ui.add(DragValue::new(&mut self.component.intensity).speed(0.05)); }); - } - ui.horizontal(|ui| { - ui.checkbox(&mut self.component.enabled, "Enabled"); - ui.checkbox(&mut self.component.visible, "Visible"); - }); + if matches!( + self.component.light_type, + LightType::Point | LightType::Spot + ) { + ui.horizontal(|ui| { + ComboBox::from_id_salt("Attenuation Range") + .selected_text(format!("Range {}", self.component.attenuation.range)) + .show_ui(ui, |ui| { + for (preset, label) in ATTENUATION_PRESETS { + ui.selectable_value( + &mut self.component.attenuation, + *preset, + *label, + ); + } + }); + }); + } - if matches!(self.component.light_type, LightType::Spot) { ui.horizontal(|ui| { - ui.label("Cutoff"); - ui.add(DragValue::new(&mut self.component.cutoff_angle).speed(0.1)); + ui.checkbox(&mut self.component.enabled, "Enabled"); + ui.checkbox(&mut self.component.visible, "Visible"); }); + + if matches!(self.component.light_type, LightType::Spot) { + ui.horizontal(|ui| { + ui.label("Cutoff"); + ui.add(DragValue::new(&mut self.component.cutoff_angle).speed(0.1)); + }); + ui.horizontal(|ui| { + ui.label("Outer Cutoff"); + ui.add(DragValue::new(&mut self.component.outer_cutoff_angle).speed(0.1)); + }); + + if self.component.outer_cutoff_angle <= self.component.cutoff_angle { + self.component.outer_cutoff_angle = self.component.cutoff_angle + 1.0; + } + } + + ui.separator(); + ui.label("Shadows"); + ui.checkbox(&mut self.component.cast_shadows, "Cast Shadows"); ui.horizontal(|ui| { - ui.label("Outer Cutoff"); - ui.add(DragValue::new(&mut self.component.outer_cutoff_angle).speed(0.1)); + ui.label("Depth"); + ui.add(DragValue::new(&mut self.component.depth.start).speed(0.1)); + ui.label(".."); + ui.add(DragValue::new(&mut self.component.depth.end).speed(0.1)); }); - if self.component.outer_cutoff_angle <= self.component.cutoff_angle { - self.component.outer_cutoff_angle = self.component.cutoff_angle + 1.0; + if self.component.depth.end < self.component.depth.start { + self.component.depth.end = self.component.depth.start; } - } - - ui.separator(); - ui.label("Shadows"); - ui.checkbox(&mut self.component.cast_shadows, "Cast Shadows"); - ui.horizontal(|ui| { - ui.label("Depth"); - ui.add(DragValue::new(&mut self.component.depth.start).speed(0.1)); - ui.label(".."); - ui.add(DragValue::new(&mut self.component.depth.end).speed(0.1)); }); - - if self.component.depth.end < self.component.depth.start { - self.component.depth.end = self.component.depth.start; - } - }); } } #[repr(C)] #[derive(Clone, Copy, Debug)] struct NColour { - r: u8, - g: u8, - b: u8, - a: u8, + r: u8, + g: u8, + b: u8, + a: u8, } 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, - } - } + 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 JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { - let class = env - .find_class("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: &str| -> DropbearNativeResult<u8> { - let v = env - .get_field(obj, field, "B") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .b() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - Ok(v as u8) - }; - - Ok(Self { - r: get_byte("r")?, - g: get_byte("g")?, - b: get_byte("b")?, - a: get_byte("a")?, - }) - } + fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { + let class = env + .find_class("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: &str| -> DropbearNativeResult<u8> { + let v = env + .get_field(obj, field, "B") + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .b() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + Ok(v as u8) + }; + + Ok(Self { + r: get_byte("r")?, + g: get_byte("g")?, + b: get_byte("b")?, + a: get_byte("a")?, + }) + } } impl ToJObject for NColour { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .find_class("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, "(BBBB)V", &args) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) - } + fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .find_class("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, "(BBBB)V", &args) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } } #[repr(C)] #[derive(Clone, Copy, Debug)] struct NRange { - start: f32, - end: f32, + start: f32, + end: f32, } impl FromJObject for NRange { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { - let class = env - .find_class("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, "start", "D") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32; - - let end = env - .get_field(obj, "end", "D") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .d() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32; - - Ok(Self { start, end }) - } -} + fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { + let class = env + .find_class("com/dropbear/utils/Range") + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + + if !env + .is_instance_of(obj, &class) + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? + { + return Err(DropbearNativeError::InvalidArgument); + } -impl ToJObject for NRange { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .find_class("com/dropbear/utils/Range") - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + let start = env + .get_field(obj, "start", "D") + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32; - let args = [ - JValue::Double(self.start as f64), - JValue::Double(self.end as f64), - ]; + let end = env + .get_field(obj, "end", "D") + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32; - env.new_object(&class, "(DD)V", &args) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) - } + Ok(Self { start, end }) + } +} + +impl ToJObject for NRange { + fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .find_class("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, "(DD)V", &args) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } } #[repr(C)] #[derive(Clone, Copy, Debug)] struct NAttenuation { - constant: f32, - linear: f32, - quadratic: f32, + constant: f32, + linear: f32, + quadratic: f32, } impl FromJObject for NAttenuation { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { - let class = env - .find_class("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, "getConstant", "()F", &[]) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .f() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let linear = env - .call_method(obj, "getLinear", "()F", &[]) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .f() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let quadratic = env - .call_method(obj, "getQuadratic", "()F", &[]) - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .f() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - Ok(Self { - constant, - linear, - quadratic, - }) - } + fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { + let class = env + .find_class("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, "getConstant", "()F", &[]) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let linear = env + .call_method(obj, "getLinear", "()F", &[]) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let quadratic = env + .call_method(obj, "getQuadratic", "()F", &[]) + .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 JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .find_class("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, "(FFF)V", &args) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) - } + fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .find_class("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, "(FFF)V", &args) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) + } } pub mod shared { - use hecs::{Entity, World}; + use hecs::{Entity, World}; - pub fn light_exists_for_entity(world: &World, entity: Entity) -> bool { - world.get::<&dropbear_engine::lighting::LightComponent>(entity).is_ok() - } + 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> { @@ -427,14 +464,15 @@ fn set_transform_rotation( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "lightExistsForEntity"), + 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, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<bool> { Ok(shared::light_exists_for_entity(world, entity)) } @@ -444,10 +482,8 @@ fn light_exists_for_entity( c )] fn get_position( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<NVector3> { let transform = get_transform(world, entity)?; Ok(NVector3::from(transform.position)) @@ -458,10 +494,8 @@ fn get_position( c )] fn set_position( - #[dropbear_macro::define(WorldPtr)] - world: &mut World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, position: &NVector3, ) -> DropbearNativeResult<()> { set_transform_position(world, entity, (*position).into()) @@ -472,10 +506,8 @@ fn set_position( c )] fn get_direction( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<NVector3> { let transform = get_transform(world, entity)?; let forward = DVec3::new(0.0, 0.0, -1.0); @@ -488,10 +520,8 @@ fn get_direction( c )] fn set_direction( - #[dropbear_macro::define(WorldPtr)] - world: &mut World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, direction: &NVector3, ) -> DropbearNativeResult<()> { let dir: DVec3 = (*direction).into(); @@ -510,10 +540,8 @@ fn set_direction( c )] fn get_colour( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<NColour> { let light = world .get::<&Light>(entity) @@ -526,10 +554,8 @@ fn get_colour( c )] fn set_colour( - #[dropbear_macro::define(WorldPtr)] - world: &mut World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, colour: &NColour, ) -> DropbearNativeResult<()> { let mut light = world @@ -544,10 +570,8 @@ fn set_colour( c )] fn get_light_type( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<i32> { let light = world .get::<&Light>(entity) @@ -560,10 +584,8 @@ fn get_light_type( c )] fn set_light_type( - #[dropbear_macro::define(WorldPtr)] - world: &mut World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, light_type: i32, ) -> DropbearNativeResult<()> { let mut light = world @@ -585,10 +607,8 @@ fn set_light_type( c )] fn get_intensity( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<f64> { let light = world .get::<&Light>(entity) @@ -601,10 +621,8 @@ fn get_intensity( c )] fn set_intensity( - #[dropbear_macro::define(WorldPtr)] - world: &mut World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, intensity: f64, ) -> DropbearNativeResult<()> { let mut light = world @@ -619,10 +637,8 @@ fn set_intensity( c )] fn get_attenuation( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<NAttenuation> { let light = world .get::<&Light>(entity) @@ -640,10 +656,8 @@ fn get_attenuation( c )] fn set_attenuation( - #[dropbear_macro::define(WorldPtr)] - world: &mut World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, attenuation: &NAttenuation, ) -> DropbearNativeResult<()> { let mut light = world @@ -661,10 +675,8 @@ fn set_attenuation( c )] fn get_enabled( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<bool> { let light = world .get::<&Light>(entity) @@ -677,10 +689,8 @@ fn get_enabled( c )] fn set_enabled( - #[dropbear_macro::define(WorldPtr)] - world: &mut World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, enabled: bool, ) -> DropbearNativeResult<()> { let mut light = world @@ -695,10 +705,8 @@ fn set_enabled( c )] fn get_cutoff_angle( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<f64> { let light = world .get::<&Light>(entity) @@ -711,10 +719,8 @@ fn get_cutoff_angle( c )] fn set_cutoff_angle( - #[dropbear_macro::define(WorldPtr)] - world: &mut World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, cutoff_angle: f64, ) -> DropbearNativeResult<()> { let mut light = world @@ -725,14 +731,15 @@ fn set_cutoff_angle( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "getOuterCutoffAngle"), + 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, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<f64> { let light = world .get::<&Light>(entity) @@ -741,14 +748,15 @@ fn get_outer_cutoff_angle( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.lighting.LightNative", func = "setOuterCutoffAngle"), + 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, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, outer_cutoff_angle: f64, ) -> DropbearNativeResult<()> { let mut light = world @@ -763,10 +771,8 @@ fn set_outer_cutoff_angle( c )] fn get_casts_shadows( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<bool> { let light = world .get::<&Light>(entity) @@ -779,10 +785,8 @@ fn get_casts_shadows( c )] fn set_casts_shadows( - #[dropbear_macro::define(WorldPtr)] - world: &mut World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, casts_shadows: bool, ) -> DropbearNativeResult<()> { let mut light = world @@ -797,10 +801,8 @@ fn set_casts_shadows( c )] fn get_depth( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, ) -> DropbearNativeResult<NRange> { let light = world .get::<&Light>(entity) @@ -817,10 +819,8 @@ fn get_depth( c )] fn set_depth( - #[dropbear_macro::define(WorldPtr)] - world: &mut World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, depth: &NRange, ) -> DropbearNativeResult<()> { if !(depth.start.is_finite() && depth.end.is_finite()) { @@ -835,4 +835,4 @@ fn set_depth( .map_err(|_| DropbearNativeError::MissingComponent)?; light.component.depth = depth.start..depth.end; Ok(()) -} +} @@ -12,20 +12,14 @@ pub mod shared { material.texture_tag.as_deref() == Some(target) || material.name == target } - pub fn resolve_target_material_index( - model: &Model, - target_identifier: &str, - ) -> Option<usize> { + 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> { + pub fn resolve_target_material_name(model: &Model, target_identifier: &str) -> Option<String> { model .materials .iter() @@ -50,26 +44,24 @@ 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") -)] +#[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, + #[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") -)] +#[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, + #[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) @@ -78,16 +70,14 @@ fn get_model( } } -#[dropbear_macro::export( - kotlin(class = "com.dropbear.components.MeshRendererNative", func = "setModel") -)] +#[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, + #[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); @@ -103,23 +93,21 @@ fn set_model( } } -#[dropbear_macro::export( - kotlin(class = "com.dropbear.components.MeshRendererNative", func = "getAllTextureIds") -)] +#[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, + #[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 model = + shared::model_for_renderer(&reader, &renderer).ok_or(DropbearNativeError::AssetNotFound)?; let mut ids = HashSet::new(); let mut push_handle = |texture: &Texture| { @@ -148,24 +136,24 @@ fn get_all_texture_ids( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.MeshRendererNative", func = "getTexture"), + 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, + #[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 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), @@ -183,16 +171,16 @@ fn get_texture( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.MeshRendererNative", func = "setTextureOverride"), + 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, + #[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<()> { @@ -200,8 +188,8 @@ fn set_texture_override( let renderer = world .get::<&MeshRenderer>(entity) .map_err(|_| DropbearNativeError::NoSuchComponent)?; - let model = shared::model_for_renderer(&reader, &renderer) - .ok_or(DropbearNativeError::AssetNotFound)?; + 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)?; @@ -224,18 +212,17 @@ fn set_texture_override( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.MeshRendererNative", func = "setMaterialTint"), + 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, + #[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, @@ -255,4 +242,4 @@ fn set_material_tint( material.tint = [r, g, b, a]; material.sync_uniform(graphics); Ok(()) -} +} @@ -1,22 +1,22 @@ //! Components in the eucalyptus-editor and redback-runtime that relate to rapier3d based physics. use crate::physics::rigidbody::RigidBodyMode; +use crate::ptr::PhysicsStatePtr; +use crate::scripting::result::DropbearNativeResult; use crate::states::Label; +use crate::types::{IndexNative, NCollider, NShapeCastHit, NVector3, RayHit}; use dropbear_engine::entity::Transform; use hecs::Entity; +use rapier3d::control::CharacterCollision; use rapier3d::na::{Quaternion, UnitQuaternion}; +use rapier3d::parry::query::{DefaultQueryDispatcher, ShapeCastOptions}; use rapier3d::prelude::*; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use rapier3d::control::CharacterCollision; -use rapier3d::parry::query::{DefaultQueryDispatcher, ShapeCastOptions}; -use crate::ptr::PhysicsStatePtr; -use crate::scripting::result::DropbearNativeResult; -use crate::types::{IndexNative, NVector3, RayHit, NShapeCastHit, NCollider}; -pub mod rigidbody; pub mod collider; pub mod kcc; +pub mod rigidbody; /// A serializable [rapier3d] state that shows all the different actions and types related /// to physics rendering. @@ -74,7 +74,13 @@ impl PhysicsState { } } - pub fn step(&mut self, entity_label_map: HashMap<Entity, Label>, pipeline: &mut PhysicsPipeline, physics_hooks: &dyn PhysicsHooks, event_handler: &dyn EventHandler) { + pub fn step( + &mut self, + entity_label_map: HashMap<Entity, Label>, + pipeline: &mut PhysicsPipeline, + physics_hooks: &dyn PhysicsHooks, + event_handler: &dyn EventHandler, + ) { self.entity_label_map = entity_label_map; pipeline.step( Vector::new(self.gravity[0], self.gravity[1], self.gravity[2]), // a panic is deserved for those who don't specify a 3rd type in a vector array @@ -127,12 +133,13 @@ impl PhysicsState { bits = bits | LockedAxes::ROTATION_LOCKED_Z; } - let body = RigidBodyBuilder::new(mode) .translation(Vector::from_array(pos)) - .rotation(UnitQuaternion::from_quaternion(Quaternion::new( - rot[3], rot[0], rot[1], rot[2] - )).scaled_axis().into()) + .rotation( + UnitQuaternion::from_quaternion(Quaternion::new(rot[3], rot[0], rot[1], rot[2])) + .scaled_axis() + .into(), + ) .gravity_scale(rigid_body.gravity_scale) .sleeping(rigid_body.sleeping) .can_sleep(rigid_body.can_sleep) @@ -145,13 +152,15 @@ impl PhysicsState { .build(); let body_handle = self.bodies.insert(body); - self.bodies_entity_map.insert(rigid_body.entity.clone(), body_handle); - + self.bodies_entity_map + .insert(rigid_body.entity.clone(), body_handle); + if let Some(collider_handles) = self.colliders_entity_map.get(&rigid_body.entity) { let handles_to_attach = collider_handles.clone(); for (_, handle) in handles_to_attach { - self.colliders.set_parent(handle, Some(body_handle), &mut self.bodies); + self.colliders + .set_parent(handle, Some(body_handle), &mut self.bodies); } } } @@ -160,21 +169,24 @@ impl PhysicsState { use collider::ColliderShape; let mut builder = match &collider_component.shape { - ColliderShape::Box { half_extents } => { - ColliderBuilder::cuboid(half_extents.x as f32, half_extents.y as f32, half_extents.z as f32) - } - ColliderShape::Sphere { radius } => { - ColliderBuilder::ball(*radius) - } - ColliderShape::Capsule { half_height, radius } => { - ColliderBuilder::capsule_y(*half_height, *radius) - } - ColliderShape::Cylinder { half_height, radius } => { - ColliderBuilder::cylinder(*half_height, *radius) - } - ColliderShape::Cone { half_height, radius } => { - ColliderBuilder::cone(*half_height, *radius) - } + ColliderShape::Box { half_extents } => ColliderBuilder::cuboid( + half_extents.x as f32, + half_extents.y as f32, + half_extents.z as f32, + ), + ColliderShape::Sphere { radius } => ColliderBuilder::ball(*radius), + ColliderShape::Capsule { + half_height, + radius, + } => ColliderBuilder::capsule_y(*half_height, *radius), + ColliderShape::Cylinder { + half_height, + radius, + } => ColliderBuilder::cylinder(*half_height, *radius), + ColliderShape::Cone { + half_height, + radius, + } => ColliderBuilder::cone(*half_height, *radius), }; builder = builder @@ -194,16 +206,15 @@ impl PhysicsState { let rotation = UnitQuaternion::from_euler_angles( collider_component.rotation[0], collider_component.rotation[1], - collider_component.rotation[2] + collider_component.rotation[2], ); builder = builder.rotation(rotation.scaled_axis().into()); - let handle = if let Some(&rigid_body_handle) = self.bodies_entity_map.get(&collider_component.entity) { - self.colliders.insert_with_parent( - builder.build(), - rigid_body_handle, - &mut self.bodies - ) + let handle = if let Some(&rigid_body_handle) = + self.bodies_entity_map.get(&collider_component.entity) + { + self.colliders + .insert_with_parent(builder.build(), rigid_body_handle, &mut self.bodies) } else { self.colliders.insert(builder.build()) }; @@ -220,12 +231,8 @@ impl PhysicsState { pub fn remove_colliders(&mut self, entity: &Label) { if let Some(handles) = self.colliders_entity_map.remove(entity) { for (_id, handle) in handles { - self.colliders.remove( - handle, - &mut self.islands, - &mut self.bodies, - false - ); + self.colliders + .remove(handle, &mut self.islands, &mut self.bodies, false); } } } @@ -239,7 +246,7 @@ impl PhysicsState { &mut self.colliders, &mut self.impulse_joints, &mut self.multibody_joints, - false + false, ); } self.colliders_entity_map.remove(entity); @@ -271,7 +278,11 @@ pub mod shared { ColliderHandle::from_raw_parts(collider.index.index, collider.index.generation) } - pub fn overlapping(physics: &PhysicsState, collider1: &NCollider, collider2: &NCollider) -> bool { + pub fn overlapping( + physics: &PhysicsState, + collider1: &NCollider, + collider2: &NCollider, + ) -> bool { let h1 = collider_handle_from_ffi(collider1); let h2 = collider_handle_from_ffi(collider2); @@ -285,7 +296,11 @@ pub mod shared { .unwrap_or(false) } - pub fn triggering(physics: &PhysicsState, collider1: &NCollider, collider2: &NCollider) -> bool { + pub fn triggering( + physics: &PhysicsState, + collider1: &NCollider, + collider2: &NCollider, + ) -> bool { let h1 = collider_handle_from_ffi(collider1); let h2 = collider_handle_from_ffi(collider2); @@ -337,8 +352,7 @@ pub mod shared { c )] fn get_gravity( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, ) -> DropbearNativeResult<NVector3> { Ok(shared::get_gravity(physics)) } @@ -348,8 +362,7 @@ fn get_gravity( c )] fn set_gravity( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &mut PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, gravity: &NVector3, ) -> DropbearNativeResult<()> { Ok(shared::set_gravity(physics, *gravity)) @@ -360,14 +373,18 @@ fn set_gravity( c )] fn raycast( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &mut PhysicsState, + #[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 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(), @@ -415,7 +432,6 @@ fn raycast( distance: distance as f64, }; Ok(Some(rayhit)) - } } else { Ok(None) @@ -427,8 +443,7 @@ fn raycast( c )] fn shape_cast( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &mut PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, origin: &NVector3, direction: &NVector3, shape: &collider::ColliderShape, @@ -442,7 +457,9 @@ fn shape_cast( QueryFilter::new(), ); - let dir_len = ((direction.x * direction.x) + (direction.y * direction.y) + (direction.z * direction.z)).sqrt(); + let dir_len = + ((direction.x * direction.x) + (direction.y * direction.y) + (direction.z * direction.z)) + .sqrt(); if dir_len <= f64::EPSILON { return Ok(None); } @@ -453,25 +470,34 @@ fn shape_cast( 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) + rapier3d::geometry::SharedShape::cuboid( + half_extents.x as f32, + half_extents.y as f32, + half_extents.z as f32, + ) } - crate::physics::collider::ColliderShape::Cone { half_height, radius } => { - rapier3d::geometry::SharedShape::cone(*half_height, *radius) + 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 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 { @@ -505,8 +531,7 @@ fn shape_cast( c )] fn is_overlapping( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, collider1: &NCollider, collider2: &NCollider, ) -> DropbearNativeResult<bool> { @@ -518,8 +543,7 @@ fn is_overlapping( c )] fn is_triggering( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, collider1: &NCollider, collider2: &NCollider, ) -> DropbearNativeResult<bool> { @@ -531,17 +555,17 @@ fn is_triggering( c )] fn is_touching( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &PhysicsState, - #[dropbear_macro::entity] - entity1: Entity, - #[dropbear_macro::entity] - entity2: Entity, + #[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 { +fn collider_ffi_from_handle( + physics: &PhysicsState, + handle: rapier3d::prelude::ColliderHandle, +) -> NCollider { let (idx, generation) = handle.into_raw_parts(); let mut found_label = None; @@ -569,7 +593,10 @@ fn collider_ffi_from_handle(physics: &PhysicsState, handle: rapier3d::prelude::C }; NCollider { - index: IndexNative { index: idx, generation }, + index: IndexNative { + index: idx, + generation, + }, entity_id, id: idx, } @@ -13,32 +13,30 @@ //! - `col-` or `-col` //! - `-colonly` (invisible collision mesh) -pub mod shader; pub mod collider_group; +pub mod shader; +use crate::component::{Component, ComponentDescriptor, 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::JNIEnv; +use ::jni::objects::{JObject, JValue}; use dropbear_engine::graphics::SharedGraphicsContext; use dropbear_engine::wgpu::util::{BufferInitDescriptor, DeviceExt}; use dropbear_engine::wgpu::{Buffer, BufferUsages}; -use std::any::Any; -use ::jni::objects::{JObject, JValue}; -use ::jni::JNIEnv; -use rapier3d::prelude::ColliderBuilder; -use serde::{Deserialize, Serialize}; -use std::sync::Arc; use egui::{CollapsingHeader, ComboBox, Ui}; -use crate::physics::collider::shared::{get_collider, get_collider_mut}; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; -use crate::types::{NCollider, NVector3}; use glam::DQuat; use hecs::{Entity, World}; +use rapier3d::prelude::ColliderBuilder; use rapier3d::prelude::{Rotation, SharedShape, TypedShape, Vector}; -use dropbear_engine::animation::AnimationComponent; -use crate::component::{Component, ComponentDescriptor, InspectableComponent, SerializedComponent}; -use crate::physics::PhysicsState; -use crate::ptr::PhysicsStatePtr; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct ColliderGroup { @@ -61,7 +59,7 @@ impl SerializedComponent for ColliderGroup {} impl Component for ColliderGroup { type SerializedForm = Self; - type RequiredComponentTypes = (Self, ); + type RequiredComponentTypes = (Self,); fn descriptor() -> ComponentDescriptor { ComponentDescriptor { @@ -76,10 +74,17 @@ impl Component for ColliderGroup { ser: &'a Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, ) -> crate::component::ComponentInitFuture<'a, Self> { - Box::pin(async move { Ok((ser.clone(), )) }) + Box::pin(async move { Ok((ser.clone(),)) }) } - fn update_component(&mut self, world: &World, _physics: &mut PhysicsState, entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) { + fn update_component( + &mut self, + world: &World, + _physics: &mut PhysicsState, + entity: Entity, + _dt: f32, + _graphics: Arc<SharedGraphicsContext>, + ) { let Ok(label) = world.get::<&Label>(entity) else { return; }; @@ -98,34 +103,36 @@ impl Component for ColliderGroup { impl InspectableComponent for ColliderGroup { fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { - CollapsingHeader::new("Colliders").default_open(true).show(ui, |ui| { - let mut remove_index: Option<usize> = None; - - for (index, collider) in self.colliders.iter_mut().enumerate() { - ui.push_id(index, |ui| { - CollapsingHeader::new(format!("Collider {}", index + 1)) - .default_open(true) - .show(ui, |ui| { - collider.inspect(ui); - - ui.add_space(6.0); - if ui.button("Remove Collider").clicked() { - remove_index = Some(index); - } - }); - }); + CollapsingHeader::new("Colliders") + .default_open(true) + .show(ui, |ui| { + let mut remove_index: Option<usize> = None; + + for (index, collider) in self.colliders.iter_mut().enumerate() { + ui.push_id(index, |ui| { + CollapsingHeader::new(format!("Collider {}", index + 1)) + .default_open(true) + .show(ui, |ui| { + collider.inspect(ui); + + ui.add_space(6.0); + if ui.button("Remove Collider").clicked() { + remove_index = Some(index); + } + }); + }); - ui.add_space(6.0); - } + ui.add_space(6.0); + } - if let Some(index) = remove_index { - self.colliders.remove(index); - } + if let Some(index) = remove_index { + self.colliders.remove(index); + } - if ui.button("Add Collider").clicked() { - self.colliders.push(Collider::new()); - } - }); + if ui.button("Add Collider").clicked() { + self.colliders.push(Collider::new()); + } + }); } } @@ -178,27 +185,50 @@ impl Collider { .show_ui(ui, |ui| { if ui.selectable_label(current_shape == "Box", "Box").clicked() { if current_shape != "Box" { - self.shape = ColliderShape::Box { half_extents: [0.5, 0.5, 0.5].into() }; + self.shape = ColliderShape::Box { + half_extents: [0.5, 0.5, 0.5].into(), + }; } } - if ui.selectable_label(current_shape == "Sphere", "Sphere").clicked() { + if ui + .selectable_label(current_shape == "Sphere", "Sphere") + .clicked() + { if current_shape != "Sphere" { self.shape = ColliderShape::Sphere { radius: 0.5 }; } } - if ui.selectable_label(current_shape == "Capsule", "Capsule").clicked() { + if ui + .selectable_label(current_shape == "Capsule", "Capsule") + .clicked() + { if current_shape != "Capsule" { - self.shape = ColliderShape::Capsule { half_height: 0.5, radius: 0.25 }; + self.shape = ColliderShape::Capsule { + half_height: 0.5, + radius: 0.25, + }; } } - if ui.selectable_label(current_shape == "Cylinder", "Cylinder").clicked() { + if ui + .selectable_label(current_shape == "Cylinder", "Cylinder") + .clicked() + { if current_shape != "Cylinder" { - self.shape = ColliderShape::Cylinder { half_height: 0.5, radius: 0.25 }; + self.shape = ColliderShape::Cylinder { + half_height: 0.5, + radius: 0.25, + }; } } - if ui.selectable_label(current_shape == "Cone", "Cone").clicked() { + if ui + .selectable_label(current_shape == "Cone", "Cone") + .clicked() + { if current_shape != "Cone" { - self.shape = ColliderShape::Cone { half_height: 0.5, radius: 0.25 }; + self.shape = ColliderShape::Cone { + half_height: 0.5, + radius: 0.25, + }; } } }); @@ -210,57 +240,56 @@ impl Collider { ui.label("Half Extents:"); ui.horizontal(|ui| { ui.label("X:"); - ui.add(egui::DragValue::new(&mut half_extents.x) - .speed(0.01)); + ui.add(egui::DragValue::new(&mut half_extents.x).speed(0.01)); ui.label("Y:"); - ui.add(egui::DragValue::new(&mut half_extents.y) - .speed(0.01)); + ui.add(egui::DragValue::new(&mut half_extents.y).speed(0.01)); ui.label("Z:"); - ui.add(egui::DragValue::new(&mut half_extents.z) - .speed(0.01)); + ui.add(egui::DragValue::new(&mut half_extents.z).speed(0.01)); }); } ColliderShape::Sphere { radius } => { ui.horizontal(|ui| { ui.label("Radius:"); - ui.add(egui::DragValue::new(radius) - .speed(0.01)); + ui.add(egui::DragValue::new(radius).speed(0.01)); }); } - ColliderShape::Capsule { half_height, radius } => { + ColliderShape::Capsule { + half_height, + radius, + } => { ui.horizontal(|ui| { ui.label("Half Height:"); - ui.add(egui::DragValue::new(half_height) - .speed(0.01)); + ui.add(egui::DragValue::new(half_height).speed(0.01)); }); ui.horizontal(|ui| { ui.label("Radius:"); - ui.add(egui::DragValue::new(radius) - .speed(0.01)); + ui.add(egui::DragValue::new(radius).speed(0.01)); }); } - ColliderShape::Cylinder { half_height, radius } => { + ColliderShape::Cylinder { + half_height, + radius, + } => { ui.horizontal(|ui| { ui.label("Half Height:"); - ui.add(egui::DragValue::new(half_height) - .speed(0.01)); + ui.add(egui::DragValue::new(half_height).speed(0.01)); }); ui.horizontal(|ui| { ui.label("Radius:"); - ui.add(egui::DragValue::new(radius) - .speed(0.01)); + ui.add(egui::DragValue::new(radius).speed(0.01)); }); } - ColliderShape::Cone { half_height, radius } => { + ColliderShape::Cone { + half_height, + radius, + } => { ui.horizontal(|ui| { ui.label("Half Height:"); - ui.add(egui::DragValue::new(half_height) - .speed(0.01)); + ui.add(egui::DragValue::new(half_height).speed(0.01)); }); ui.horizontal(|ui| { ui.label("Radius:"); - ui.add(egui::DragValue::new(radius) - .speed(0.01)); + ui.add(egui::DragValue::new(radius).speed(0.01)); }); } } @@ -272,8 +301,7 @@ impl Collider { ui.horizontal(|ui| { ui.label("Density:"); - ui.add(egui::DragValue::new(&mut self.density) - .speed(0.01)); + ui.add(egui::DragValue::new(&mut self.density).speed(0.01)); }); ui.horizontal(|ui| { @@ -307,17 +335,26 @@ impl Collider { ui.horizontal(|ui| { ui.label("X:"); let mut deg_x = self.rotation[0].to_degrees(); - if ui.add(egui::DragValue::new(&mut deg_x).speed(1.0)).changed() { + if ui + .add(egui::DragValue::new(&mut deg_x).speed(1.0)) + .changed() + { self.rotation[0] = deg_x.to_radians(); } ui.label("Y:"); let mut deg_y = self.rotation[1].to_degrees(); - if ui.add(egui::DragValue::new(&mut deg_y).speed(1.0)).changed() { + if ui + .add(egui::DragValue::new(&mut deg_y).speed(1.0)) + .changed() + { self.rotation[1] = deg_y.to_radians(); } ui.label("Z:"); let mut deg_z = self.rotation[2].to_degrees(); - if ui.add(egui::DragValue::new(&mut deg_z).speed(1.0)).changed() { + if ui + .add(egui::DragValue::new(&mut deg_z).speed(1.0)) + .changed() + { self.rotation[2] = deg_z.to_radians(); } }); @@ -336,11 +373,24 @@ pub enum ColliderShapeType { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ColliderShapeKey { - Box { half_extents_bits: [u32; 3] }, - Sphere { radius_bits: u32 }, - Capsule { half_height_bits: u32, radius_bits: u32 }, - Cylinder { half_height_bits: u32, radius_bits: u32 }, - Cone { half_height_bits: u32, radius_bits: u32 }, + Box { + half_extents_bits: [u32; 3], + }, + Sphere { + radius_bits: u32, + }, + Capsule { + half_height_bits: u32, + radius_bits: u32, + }, + Cylinder { + half_height_bits: u32, + radius_bits: u32, + }, + Cone { + half_height_bits: u32, + radius_bits: u32, + }, } impl From<&ColliderShape> for ColliderShapeKey { @@ -356,15 +406,24 @@ impl From<&ColliderShape> for ColliderShapeKey { ColliderShape::Sphere { radius } => Self::Sphere { radius_bits: radius.to_bits(), }, - ColliderShape::Capsule { half_height, radius } => Self::Capsule { + ColliderShape::Capsule { + half_height, + radius, + } => Self::Capsule { half_height_bits: half_height.to_bits(), radius_bits: radius.to_bits(), }, - ColliderShape::Cylinder { half_height, radius } => Self::Cylinder { + ColliderShape::Cylinder { + half_height, + radius, + } => Self::Cylinder { half_height_bits: half_height.to_bits(), radius_bits: radius.to_bits(), }, - ColliderShape::Cone { half_height, radius } => Self::Cone { + ColliderShape::Cone { + half_height, + radius, + } => Self::Cone { half_height_bits: half_height.to_bits(), radius_bits: radius.to_bits(), }, @@ -395,7 +454,7 @@ pub enum ColliderShape { impl Default for ColliderShape { fn default() -> Self { ColliderShape::Box { - half_extents: NVector3::from([0.5, 0.5, 0.5]) + half_extents: NVector3::from([0.5, 0.5, 0.5]), } } } @@ -404,87 +463,108 @@ impl ToJObject for ColliderShape { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { match self { ColliderShape::Box { half_extents } => { - let vec_cls = env.find_class("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, - "(DDD)V", - &[ - JValue::Double(half_extents.x), - JValue::Double(half_extents.y), - JValue::Double(half_extents.z) - ] - ).map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - let cls = env.find_class("com/dropbear/physics/ColliderShape$Box") + let vec_cls = env.find_class("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, + "(DDD)V", + &[ + JValue::Double(half_extents.x), + JValue::Double(half_extents.y), + JValue::Double(half_extents.z), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + let cls = env + .find_class("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, - "(Lcom/dropbear/math/Vector3d;)V", - &[JValue::Object(&vec_obj)] - ).map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + let obj = env + .new_object( + &cls, + "(Lcom/dropbear/math/Vector3d;)V", + &[JValue::Object(&vec_obj)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) - }, + } ColliderShape::Sphere { radius } => { - let cls = env.find_class("com/dropbear/physics/ColliderShape$Sphere") + let cls = env + .find_class("com/dropbear/physics/ColliderShape$Sphere") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let obj = env.new_object( - &cls, - "(F)V", - &[JValue::Float(*radius)] - ).map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + let obj = env + .new_object(&cls, "(F)V", &[JValue::Float(*radius)]) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) - }, - ColliderShape::Capsule { half_height, radius } => { - let cls = env.find_class("com/dropbear/physics/ColliderShape$Capsule") + } + ColliderShape::Capsule { + half_height, + radius, + } => { + let cls = env + .find_class("com/dropbear/physics/ColliderShape$Capsule") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let obj = env.new_object( - &cls, - "(FF)V", - &[JValue::Float(*half_height), JValue::Float(*radius)] - ).map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + let obj = env + .new_object( + &cls, + "(FF)V", + &[JValue::Float(*half_height), JValue::Float(*radius)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) - }, - ColliderShape::Cylinder { half_height, radius } => { - let cls = env.find_class("com/dropbear/physics/ColliderShape$Cylinder") + } + ColliderShape::Cylinder { + half_height, + radius, + } => { + let cls = env + .find_class("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, - "(FF)V", - &[JValue::Float(*half_height), JValue::Float(*radius)] - ).map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + let obj = env + .new_object( + &cls, + "(FF)V", + &[JValue::Float(*half_height), JValue::Float(*radius)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) - }, - ColliderShape::Cone { half_height, radius } => { - let cls = env.find_class("com/dropbear/physics/ColliderShape$Cone") + } + ColliderShape::Cone { + half_height, + radius, + } => { + let cls = env + .find_class("com/dropbear/physics/ColliderShape$Cone") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let obj = env.new_object( - &cls, - "(FF)V", - &[JValue::Float(*half_height), JValue::Float(*radius)] - ).map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + let obj = env + .new_object( + &cls, + "(FF)V", + &[JValue::Float(*half_height), JValue::Float(*radius)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) - }, + } } } } @@ -492,62 +572,103 @@ impl ToJObject for ColliderShape { impl FromJObject for ColliderShape { fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> where - Self: Sized + Self: Sized, { let is_instance = |env: &mut JNIEnv, obj: &JObject, class_name: &str| -> bool { env.is_instance_of(obj, class_name).unwrap_or(false) }; if is_instance(env, obj, "com/dropbear/physics/ColliderShape$Box") { - let vec_obj_val = env.get_field(obj, "halfExtents", "Lcom/dropbear/math/Vector3d;") + let vec_obj_val = env + .get_field(obj, "halfExtents", "Lcom/dropbear/math/Vector3d;") .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - let vec_obj = vec_obj_val.l() + let vec_obj = vec_obj_val + .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let x = env.get_field(&vec_obj, "x", "D") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?.d().unwrap_or(0.0); - let y = env.get_field(&vec_obj, "y", "D") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?.d().unwrap_or(0.0); - let z = env.get_field(&vec_obj, "z", "D") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?.d().unwrap_or(0.0); + let x = env + .get_field(&vec_obj, "x", "D") + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .unwrap_or(0.0); + let y = env + .get_field(&vec_obj, "y", "D") + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .d() + .unwrap_or(0.0); + let z = env + .get_field(&vec_obj, "z", "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]) + half_extents: NVector3::from([x as f32, y as f32, z as f32]), }); } if is_instance(env, obj, "com/dropbear/physics/ColliderShape$Sphere") { - let radius = env.get_field(obj, "radius", "F") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?.f().unwrap_or(0.0); + let radius = env + .get_field(obj, "radius", "F") + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .unwrap_or(0.0); return Ok(ColliderShape::Sphere { radius }); } if is_instance(env, obj, "com/dropbear/physics/ColliderShape$Capsule") { - let hh = env.get_field(obj, "halfHeight", "F") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?.f().unwrap_or(0.0); - let r = env.get_field(obj, "radius", "F") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?.f().unwrap_or(0.0); - - return Ok(ColliderShape::Capsule { half_height: hh, radius: r }); + let hh = env + .get_field(obj, "halfHeight", "F") + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .unwrap_or(0.0); + let r = env + .get_field(obj, "radius", "F") + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .unwrap_or(0.0); + + return Ok(ColliderShape::Capsule { + half_height: hh, + radius: r, + }); } if is_instance(env, obj, "com/dropbear/physics/ColliderShape$Cylinder") { - let hh = env.get_field(obj, "halfHeight", "F") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?.f().unwrap_or(0.0); - let r = env.get_field(obj, "radius", "F") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?.f().unwrap_or(0.0); - - return Ok(ColliderShape::Cylinder { half_height: hh, radius: r }); + let hh = env + .get_field(obj, "halfHeight", "F") + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .unwrap_or(0.0); + let r = env + .get_field(obj, "radius", "F") + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .unwrap_or(0.0); + + return Ok(ColliderShape::Cylinder { + half_height: hh, + radius: r, + }); } if is_instance(env, obj, "com/dropbear/physics/ColliderShape$Cone") { - let hh = env.get_field(obj, "halfHeight", "F") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?.f().unwrap_or(0.0); - let r = env.get_field(obj, "radius", "F") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?.f().unwrap_or(0.0); - - return Ok(ColliderShape::Cone { half_height: hh, radius: r }); + let hh = env + .get_field(obj, "halfHeight", "F") + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .unwrap_or(0.0); + let r = env + .get_field(obj, "radius", "F") + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .f() + .unwrap_or(0.0); + + return Ok(ColliderShape::Cone { + half_height: hh, + radius: r, + }); } Err(DropbearNativeError::GenericError) @@ -555,8 +676,12 @@ impl FromJObject for ColliderShape { } impl Collider { - fn default_density() -> f32 { 1.0 } - fn default_friction() -> f32 { 0.5 } + fn default_density() -> f32 { + 1.0 + } + fn default_friction() -> f32 { + 0.5 + } pub fn new() -> Self { Self { @@ -575,7 +700,9 @@ impl Collider { /// Create a box collider pub fn box_collider(half_extents: [f32; 3]) -> Self { Self { - shape: ColliderShape::Box { half_extents: NVector3::from(half_extents) }, + shape: ColliderShape::Box { + half_extents: NVector3::from(half_extents), + }, ..Self::new() } } @@ -591,7 +718,10 @@ impl Collider { /// Create a capsule collider pub fn capsule(half_height: f32, radius: f32) -> Self { Self { - shape: ColliderShape::Capsule { half_height, radius }, + shape: ColliderShape::Capsule { + half_height, + radius, + }, ..Self::new() } } @@ -599,7 +729,10 @@ impl Collider { /// Create a cylinder collider pub fn cylinder(half_height: f32, radius: f32) -> Self { Self { - shape: ColliderShape::Cylinder { half_height, radius }, + shape: ColliderShape::Cylinder { + half_height, + radius, + }, ..Self::new() } } @@ -642,21 +775,24 @@ impl Collider { pub fn to_rapier(&self) -> rapier3d::prelude::Collider { let shape: ColliderBuilder = match &self.shape { - ColliderShape::Box { half_extents } => { - ColliderBuilder::cuboid(half_extents.x as f32, half_extents.y as f32, half_extents.z as f32) - } - ColliderShape::Sphere { radius } => { - ColliderBuilder::ball(*radius) - } - ColliderShape::Capsule { half_height, radius } => { - ColliderBuilder::capsule_y(*half_height, *radius) - } - ColliderShape::Cylinder { half_height, radius } => { - ColliderBuilder::cylinder(*half_height, *radius) - } - ColliderShape::Cone { half_height, radius } => { - ColliderBuilder::cone(*half_height, *radius) - } + ColliderShape::Box { half_extents } => ColliderBuilder::cuboid( + half_extents.x as f32, + half_extents.y as f32, + half_extents.z as f32, + ), + ColliderShape::Sphere { radius } => ColliderBuilder::ball(*radius), + ColliderShape::Capsule { + half_height, + radius, + } => ColliderBuilder::capsule_y(*half_height, *radius), + ColliderShape::Cylinder { + half_height, + radius, + } => ColliderBuilder::cylinder(*half_height, *radius), + ColliderShape::Cone { + half_height, + radius, + } => ColliderBuilder::cone(*half_height, *radius), }; shape @@ -695,18 +831,24 @@ impl WireframeGeometry { 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], + [-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 + 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 { @@ -728,7 +870,12 @@ impl WireframeGeometry { } } - pub fn sphere_wireframe(graphics: Arc<SharedGraphicsContext>, radius: f32, lat_segments: u32, lon_segments: u32) -> Self { + 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(); @@ -782,7 +929,12 @@ impl WireframeGeometry { } } - pub fn capsule_wireframe(graphics: Arc<SharedGraphicsContext>, half_height: f32, radius: f32, segments: u32) -> Self { + 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(); @@ -838,12 +990,22 @@ impl WireframeGeometry { } } - pub fn cylinder_wireframe(graphics: Arc<SharedGraphicsContext>, half_height: f32, radius: f32, _segments: u32) -> Self { + 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 { + 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 } @@ -856,7 +1018,7 @@ pub mod shared { pub fn get_collider_mut<'a>( physics: &'a mut PhysicsState, - ffi: &NCollider + 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) @@ -864,7 +1026,7 @@ pub mod shared { pub fn get_collider<'a>( physics: &'a PhysicsState, - ffi: &NCollider + ffi: &NCollider, ) -> Option<&'a rapier3d::prelude::Collider> { let handle = ColliderHandle::from_raw_parts(ffi.index.index, ffi.index.generation); physics.colliders.get(handle) @@ -872,16 +1034,18 @@ pub mod shared { } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.ColliderNative", func = "getColliderShape"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderShape" + ), c )] fn get_collider_shape( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, collider: &NCollider, ) -> DropbearNativeResult<ColliderShape> { - let collider = get_collider(physics, &collider) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + let collider = + get_collider(physics, &collider).ok_or(DropbearNativeError::PhysicsObjectNotFound)?; let rapier_shape = collider.shape(); let my_shape = match rapier_shape.as_typed_shape() { @@ -914,30 +1078,39 @@ fn get_collider_shape( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.ColliderNative", func = "setColliderShape"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderShape" + ), c )] fn set_collider_shape( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &mut PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, collider: &NCollider, shape: &ColliderShape, ) -> DropbearNativeResult<()> { - let collider = get_collider_mut(physics, &collider) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + 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::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), + 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); @@ -945,198 +1118,228 @@ fn set_collider_shape( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.ColliderNative", func = "getColliderDensity"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderDensity" + ), c )] fn get_collider_density( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, collider: &NCollider, ) -> DropbearNativeResult<f64> { - let collider = get_collider(physics, &collider) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + 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"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderDensity" + ), c )] fn set_collider_density( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &mut PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, collider: &NCollider, density: f64, ) -> DropbearNativeResult<()> { - let collider = get_collider_mut(physics, &collider) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + 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"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderFriction" + ), c )] fn get_collider_friction( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, collider: &NCollider, ) -> DropbearNativeResult<f64> { - let collider = get_collider(physics, &collider) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + 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"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderFriction" + ), c )] fn set_collider_friction( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &mut PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, collider: &NCollider, friction: f64, ) -> DropbearNativeResult<()> { - let collider = get_collider_mut(physics, &collider) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + 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"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderRestitution" + ), c )] fn get_collider_restitution( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, collider: &NCollider, ) -> DropbearNativeResult<f64> { - let collider = get_collider(physics, &collider) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + 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"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderRestitution" + ), c )] fn set_collider_restitution( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &mut PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, collider: &NCollider, restitution: f64, ) -> DropbearNativeResult<()> { - let collider = get_collider_mut(physics, &collider) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + 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"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderMass" + ), c )] fn get_collider_mass( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, collider: &NCollider, ) -> DropbearNativeResult<f64> { - let collider = get_collider(physics, &collider) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + 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"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderMass" + ), c )] fn set_collider_mass( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &mut PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, collider: &NCollider, mass: f64, ) -> DropbearNativeResult<()> { - let collider = get_collider_mut(physics, &collider) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + 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"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderIsSensor" + ), c )] fn get_collider_is_sensor( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, collider: &NCollider, ) -> DropbearNativeResult<bool> { - let collider = get_collider(physics, &collider) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + 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"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderIsSensor" + ), c )] fn set_collider_is_sensor( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &mut PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, collider: &NCollider, is_sensor: bool, ) -> DropbearNativeResult<()> { - let collider = get_collider_mut(physics, &collider) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + 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"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderTranslation" + ), c )] fn get_collider_translation( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, collider: &NCollider, ) -> DropbearNativeResult<NVector3> { - let collider = get_collider(physics, &collider) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + 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"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderTranslation" + ), c )] fn set_collider_translation( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &mut PhysicsState, + #[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); + 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"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "getColliderRotation" + ), c )] fn get_collider_rotation( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &PhysicsState, collider: &NCollider, ) -> DropbearNativeResult<NVector3> { - let collider = get_collider(physics, &collider) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + 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); @@ -1144,19 +1347,21 @@ fn get_collider_rotation( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.ColliderNative", func = "setColliderRotation"), + kotlin( + class = "com.dropbear.physics.ColliderNative", + func = "setColliderRotation" + ), c )] fn set_collider_rotation( - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &mut PhysicsState, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, collider: &NCollider, rotation: &NVector3, ) -> DropbearNativeResult<()> { - let collider = get_collider_mut(physics, &collider) - .ok_or(DropbearNativeError::PhysicsObjectNotFound)?; + 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,39 +1,41 @@ -//! Scripting module for collider groups. +//! Scripting module for collider groups. -use crate::physics::collider::ColliderGroup; 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"), + 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 + #[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"), + 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 + #[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 + let handles_opt = physics + .entity_label_map .get(&entity) .and_then(|label| physics.colliders_entity_map.get(label)); @@ -59,4 +61,4 @@ fn get_colliders( } else { Err(DropbearNativeError::MissingComponent)? } -} +} @@ -3,13 +3,19 @@ use std::mem::size_of; use std::sync::Arc; -use dropbear_engine::{entity::Transform, texture::Texture}; +use crate::physics::collider::{ColliderShape, WireframeGeometry}; use dropbear_engine::graphics::SharedGraphicsContext; +use dropbear_engine::pipelines::DropbearShaderPipeline; use dropbear_engine::shader::Shader; +use dropbear_engine::wgpu::{ + BlendState, BufferAddress, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, + DepthStencilState, FragmentState, FrontFace, MultisampleState, PipelineLayout, + PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology, RenderPipeline, + RenderPipelineDescriptor, StencilState, VertexAttribute, VertexBufferLayout, VertexFormat, + VertexState, VertexStepMode, +}; +use dropbear_engine::{entity::Transform, texture::Texture}; use glam::Mat4; -use dropbear_engine::pipelines::DropbearShaderPipeline; -use dropbear_engine::wgpu::{BlendState, BufferAddress, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, DepthStencilState, FragmentState, FrontFace, MultisampleState, PipelineLayout, PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology, RenderPipeline, RenderPipelineDescriptor, StencilState, VertexAttribute, VertexBufferLayout, VertexFormat, VertexState, VertexStepMode}; -use crate::physics::collider::{ColliderShape, WireframeGeometry}; pub struct ColliderWireframePipeline { pub shader: Shader, @@ -25,71 +31,73 @@ impl DropbearShaderPipeline for ColliderWireframePipeline { Some("collider wireframe shaders"), ); - let pipeline_layout = graphics.device.create_pipeline_layout(&PipelineLayoutDescriptor { - label: Some("collider wireframe pipeline layout descriptor"), - bind_group_layouts: &[ - &graphics.layouts.camera_bind_group_layout, // @group(0) - ], - push_constant_ranges: &[], - }); + let pipeline_layout = graphics + .device + .create_pipeline_layout(&PipelineLayoutDescriptor { + label: Some("collider wireframe pipeline layout descriptor"), + bind_group_layouts: &[ + &graphics.layouts.camera_bind_group_layout, // @group(0) + ], + push_constant_ranges: &[], + }); let hdr_format = graphics.hdr.read().format(); - let pipeline = graphics.device.create_render_pipeline(&RenderPipelineDescriptor { - label: Some("Collider Wireframe Pipeline"), - layout: Some(&pipeline_layout), - vertex: VertexState { - module: &shader.module, - entry_point: Some("vs_main"), - buffers: &[ - VertexBufferLayout { - array_stride: size_of::<[f32; 3]>() as BufferAddress, - step_mode: VertexStepMode::Vertex, - attributes: &[ - VertexAttribute { + let pipeline = graphics + .device + .create_render_pipeline(&RenderPipelineDescriptor { + label: Some("Collider Wireframe Pipeline"), + layout: Some(&pipeline_layout), + vertex: VertexState { + module: &shader.module, + entry_point: Some("vs_main"), + buffers: &[ + VertexBufferLayout { + array_stride: size_of::<[f32; 3]>() as BufferAddress, + step_mode: VertexStepMode::Vertex, + attributes: &[VertexAttribute { offset: 0, shader_location: 0, format: VertexFormat::Float32x3, - }, - ], - }, - ColliderInstanceRaw::desc(), - ], - compilation_options: Default::default(), - }, - fragment: Some(FragmentState { - module: &shader.module, - entry_point: Some("fs_main"), - targets: &[Some(ColorTargetState { - format: hdr_format, - blend: Some(BlendState::ALPHA_BLENDING), - write_mask: ColorWrites::ALL, - })], - compilation_options: Default::default(), - }), - primitive: PrimitiveState { - topology: PrimitiveTopology::LineList, - strip_index_format: None, - front_face: FrontFace::Ccw, - cull_mode: None, - unclipped_depth: false, - polygon_mode: PolygonMode::Fill, - conservative: false, - }, - depth_stencil: Some(DepthStencilState { - format: Texture::DEPTH_FORMAT, - depth_write_enabled: false, - depth_compare: CompareFunction::Always, - stencil: StencilState::default(), - bias: DepthBiasState::default(), - }), - multisample: MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - cache: None, - }); + }], + }, + ColliderInstanceRaw::desc(), + ], + compilation_options: Default::default(), + }, + fragment: Some(FragmentState { + module: &shader.module, + entry_point: Some("fs_main"), + targets: &[Some(ColorTargetState { + format: hdr_format, + blend: Some(BlendState::ALPHA_BLENDING), + write_mask: ColorWrites::ALL, + })], + compilation_options: Default::default(), + }), + primitive: PrimitiveState { + topology: PrimitiveTopology::LineList, + strip_index_format: None, + front_face: FrontFace::Ccw, + cull_mode: None, + unclipped_depth: false, + polygon_mode: PolygonMode::Fill, + conservative: false, + }, + depth_stencil: Some(DepthStencilState { + format: Texture::DEPTH_FORMAT, + depth_write_enabled: false, + depth_compare: CompareFunction::Always, + stencil: StencilState::default(), + bias: DepthBiasState::default(), + }), + multisample: MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + cache: None, + }); Self { shader, @@ -197,14 +205,17 @@ pub fn create_wireframe_geometry( ColliderShape::Sphere { radius } => { WireframeGeometry::sphere_wireframe(graphics, *radius, 16, 16) } - ColliderShape::Capsule { half_height, radius } => { - WireframeGeometry::capsule_wireframe(graphics, *half_height, *radius, 16) - } - ColliderShape::Cylinder { half_height, radius } => { - WireframeGeometry::cylinder_wireframe(graphics, *half_height, *radius, 16) - } - ColliderShape::Cone { half_height, radius } => { - WireframeGeometry::cone_wireframe(graphics, *half_height, *radius, 16) - } + ColliderShape::Capsule { + half_height, + radius, + } => WireframeGeometry::capsule_wireframe(graphics, *half_height, *radius, 16), + ColliderShape::Cylinder { + half_height, + radius, + } => WireframeGeometry::cylinder_wireframe(graphics, *half_height, *radius, 16), + ColliderShape::Cone { + half_height, + radius, + } => WireframeGeometry::cone_wireframe(graphics, *half_height, *radius, 16), } -} +} @@ -3,28 +3,28 @@ pub mod character_collision; -use rapier3d::control::{CharacterAutostep, CharacterCollision, CharacterLength, KinematicCharacterController}; -use serde::{Deserialize, Serialize}; -use crate::states::Label; -use std::any::Any; -use std::sync::Arc; -use egui::{ComboBox, DragValue, Ui}; -use hecs::{Entity, World}; +use crate::component::{Component, ComponentDescriptor, 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 jni::objects::{JObject, JValue}; +use dropbear_engine::graphics::SharedGraphicsContext; +use egui::{ComboBox, DragValue, Ui}; +use hecs::{Entity, World}; use jni::JNIEnv; +use jni::objects::{JObject, JValue}; +use rapier3d::control::{ + CharacterAutostep, CharacterCollision, CharacterLength, KinematicCharacterController, +}; use rapier3d::dynamics::RigidBodyType; -use rapier3d::na::{UnitVector3, Vector3}; use rapier3d::math::Rotation; +use rapier3d::na::{UnitVector3, Vector3}; use rapier3d::prelude::QueryFilter; -use dropbear_engine::animation::AnimationComponent; -use dropbear_engine::graphics::SharedGraphicsContext; -use crate::component::{Component, ComponentDescriptor, InspectableComponent, SerializedComponent}; -use crate::ptr::WorldPtr; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; /// The kinematic character controller (kcc) component. #[derive(Debug, Default, Serialize, Deserialize, Clone)] @@ -40,7 +40,7 @@ impl SerializedComponent for KCC {} impl Component for KCC { type SerializedForm = Self; - type RequiredComponentTypes = (Self, ); + type RequiredComponentTypes = (Self,); fn descriptor() -> ComponentDescriptor { ComponentDescriptor { @@ -55,10 +55,18 @@ impl Component for KCC { ser: &'a Self::SerializedForm, _: Arc<SharedGraphicsContext>, ) -> crate::component::ComponentInitFuture<'a, Self> { - Box::pin(async move { Ok((ser.clone(), )) }) + Box::pin(async move { Ok((ser.clone(),)) }) } - fn update_component(&mut self, _world: &World, _physics: &mut PhysicsState, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} + fn update_component( + &mut self, + _world: &World, + _physics: &mut PhysicsState, + _entity: Entity, + _dt: f32, + _graphics: Arc<SharedGraphicsContext>, + ) { + } fn save(&self, _world: &World, _entity: Entity) -> Box<dyn SerializedComponent> { Box::new(self.clone()) @@ -67,125 +75,145 @@ impl Component for KCC { impl InspectableComponent for KCC { fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { - egui::CollapsingHeader::new("Kinematic Character Controller").default_open(true).show(ui, |ui| { - fn edit_character_length(ui: &mut Ui, id_salt: impl std::hash::Hash, value: &mut CharacterLength, text: &str) { - ui.horizontal(|ui| { - ui.label(text); - - let (mut kind, mut v) = match *value { - CharacterLength::Absolute(x) => (0, x), - CharacterLength::Relative(x) => (1, x), - }; - - ComboBox::from_id_salt(id_salt) - .selected_text(match kind { - 0 => "Absolute", - _ => "Relative", - }) - .show_ui(ui, |ui| { - ui.selectable_value(&mut kind, 0, "Absolute"); - ui.selectable_value(&mut kind, 1, "Relative"); - }); - - ui.add(DragValue::new(&mut v).speed(0.01)); - - *value = if kind == 0 { - CharacterLength::Absolute(v) - } else { - CharacterLength::Relative(v) - }; - }); - } - - ui.vertical(|ui| { - ui.label("Up Vector:"); - let up = self.controller.up; - let mut up_v = Vector3::new(up.x, up.y, up.z); - - ui.horizontal(|ui| { - ui.label("X:"); - ui.add(DragValue::new(&mut up_v.x).speed(0.01)); - ui.label("Y:"); - ui.add(DragValue::new(&mut up_v.y).speed(0.01)); - ui.label("Z:"); - ui.add(DragValue::new(&mut up_v.z).speed(0.01)); - }); - - if up_v.norm_squared() > 0.0 { - self.controller.up = UnitVector3::new_normalize(up_v).into(); + egui::CollapsingHeader::new("Kinematic Character Controller") + .default_open(true) + .show(ui, |ui| { + fn edit_character_length( + ui: &mut Ui, + id_salt: impl std::hash::Hash, + value: &mut CharacterLength, + text: &str, + ) { + ui.horizontal(|ui| { + ui.label(text); + + let (mut kind, mut v) = match *value { + CharacterLength::Absolute(x) => (0, x), + CharacterLength::Relative(x) => (1, x), + }; + + ComboBox::from_id_salt(id_salt) + .selected_text(match kind { + 0 => "Absolute", + _ => "Relative", + }) + .show_ui(ui, |ui| { + ui.selectable_value(&mut kind, 0, "Absolute"); + ui.selectable_value(&mut kind, 1, "Relative"); + }); + + ui.add(DragValue::new(&mut v).speed(0.01)); + + *value = if kind == 0 { + CharacterLength::Absolute(v) + } else { + CharacterLength::Relative(v) + }; + }); } - ui.add_space(8.0); - - edit_character_length(ui, "kcc_offset", &mut self.controller.offset, "Offset:"); - - ui.add_space(8.0); - ui.separator(); - - ui.checkbox(&mut self.controller.slide, "Slide against floor?"); - - ui.label("Slope Angles (degrees):"); - ui.horizontal(|ui| { - ui.label("Max climb:"); - let mut deg = self.controller.max_slope_climb_angle.to_degrees(); - if ui.add(DragValue::new(&mut deg).speed(1.0)).changed() { - self.controller.max_slope_climb_angle = deg.to_radians(); - } - }); - ui.horizontal(|ui| { - ui.label("Min slide:"); - let mut deg = self.controller.min_slope_slide_angle.to_degrees(); - if ui.add(DragValue::new(&mut deg).speed(1.0)).changed() { - self.controller.min_slope_slide_angle = deg.to_radians(); + ui.vertical(|ui| { + ui.label("Up Vector:"); + let up = self.controller.up; + let mut up_v = Vector3::new(up.x, up.y, up.z); + + ui.horizontal(|ui| { + ui.label("X:"); + ui.add(DragValue::new(&mut up_v.x).speed(0.01)); + ui.label("Y:"); + ui.add(DragValue::new(&mut up_v.y).speed(0.01)); + ui.label("Z:"); + ui.add(DragValue::new(&mut up_v.z).speed(0.01)); + }); + + if up_v.norm_squared() > 0.0 { + self.controller.up = UnitVector3::new_normalize(up_v).into(); } - }); - ui.add_space(8.0); - - ui.horizontal(|ui| { - ui.label("Normal nudge:"); - ui.add( - egui::Slider::new(&mut self.controller.normal_nudge_factor, 0.001..=0.5) + ui.add_space(8.0); + + edit_character_length(ui, "kcc_offset", &mut self.controller.offset, "Offset:"); + + ui.add_space(8.0); + ui.separator(); + + ui.checkbox(&mut self.controller.slide, "Slide against floor?"); + + ui.label("Slope Angles (degrees):"); + ui.horizontal(|ui| { + ui.label("Max climb:"); + let mut deg = self.controller.max_slope_climb_angle.to_degrees(); + if ui.add(DragValue::new(&mut deg).speed(1.0)).changed() { + self.controller.max_slope_climb_angle = deg.to_radians(); + } + }); + ui.horizontal(|ui| { + ui.label("Min slide:"); + let mut deg = self.controller.min_slope_slide_angle.to_degrees(); + if ui.add(DragValue::new(&mut deg).speed(1.0)).changed() { + self.controller.min_slope_slide_angle = deg.to_radians(); + } + }); + + ui.add_space(8.0); + + ui.horizontal(|ui| { + ui.label("Normal nudge:"); + ui.add( + egui::Slider::new( + &mut self.controller.normal_nudge_factor, + 0.001..=0.5, + ) .logarithmic(true) .smallest_positive(0.001) .largest_finite(0.5) - .suffix(" m") - ); - }); - - ui.add_space(8.0); - ui.separator(); - - let mut enable_snap = self.controller.snap_to_ground.is_some(); - ui.checkbox(&mut enable_snap, "Snap to ground"); - if enable_snap && self.controller.snap_to_ground.is_none() { - self.controller.snap_to_ground = Some(CharacterLength::Absolute(0.2)); - } else if !enable_snap { - self.controller.snap_to_ground = None; - } + .suffix(" m"), + ); + }); + + ui.add_space(8.0); + ui.separator(); + + let mut enable_snap = self.controller.snap_to_ground.is_some(); + ui.checkbox(&mut enable_snap, "Snap to ground"); + if enable_snap && self.controller.snap_to_ground.is_none() { + self.controller.snap_to_ground = Some(CharacterLength::Absolute(0.2)); + } else if !enable_snap { + self.controller.snap_to_ground = None; + } - if let Some(ref mut snap) = self.controller.snap_to_ground { - edit_character_length(ui, "kcc_snap_to_ground", snap, "Snap distance:"); - } + if let Some(ref mut snap) = self.controller.snap_to_ground { + edit_character_length(ui, "kcc_snap_to_ground", snap, "Snap distance:"); + } - ui.add_space(8.0); - ui.separator(); + ui.add_space(8.0); + ui.separator(); - let mut enable_autostep = self.controller.autostep.is_some(); - ui.checkbox(&mut enable_autostep, "Enable autostep"); - if enable_autostep && self.controller.autostep.is_none() { - self.controller.autostep = Some(CharacterAutostep::default()); - } else if !enable_autostep { - self.controller.autostep = None; - } + let mut enable_autostep = self.controller.autostep.is_some(); + ui.checkbox(&mut enable_autostep, "Enable autostep"); + if enable_autostep && self.controller.autostep.is_none() { + self.controller.autostep = Some(CharacterAutostep::default()); + } else if !enable_autostep { + self.controller.autostep = None; + } - if let Some(step) = &mut self.controller.autostep { - edit_character_length(ui, "kcc_autostep_max_height", &mut step.max_height, "Max height:"); - edit_character_length(ui, "kcc_autostep_min_width", &mut step.min_width, "Min width:"); - ui.checkbox(&mut step.include_dynamic_bodies, "Include dynamic bodies"); - } + if let Some(step) = &mut self.controller.autostep { + edit_character_length( + ui, + "kcc_autostep_max_height", + &mut step.max_height, + "Max height:", + ); + edit_character_length( + ui, + "kcc_autostep_min_width", + &mut step.min_width, + "Min width:", + ); + ui.checkbox(&mut step.include_dynamic_bodies, "Include dynamic bodies"); + } + }); }); - }); } } @@ -207,26 +235,35 @@ struct CharacterCollisionArray { impl ToJObject for CharacterCollisionArray { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let collision_cls = env.find_class("com/dropbear/physics/CharacterCollision") + let collision_cls = env + .find_class("com/dropbear/physics/CharacterCollision") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let entity_cls = env.find_class("com/dropbear/EntityId") + let entity_cls = env + .find_class("com/dropbear/EntityId") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let entity_obj = env.new_object(&entity_cls, "(J)V", &[JValue::Long(self.entity_id as i64)]) + let entity_obj = env + .new_object(&entity_cls, "(J)V", &[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()) + 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, - "(Lcom/dropbear/EntityId;Lcom/dropbear/physics/Index;)V", - &[JValue::Object(&entity_obj), JValue::Object(&index_obj)], - ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + let collision_obj = env + .new_object( + &collision_cls, + "(Lcom/dropbear/EntityId;Lcom/dropbear/physics/Index;)V", + &[JValue::Object(&entity_obj), JValue::Object(&index_obj)], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; env.set_object_array_element(&out, i as i32, collision_obj) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; @@ -237,29 +274,30 @@ impl ToJObject for CharacterCollisionArray { } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.KinematicCharacterControllerNative", func = "existsForEntity"), + 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, + #[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"), + 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, + #[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<()> { @@ -318,10 +356,14 @@ fn move_character( translation.z as f32, ), |collision| { - if let Some(collisions) = physics_state.collision_events_to_deal_with.get_mut(&entity) { + 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]); + physics_state + .collision_events_to_deal_with + .insert(entity, vec![collision]); } }, ); @@ -339,16 +381,16 @@ fn move_character( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.KinematicCharacterControllerNative", func = "setRotation"), + 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, + #[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() { @@ -397,14 +439,15 @@ fn set_rotation( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.KinematicCharacterControllerNative", func = "getHit"), + 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, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<CharacterCollisionArray> { let kcc = world .get::<&KCC>(entity) @@ -413,11 +456,14 @@ fn get_hit( 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 }); + collisions.push(IndexNative { + index: idx, + generation, + }); } Ok(CharacterCollisionArray { entity_id: entity.to_bits().get(), collisions, }) -} +} @@ -1,10 +1,9 @@ -use ::jni::JNIEnv; -use ::jni::objects::{JObject, JValue}; 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 dropbear_engine::entity::Transform; +use ::jni::JNIEnv; +use ::jni::objects::{JObject, JValue}; use hecs::{Entity, World}; use rapier3d::control::CharacterCollision; @@ -12,279 +11,363 @@ 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 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 +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 JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env - .find_class("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, - "valueOf", - "(Ljava/lang/String;)Lcom/dropbear/physics/ShapeCastStatus;", - &[JValue::Object(&name_jstring)], - ) - .map_err(|_| DropbearNativeError::JNIMethodNotFound)? - .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - Ok(value) - } + fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .find_class("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, + "valueOf", + "(Ljava/lang/String;)Lcom/dropbear/physics/ShapeCastStatus;", + &[JValue::Object(&name_jstring)], + ) + .map_err(|_| DropbearNativeError::JNIMethodNotFound)? + .l() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + Ok(value) + } } pub mod shared { - use super::*; - use glam::{DQuat, DVec3}; - use rapier3d::na::Quaternion; - use crate::types::NVector3; - - 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()) - } + 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 + 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, + #[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 + 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, + #[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 + 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, + #[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 + 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, + #[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 + 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, + #[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 + 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, + #[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 + 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, + #[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 + 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, + #[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 + 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, + #[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 + 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, + #[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) -} +} @@ -1,22 +1,21 @@ //! [rapier3d] RigidBodies +use crate::component::{Component, ComponentDescriptor, InspectableComponent, SerializedComponent}; +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 std::any::Any; -use std::sync::Arc; -use ::jni::objects::{JObject, JValue}; +use crate::types::{IndexNative, NCollider, NVector3, RigidBodyContext}; use ::jni::JNIEnv; +use ::jni::objects::{JObject, JValue}; +use dropbear_engine::graphics::SharedGraphicsContext; use egui::{CollapsingHeader, ComboBox, DragValue, Ui}; use hecs::{Entity, World}; use rapier3d::prelude::RigidBodyType; use serde::{Deserialize, Serialize}; -use dropbear_engine::graphics::SharedGraphicsContext; -use crate::component::{Component, ComponentDescriptor, InspectableComponent, SerializedComponent}; -use crate::types::{IndexNative, NCollider, RigidBodyContext, NVector3}; -use crate::physics::PhysicsState; -use crate::ptr::{PhysicsStatePtr, WorldPtr}; +use std::sync::Arc; /// How this entity behaves in the physics simulation. /// @@ -24,701 +23,870 @@ use crate::ptr::{PhysicsStatePtr, WorldPtr}; #[repr(C)] #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] pub enum RigidBodyMode { - /// A fully simulated body affected by forces and contacts. - Dynamic = 0, - /// An immovable body. - Fixed, - /// A kinematic body controlled by setting its next position. - KinematicPosition, - /// A kinematic body controlled by setting its velocities. - KinematicVelocity, + /// A fully simulated body affected by forces and contacts. + Dynamic = 0, + /// An immovable body. + Fixed, + /// A kinematic body controlled by setting its next position. + KinematicPosition, + /// A kinematic body controlled by setting its velocities. + KinematicVelocity, } impl Default for RigidBodyMode { - fn default() -> Self { - Self::Dynamic - } + fn default() -> Self { + Self::Dynamic + } } impl From<RigidBodyType> for RigidBodyMode { - fn from(value: RigidBodyType) -> Self { - match value { - RigidBodyType::Dynamic => RigidBodyMode::Dynamic, - RigidBodyType::Fixed => Self::Fixed, - RigidBodyType::KinematicPositionBased => Self::KinematicPosition, - RigidBodyType::KinematicVelocityBased => Self::KinematicVelocity, - } - } + fn from(value: RigidBodyType) -> Self { + match value { + RigidBodyType::Dynamic => RigidBodyMode::Dynamic, + RigidBodyType::Fixed => Self::Fixed, + RigidBodyType::KinematicPositionBased => Self::KinematicPosition, + RigidBodyType::KinematicVelocityBased => Self::KinematicVelocity, + } + } } #[repr(C)] #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default)] pub struct AxisLock { - pub x: bool, - pub y: bool, - pub z: bool, + pub x: bool, + pub y: bool, + pub z: bool, } impl FromJObject for AxisLock { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> - where - Self: Sized - { - let class = env.find_class("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, "x", "Z") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .z() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let y = env.get_field(obj, "y", "Z") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .z() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - let z = env.get_field(obj, "z", "Z") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)? - .z() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - - Ok(AxisLock { x, y, z }) - } + fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> + where + Self: Sized, + { + let class = env + .find_class("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, "x", "Z") + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .z() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let y = env + .get_field(obj, "y", "Z") + .map_err(|_| DropbearNativeError::JNIFailedToGetField)? + .z() + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + + let z = env + .get_field(obj, "z", "Z") + .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 JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/physics/AxisLock") - .map_err(|_| DropbearNativeError::JNIClassNotFound)?; + fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + let class = env + .find_class("com/dropbear/physics/AxisLock") + .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let constructor_sig = "(ZZZ)V"; + let constructor_sig = "(ZZZ)V"; - let args = [ - JValue::Bool(self.x as u8), - JValue::Bool(self.y as u8), - JValue::Bool(self.z as u8), - ]; + let args = [ + JValue::Bool(self.x as u8), + JValue::Bool(self.y as u8), + JValue::Bool(self.z as u8), + ]; - let obj = env.new_object(&class, constructor_sig, &args) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + let obj = env + .new_object(&class, constructor_sig, &args) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - Ok(obj) - } + Ok(obj) + } } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct RigidBody { - /// The entity this component is attached to. - #[serde(default)] - pub entity: Label, + /// The entity this component is attached to. + #[serde(default)] + pub entity: Label, - #[serde(default)] - pub disable_physics: bool, + #[serde(default)] + pub disable_physics: bool, - /// Body type/mode. - #[serde(default)] - pub mode: RigidBodyMode, + /// Body type/mode. + #[serde(default)] + pub mode: RigidBodyMode, - /// Scaling factor applied to gravity for this body. - #[serde(default = "RigidBody::default_gravity_scale")] - pub gravity_scale: f32, + /// Scaling factor applied to gravity for this body. + #[serde(default = "RigidBody::default_gravity_scale")] + pub gravity_scale: f32, - #[serde(default)] - /// If the rigidbody is currently sleeping or not. - pub sleeping: bool, + #[serde(default)] + /// If the rigidbody is currently sleeping or not. + pub sleeping: bool, - /// Whether this body is allowed to sleep. - #[serde(default = "RigidBody::default_sleep")] - pub can_sleep: bool, + /// Whether this body is allowed to sleep. + #[serde(default = "RigidBody::default_sleep")] + pub can_sleep: bool, - /// Whether continuous collision detection is enabled. - #[serde(default)] - pub ccd_enabled: bool, + /// Whether continuous collision detection is enabled. + #[serde(default)] + pub ccd_enabled: bool, - /// Initial linear velocity (m/s). - #[serde(default)] - pub linvel: [f32; 3], + /// Initial linear velocity (m/s). + #[serde(default)] + pub linvel: [f32; 3], - /// Initial angular velocity (rad/s). - #[serde(default)] - pub angvel: [f32; 3], + /// Initial angular velocity (rad/s). + #[serde(default)] + pub angvel: [f32; 3], - /// Linear damping coefficient. - #[serde(default)] - pub linear_damping: f32, + /// Linear damping coefficient. + #[serde(default)] + pub linear_damping: f32, - /// Angular damping coefficient. - #[serde(default)] - pub angular_damping: f32, + /// Angular damping coefficient. + #[serde(default)] + pub angular_damping: f32, - /// Locks translation along specific axes. - #[serde(default)] - pub lock_translation: AxisLock, + /// Locks translation along specific axes. + #[serde(default)] + pub lock_translation: AxisLock, - /// Locks rotation around specific axes. - #[serde(default)] - pub lock_rotation: AxisLock, + /// Locks rotation around specific axes. + #[serde(default)] + pub lock_rotation: AxisLock, } impl Default for RigidBody { - fn default() -> Self { - Self { - entity: Label::default(), - disable_physics: false, - mode: RigidBodyMode::default(), - gravity_scale: Self::default_gravity_scale(), - sleeping: false, - can_sleep: Self::default_sleep(), - ccd_enabled: false, - linvel: [0.0, 0.0, 0.0], - angvel: [0.0, 0.0, 0.0], - linear_damping: 0.0, - angular_damping: 0.0, - lock_translation: AxisLock::default(), - lock_rotation: AxisLock::default(), - } - } + fn default() -> Self { + Self { + entity: Label::default(), + disable_physics: false, + mode: RigidBodyMode::default(), + gravity_scale: Self::default_gravity_scale(), + sleeping: false, + can_sleep: Self::default_sleep(), + ccd_enabled: false, + linvel: [0.0, 0.0, 0.0], + angvel: [0.0, 0.0, 0.0], + linear_damping: 0.0, + angular_damping: 0.0, + lock_translation: AxisLock::default(), + lock_rotation: AxisLock::default(), + } + } } #[typetag::serde] impl SerializedComponent for RigidBody {} impl Component for RigidBody { - type SerializedForm = Self; - type RequiredComponentTypes = (Self, ); - - fn descriptor() -> ComponentDescriptor { - ComponentDescriptor { - fqtn: "eucalyptus_core::physics::rigidbody::RigidBody".to_string(), - type_name: "RigidBody".to_string(), - category: Some("Physics".to_string()), - description: Some("An object that can move, rotate or collide with another object".to_string()), - } - } - - fn init<'a>( - ser: &'a Self::SerializedForm, - _graphics: Arc<SharedGraphicsContext>, - ) -> crate::component::ComponentInitFuture<'a, Self> { - Box::pin(async move { Ok((ser.clone(), )) }) - } - - fn update_component(&mut self, _world: &World, _physics: &mut PhysicsState, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} - - fn save(&self, _world: &World, _entity: Entity) -> Box<dyn SerializedComponent> { - Box::new(self.clone()) - } + type SerializedForm = Self; + type RequiredComponentTypes = (Self,); + + fn descriptor() -> ComponentDescriptor { + ComponentDescriptor { + fqtn: "eucalyptus_core::physics::rigidbody::RigidBody".to_string(), + type_name: "RigidBody".to_string(), + category: Some("Physics".to_string()), + description: Some( + "An object that can move, rotate or collide with another object".to_string(), + ), + } + } + + fn init<'a>( + ser: &'a Self::SerializedForm, + _graphics: Arc<SharedGraphicsContext>, + ) -> crate::component::ComponentInitFuture<'a, Self> { + Box::pin(async move { Ok((ser.clone(),)) }) + } + + fn update_component( + &mut self, + _world: &World, + _physics: &mut PhysicsState, + _entity: Entity, + _dt: f32, + _graphics: Arc<SharedGraphicsContext>, + ) { + } + + fn save(&self, _world: &World, _entity: Entity) -> Box<dyn SerializedComponent> { + Box::new(self.clone()) + } } impl InspectableComponent for RigidBody { - fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { - CollapsingHeader::new("RigidBody").default_open(true).show(ui, |ui| { - ui.vertical(|ui| { - let mut selected = self.mode; - ComboBox::from_id_salt("rb") - .selected_text(format!("{:?}", self.mode)) - .show_ui(ui, |ui| { - ui.selectable_value(&mut selected, RigidBodyMode::Dynamic, "Dynamic"); - ui.selectable_value(&mut selected, RigidBodyMode::Fixed, "Fixed"); - ui.selectable_value(&mut selected, RigidBodyMode::KinematicPosition, "Kinematic Position"); - ui.selectable_value(&mut selected, RigidBodyMode::KinematicVelocity, "Kinematic Velocity"); - }); - - if selected != self.mode { - self.mode = selected; - } - - ui.add_space(8.0); - - ui.horizontal(|ui| { - ui.label("Gravity Scale:"); - ui.add(DragValue::new(&mut self.gravity_scale) - .speed(0.1) - .range(0.0..=10.0)); - }); - - ui.checkbox(&mut self.can_sleep, "Can Sleep"); - ui.checkbox(&mut self.sleeping, "Initially sleeping?"); - ui.checkbox(&mut self.ccd_enabled, "CCD Enabled"); - - ui.add_space(8.0); - - ui.label("Linear Velocity:"); - ui.horizontal(|ui| { - ui.label("X:"); - ui.add(DragValue::new(&mut self.linvel[0]).speed(0.1)); - ui.label("Y:"); - ui.add(DragValue::new(&mut self.linvel[1]).speed(0.1)); - ui.label("Z:"); - ui.add(DragValue::new(&mut self.linvel[2]).speed(0.1)); - }); - - ui.label("Angular Velocity:"); - ui.horizontal(|ui| { - ui.label("X:"); - ui.add(DragValue::new(&mut self.angvel[0]).speed(0.1)); - ui.label("Y:"); - ui.add(DragValue::new(&mut self.angvel[1]).speed(0.1)); - ui.label("Z:"); - ui.add(DragValue::new(&mut self.angvel[2]).speed(0.1)); - }); - - ui.add_space(8.0); - - ui.horizontal(|ui| { - ui.label("Linear Damping:"); - ui.add(DragValue::new(&mut self.linear_damping) - .speed(0.01) - .range(0.0..=10.0)); - }); - - ui.horizontal(|ui| { - ui.label("Angular Damping:"); - ui.add(DragValue::new(&mut self.angular_damping) - .speed(0.01) - .range(0.0..=10.0)); - }); - - ui.add_space(8.0); - - ui.label("Lock Translation:"); - ui.horizontal(|ui| { - ui.checkbox(&mut self.lock_translation.x, "X"); - ui.checkbox(&mut self.lock_translation.y, "Y"); - ui.checkbox(&mut self.lock_translation.z, "Z"); - }); - - ui.label("Lock Rotation:"); - ui.horizontal(|ui| { - ui.checkbox(&mut self.lock_rotation.x, "X"); - ui.checkbox(&mut self.lock_rotation.y, "Y"); - ui.checkbox(&mut self.lock_rotation.z, "Z"); - }); - }); - }); - } + fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { + CollapsingHeader::new("RigidBody") + .default_open(true) + .show(ui, |ui| { + ui.vertical(|ui| { + let mut selected = self.mode; + ComboBox::from_id_salt("rb") + .selected_text(format!("{:?}", self.mode)) + .show_ui(ui, |ui| { + ui.selectable_value(&mut selected, RigidBodyMode::Dynamic, "Dynamic"); + ui.selectable_value(&mut selected, RigidBodyMode::Fixed, "Fixed"); + ui.selectable_value( + &mut selected, + RigidBodyMode::KinematicPosition, + "Kinematic Position", + ); + ui.selectable_value( + &mut selected, + RigidBodyMode::KinematicVelocity, + "Kinematic Velocity", + ); + }); + + if selected != self.mode { + self.mode = selected; + } + + ui.add_space(8.0); + + ui.horizontal(|ui| { + ui.label("Gravity Scale:"); + ui.add( + DragValue::new(&mut self.gravity_scale) + .speed(0.1) + .range(0.0..=10.0), + ); + }); + + ui.checkbox(&mut self.can_sleep, "Can Sleep"); + ui.checkbox(&mut self.sleeping, "Initially sleeping?"); + ui.checkbox(&mut self.ccd_enabled, "CCD Enabled"); + + ui.add_space(8.0); + + ui.label("Linear Velocity:"); + ui.horizontal(|ui| { + ui.label("X:"); + ui.add(DragValue::new(&mut self.linvel[0]).speed(0.1)); + ui.label("Y:"); + ui.add(DragValue::new(&mut self.linvel[1]).speed(0.1)); + ui.label("Z:"); + ui.add(DragValue::new(&mut self.linvel[2]).speed(0.1)); + }); + + ui.label("Angular Velocity:"); + ui.horizontal(|ui| { + ui.label("X:"); + ui.add(DragValue::new(&mut self.angvel[0]).speed(0.1)); + ui.label("Y:"); + ui.add(DragValue::new(&mut self.angvel[1]).speed(0.1)); + ui.label("Z:"); + ui.add(DragValue::new(&mut self.angvel[2]).speed(0.1)); + }); + + ui.add_space(8.0); + + ui.horizontal(|ui| { + ui.label("Linear Damping:"); + ui.add( + DragValue::new(&mut self.linear_damping) + .speed(0.01) + .range(0.0..=10.0), + ); + }); + + ui.horizontal(|ui| { + ui.label("Angular Damping:"); + ui.add( + DragValue::new(&mut self.angular_damping) + .speed(0.01) + .range(0.0..=10.0), + ); + }); + + ui.add_space(8.0); + + ui.label("Lock Translation:"); + ui.horizontal(|ui| { + ui.checkbox(&mut self.lock_translation.x, "X"); + ui.checkbox(&mut self.lock_translation.y, "Y"); + ui.checkbox(&mut self.lock_translation.z, "Z"); + }); + + ui.label("Lock Rotation:"); + ui.horizontal(|ui| { + ui.checkbox(&mut self.lock_rotation.x, "X"); + ui.checkbox(&mut self.lock_rotation.y, "Y"); + ui.checkbox(&mut self.lock_rotation.z, "Z"); + }); + }); + }); + } } impl RigidBody { - const fn default_gravity_scale() -> f32 { - 1.0 - } + const fn default_gravity_scale() -> f32 { + 1.0 + } - const fn default_sleep() -> bool { - true - } + const fn default_sleep() -> bool { + true + } } pub mod shared { - use crate::physics::rigidbody::{AxisLock, RigidBody, RigidBodyMode}; - use crate::physics::PhysicsState; - use crate::scripting::native::DropbearNativeError; - use crate::scripting::result::DropbearNativeResult; - use crate::states::Label; - use crate::types::{IndexNative, RigidBodyContext, NVector3}; - use hecs::{Entity, World}; - use rapier3d::dynamics::{RigidBodyHandle, RigidBodyType}; - use rapier3d::prelude::Vector; - use rapier3d::prelude::{ColliderHandle, LockedAxes}; - - pub fn rigid_body_exists_for_entity( - world: &World, - physics: &PhysicsState, - entity: Entity - ) -> Option<IndexNative> { - if let Ok((label, _)) = world.query_one::<(&Label, &RigidBody)>(entity).get() - { - if let Some(handle) = physics.bodies_entity_map.get(label) { - Some(IndexNative::from(handle.0)) - } else { - None - } - } else { - None - } - } - - pub fn get_rigidbody_type(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<RigidBodyType> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get(handle) { - Ok(rb.body_type()) - } else { - Err(DropbearNativeError::PhysicsObjectNotFound) - } - } - - pub fn set_rigidbody_type(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, mode: i64) -> DropbearNativeResult<()> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get_mut(handle) { - let mode = match mode { - 0 => RigidBodyType::Dynamic, - 1 => RigidBodyType::Fixed, - 2 => RigidBodyType::KinematicPositionBased, - 3 => RigidBodyType::KinematicVelocityBased, - _ => { return Err(DropbearNativeError::InvalidArgument); } - }; - rb.set_body_type(mode, true); - - let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?; - - if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { - let rb_mode = RigidBodyMode::from(mode); - rb.mode = rb_mode; - } - - Ok(()) - } else { - Err(DropbearNativeError::NoSuchHandle) - } - } - - pub fn get_rigidbody_gravity_scale(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<f64> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get(handle) { - Ok(rb.gravity_scale().into()) - } else { - Err(DropbearNativeError::PhysicsObjectNotFound) - } - } - - pub fn set_rigidbody_gravity_scale(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new_scale: f64) -> DropbearNativeResult<()> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get_mut(handle) { - rb.set_gravity_scale(new_scale as f32, true); - - let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?; - - if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { - rb.gravity_scale = new_scale as f32; - } - - Ok(()) - } else { - Err(DropbearNativeError::NoSuchHandle) - } - } - - pub fn get_rigidbody_linear_damping(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<f64> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get(handle) { - Ok(rb.linear_damping().into()) - } else { - Err(DropbearNativeError::PhysicsObjectNotFound) - } - } - - pub fn set_rigidbody_linear_damping(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: f64) -> DropbearNativeResult<()> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get_mut(handle) { - rb.set_linear_damping(new as f32); - - let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?; - - if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { - rb.linear_damping = new as f32; - } - - Ok(()) - } else { - Err(DropbearNativeError::NoSuchHandle) - } - } - - pub fn get_rigidbody_angular_damping(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<f64> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get(handle) { - Ok(rb.angular_damping().into()) - } else { - Err(DropbearNativeError::PhysicsObjectNotFound) - } - } - - pub fn set_rigidbody_angular_damping(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: f64) -> DropbearNativeResult<()> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get_mut(handle) { - rb.set_angular_damping(new as f32); - - let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?; - - if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { - rb.angular_damping = new as f32; - } - - Ok(()) - } else { - Err(DropbearNativeError::NoSuchHandle) - } - } - - pub fn get_rigidbody_sleep(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<bool> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get(handle) { - Ok(rb.is_sleeping().into()) - } else { - Err(DropbearNativeError::PhysicsObjectNotFound) - } - } - - pub fn set_rigidbody_sleep(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: bool) -> DropbearNativeResult<()> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get_mut(handle) { - if new { - rb.sleep(); - } else { - rb.wake_up(true); - } - - let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?; - - if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { - rb.sleeping = new; - } - - Ok(()) - } else { - Err(DropbearNativeError::NoSuchHandle) - } - } - - pub fn get_rigidbody_ccd(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<bool> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get(handle) { - Ok(rb.is_ccd_enabled().into()) - } else { - Err(DropbearNativeError::PhysicsObjectNotFound) - } - } - - pub fn set_rigidbody_ccd(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: bool) -> DropbearNativeResult<()> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get_mut(handle) { - rb.enable_ccd(new); - - let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?; - - if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { - rb.ccd_enabled = new; - } - - Ok(()) - } else { - Err(DropbearNativeError::NoSuchHandle) - } - } - - pub fn get_rigidbody_linvel(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<NVector3> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get(handle) { - let linvel = rb.linvel().clone(); - Ok(NVector3::new(linvel.x as f64, linvel.y as f64, linvel.z as f64)) - } else { - Err(DropbearNativeError::PhysicsObjectNotFound) - } - } - - pub fn set_rigidbody_linvel(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: NVector3) -> DropbearNativeResult<()> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get_mut(handle) { - rb.set_linvel(Vector::new(new.x as f32, new.y as f32, new.z as f32), true); - - let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?; - - if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { - rb.linvel = [new.x as f32, new.y as f32, new.z as f32]; - } - - Ok(()) - } else { - Err(DropbearNativeError::NoSuchHandle) - } - } - - pub fn get_rigidbody_angvel(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<NVector3> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get(handle) { - let angvel = rb.angvel().clone(); - Ok(NVector3::new(angvel.x as f64, angvel.y as f64, angvel.z as f64)) - } else { - Err(DropbearNativeError::PhysicsObjectNotFound) - } - } - - pub fn set_rigidbody_angvel(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: NVector3) -> DropbearNativeResult<()> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get_mut(handle) { - rb.set_angvel(Vector::new(new.x as f32, new.y as f32, new.z as f32), true); - - let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?; - - if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { - rb.angvel = [new.x as f32, new.y as f32, new.z as f32]; - } - - Ok(()) - } else { - Err(DropbearNativeError::NoSuchHandle) - } - } - - pub fn get_rigidbody_lock_translation(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<AxisLock> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get(handle) { - let lock = rb.locked_axes(); - let translation_lock = AxisLock { - x: lock.contains(LockedAxes::TRANSLATION_LOCKED_X), - y: lock.contains(LockedAxes::TRANSLATION_LOCKED_Y), - z: lock.contains(LockedAxes::TRANSLATION_LOCKED_Z), - }; - Ok(translation_lock) - } else { - Err(DropbearNativeError::PhysicsObjectNotFound) - } - } - - pub fn set_rigidbody_lock_translation(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: AxisLock) -> DropbearNativeResult<()> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get_mut(handle) { - let mut bits = rb.locked_axes().clone(); - - bits.remove(LockedAxes::TRANSLATION_LOCKED_X); - bits.remove(LockedAxes::TRANSLATION_LOCKED_Y); - bits.remove(LockedAxes::TRANSLATION_LOCKED_Z); - - if new.x { - bits = bits | LockedAxes::TRANSLATION_LOCKED_X; - } - if new.y { - bits = bits | LockedAxes::TRANSLATION_LOCKED_Y; - } - if new.z { - bits = bits | LockedAxes::TRANSLATION_LOCKED_Z; - } - - rb.set_locked_axes(bits, true); - - let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?; - - if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { - rb.lock_translation = new; - } - - Ok(()) - } else { - Err(DropbearNativeError::NoSuchHandle) - } - } - - pub fn get_rigidbody_lock_rotation(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<AxisLock> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get(handle) { - let lock = rb.locked_axes(); - let rot_lock = AxisLock { - x: lock.contains(LockedAxes::ROTATION_LOCKED_X), - y: lock.contains(LockedAxes::ROTATION_LOCKED_Y), - z: lock.contains(LockedAxes::ROTATION_LOCKED_Z), - }; - Ok(rot_lock) - } else { - Err(DropbearNativeError::PhysicsObjectNotFound) - } - } - - pub fn set_rigidbody_lock_rotation(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: AxisLock) -> DropbearNativeResult<()> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get_mut(handle) { - let mut bits = rb.locked_axes().clone(); - - bits.remove(LockedAxes::ROTATION_LOCKED_X); - bits.remove(LockedAxes::ROTATION_LOCKED_Y); - bits.remove(LockedAxes::ROTATION_LOCKED_Z); - - if new.x { - bits = bits | LockedAxes::ROTATION_LOCKED_X; - } - if new.y { - bits = bits | LockedAxes::ROTATION_LOCKED_Y; - } - if new.z { - bits = bits | LockedAxes::ROTATION_LOCKED_Z; - } - - rb.set_locked_axes(bits, true); - - let entity = Entity::from_bits(rb_context.entity_id).ok_or(DropbearNativeError::InvalidEntity)?; - - if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { - rb.lock_translation = new; - } - - Ok(()) - } else { - Err(DropbearNativeError::NoSuchHandle) - } - } - - pub fn get_rigidbody_children(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<Vec<ColliderHandle>> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get(handle) { - let children = rb.colliders().to_vec(); - Ok(children) - } else { - Err(DropbearNativeError::PhysicsObjectNotFound) - } - } - - pub fn apply_impulse(physics: &mut PhysicsState, _world: &World, rb_context: &RigidBodyContext, new: NVector3) -> DropbearNativeResult<()> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get_mut(handle) { - rb.apply_impulse(Vector::new(new.x as f32, new.y as f32, new.z as f32), true); - - Ok(()) - } else { - Err(DropbearNativeError::NoSuchHandle) - } - } - - pub fn apply_torque_impulse(physics: &mut PhysicsState, _world: &World, rb_context: &RigidBodyContext, new: NVector3) -> DropbearNativeResult<()> { - let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); - if let Some(rb) = physics.bodies.get_mut(handle) { - rb.apply_torque_impulse(Vector::new(new.x as f32, new.y as f32, new.z as f32), true); - - Ok(()) - } else { - Err(DropbearNativeError::NoSuchHandle) - } - } + use crate::physics::PhysicsState; + use crate::physics::rigidbody::{AxisLock, RigidBody, RigidBodyMode}; + use crate::scripting::native::DropbearNativeError; + use crate::scripting::result::DropbearNativeResult; + use crate::states::Label; + use crate::types::{IndexNative, NVector3, RigidBodyContext}; + use hecs::{Entity, World}; + use rapier3d::dynamics::{RigidBodyHandle, RigidBodyType}; + use rapier3d::prelude::Vector; + use rapier3d::prelude::{ColliderHandle, LockedAxes}; + + pub fn rigid_body_exists_for_entity( + world: &World, + physics: &PhysicsState, + entity: Entity, + ) -> Option<IndexNative> { + if let Ok((label, _)) = world.query_one::<(&Label, &RigidBody)>(entity).get() { + if let Some(handle) = physics.bodies_entity_map.get(label) { + Some(IndexNative::from(handle.0)) + } else { + None + } + } else { + None + } + } + + pub fn get_rigidbody_type( + physics: &PhysicsState, + rb_context: &RigidBodyContext, + ) -> DropbearNativeResult<RigidBodyType> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get(handle) { + Ok(rb.body_type()) + } else { + Err(DropbearNativeError::PhysicsObjectNotFound) + } + } + + pub fn set_rigidbody_type( + physics: &mut PhysicsState, + world: &World, + rb_context: &RigidBodyContext, + mode: i64, + ) -> DropbearNativeResult<()> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get_mut(handle) { + let mode = match mode { + 0 => RigidBodyType::Dynamic, + 1 => RigidBodyType::Fixed, + 2 => RigidBodyType::KinematicPositionBased, + 3 => RigidBodyType::KinematicVelocityBased, + _ => { + return Err(DropbearNativeError::InvalidArgument); + } + }; + rb.set_body_type(mode, true); + + let entity = Entity::from_bits(rb_context.entity_id) + .ok_or(DropbearNativeError::InvalidEntity)?; + + if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { + let rb_mode = RigidBodyMode::from(mode); + rb.mode = rb_mode; + } + + Ok(()) + } else { + Err(DropbearNativeError::NoSuchHandle) + } + } + + pub fn get_rigidbody_gravity_scale( + physics: &PhysicsState, + rb_context: &RigidBodyContext, + ) -> DropbearNativeResult<f64> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get(handle) { + Ok(rb.gravity_scale().into()) + } else { + Err(DropbearNativeError::PhysicsObjectNotFound) + } + } + + pub fn set_rigidbody_gravity_scale( + physics: &mut PhysicsState, + world: &World, + rb_context: &RigidBodyContext, + new_scale: f64, + ) -> DropbearNativeResult<()> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get_mut(handle) { + rb.set_gravity_scale(new_scale as f32, true); + + let entity = Entity::from_bits(rb_context.entity_id) + .ok_or(DropbearNativeError::InvalidEntity)?; + + if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { + rb.gravity_scale = new_scale as f32; + } + + Ok(()) + } else { + Err(DropbearNativeError::NoSuchHandle) + } + } + + pub fn get_rigidbody_linear_damping( + physics: &PhysicsState, + rb_context: &RigidBodyContext, + ) -> DropbearNativeResult<f64> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get(handle) { + Ok(rb.linear_damping().into()) + } else { + Err(DropbearNativeError::PhysicsObjectNotFound) + } + } + + pub fn set_rigidbody_linear_damping( + physics: &mut PhysicsState, + world: &World, + rb_context: &RigidBodyContext, + new: f64, + ) -> DropbearNativeResult<()> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get_mut(handle) { + rb.set_linear_damping(new as f32); + + let entity = Entity::from_bits(rb_context.entity_id) + .ok_or(DropbearNativeError::InvalidEntity)?; + + if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { + rb.linear_damping = new as f32; + } + + Ok(()) + } else { + Err(DropbearNativeError::NoSuchHandle) + } + } + + pub fn get_rigidbody_angular_damping( + physics: &PhysicsState, + rb_context: &RigidBodyContext, + ) -> DropbearNativeResult<f64> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get(handle) { + Ok(rb.angular_damping().into()) + } else { + Err(DropbearNativeError::PhysicsObjectNotFound) + } + } + + pub fn set_rigidbody_angular_damping( + physics: &mut PhysicsState, + world: &World, + rb_context: &RigidBodyContext, + new: f64, + ) -> DropbearNativeResult<()> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get_mut(handle) { + rb.set_angular_damping(new as f32); + + let entity = Entity::from_bits(rb_context.entity_id) + .ok_or(DropbearNativeError::InvalidEntity)?; + + if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { + rb.angular_damping = new as f32; + } + + Ok(()) + } else { + Err(DropbearNativeError::NoSuchHandle) + } + } + + pub fn get_rigidbody_sleep( + physics: &PhysicsState, + rb_context: &RigidBodyContext, + ) -> DropbearNativeResult<bool> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get(handle) { + Ok(rb.is_sleeping().into()) + } else { + Err(DropbearNativeError::PhysicsObjectNotFound) + } + } + + pub fn set_rigidbody_sleep( + physics: &mut PhysicsState, + world: &World, + rb_context: &RigidBodyContext, + new: bool, + ) -> DropbearNativeResult<()> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get_mut(handle) { + if new { + rb.sleep(); + } else { + rb.wake_up(true); + } + + let entity = Entity::from_bits(rb_context.entity_id) + .ok_or(DropbearNativeError::InvalidEntity)?; + + if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { + rb.sleeping = new; + } + + Ok(()) + } else { + Err(DropbearNativeError::NoSuchHandle) + } + } + + pub fn get_rigidbody_ccd( + physics: &PhysicsState, + rb_context: &RigidBodyContext, + ) -> DropbearNativeResult<bool> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get(handle) { + Ok(rb.is_ccd_enabled().into()) + } else { + Err(DropbearNativeError::PhysicsObjectNotFound) + } + } + + pub fn set_rigidbody_ccd( + physics: &mut PhysicsState, + world: &World, + rb_context: &RigidBodyContext, + new: bool, + ) -> DropbearNativeResult<()> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get_mut(handle) { + rb.enable_ccd(new); + + let entity = Entity::from_bits(rb_context.entity_id) + .ok_or(DropbearNativeError::InvalidEntity)?; + + if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { + rb.ccd_enabled = new; + } + + Ok(()) + } else { + Err(DropbearNativeError::NoSuchHandle) + } + } + + pub fn get_rigidbody_linvel( + physics: &PhysicsState, + rb_context: &RigidBodyContext, + ) -> DropbearNativeResult<NVector3> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get(handle) { + let linvel = rb.linvel().clone(); + Ok(NVector3::new( + linvel.x as f64, + linvel.y as f64, + linvel.z as f64, + )) + } else { + Err(DropbearNativeError::PhysicsObjectNotFound) + } + } + + pub fn set_rigidbody_linvel( + physics: &mut PhysicsState, + world: &World, + rb_context: &RigidBodyContext, + new: NVector3, + ) -> DropbearNativeResult<()> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get_mut(handle) { + rb.set_linvel(Vector::new(new.x as f32, new.y as f32, new.z as f32), true); + + let entity = Entity::from_bits(rb_context.entity_id) + .ok_or(DropbearNativeError::InvalidEntity)?; + + if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { + rb.linvel = [new.x as f32, new.y as f32, new.z as f32]; + } + + Ok(()) + } else { + Err(DropbearNativeError::NoSuchHandle) + } + } + + pub fn get_rigidbody_angvel( + physics: &PhysicsState, + rb_context: &RigidBodyContext, + ) -> DropbearNativeResult<NVector3> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get(handle) { + let angvel = rb.angvel().clone(); + Ok(NVector3::new( + angvel.x as f64, + angvel.y as f64, + angvel.z as f64, + )) + } else { + Err(DropbearNativeError::PhysicsObjectNotFound) + } + } + + pub fn set_rigidbody_angvel( + physics: &mut PhysicsState, + world: &World, + rb_context: &RigidBodyContext, + new: NVector3, + ) -> DropbearNativeResult<()> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get_mut(handle) { + rb.set_angvel(Vector::new(new.x as f32, new.y as f32, new.z as f32), true); + + let entity = Entity::from_bits(rb_context.entity_id) + .ok_or(DropbearNativeError::InvalidEntity)?; + + if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { + rb.angvel = [new.x as f32, new.y as f32, new.z as f32]; + } + + Ok(()) + } else { + Err(DropbearNativeError::NoSuchHandle) + } + } + + pub fn get_rigidbody_lock_translation( + physics: &PhysicsState, + rb_context: &RigidBodyContext, + ) -> DropbearNativeResult<AxisLock> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get(handle) { + let lock = rb.locked_axes(); + let translation_lock = AxisLock { + x: lock.contains(LockedAxes::TRANSLATION_LOCKED_X), + y: lock.contains(LockedAxes::TRANSLATION_LOCKED_Y), + z: lock.contains(LockedAxes::TRANSLATION_LOCKED_Z), + }; + Ok(translation_lock) + } else { + Err(DropbearNativeError::PhysicsObjectNotFound) + } + } + + pub fn set_rigidbody_lock_translation( + physics: &mut PhysicsState, + world: &World, + rb_context: &RigidBodyContext, + new: AxisLock, + ) -> DropbearNativeResult<()> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get_mut(handle) { + let mut bits = rb.locked_axes().clone(); + + bits.remove(LockedAxes::TRANSLATION_LOCKED_X); + bits.remove(LockedAxes::TRANSLATION_LOCKED_Y); + bits.remove(LockedAxes::TRANSLATION_LOCKED_Z); + + if new.x { + bits = bits | LockedAxes::TRANSLATION_LOCKED_X; + } + if new.y { + bits = bits | LockedAxes::TRANSLATION_LOCKED_Y; + } + if new.z { + bits = bits | LockedAxes::TRANSLATION_LOCKED_Z; + } + + rb.set_locked_axes(bits, true); + + let entity = Entity::from_bits(rb_context.entity_id) + .ok_or(DropbearNativeError::InvalidEntity)?; + + if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { + rb.lock_translation = new; + } + + Ok(()) + } else { + Err(DropbearNativeError::NoSuchHandle) + } + } + + pub fn get_rigidbody_lock_rotation( + physics: &PhysicsState, + rb_context: &RigidBodyContext, + ) -> DropbearNativeResult<AxisLock> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get(handle) { + let lock = rb.locked_axes(); + let rot_lock = AxisLock { + x: lock.contains(LockedAxes::ROTATION_LOCKED_X), + y: lock.contains(LockedAxes::ROTATION_LOCKED_Y), + z: lock.contains(LockedAxes::ROTATION_LOCKED_Z), + }; + Ok(rot_lock) + } else { + Err(DropbearNativeError::PhysicsObjectNotFound) + } + } + + pub fn set_rigidbody_lock_rotation( + physics: &mut PhysicsState, + world: &World, + rb_context: &RigidBodyContext, + new: AxisLock, + ) -> DropbearNativeResult<()> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get_mut(handle) { + let mut bits = rb.locked_axes().clone(); + + bits.remove(LockedAxes::ROTATION_LOCKED_X); + bits.remove(LockedAxes::ROTATION_LOCKED_Y); + bits.remove(LockedAxes::ROTATION_LOCKED_Z); + + if new.x { + bits = bits | LockedAxes::ROTATION_LOCKED_X; + } + if new.y { + bits = bits | LockedAxes::ROTATION_LOCKED_Y; + } + if new.z { + bits = bits | LockedAxes::ROTATION_LOCKED_Z; + } + + rb.set_locked_axes(bits, true); + + let entity = Entity::from_bits(rb_context.entity_id) + .ok_or(DropbearNativeError::InvalidEntity)?; + + if let Ok(rb) = world.query_one::<&mut RigidBody>(entity).get() { + rb.lock_translation = new; + } + + Ok(()) + } else { + Err(DropbearNativeError::NoSuchHandle) + } + } + + pub fn get_rigidbody_children( + physics: &PhysicsState, + rb_context: &RigidBodyContext, + ) -> DropbearNativeResult<Vec<ColliderHandle>> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get(handle) { + let children = rb.colliders().to_vec(); + Ok(children) + } else { + Err(DropbearNativeError::PhysicsObjectNotFound) + } + } + + pub fn apply_impulse( + physics: &mut PhysicsState, + _world: &World, + rb_context: &RigidBodyContext, + new: NVector3, + ) -> DropbearNativeResult<()> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get_mut(handle) { + rb.apply_impulse(Vector::new(new.x as f32, new.y as f32, new.z as f32), true); + + Ok(()) + } else { + Err(DropbearNativeError::NoSuchHandle) + } + } + + pub fn apply_torque_impulse( + physics: &mut PhysicsState, + _world: &World, + rb_context: &RigidBodyContext, + new: NVector3, + ) -> DropbearNativeResult<()> { + let handle = + RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation); + if let Some(rb) = physics.bodies.get_mut(handle) { + rb.apply_torque_impulse(Vector::new(new.x as f32, new.y as f32, new.z as f32), true); + + Ok(()) + } else { + Err(DropbearNativeError::NoSuchHandle) + } + } } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "rigidBodyExistsForEntity"), - c + 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, + #[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 + 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, + #[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)?; @@ -731,14 +899,15 @@ fn get_rigidbody_mode( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "setRigidBodyMode"), - c + 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, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, rigidbody: &RigidBodyContext, mode: i32, ) -> DropbearNativeResult<()> { @@ -746,28 +915,30 @@ fn set_rigidbody_mode( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyGravityScale"), - c + 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, + #[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 + 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, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, rigidbody: &RigidBodyContext, gravity_scale: f64, ) -> DropbearNativeResult<()> { @@ -775,28 +946,30 @@ fn set_rigidbody_gravity_scale( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyLinearDamping"), - c + 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, + #[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 + 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, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, rigidbody: &RigidBodyContext, linear_damping: f64, ) -> DropbearNativeResult<()> { @@ -804,28 +977,30 @@ fn set_rigidbody_linear_damping( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyAngularDamping"), - c + 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, + #[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 + 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, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, rigidbody: &RigidBodyContext, angular_damping: f64, ) -> DropbearNativeResult<()> { @@ -833,28 +1008,30 @@ fn set_rigidbody_angular_damping( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodySleep"), - c + 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, + #[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 + 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, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, rigidbody: &RigidBodyContext, sleep: bool, ) -> DropbearNativeResult<()> { @@ -862,28 +1039,30 @@ fn set_rigidbody_sleep( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyCcdEnabled"), - c + 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, + #[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 + 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, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, rigidbody: &RigidBodyContext, ccd_enabled: bool, ) -> DropbearNativeResult<()> { @@ -891,28 +1070,30 @@ fn set_rigidbody_ccd_enabled( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyLinearVelocity"), - c + 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, + #[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 + 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, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, rigidbody: &RigidBodyContext, linear_velocity: &NVector3, ) -> DropbearNativeResult<()> { @@ -920,28 +1101,30 @@ fn set_rigidbody_linear_velocity( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyAngularVelocity"), - c + 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, + #[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 + 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, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, rigidbody: &RigidBodyContext, angular_velocity: &NVector3, ) -> DropbearNativeResult<()> { @@ -949,28 +1132,30 @@ fn set_rigidbody_angular_velocity( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyLockTranslation"), - c + 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, + #[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 + 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, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, rigidbody: &RigidBodyContext, lock_translation: &AxisLock, ) -> DropbearNativeResult<()> { @@ -978,28 +1163,30 @@ fn set_rigidbody_lock_translation( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyLockRotation"), - c + 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, + #[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 + 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, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, rigidbody: &RigidBodyContext, lock_rotation: &AxisLock, ) -> DropbearNativeResult<()> { @@ -1007,14 +1194,15 @@ fn set_rigidbody_lock_rotation( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyChildren"), - c + 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, + #[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)?; @@ -1023,7 +1211,10 @@ fn get_rigidbody_children( .map(|handle| { let (idx, generation) = handle.into_raw_parts(); NCollider { - index: IndexNative { index: idx, generation }, + index: IndexNative { + index: idx, + generation, + }, entity_id: rigidbody.entity_id, id: idx, } @@ -1035,13 +1226,11 @@ fn get_rigidbody_children( #[dropbear_macro::export( kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "applyImpulse"), - c + c )] fn apply_impulse( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropbear_macro::define(PhysicsStatePtr)] - physics: &mut PhysicsState, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, rigidbody: &RigidBodyContext, x: f64, y: f64, @@ -1052,14 +1241,15 @@ fn apply_impulse( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "applyTorqueImpulse"), - c + 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, + #[dropbear_macro::define(WorldPtr)] world: &hecs::World, + #[dropbear_macro::define(PhysicsStatePtr)] physics: &mut PhysicsState, rigidbody: &RigidBodyContext, x: f64, y: f64, @@ -1067,4 +1257,4 @@ fn apply_torque_impulse( ) -> DropbearNativeResult<()> { let torque = NVector3::new(x, y, z); shared::apply_torque_impulse(physics, world, rigidbody, torque) -} +} @@ -1,18 +1,19 @@ -use std::fmt; -use std::fmt::{Display, Formatter}; -use serde::{Deserialize, Serialize}; -use std::any::Any; -use std::sync::Arc; -use egui::{CollapsingHeader, ComboBox, DragValue, Grid, RichText, TextEdit, Ui}; -use hecs::{Entity, World}; -use dropbear_engine::graphics::SharedGraphicsContext; -use crate::component::{Component, ComponentDescriptor, ComponentInitFuture, InspectableComponent, SerializedComponent}; +use crate::component::{ + Component, ComponentDescriptor, ComponentInitFuture, 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}; +use hecs::{Entity, World}; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::fmt::{Display, Formatter}; +use std::sync::Arc; /// Properties for an entity, typically queries with `entity.getProperty<Float>` and `entity.setProperty(21)` #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] @@ -26,7 +27,7 @@ impl SerializedComponent for CustomProperties {} impl Component for CustomProperties { type SerializedForm = Self; - type RequiredComponentTypes = (Self, ); + type RequiredComponentTypes = (Self,); fn descriptor() -> ComponentDescriptor { ComponentDescriptor { @@ -41,10 +42,18 @@ impl Component for CustomProperties { ser: &'a Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, ) -> ComponentInitFuture<'a, Self> { - Box::pin(async move { Ok((ser.clone(), )) }) + Box::pin(async move { Ok((ser.clone(),)) }) } - fn update_component(&mut self, _world: &World, _physics: &mut crate::physics::PhysicsState, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} + fn update_component( + &mut self, + _world: &World, + _physics: &mut crate::physics::PhysicsState, + _entity: Entity, + _dt: f32, + _graphics: Arc<SharedGraphicsContext>, + ) { + } fn save(&self, _world: &World, _entity: Entity) -> Box<dyn SerializedComponent> { Box::new(self.clone()) @@ -53,130 +62,144 @@ impl Component for CustomProperties { impl InspectableComponent for CustomProperties { fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { - CollapsingHeader::new("Custom Properties").default_open(true).show(ui, |ui| { - ui.vertical(|ui| { - Grid::new("properties").striped(true).show(ui, |ui| { - ui.label(RichText::new("Key")); - ui.label(RichText::new("Type")); - ui.label(RichText::new("Value")); - ui.label(RichText::new("Action")); - ui.end_row(); - - let mut to_delete: Option<u64> = None; - let mut to_rename: Option<(u64, String)> = None; - - for (_i, property) in self.custom_properties.iter_mut().enumerate() { - let mut edited_key = property.key.clone(); - ui.add_sized([100.0, 20.0], TextEdit::singleline(&mut edited_key)); - - if edited_key != property.key { - to_rename = Some((property.id, edited_key)); - } + CollapsingHeader::new("Custom Properties") + .default_open(true) + .show(ui, |ui| { + ui.vertical(|ui| { + Grid::new("properties").striped(true).show(ui, |ui| { + ui.label(RichText::new("Key")); + ui.label(RichText::new("Type")); + ui.label(RichText::new("Value")); + ui.label(RichText::new("Action")); + ui.end_row(); - let current_type = ValueType::from(&mut property.value); - let mut selected_type = current_type; - - ComboBox::from_id_salt(format!("type_{}", property.id)) - .selected_text(format!("{:?}", selected_type)) - .show_ui(ui, |ui| { - ui.selectable_value( - &mut selected_type, - ValueType::String, - "String", - ); - ui.selectable_value(&mut selected_type, ValueType::Float, "Float"); - ui.selectable_value(&mut selected_type, ValueType::Int, "Int"); - ui.selectable_value(&mut selected_type, ValueType::Bool, "Bool"); - ui.selectable_value(&mut selected_type, ValueType::Vec3, "Vec3"); - }); + let mut to_delete: Option<u64> = None; + let mut to_rename: Option<(u64, String)> = None; - if selected_type != current_type { - property.value = match selected_type { - ValueType::String => Value::String(String::new()), - ValueType::Float => Value::Double(0.0), - ValueType::Int => Value::Int(0), - ValueType::Bool => Value::Bool(false), - ValueType::Vec3 => Value::Vec3([0.0, 0.0, 0.0]), - }; - } + for (_i, property) in self.custom_properties.iter_mut().enumerate() { + let mut edited_key = property.key.clone(); + ui.add_sized([100.0, 20.0], TextEdit::singleline(&mut edited_key)); - let speed = { - let input = ui.input(|i| i.modifiers); - if input.shift { - 0.01 - } else if cfg!(target_os = "macos") && input.mac_cmd - || !cfg!(target_os = "macos") && input.ctrl - { - 1.0 - } else { - 0.1 + if edited_key != property.key { + to_rename = Some((property.id, edited_key)); } - }; - match &mut property.value { - Value::String(s) => { - ui.add_sized([100.0, 20.0], egui::TextEdit::singleline(s)); - } - Value::Int(n) => { - ui.add(DragValue::new(n).speed(1.0)); - } - Value::Double(f) => { - ui.add(DragValue::new(f).speed(speed)); + let current_type = ValueType::from(&mut property.value); + let mut selected_type = current_type; + + ComboBox::from_id_salt(format!("type_{}", property.id)) + .selected_text(format!("{:?}", selected_type)) + .show_ui(ui, |ui| { + ui.selectable_value( + &mut selected_type, + ValueType::String, + "String", + ); + ui.selectable_value( + &mut selected_type, + ValueType::Float, + "Float", + ); + ui.selectable_value(&mut selected_type, ValueType::Int, "Int"); + ui.selectable_value( + &mut selected_type, + ValueType::Bool, + "Bool", + ); + ui.selectable_value( + &mut selected_type, + ValueType::Vec3, + "Vec3", + ); + }); + + if selected_type != current_type { + property.value = match selected_type { + ValueType::String => Value::String(String::new()), + ValueType::Float => Value::Double(0.0), + ValueType::Int => Value::Int(0), + ValueType::Bool => Value::Bool(false), + ValueType::Vec3 => Value::Vec3([0.0, 0.0, 0.0]), + }; } - Value::Bool(b) => { - if ui.button(if *b { "✅" } else { "❌" }).clicked() { - *b = !*b; + + let speed = { + let input = ui.input(|i| i.modifiers); + if input.shift { + 0.01 + } else if cfg!(target_os = "macos") && input.mac_cmd + || !cfg!(target_os = "macos") && input.ctrl + { + 1.0 + } else { + 0.1 + } + }; + + match &mut property.value { + Value::String(s) => { + ui.add_sized([100.0, 20.0], egui::TextEdit::singleline(s)); + } + Value::Int(n) => { + ui.add(DragValue::new(n).speed(1.0)); + } + Value::Double(f) => { + ui.add(DragValue::new(f).speed(speed)); + } + Value::Bool(b) => { + if ui.button(if *b { "✅" } else { "❌" }).clicked() { + *b = !*b; + } + } + Value::Vec3(v) => { + ui.horizontal(|ui| { + ui.add(DragValue::new(&mut v[0]).speed(speed)); + ui.add(DragValue::new(&mut v[1]).speed(speed)); + ui.add(DragValue::new(&mut v[2]).speed(speed)); + }); } } - Value::Vec3(v) => { - ui.horizontal(|ui| { - ui.add(DragValue::new(&mut v[0]).speed(speed)); - ui.add(DragValue::new(&mut v[1]).speed(speed)); - ui.add(DragValue::new(&mut v[2]).speed(speed)); - }); + + if ui.button("🗑️").clicked() { + log::debug!("Trashing {}", property.key); + to_delete = Some(property.id); } + + ui.end_row(); } - if ui.button("🗑️").clicked() { - log::debug!("Trashing {}", property.key); - to_delete = Some(property.id); + if let Some(id) = to_delete { + self.custom_properties.retain(|p| p.id != id); } - ui.end_row(); - } - - if let Some(id) = to_delete { - self.custom_properties.retain(|p| p.id != id); - } - - if let Some((id, new_key)) = to_rename { - if let Some(property) = - self.custom_properties.iter_mut().find(|p| p.id == id) - { - property.key = new_key; - } else { - warn!("Failed to rename property: id not found"); + if let Some((id, new_key)) = to_rename { + if let Some(property) = + self.custom_properties.iter_mut().find(|p| p.id == id) + { + property.key = new_key; + } else { + warn!("Failed to rename property: id not found"); + } } - } - - if ui.button("Add").clicked() { - log::debug!("Inserting new default value"); - let mut new_key = String::from("new_property"); - let mut counter = 1; - while self.custom_properties.iter().any(|p| p.key == new_key) { - new_key = format!("new_property_{}", counter); - counter += 1; + + if ui.button("Add").clicked() { + log::debug!("Inserting new default value"); + let mut new_key = String::from("new_property"); + let mut counter = 1; + while self.custom_properties.iter().any(|p| p.key == new_key) { + new_key = format!("new_property_{}", counter); + counter += 1; + } + self.custom_properties.push(Property { + id: self.next_id, + key: new_key, + value: Value::default(), + }); + self.next_id += 1; } - self.custom_properties.push(Property { - id: self.next_id, - key: new_key, - value: Value::default(), - }); - self.next_id += 1; - } + }); }); }); - }); } } @@ -351,10 +374,9 @@ impl From<&mut Value> for ValueType { } } - pub mod shared { - use hecs::World; use crate::properties::CustomProperties; + use hecs::World; pub fn custom_properties_exists_for_entity(world: &World, entity: hecs::Entity) -> bool { world.get::<&CustomProperties>(entity).is_ok() @@ -362,27 +384,29 @@ pub mod shared { } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "customPropertiesExistsForEntity"), + 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, + #[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"), + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "getStringProperty" + ), c )] fn get_string_property( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, key: String, ) -> DropbearNativeResult<Option<String>> { let props = world @@ -396,14 +420,15 @@ fn get_string_property( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "getIntProperty"), + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "getIntProperty" + ), c )] fn get_int_property( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, key: String, ) -> DropbearNativeResult<Option<i32>> { let props = world @@ -417,14 +442,15 @@ fn get_int_property( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "getLongProperty"), + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "getLongProperty" + ), c )] fn get_long_property( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, key: String, ) -> DropbearNativeResult<Option<i64>> { let props = world @@ -438,14 +464,15 @@ fn get_long_property( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "getDoubleProperty"), + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "getDoubleProperty" + ), c )] fn get_double_property( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, key: String, ) -> DropbearNativeResult<Option<f64>> { let props = world @@ -459,14 +486,15 @@ fn get_double_property( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "getFloatProperty"), + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "getFloatProperty" + ), c )] fn get_float_property( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, key: String, ) -> DropbearNativeResult<Option<f32>> { let props = world @@ -480,14 +508,15 @@ fn get_float_property( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "getBoolProperty"), + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "getBoolProperty" + ), c )] fn get_bool_property( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, key: String, ) -> DropbearNativeResult<Option<bool>> { let props = world @@ -501,14 +530,15 @@ fn get_bool_property( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "getVec3Property"), + kotlin( + class = "com.dropbear.components.CustomPropertiesNative", + func = "getVec3Property" + ), c )] fn get_vec3_property( - #[dropbear_macro::define(WorldPtr)] - world: &World, - #[dropbear_macro::entity] - entity: Entity, + #[dropbear_macro::define(WorldPtr)] world: &World, + #[dropbear_macro::entity] entity: Entity, key: String, ) -> DropbearNativeResult<Option<NVector3>> { let props = world @@ -522,14 +552,15 @@ fn get_vec3_property( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "setStringProperty"), + 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, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, key: String, value: String, ) -> DropbearNativeResult<()> { @@ -541,14 +572,15 @@ fn set_string_property( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "setIntProperty"), + 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, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, key: String, value: i32, ) -> DropbearNativeResult<()> { @@ -560,14 +592,15 @@ fn set_int_property( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "setLongProperty"), + 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, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, key: String, value: i64, ) -> DropbearNativeResult<()> { @@ -579,14 +612,15 @@ fn set_long_property( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "setDoubleProperty"), + 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, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, key: String, value: f64, ) -> DropbearNativeResult<()> { @@ -598,14 +632,15 @@ fn set_double_property( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "setFloatProperty"), + 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, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, key: String, value: f64, ) -> DropbearNativeResult<()> { @@ -617,14 +652,15 @@ fn set_float_property( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "setBoolProperty"), + 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, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, key: String, value: bool, ) -> DropbearNativeResult<()> { @@ -636,14 +672,15 @@ fn set_bool_property( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "setVec3Property"), + 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, + #[dropbear_macro::define(WorldPtr)] world: &mut World, + #[dropbear_macro::entity] entity: Entity, key: String, value: &NVector3, ) -> DropbearNativeResult<()> { @@ -652,4 +689,4 @@ fn set_vec3_property( .map_err(|_| DropbearNativeError::NoSuchComponent)?; props.set_property(key, Value::Vec3(value.to_array())); Ok(()) -} +} @@ -1,16 +1,16 @@ //! Helper pointers and typedef definitions. -use std::sync::Arc; -use std::ffi::c_void; -use crate::input::InputState; use crate::command::CommandBuffer; +use crate::input::InputState; +use crate::physics::PhysicsState; +use crate::scene::loading::SceneLoader; use crossbeam_channel::Sender; use dropbear_engine::asset::AssetRegistry; use dropbear_engine::graphics::SharedGraphicsContext; use hecs::World; use parking_lot::{Mutex, RwLock}; -use crate::physics::PhysicsState; -use crate::scene::loading::SceneLoader; +use std::ffi::c_void; +use std::sync::Arc; /// A mutable pointer to a [`World`]. /// @@ -58,4 +58,4 @@ pub type PhysicsStatePtr = *mut PhysicsState; /// A mutable pointer to the UI command buffer/state. /// /// This is treated as an opaque pointer by scripting layers. -pub type UiBufferPtr = *mut c_void; +pub type UiBufferPtr = *mut c_void; @@ -15,10 +15,10 @@ use semver::Version; /// such as initial scene and stuff like that. #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct RuntimeSettings { - /// The first scene that shows up when redback-runtime is ran. - /// + /// The first scene that shows up when redback-runtime is ran. + /// /// The first scene is not set is expected to be the first scene out of the - /// projects scene list, or just a normal anyhow error. + /// projects scene list, or just a normal anyhow error. #[serde(default)] pub initial_scene: Option<String>, #[serde(default)] @@ -65,9 +65,9 @@ pub struct RuntimeProjectConfig { /// The name of the project pub project_name: String, - /// The initial/first scene that will show up. - /// - /// Access to other scenes are done with the game's scripting. + /// The initial/first scene that will show up. + /// + /// Access to other scenes are done with the game's scripting. pub initial_scene: String, /// Authors and creators of the game @@ -98,9 +98,11 @@ impl RuntimeProjectConfig { Some(val) => val.clone(), None => { log::warn!("Unable to fetch initial settings, using first scene available"); - let scene = scenes.first().ok_or(anyhow::anyhow!("Unable to locate first scene in SCENES"))?; + let scene = scenes + .first() + .ok_or(anyhow::anyhow!("Unable to locate first scene in SCENES"))?; scene.scene_name.clone() - }, + } }; let result = Self { @@ -122,12 +124,14 @@ impl RuntimeProjectConfig { Ok(result) } - /// Populates the states (such as [PROJECT]) with all the context from the RuntimeProjectConfig. + /// Populates the states (such as [PROJECT]) with all the context from the RuntimeProjectConfig. pub fn populate(&self) -> anyhow::Result<()> { let exe_dir = std::env::current_exe() .context("Unable to locate runtime executable")? .parent() - .ok_or_else(|| anyhow::anyhow!("Unable to locate parent directory of runtime executable"))? + .ok_or_else(|| { + anyhow::anyhow!("Unable to locate parent directory of runtime executable") + })? .to_path_buf(); let now = format!("{}", Utc::now().format("%Y-%m-%d %H:%M:%S")); @@ -4,13 +4,20 @@ pub mod loading; pub mod scripting; use crate::camera::CameraComponent; +use crate::component::{Component, ComponentRegistry, SerializedComponent}; use crate::hierarchy::{Children, Parent, SceneHierarchy}; -use crate::states::{SerializableCamera, Label, SerializedLight, Script, SerializedMeshRenderer, WorldLoadingStatus, PROJECT}; +use crate::physics::PhysicsState; +use crate::physics::collider::ColliderGroup; +use crate::physics::kcc::KCC; +use crate::physics::rigidbody::RigidBody; +use crate::properties::CustomProperties; +use crate::states::{Label, SerializedLight, WorldLoadingStatus}; +use crossbeam_channel::Sender; use dropbear_engine::camera::Camera; -use dropbear_engine::entity::{EntityTransform, MeshRenderer, Transform}; +use dropbear_engine::entity::EntityTransform; use dropbear_engine::graphics::SharedGraphicsContext; use dropbear_engine::lighting::{Light, LightComponent}; -use glam::{DQuat, DVec3}; +use hecs::{Entity, EntityBuilder}; use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; use ron::ser::PrettyConfig; use serde::{Deserialize, Serialize}; @@ -18,15 +25,6 @@ use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; use std::sync::Arc; -use crossbeam_channel::Sender; -use egui::Ui; -use hecs::{Entity, EntityBuilder}; -use crate::component::{Component, ComponentRegistry, SerializedComponent}; -use crate::physics::collider::ColliderGroup; -use crate::physics::kcc::KCC; -use crate::physics::PhysicsState; -use crate::physics::rigidbody::RigidBody; -use crate::properties::CustomProperties; #[derive(Default, Serialize, Deserialize, Clone)] pub struct SceneEntity { @@ -46,8 +44,7 @@ impl SceneEntity { entity: hecs::Entity, registry: &ComponentRegistry, ) -> Option<Self> { - let label = if let Ok(label) = world.query_one::<&Label>(entity).get() - { + let label = if let Ok(label) = world.query_one::<&Label>(entity).get() { label.clone() } else { return None; @@ -67,7 +64,7 @@ impl SceneEntity { #[derive(Default, Debug, Serialize, Deserialize, Clone)] pub struct SceneSettings { /// Ensures a scene's assets are preloaded at the start of the game. - /// + /// /// This is useful for situations where you might need a loading screen /// and want to make sure an image is loaded into memory. #[serde(default)] @@ -215,7 +212,6 @@ impl SceneConfig { builder.add(label_for_map.clone()); for component in components.iter() { - if component.as_any().downcast_ref::<Parent>().is_some() { log::debug!( "Skipping Parent component for '{}' - will be rebuilt from hierarchy_map", @@ -254,7 +250,8 @@ impl SceneConfig { } self.rebuild_hierarchy(world, &label_to_entity); - self.ensure_default_light(world, graphics.clone(), progress_sender.as_ref()).await?; + self.ensure_default_light(world, graphics.clone(), progress_sender.as_ref()) + .await?; log::info!("Loaded {} entities from scene", self.entities.len()); @@ -264,19 +261,16 @@ impl SceneConfig { } fn register_physics_for_entity(&mut self, world: &mut hecs::World, entity: hecs::Entity) { - if let Ok(( - label, - e_trans, - rigid, - col_group, - kcc - )) = world.query_one::<( - &Label, - &EntityTransform, - Option<&mut RigidBody>, - Option<&mut ColliderGroup>, - Option<&mut KCC> - )>(entity).get() { + if let Ok((label, e_trans, rigid, col_group, kcc)) = world + .query_one::<( + &Label, + &EntityTransform, + Option<&mut RigidBody>, + Option<&mut ColliderGroup>, + Option<&mut KCC>, + )>(entity) + .get() + { if let Some(body) = rigid { body.entity = label.clone(); self.physics_state.register_rigidbody(body, e_trans.sync()); @@ -380,12 +374,7 @@ impl SceneConfig { progress_sender: Option<&Sender<WorldLoadingStatus>>, ) -> anyhow::Result<()> { let mut has_light = false; - if world - .query::<&Light>() - .iter() - .next() - .is_some() - { + if world.query::<&Light>().iter().next().is_some() { has_light = true; } @@ -400,9 +389,7 @@ impl SceneConfig { }); } let comp = LightComponent::directional(glam::DVec3::ONE, 1.0); - let light = - Light::new(graphics.clone(), comp.clone(), Some("Default Light")) - .await; + let light = Light::new(graphics.clone(), comp.clone(), Some("Default Light")).await; let light_config = SerializedLight { label: "Default Light".to_string(), @@ -473,7 +460,9 @@ impl SceneConfig { log::info!("Using existing debug camera for editor"); Ok(camera_entity) } else { - log::info!("No debug or starting camera found, creating viewport camera for editor"); + log::info!( + "No debug or starting camera found, creating viewport camera for editor" + ); if let Some(s) = progress_sender { let _ = s.send(WorldLoadingStatus::LoadingEntity { @@ -490,4 +479,4 @@ impl SceneConfig { } } } -} +} @@ -11,7 +11,8 @@ use dropbear_engine::future::FutureHandle; use crate::states::WorldLoadingStatus; use crate::utils::Progress; -pub static SCENE_LOADER: LazyLock<Mutex<SceneLoader>> = LazyLock::new(|| Mutex::new(SceneLoader::new())); +pub static SCENE_LOADER: LazyLock<Mutex<SceneLoader>> = + LazyLock::new(|| Mutex::new(SceneLoader::new())); pub struct SceneLoader { scenes_to_load: HashMap<u64, SceneLoadEntry>, @@ -53,19 +54,22 @@ impl SceneLoader { self.next_id += 1; let id = self.next_id; - self.scenes_to_load.insert(id, SceneLoadEntry { - scene_name, - result: SceneLoadResult::Pending, - progress: Progress { - current: 0, - total: 1, - message: "Idle".to_string(), + self.scenes_to_load.insert( + id, + SceneLoadEntry { + scene_name, + result: SceneLoadResult::Pending, + progress: Progress { + current: 0, + total: 1, + message: "Idle".to_string(), + }, + status: None, + loaded: None, + loaded_scene: None, + thread_handle: None, }, - status: None, - loaded: None, - loaded_scene: None, - thread_handle: None, - }); + ); id } @@ -160,4 +164,4 @@ impl IsSceneLoaded { pub fn is_first_scene(&self) -> bool { self.is_first_scene } -} +} @@ -30,7 +30,8 @@ pub mod shared { }; // Send load command - command_buffer.try_send(CommandBuffer::LoadSceneAsync(handle)) + command_buffer + .try_send(CommandBuffer::LoadSceneAsync(handle)) .map_err(|_| DropbearNativeError::SendError)?; Ok(id) @@ -40,7 +41,8 @@ pub mod shared { command_buffer: &Sender<CommandBuffer>, scene_name: String, ) -> DropbearNativeResult<()> { - command_buffer.try_send(CommandBuffer::SwitchSceneImmediate(scene_name)) + command_buffer + .try_send(CommandBuffer::SwitchSceneImmediate(scene_name)) .map_err(|_| DropbearNativeError::SendError)?; Ok(()) } @@ -59,7 +61,8 @@ pub mod shared { scene_name: entry.scene_name.clone(), }; - command_buffer.try_send(CommandBuffer::SwitchToAsync(handle)) + command_buffer + .try_send(CommandBuffer::SwitchToAsync(handle)) .map_err(|_| DropbearNativeError::SendError)?; Ok(()) } else { @@ -96,12 +99,12 @@ pub mod shared { match status { crate::states::WorldLoadingStatus::Idle => { entry.progress.message = "Idle".to_string(); - }, + } crate::states::WorldLoadingStatus::LoadingEntity { index, name, total } => { entry.progress.current = index; entry.progress.total = total; entry.progress.message = format!("Loading entity: {}", name); - }, + } crate::states::WorldLoadingStatus::Completed => { entry.progress.current = entry.progress.total; entry.progress.message = "Completed".to_string(); @@ -136,92 +139,123 @@ pub mod shared { } #[dropbear_macro::export( - kotlin(class = "com.dropbear.scene.SceneManagerNative", func = "loadSceneAsync"), + 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, + #[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)?) + Ok(shared::load_scene_async( + command_buffer, + scene_loader, + scene_name, + None, + )?) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.scene.SceneManagerNative", func = "loadSceneAsyncWithLoading"), + 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, + #[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))?) + 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"), + kotlin( + class = "com.dropbear.scene.SceneManagerNative", + func = "switchToSceneImmediate" + ), c )] fn switch_to_scene_immediate( - #[dropbear_macro::define(CommandBufferPtr)] - command_buffer: &CommandBufferUnwrapped, + #[dropbear_macro::define(CommandBufferPtr)] command_buffer: &CommandBufferUnwrapped, scene_name: String, ) -> DropbearNativeResult<()> { - Ok(shared::switch_to_scene_immediate(command_buffer, scene_name)?) + Ok(shared::switch_to_scene_immediate( + command_buffer, + scene_name, + )?) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.scene.SceneLoadHandleNative", func = "getSceneLoadHandleSceneName"), + kotlin( + class = "com.dropbear.scene.SceneLoadHandleNative", + func = "getSceneLoadHandleSceneName" + ), c )] fn get_scene_load_handle_scene_name( - #[dropbear_macro::define(SceneLoaderPtr)] - scene_loader: &SceneLoaderUnwrapped, + #[dropbear_macro::define(SceneLoaderPtr)] scene_loader: &SceneLoaderUnwrapped, scene_id: u64, ) -> DropbearNativeResult<String> { - Ok(shared::get_scene_load_handle_scene_name(scene_loader, scene_id)?) + Ok(shared::get_scene_load_handle_scene_name( + scene_loader, + scene_id, + )?) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.scene.SceneLoadHandleNative", func = "switchToSceneAsync"), + 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, + #[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)?) + Ok(shared::switch_to_scene_async( + command_buffer, + scene_loader, + scene_id, + )?) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.scene.SceneLoadHandleNative", func = "getSceneLoadProgress"), + kotlin( + class = "com.dropbear.scene.SceneLoadHandleNative", + func = "getSceneLoadProgress" + ), c )] fn get_scene_load_progress( - #[dropbear_macro::define(SceneLoaderPtr)] - scene_loader: &SceneLoaderUnwrapped, + #[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"), + kotlin( + class = "com.dropbear.scene.SceneLoadHandleNative", + func = "getSceneLoadStatus" + ), c )] fn get_scene_load_status( - #[dropbear_macro::define(SceneLoaderPtr)] - scene_loader: &SceneLoaderUnwrapped, + #[dropbear_macro::define(SceneLoaderPtr)] scene_loader: &SceneLoaderUnwrapped, scene_id: u64, ) -> DropbearNativeResult<u32> { Ok(shared::get_scene_load_status(scene_loader, scene_id)?) -} +} @@ -1,32 +1,35 @@ -//! The scripting module, primarily for JVM based languages and Kotlin/Native generated libraries. -//! -//! Other native languages are available (not tested) such as Python or C++, -//! it is that JVM and Kotlin/Native languages are prioritised in the dropbear project. +//! The scripting module, primarily for JVM based languages and Kotlin/Native generated libraries. +//! +//! Other native languages are available (not tested) such as Python or C++, +//! it is that JVM and Kotlin/Native languages are prioritised in the dropbear project. pub mod error; pub mod jni; pub mod native; -pub mod utils; pub mod result; +pub mod utils; pub static JVM_ARGS: OnceLock<String> = OnceLock::new(); pub static AWAIT_JDB: OnceLock<bool> = OnceLock::new(); -use std::sync::OnceLock; -use crate::ptr::{AssetRegistryPtr, CommandBufferPtr, GraphicsContextPtr, InputStatePtr, PhysicsStatePtr, SceneLoaderPtr, UiBufferPtr, WorldPtr}; +use crate::ptr::{ + AssetRegistryPtr, CommandBufferPtr, GraphicsContextPtr, InputStatePtr, PhysicsStatePtr, + SceneLoaderPtr, UiBufferPtr, WorldPtr, +}; +use crate::scene::loading::SCENE_LOADER; use crate::scripting::jni::JavaContext; use crate::scripting::native::NativeLibrary; -use crate::states::{Script}; +use crate::states::Script; +use crate::types::{CollisionEvent, ContactForceEvent}; use anyhow::Context; use crossbeam_channel::Sender; use dropbear_engine::asset::ASSET_REGISTRY; use hecs::{Entity, World}; +use magna_carta::Target; use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; +use std::sync::OnceLock; use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::process::Command; -use magna_carta::Target; -use crate::scene::loading::SCENE_LOADER; -use crate::types::{CollisionEvent, ContactForceEvent}; /// The target of the script. This can be either a JVM or a native library. #[derive(Default, Clone, Debug)] @@ -146,17 +149,14 @@ impl ScriptManager { ScriptTarget::None => None, }; - let target_kind_changed = std::mem::discriminant(&previous_target) != std::mem::discriminant(&target); + let target_kind_changed = + std::mem::discriminant(&previous_target) != std::mem::discriminant(&target); let path_changed = previous_path != new_path; if target_kind_changed || path_changed { self.destroy_all().ok(); } else if self.scripts_loaded { - let removed: Vec<String> = self - .active_tags - .difference(&next_active) - .cloned() - .collect(); + let removed: Vec<String> = self.active_tags.difference(&next_active).cloned().collect(); for tag in removed { let _ = self.destroy_in_scope_tagged(&tag); @@ -237,14 +237,30 @@ impl ScriptManager { ui_buffer, }; - if world.is_null() { log::error!("World pointer is null"); } - if input.is_null() { log::error!("InputState pointer is null"); } - if graphics.is_null() { log::error!("CommandBuffer pointer is null"); } - if graphics_context.is_null() { log::error!("SharedGraphicsContext pointer is null"); } - if assets.is_null() { log::error!("AssetRegistry pointer is null"); } - if scene_loader.is_null() { log::error!("SceneLoader pointer is null"); } - if physics_state.is_null() { log::error!("PhysicsState pointer is null"); } - if ui_buffer.is_null() { log::error!("UiBuffer pointer is null"); } + if world.is_null() { + log::error!("World pointer is null"); + } + if input.is_null() { + log::error!("InputState pointer is null"); + } + if graphics.is_null() { + log::error!("CommandBuffer pointer is null"); + } + if graphics_context.is_null() { + log::error!("SharedGraphicsContext pointer is null"); + } + if assets.is_null() { + log::error!("AssetRegistry pointer is null"); + } + if scene_loader.is_null() { + log::error!("SceneLoader pointer is null"); + } + if physics_state.is_null() { + log::error!("PhysicsState pointer is null"); + } + if ui_buffer.is_null() { + log::error!("UiBuffer pointer is null"); + } match &self.script_target { ScriptTarget::JVM { .. } => { @@ -301,7 +317,11 @@ impl ScriptManager { Err(anyhow::anyhow!("Invalid script target configuration")) } - pub fn collision_event_script(&mut self, world: &World, event: &CollisionEvent) -> anyhow::Result<()> { + pub fn collision_event_script( + &mut self, + world: &World, + event: &CollisionEvent, + ) -> anyhow::Result<()> { self.rebuild_entity_tag_database(world)?; let a = event.collider1_entity_id(); @@ -350,7 +370,11 @@ impl ScriptManager { Ok(()) } - pub fn contact_force_event_script(&mut self, world: &World, event: &ContactForceEvent) -> anyhow::Result<()> { + pub fn contact_force_event_script( + &mut self, + world: &World, + event: &ContactForceEvent, + ) -> anyhow::Result<()> { self.rebuild_entity_tag_database(world)?; let a = event.collider1_entity_id(); @@ -410,11 +434,7 @@ impl ScriptManager { /// - [`ScriptTarget::Native`] - This runs [`NativeLibrary::update_all`] if the database is /// empty or [`NativeLibrary::update_systems_for_entities`] if there are tags. /// - [`ScriptTarget::None`] - This returns an error. - pub fn update_script( - &mut self, - world: &World, - dt: f64, - ) -> anyhow::Result<()> { + pub fn update_script(&mut self, world: &World, dt: f64) -> anyhow::Result<()> { self.rebuild_entity_tag_database(world)?; match self.script_target { @@ -459,7 +479,11 @@ impl ScriptManager { if entity_ids.is_empty() { library.update_tagged(tag, dt)?; } else { - library.update_systems_for_entities(tag, entity_ids.as_slice(), dt)?; + library.update_systems_for_entities( + tag, + entity_ids.as_slice(), + dt, + )?; } } } @@ -476,11 +500,7 @@ impl ScriptManager { /// /// A physics update is determined by [dropbear_engine::PHYSICS_STEP_RATE], which is set to a /// constant `50`. - pub fn physics_update_script( - &mut self, - world: &World, - dt: f64, - ) -> anyhow::Result<()> { + pub fn physics_update_script(&mut self, world: &World, dt: f64) -> anyhow::Result<()> { self.rebuild_entity_tag_database(world)?; match self.script_target { @@ -525,7 +545,11 @@ impl ScriptManager { if entity_ids.is_empty() { library.physics_update_tagged(tag, dt)?; } else { - library.physics_update_systems_for_entities(tag, entity_ids.as_slice(), dt)?; + library.physics_update_systems_for_entities( + tag, + entity_ids.as_slice(), + dt, + )?; } } } @@ -637,15 +661,8 @@ impl ScriptManager { if self.scripts_loaded { let next_active: HashSet<String> = new_map.keys().cloned().collect(); - let removed: Vec<String> = self - .active_tags - .difference(&next_active) - .cloned() - .collect(); - let added: Vec<String> = next_active - .difference(&self.active_tags) - .cloned() - .collect(); + let removed: Vec<String> = self.active_tags.difference(&next_active).cloned().collect(); + let added: Vec<String> = next_active.difference(&self.active_tags).cloned().collect(); for tag in removed { self.destroy_in_scope_tagged(&tag)?; @@ -710,12 +727,26 @@ pub async fn build_jvm( let _ = status_sender.send(BuildStatus::Started); - let _ = status_sender.send(BuildStatus::Building(format!("Building manifest at {}", project_root.join("build/magna-carta/jvmMain/RunnableRegistry.kt").display()))); - if let Err(e) = magna_carta::parse(project_root.join("src"), Target::Jvm, project_root.join("build/magna-carta/jvmMain")) { - let _ = status_sender.send(BuildStatus::Failed(format!("Failed to build manifest: {}", e))); + let _ = status_sender.send(BuildStatus::Building(format!( + "Building manifest at {}", + project_root + .join("build/magna-carta/jvmMain/RunnableRegistry.kt") + .display() + ))); + if let Err(e) = magna_carta::parse( + project_root.join("src"), + Target::Jvm, + project_root.join("build/magna-carta/jvmMain"), + ) { + let _ = status_sender.send(BuildStatus::Failed(format!( + "Failed to build manifest: {}", + e + ))); return Err(e); } - let _ = status_sender.send(BuildStatus::Building(String::from("Successfully built manifest!"))); + let _ = status_sender.send(BuildStatus::Building(String::from( + "Successfully built manifest!", + ))); if !(project_root.join("build.gradle").exists() || project_root.join("build.gradle.kts").exists()) @@ -727,7 +758,10 @@ pub async fn build_jvm( let gradle_cmd = get_gradle_command(project_root); - let _ = status_sender.send(BuildStatus::Building(format!("Running: {} shadowJar", gradle_cmd))); + let _ = status_sender.send(BuildStatus::Building(format!( + "Running: {} shadowJar", + gradle_cmd + ))); let mut child = Command::new(&gradle_cmd) .current_dir(project_root) @@ -873,12 +907,26 @@ pub async fn build_native( } } - let _ = status_sender.send(BuildStatus::Building(format!("Building manifest at {}", project_root.join("build/magna-carta/jvmMain/RunnableRegistry.kt").display()))); - if let Err(e) = magna_carta::parse(project_root.join("src"), Target::Jvm, project_root.join("build/magna-carta/jvmMain")) { - let _ = status_sender.send(BuildStatus::Failed(format!("Failed to build manifest: {}", e))); + let _ = status_sender.send(BuildStatus::Building(format!( + "Building manifest at {}", + project_root + .join("build/magna-carta/jvmMain/RunnableRegistry.kt") + .display() + ))); + if let Err(e) = magna_carta::parse( + project_root.join("src"), + Target::Jvm, + project_root.join("build/magna-carta/jvmMain"), + ) { + let _ = status_sender.send(BuildStatus::Failed(format!( + "Failed to build manifest: {}", + e + ))); return Err(e); } - let _ = status_sender.send(BuildStatus::Building(String::from("Successfully built manifest!"))); + let _ = status_sender.send(BuildStatus::Building(String::from( + "Successfully built manifest!", + ))); let gradle_cmd = get_gradle_command(project_root); let _ = status_sender.send(BuildStatus::Building(format!( @@ -972,4 +1020,4 @@ pub struct DropbearContext { pub scene_loader: SceneLoaderPtr, pub physics_state: PhysicsStatePtr, pub ui_buffer: UiBufferPtr, -} +} @@ -1,24 +1,24 @@ #![allow(non_snake_case)] //! Deals with the Java Native Interface (JNI) with the help of the [`jni`] crate -pub mod utils; pub mod primitives; +pub mod utils; use crate::APP_INFO; use crate::logging::LOG_LEVEL; use crate::ptr::WorldPtr; +use crate::scripting::DropbearContext; +use crate::scripting::JVM_ARGS; use crate::scripting::error::LastErrorMessage; +use crate::scripting::jni::utils::ToJObject; +use crate::types::{CollisionEvent, ContactForceEvent}; use jni::objects::{GlobalRef, JClass, JLongArray, JObject, JObjectArray, JString, JValue}; use jni::sys::jlong; use jni::{InitArgsBuilder, JNIEnv, JNIVersion, JavaVM}; +use once_cell::sync::OnceCell; use sha2::{Digest, Sha256}; use std::fs; use std::path::PathBuf; -use once_cell::sync::OnceCell; -use crate::scripting::JVM_ARGS; -use crate::scripting::DropbearContext; -use crate::scripting::jni::utils::ToJObject; -use crate::types::{CollisionEvent, ContactForceEvent}; #[cfg(feature = "jvm_debug")] use crate::scripting::AWAIT_JDB; @@ -140,9 +140,8 @@ impl JavaContext { log::debug!("JVM classpath path: {}", classpath); - let mut jvm_args = InitArgsBuilder::new() - .version(JNIVersion::V8); - + let mut jvm_args = InitArgsBuilder::new().version(JNIVersion::V8); + let mut args_log = Vec::new(); if let Some(args) = external_vm_args { @@ -155,7 +154,10 @@ impl JavaContext { #[cfg(feature = "jvm_debug")] { - let play_mode = RUNTIME_MODE.get().and_then(|b| Some(b.clone())).unwrap_or_default(); + let play_mode = RUNTIME_MODE + .get() + .and_then(|b| Some(b.clone())) + .unwrap_or_default(); match play_mode { RuntimeMode::None => { log::warn!("No runtime mode set, therefore no JWDB available"); @@ -173,14 +175,23 @@ impl JavaContext { port = p; debug_arg = if let Some(wait) = AWAIT_JDB.get() { if *wait { - format!("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:{}", port) + format!( + "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:{}", + port + ) } else { - format!("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:{}", port) + format!( + "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:{}", + port + ) } } else { - format!("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:{}", port) + format!( + "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:{}", + port + ) }; - + break; } else { log::debug!("Port {} is not available", p); @@ -188,7 +199,9 @@ impl JavaContext { } if debug_arg.is_empty() { - log::warn!("Could not find an available port for JDWP debugger (tried 6751-6770). Debugging will be disabled."); + log::warn!( + "Could not find an available port for JDWP debugger (tried 6751-6770). Debugging will be disabled." + ); } else { args_log.push(debug_arg.clone()); jvm_args = jvm_args.option(debug_arg); @@ -198,21 +211,21 @@ impl JavaContext { } } - #[cfg(feature = "jvm")] - { - #[allow(unused)] - let pathbuf = std::env::current_exe()?; - #[allow(unused)] - let path = pathbuf + #[cfg(feature = "jvm")] + { + #[allow(unused)] + let pathbuf = std::env::current_exe()?; + #[allow(unused)] + let path = pathbuf .parent() .ok_or_else(|| anyhow::anyhow!("Unable to locate parent"))?; log::debug!("Libs folder at {}", path.display()); if !path.exists() { log::warn!( - "Libs folder ({}) does not exist; native libraries may fail to load", - path.display() - ); + "Libs folder ({}) does not exist; native libraries may fail to load", + path.display() + ); } let path_str = path.to_string_lossy(); @@ -273,10 +286,7 @@ impl JavaContext { }) } - pub fn init( - &mut self, - context: &DropbearContext, - ) -> anyhow::Result<()> { + pub fn init(&mut self, context: &DropbearContext) -> anyhow::Result<()> { let mut env = self.jvm.attach_current_thread()?; let result = (|| -> anyhow::Result<()> { @@ -307,17 +317,15 @@ impl JavaContext { sig.push(')'); sig.push('V'); - let dropbear_context_class: JClass = env.find_class("com/dropbear/ffi/DropbearContext")?; - let dropbear_context_obj = env.new_object( - dropbear_context_class, - sig, - &args - )?; + let dropbear_context_class: JClass = + env.find_class("com/dropbear/ffi/DropbearContext")?; + let dropbear_context_obj = env.new_object(dropbear_context_class, sig, &args)?; log::trace!("Locating \"com/dropbear/ffi/NativeEngine\" class"); let native_engine_class: JClass = env.find_class("com/dropbear/ffi/NativeEngine")?; - let native_engine_obj = if let Some(ref native_engine_ref) = self.native_engine_instance { + let native_engine_obj = if let Some(ref native_engine_ref) = self.native_engine_instance + { native_engine_ref.as_obj() } else { log::trace!("Creating new instance of NativeEngine"); @@ -330,7 +338,9 @@ impl JavaContext { .as_obj() }; - log::trace!("Calling NativeEngine.init() with arg [\"com/dropbear/ffi/DropbearContext\"]"); + log::trace!( + "Calling NativeEngine.init() with arg [\"com/dropbear/ffi/DropbearContext\"]" + ); env.call_method( native_engine_obj, "init", @@ -367,16 +377,22 @@ impl JavaContext { Some(RuntimeMode::Editor) | Some(RuntimeMode::PlayMode) => { let port = 56624; if std::net::TcpStream::connect(format!("127.0.0.1:{}", port)).is_ok() { - let socket_writer_class = env.find_class("com/dropbear/logging/SocketWriter")?; + let socket_writer_class = + env.find_class("com/dropbear/logging/SocketWriter")?; env.new_object(socket_writer_class, "()V", &[])? } else { - log::debug!("Editor console not reachable at 127.0.0.1:{}. Falling back to StdoutWriter.", port); - let std_out_writer_class = env.find_class("com/dropbear/logging/StdoutWriter")?; + log::debug!( + "Editor console not reachable at 127.0.0.1:{}. Falling back to StdoutWriter.", + port + ); + let std_out_writer_class = + env.find_class("com/dropbear/logging/StdoutWriter")?; env.new_object(std_out_writer_class, "()V", &[])? } } _ => { - let std_out_writer_class = env.find_class("com/dropbear/logging/StdoutWriter")?; + let std_out_writer_class = + env.find_class("com/dropbear/logging/StdoutWriter")?; env.new_object(std_out_writer_class, "()V", &[])? } }; @@ -389,10 +405,11 @@ impl JavaContext { .as_obj(); log::trace!("Locating \"com/dropbear/host/SystemManager\" class"); - let system_manager_class: JClass = env.find_class("com/dropbear/host/SystemManager")?; + let system_manager_class: JClass = + env.find_class("com/dropbear/host/SystemManager")?; log::trace!( - "Creating SystemManager constructor with args (jar_path_string, dropbear_engine_object, log_writer_object, log_level_enum, log_target_string)" - ); + "Creating SystemManager constructor with args (jar_path_string, dropbear_engine_object, log_writer_object, log_level_enum, log_target_string)" + ); let log_target_jstring = env.new_string("dropbear_rust_host")?; @@ -429,12 +446,7 @@ impl JavaContext { env.exception_clear()?; - let message_result = env.call_method( - &ex, - "toString", - "()Ljava/lang/String;", - &[] - )?; + let message_result = env.call_method(&ex, "toString", "()Ljava/lang/String;", &[])?; let message_obj = message_result.l()?; let message_jstring = JString::from(message_obj); let message_str: String = env.get_string(&message_jstring)?.into(); @@ -443,7 +455,7 @@ impl JavaContext { &ex, "getStackTrace", "()[Ljava/lang/StackTraceElement;", - &[] + &[], )?; let stack_trace_obj = stack_trace_result.l()?; let stack_trace_array = JObjectArray::from(stack_trace_obj); @@ -454,12 +466,8 @@ impl JavaContext { for i in 0..stack_len { let element = env.get_object_array_element(&stack_trace_array, i)?; - let element_str_result = env.call_method( - &element, - "toString", - "()Ljava/lang/String;", - &[] - )?; + let element_str_result = + env.call_method(&element, "toString", "()Ljava/lang/String;", &[])?; let element_str_obj = element_str_result.l()?; let element_jstring = JString::from(element_str_obj); let element_string: String = env.get_string(&element_jstring)?.into(); @@ -467,21 +475,12 @@ impl JavaContext { error_msg.push_str(&format!(" at {}\n", element_string)); } - let cause_result = env.call_method( - &ex, - "getCause", - "()Ljava/lang/Throwable;", - &[] - )?; + let cause_result = env.call_method(&ex, "getCause", "()Ljava/lang/Throwable;", &[])?; let cause_obj = cause_result.l()?; if !cause_obj.is_null() { - let cause_str_result = env.call_method( - &cause_obj, - "toString", - "()Ljava/lang/String;", - &[] - )?; + let cause_str_result = + env.call_method(&cause_obj, "toString", "()Ljava/lang/String;", &[])?; let cause_str_obj = cause_str_result.l()?; let cause_jstring = JString::from(cause_str_obj); let cause_string: String = env.get_string(&cause_jstring)?.into(); @@ -531,9 +530,9 @@ impl JavaContext { let result = (|| -> anyhow::Result<()> { log::trace!( - "Calling SystemManager.loadSystemsForTag() with tag: {}", - tag - ); + "Calling SystemManager.loadSystemsForTag() with tag: {}", + tag + ); let tag_jstring = env.new_string(tag)?; env.call_method( manager_ref, @@ -557,7 +556,11 @@ impl JavaContext { } } - pub fn load_systems_for_entities(&mut self, tag: &str, entity_ids: &[u64]) -> anyhow::Result<()> { + pub fn load_systems_for_entities( + &mut self, + tag: &str, + entity_ids: &[u64], + ) -> anyhow::Result<()> { if let Some(ref manager_ref) = self.system_manager_instance { let mut env = self.jvm.attach_current_thread()?; @@ -604,7 +607,12 @@ impl JavaContext { } } - pub fn collision_event(&self, tag: &str, entity_id: u64, event: &CollisionEvent) -> anyhow::Result<()> { + pub fn collision_event( + &self, + tag: &str, + entity_id: u64, + event: &CollisionEvent, + ) -> anyhow::Result<()> { if let Some(ref manager_ref) = self.system_manager_instance { let mut env = self.jvm.attach_current_thread()?; @@ -637,15 +645,20 @@ impl JavaContext { } } - pub fn contact_force_event(&self, tag: &str, entity_id: u64, event: &ContactForceEvent) -> anyhow::Result<()> { + pub fn contact_force_event( + &self, + tag: &str, + entity_id: u64, + event: &ContactForceEvent, + ) -> anyhow::Result<()> { if let Some(ref manager_ref) = self.system_manager_instance { let mut env = self.jvm.attach_current_thread()?; let result = (|| -> anyhow::Result<()> { let tag_jstring = env.new_string(tag)?; - let event_obj = event - .to_jobject(&mut env) - .map_err(|e| anyhow::anyhow!("Failed to marshal ContactForceEvent to JVM: {e}"))?; + let event_obj = event.to_jobject(&mut env).map_err(|e| { + anyhow::anyhow!("Failed to marshal ContactForceEvent to JVM: {e}") + })?; env.call_method( manager_ref, @@ -703,7 +716,10 @@ impl JavaContext { let mut env = self.jvm.attach_current_thread()?; let result = (|| -> anyhow::Result<()> { - log_once::trace_once!("Calling SystemManager.physicsUpdateAllSystems() with dt: {}", dt); + log_once::trace_once!( + "Calling SystemManager.physicsUpdateAllSystems() with dt: {}", + dt + ); env.call_method( manager_ref, "physicsUpdateAllSystems", @@ -730,10 +746,10 @@ impl JavaContext { let result = (|| -> anyhow::Result<()> { log::trace!( - "Calling SystemManager.updateSystemsByTag() with tag: {}, dt: {}", - tag, - dt - ); + "Calling SystemManager.updateSystemsByTag() with tag: {}, dt: {}", + tag, + dt + ); let tag_jstring = env.new_string(tag)?; env.call_method( manager_ref, @@ -911,7 +927,10 @@ impl JavaContext { let mut env = self.jvm.attach_current_thread()?; let result = (|| -> anyhow::Result<()> { - log::trace!("Calling SystemManager.unloadSystemsByTag() with tag: {}", tag); + log::trace!( + "Calling SystemManager.unloadSystemsByTag() with tag: {}", + tag + ); let tag_jstring = env.new_string(tag)?; env.call_method( manager_ref, @@ -1,20 +1,21 @@ -use jni::JNIEnv; -use jni::objects::{JObject, JValue}; -use jni::sys::{jdouble, jint, jlong}; use crate::scripting::jni::utils::{FromJObject, ToJObject}; use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; +use jni::JNIEnv; +use jni::objects::{JObject, JValue}; +use jni::sys::{jdouble, jint, jlong}; impl ToJObject for Option<i32> { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { match self { Some(value) => { - let class = env.find_class("java/lang/Integer") + let class = env + .find_class("java/lang/Integer") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; env.new_object(&class, "(I)V", &[JValue::Int(*value)]) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) - }, + } None => Ok(JObject::null()), } } @@ -80,7 +81,8 @@ impl ToJObject for Option<f32> { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { match self { Some(value) => { - let class = env.find_class("java/lang/Float") + let class = env + .find_class("java/lang/Float") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; env.new_object(&class, "(F)V", &[JValue::Float(*value)]) @@ -95,7 +97,8 @@ impl ToJObject for Option<f64> { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { match self { Some(value) => { - let class = env.find_class("java/lang/Double") + let class = env + .find_class("java/lang/Double") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; env.new_object(&class, "(D)V", &[JValue::Double(*value)]) @@ -136,15 +139,21 @@ impl ToJObject for &[Vec<f64>] { } fn new_array_list<'a>(env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("java/util/ArrayList") + let class = env + .find_class("java/util/ArrayList") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; env.new_object(&class, "()V", &[]) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) } fn array_list_add(env: &mut JNIEnv, list: &JObject, item: &JObject) -> DropbearNativeResult<()> { - env.call_method(list, "add", "(Ljava/lang/Object;)Z", &[JValue::Object(item)]) - .map_err(|_| DropbearNativeError::JNIMethodNotFound)?; + env.call_method( + list, + "add", + "(Ljava/lang/Object;)Z", + &[JValue::Object(item)], + ) + .map_err(|_| DropbearNativeError::JNIMethodNotFound)?; Ok(()) } @@ -156,8 +165,7 @@ impl ToJObject for Vec<u64> { impl ToJObject for &[u64] { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let array = env - .new_long_array(self.len() as i32)?; + let array = env.new_long_array(self.len() as i32)?; let buf: Vec<jlong> = self.iter().map(|v| *v as jlong).collect(); env.set_long_array_region(&array, 0, &buf)?; Ok(JObject::from(array)) @@ -169,4 +177,4 @@ impl ToJObject for String { let result = JObject::from(env.new_string(self)?); Ok(result) } -} +} @@ -1,9 +1,9 @@ //! Utilities for JNI and JVM based code. use crate::scripting::result::DropbearNativeResult; +use jni::JNIEnv; use jni::objects::{JObject, JValue}; use jni::sys::jint; -use jni::JNIEnv; const JAVA_MOUSE_BUTTON_LEFT: jint = 0; const JAVA_MOUSE_BUTTON_RIGHT: jint = 1; @@ -46,7 +46,12 @@ where for item in self { let obj = item.to_jobject(env)?; - let _ = env.call_method(&list_obj, "add", "(Ljava/lang/Object;)Z", &[JValue::Object(&obj)])?; + let _ = env.call_method( + &list_obj, + "add", + "(Ljava/lang/Object;)Z", + &[JValue::Object(&obj)], + )?; } Ok(list_obj) @@ -65,11 +70,13 @@ where let mut out = Vec::with_capacity(size as usize); for i in 0..size { - let item = env.call_method(obj, "get", "(I)Ljava/lang/Object;", &[JValue::Int(i)])?.l()?; + let item = env + .call_method(obj, "get", "(I)Ljava/lang/Object;", &[JValue::Int(i)])? + .l()?; let value = T::from_jobject(env, &item)?; out.push(value); } Ok(out) } -} +} @@ -6,18 +6,24 @@ pub mod sig; pub mod utils; use crate::scripting::error::LastErrorMessage; -use crate::scripting::native::sig::{CollisionEvent, ContactForceEvent, DestroyAll, DestroyInScopeTagged, DestroyTagged, Init, LoadTagged, LoadWithEntities, PhysicsUpdateAll, PhysicsUpdateTagged, PhysicsUpdateWithEntities, UpdateAll, UpdateTagged, UpdateWithEntities}; +use crate::scripting::native::sig::{ + CollisionEvent, ContactForceEvent, DestroyAll, DestroyInScopeTagged, DestroyTagged, Init, + LoadTagged, LoadWithEntities, PhysicsUpdateAll, PhysicsUpdateTagged, PhysicsUpdateWithEntities, + UpdateAll, UpdateTagged, UpdateWithEntities, +}; use anyhow::anyhow; use libloading::{Library, Symbol}; use std::ffi::CString; // use std::fmt::{Display, Formatter}; // Display derived by thiserror -use std::path::Path; -use hecs::ComponentError; use crate::scripting::DropbearContext; -use crate::types::{CollisionEvent as CollisionEventFFI, ContactForceEvent as ContactForceEventFFI}; -use thiserror::Error; -use jni::signature::TypeSignature; +use crate::types::{ + CollisionEvent as CollisionEventFFI, ContactForceEvent as ContactForceEventFFI, +}; +use hecs::ComponentError; use jni::errors::JniError; +use jni::signature::TypeSignature; +use std::path::Path; +use thiserror::Error; pub struct NativeLibrary { #[allow(dead_code)] @@ -57,8 +63,8 @@ impl NativeLibrary { ); } unsafe { - let library: Library = Library::new(lib_path) - .map_err(|err| enhance_library_error(lib_path, err))?; + let library: Library = + Library::new(lib_path).map_err(|err| enhance_library_error(lib_path, err))?; let init_fn = load_symbol(&library, &[b"dropbear_init\0"], "dropbear_init")?; let load_systems_fn = load_symbol( @@ -168,10 +174,7 @@ impl NativeLibrary { } /// Initialises the NativeLibrary by populating it with context. - pub fn init( - &mut self, - dropbear_context: &DropbearContext - ) -> anyhow::Result<()> { + pub fn init(&mut self, dropbear_context: &DropbearContext) -> anyhow::Result<()> { unsafe { let result = (self.init_fn)(dropbear_context as *const DropbearContext); self.handle_result(result, "init") @@ -186,7 +189,11 @@ impl NativeLibrary { } } - pub fn load_systems_for_entities(&mut self, tag: &str, entity_ids: &[u64]) -> anyhow::Result<()> { + pub fn load_systems_for_entities( + &mut self, + tag: &str, + entity_ids: &[u64], + ) -> anyhow::Result<()> { unsafe { let c_string = CString::new(tag)?; let result = (self.load_systems_with_entities_fn)( @@ -198,7 +205,12 @@ impl NativeLibrary { } } - pub fn collision_event(&self, tag: &str, current_entity_id: u64, event: &CollisionEventFFI) -> anyhow::Result<()> { + pub fn collision_event( + &self, + tag: &str, + current_entity_id: u64, + event: &CollisionEventFFI, + ) -> anyhow::Result<()> { unsafe { let c_string = CString::new(tag)?; let event_type = match event.event_type { @@ -224,7 +236,12 @@ impl NativeLibrary { } } - pub fn contact_force_event(&self, tag: &str, current_entity_id: u64, event: &ContactForceEventFFI) -> anyhow::Result<()> { + pub fn contact_force_event( + &self, + tag: &str, + current_entity_id: u64, + event: &ContactForceEventFFI, + ) -> anyhow::Result<()> { unsafe { let c_string = CString::new(tag)?; let result = (self.contact_force_event_fn)( @@ -293,7 +310,7 @@ impl NativeLibrary { c_string.as_ptr(), entity_ids.as_ptr(), entity_ids.len() as i32, - dt + dt, ); self.handle_result(result, "update_systems_for_entities") } @@ -635,14 +652,22 @@ impl From<jni::errors::Error> for DropbearNativeError { jni::errors::Error::WrongJValueType(a, b) => DropbearNativeError::WrongJValueType(a, b), jni::errors::Error::InvalidCtorReturn => DropbearNativeError::InvalidCtorReturn, jni::errors::Error::InvalidArgList(s) => DropbearNativeError::InvalidArgList(s), - jni::errors::Error::MethodNotFound { name, sig } => DropbearNativeError::MethodNotFound { name, sig }, - jni::errors::Error::FieldNotFound { name, sig } => DropbearNativeError::FieldNotFound { name, sig }, + jni::errors::Error::MethodNotFound { name, sig } => { + DropbearNativeError::MethodNotFound { name, sig } + } + jni::errors::Error::FieldNotFound { name, sig } => { + DropbearNativeError::FieldNotFound { name, sig } + } jni::errors::Error::JavaException => DropbearNativeError::JavaException, - jni::errors::Error::JNIEnvMethodNotFound(s) => DropbearNativeError::JNIEnvMethodNotFound(s), + jni::errors::Error::JNIEnvMethodNotFound(s) => { + DropbearNativeError::JNIEnvMethodNotFound(s) + } jni::errors::Error::NullPtr(s) => DropbearNativeError::NullPtr(s), jni::errors::Error::NullDeref(s) => DropbearNativeError::NullDeref(s), jni::errors::Error::TryLock => DropbearNativeError::TryLock, - jni::errors::Error::JavaVMMethodNotFound(s) => DropbearNativeError::JavaVMMethodNotFound(s), + jni::errors::Error::JavaVMMethodNotFound(s) => { + DropbearNativeError::JavaVMMethodNotFound(s) + } jni::errors::Error::FieldAlreadySet(s) => DropbearNativeError::FieldAlreadySet(s), jni::errors::Error::ThrowFailed(i) => DropbearNativeError::ThrowFailed(i), jni::errors::Error::ParseFailed(e, s) => DropbearNativeError::ParseFailed(e, s), @@ -655,7 +680,7 @@ impl From<hecs::ComponentError> for DropbearNativeError { fn from(e: hecs::ComponentError) -> Self { match e { ComponentError::NoSuchEntity => DropbearNativeError::NoSuchEntity, - ComponentError::MissingComponent(_) => DropbearNativeError::MissingComponent + ComponentError::MissingComponent(_) => DropbearNativeError::MissingComponent, } } -} +} @@ -1,6 +1,6 @@ +use crate::scripting::DropbearContext; /// Different input signatures for Native implementations use std::ffi::c_char; -use crate::scripting::DropbearContext; /// CName: `dropbear_init` pub type Init = unsafe extern "C" fn(dropbear_context: *const DropbearContext) -> i32; @@ -8,11 +8,8 @@ pub type Init = unsafe extern "C" fn(dropbear_context: *const DropbearContext) - pub type LoadTagged = unsafe extern "C" fn(tag: *const c_char) -> i32; /// CName: `dropbear_load_with_entities` -pub type LoadWithEntities = unsafe extern "C" fn( - tag: *const c_char, - entities: *const u64, - entity_count: i32, -) -> i32; +pub type LoadWithEntities = + unsafe extern "C" fn(tag: *const c_char, entities: *const u64, entity_count: i32) -> i32; /// CName: `dropbear_update_all` pub type UpdateAll = unsafe extern "C" fn(dt: f64) -> i32; /// CName: `dropbear_update_tagged` @@ -22,7 +19,7 @@ pub type UpdateWithEntities = unsafe extern "C" fn( tag: *const c_char, entities: *const u64, entity_count: i32, - dt: f64 + dt: f64, ) -> i32; /// CName: `dropbear_physics_update_all` @@ -34,7 +31,7 @@ pub type PhysicsUpdateWithEntities = unsafe extern "C" fn( tag: *const c_char, entities: *const u64, entity_count: i32, - dt: f64 + dt: f64, ) -> i32; /// CName: `dropbear_collision_event` @@ -85,4 +82,3 @@ pub type DestroyAll = unsafe extern "C" fn() -> i32; pub type GetLastErrorMessage = unsafe extern "C" fn() -> *const c_char; /// CName: `dropbear_set_last_error_message` pub type SetLastErrorMessage = unsafe extern "C" fn(msg: *const c_char); - @@ -24,4 +24,4 @@ pub fn button_from_ordinal(ordinal: i32) -> Result<gilrs::Button, ()> { 19 => Ok(gilrs::Button::DPadRight), _ => Err(()), } -} +} @@ -1,4 +1,4 @@ use crate::scripting::native::DropbearNativeError; /// A result type for dropbear based native functions. -pub type DropbearNativeResult<T> = Result<T, DropbearNativeError>; +pub type DropbearNativeResult<T> = Result<T, DropbearNativeError>; @@ -24,4 +24,4 @@ pub fn button_from_ordinal(ordinal: i32) -> Result<gilrs::Button, ()> { 19 => Ok(gilrs::Button::DPadRight), _ => Err(()), } -} +} @@ -1,34 +1,34 @@ -//! Different states and objects that exist in the scene. -//! -//! It's really just a "throw everything in here, organise later". +//! Different states and objects that exist in the scene. +//! +//! It's really just a "throw everything in here, organise later". use crate::camera::{CameraComponent, CameraType}; +use crate::component::{ + Component, ComponentDescriptor, ComponentInitFuture, InspectableComponent, SerializedComponent, +}; use crate::config::{ProjectConfig, ResourceConfig, SourceConfig}; +use crate::properties::Value; use crate::scene::SceneConfig; use dropbear_engine::camera::Camera; -use dropbear_engine::camera::CameraBuilder; -use dropbear_engine::entity::{MeshRenderer, Transform}; -use dropbear_engine::lighting::LightComponent; +use dropbear_engine::entity::Transform; use dropbear_engine::graphics::SharedGraphicsContext; -use dropbear_engine::asset::ASSET_REGISTRY; -use dropbear_engine::utils::{ResourceReference, ResourceReferenceType, EUCA_SCHEME}; +use dropbear_engine::lighting::LightComponent; +use dropbear_engine::model::AlphaMode; +use dropbear_engine::texture::TextureWrapMode; +use dropbear_engine::utils::ResourceReference; +use egui::{CollapsingHeader, TextEdit, Ui}; +use hecs::{Entity, World}; use once_cell::sync::Lazy; use parking_lot::RwLock; use serde::{Deserialize, Serialize}; -use std::borrow::Borrow; use std::any::Any; +use std::borrow::Borrow; use std::collections::HashMap; use std::fmt; use std::fmt::{Display, Formatter}; use std::ops::{Deref, DerefMut}; use std::path::PathBuf; use std::sync::Arc; -use egui::{CollapsingHeader, TextEdit, Ui}; -use hecs::{Entity, World}; -use dropbear_engine::model::AlphaMode; -use dropbear_engine::texture::TextureWrapMode; -use crate::component::{Component, ComponentDescriptor, ComponentInitFuture, InspectableComponent, SerializedComponent}; -use crate::properties::Value; /// A global "singleton" that contains the configuration of a project. pub static PROJECT: Lazy<RwLock<ProjectConfig>> = @@ -163,7 +163,7 @@ impl SerializedComponent for Script {} impl Component for Script { type SerializedForm = Self; - type RequiredComponentTypes = (Self, ); + type RequiredComponentTypes = (Self,); fn descriptor() -> ComponentDescriptor { ComponentDescriptor { @@ -178,10 +178,18 @@ impl Component for Script { ser: &'a Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, ) -> ComponentInitFuture<'a, Self> { - Box::pin(async move { Ok((ser.clone(), )) }) + Box::pin(async move { Ok((ser.clone(),)) }) } - fn update_component(&mut self, _world: &World, _physics: &mut crate::physics::PhysicsState, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} + fn update_component( + &mut self, + _world: &World, + _physics: &mut crate::physics::PhysicsState, + _entity: Entity, + _dt: f32, + _graphics: Arc<SharedGraphicsContext>, + ) { + } fn save(&self, _world: &World, _entity: Entity) -> Box<dyn SerializedComponent> { Box::new(self.clone()) @@ -190,31 +198,33 @@ impl Component for Script { impl InspectableComponent for Script { fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { - CollapsingHeader::new("Scripting").default_open(true).show(ui, |ui| { - CollapsingHeader::new("Tags") - .default_open(true) - .show(ui, |ui| { - let mut local_del: Option<usize> = None; - for (i, tag) in self.tags.iter_mut().enumerate() { - let current_width = ui.available_width(); - ui.horizontal(|ui| { - ui.add_sized( - [current_width * 70.0 / 100.0, 20.0], - TextEdit::singleline(tag), - ); - if ui.button("🗑️").clicked() { - local_del = Some(i); - } - }); - } - if let Some(i) = local_del { - self.tags.remove(i); - } - if ui.button("➕ Add").clicked() { - self.tags.push(String::new()) - } - }); - }); + CollapsingHeader::new("Scripting") + .default_open(true) + .show(ui, |ui| { + CollapsingHeader::new("Tags") + .default_open(true) + .show(ui, |ui| { + let mut local_del: Option<usize> = None; + for (i, tag) in self.tags.iter_mut().enumerate() { + let current_width = ui.available_width(); + ui.horizontal(|ui| { + ui.add_sized( + [current_width * 70.0 / 100.0, 20.0], + TextEdit::singleline(tag), + ); + if ui.button("🗑️").clicked() { + local_del = Some(i); + } + }); + } + if let Some(i) = local_del { + self.tags.remove(i); + } + if ui.button("➕ Add").clicked() { + self.tags.push(String::new()) + } + }); + }); } } @@ -382,9 +392,12 @@ impl Label { pub fn is_empty(&self) -> bool { self.0.is_empty() } - + pub fn locate_entity(&self, world: &World) -> Option<hecs::Entity> { - world.query::<(Entity, &Label)>().iter().find_map(|(e, l)| if l == self { Some(e.clone()) } else { None }) + world + .query::<(Entity, &Label)>() + .iter() + .find_map(|(e, l)| if l == self { Some(e.clone()) } else { None }) } } @@ -461,4 +474,4 @@ pub struct SerializedMaterialCustomisation { pub normal_texture: Option<ResourceReference>, pub occlusion_texture: Option<ResourceReference>, pub metallic_roughness_texture: Option<ResourceReference>, -} +} @@ -1,25 +1,25 @@ -use std::sync::Arc; +use crate::component::{Component, ComponentDescriptor, InspectableComponent, SerializedComponent}; use crate::hierarchy::EntityTransformExt; use crate::ptr::WorldPtr; use crate::scripting::jni::utils::{FromJObject, ToJObject}; use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; -use dropbear_engine::entity::{EntityTransform, Transform}; -use glam::{DQuat, DVec3}; -use ::jni::objects::{JObject, JValue}; +use crate::types::{NTransform, NVector3}; use ::jni::JNIEnv; +use ::jni::objects::{JObject, JValue}; +use dropbear_engine::entity::{EntityTransform, Transform}; +use dropbear_engine::graphics::SharedGraphicsContext; use egui::{CollapsingHeader, Ui}; +use glam::{DQuat, DVec3}; use hecs::{Entity, World}; -use dropbear_engine::graphics::SharedGraphicsContext; -use crate::component::{Component, ComponentDescriptor, InspectableComponent, SerializedComponent}; -use crate::types::{NTransform, NVector3}; +use std::sync::Arc; #[typetag::serde] impl SerializedComponent for EntityTransform {} impl Component for EntityTransform { type SerializedForm = Self; - type RequiredComponentTypes = (Self, ); + type RequiredComponentTypes = (Self,); fn descriptor() -> ComponentDescriptor { ComponentDescriptor { @@ -34,10 +34,18 @@ impl Component for EntityTransform { ser: &'a Self::SerializedForm, _: Arc<SharedGraphicsContext>, ) -> crate::component::ComponentInitFuture<'a, Self> { - Box::pin(async move { Ok((ser.clone(), )) }) + Box::pin(async move { Ok((ser.clone(),)) }) } - fn update_component(&mut self, _: &World, _physics: &mut crate::physics::PhysicsState, _: Entity, _: f32, _: Arc<SharedGraphicsContext>) {} + fn update_component( + &mut self, + _: &World, + _physics: &mut crate::physics::PhysicsState, + _: Entity, + _: f32, + _: Arc<SharedGraphicsContext>, + ) { + } fn save(&self, _: &World, _: Entity) -> Box<dyn SerializedComponent> { Box::new(self.clone()) @@ -60,22 +68,28 @@ impl InspectableComponent for EntityTransform { impl FromJObject for Transform { fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { - let pos_val = env.get_field(obj, "position", "Lcom/dropbear/math/Vector3d;") + let pos_val = env + .get_field(obj, "position", "Lcom/dropbear/math/Vector3d;") .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - let pos_obj = pos_val.l() + let pos_obj = pos_val + .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let rot_val = env.get_field(obj, "rotation", "Lcom/dropbear/math/Quaterniond;") + let rot_val = env + .get_field(obj, "rotation", "Lcom/dropbear/math/Quaterniond;") .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - let rot_obj = rot_val.l() + let rot_obj = rot_val + .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let scale_val = env.get_field(obj, "scale", "Lcom/dropbear/math/Vector3d;") + let scale_val = env + .get_field(obj, "scale", "Lcom/dropbear/math/Vector3d;") .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - let scale_obj = scale_val.l() + let scale_obj = scale_val + .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let position: DVec3 = NVector3::from_jobject(env, &pos_obj)?.into(); @@ -105,28 +119,32 @@ impl FromJObject for Transform { impl ToJObject for Transform { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let cls = env.find_class("com/dropbear/math/Transform") - .map_err(|e| { - eprintln!("Could not find Transform class: {:?}", e); - DropbearNativeError::JNIClassNotFound - })?; + let cls = env.find_class("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), + 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, "(DDDDDDDDDD)V", &args) - .map_err(|e| { - eprintln!("Failed to create Transform object: {:?}", e); - DropbearNativeError::JNIFailedToCreateObject - })?; + let obj = env.new_object(cls, "(DDDDDDDDDD)V", &args).map_err(|e| { + eprintln!("Failed to create Transform object: {:?}", e); + DropbearNativeError::JNIFailedToCreateObject + })?; Ok(obj) } @@ -134,16 +152,20 @@ impl ToJObject for Transform { impl FromJObject for EntityTransform { fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { - let local_val = env.get_field(obj, "local", "Lcom/dropbear/math/Transform;") + let local_val = env + .get_field(obj, "local", "Lcom/dropbear/math/Transform;") .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - let local_obj = local_val.l() + let local_obj = local_val + .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let world_val = env.get_field(obj, "world", "Lcom/dropbear/math/Transform;") + let world_val = env + .get_field(obj, "world", "Lcom/dropbear/math/Transform;") .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - let world_obj = world_val.l() + let world_obj = world_val + .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let local = Transform::from_jobject(env, &local_obj)?; @@ -155,7 +177,8 @@ impl FromJObject for EntityTransform { impl ToJObject for EntityTransform { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let cls = env.find_class("com/dropbear/components/EntityTransform") + let cls = env + .find_class("com/dropbear/components/EntityTransform") .map_err(|e| { eprintln!("Could not find EntityTransform class: {:?}", e); DropbearNativeError::JNIClassNotFound @@ -164,46 +187,47 @@ impl ToJObject for EntityTransform { 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 args = [JValue::Object(&local_obj), JValue::Object(&world_obj)]; - let obj = env.new_object( - cls, - "(Lcom/dropbear/math/Transform;Lcom/dropbear/math/Transform;)V", - &args - ).map_err(|e| { - eprintln!("Failed to create EntityTransform object: {:?}", e); - DropbearNativeError::JNIFailedToCreateObject - })?; + let obj = env + .new_object( + cls, + "(Lcom/dropbear/math/Transform;Lcom/dropbear/math/Transform;)V", + &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"), + 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, + #[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"), + 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, + #[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()) @@ -213,15 +237,16 @@ fn get_local_transform( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.EntityTransformNative", func = "setLocalTransform"), + 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 + #[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(); @@ -233,14 +258,15 @@ fn set_local_transform( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.EntityTransformNative", func = "getWorldTransform"), + 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, + #[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()) @@ -250,15 +276,16 @@ fn get_world_transform( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.EntityTransformNative", func = "setWorldTransform"), + 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 + #[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(); @@ -270,14 +297,15 @@ fn set_world_transform( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.EntityTransformNative", func = "propogateTransform"), + kotlin( + class = "com.dropbear.components.EntityTransformNative", + func = "propogateTransform" + ), c )] fn propogate_transform( - #[dropbear_macro::define(WorldPtr)] - world: &hecs::World, - #[dropbear_macro::entity] - entity: hecs::Entity, + #[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); @@ -285,4 +313,4 @@ fn propogate_transform( } else { Err(DropbearNativeError::MissingComponent) } -} +} @@ -1,17 +1,17 @@ //! 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::JNIEnv; use jni::objects::{JObject, JValue}; use jni::sys::jdouble; use rapier3d::data::Index; -use rapier3d::parry::query::{ShapeCastOptions, ShapeCastStatus}; +use rapier3d::parry::query::ShapeCastStatus; use rapier3d::prelude::ColliderHandle; -use dropbear_engine::entity::Transform; -use crate::physics::PhysicsState; -use crate::scripting::jni::utils::{FromJObject, ToJObject}; -use crate::scripting::native::DropbearNativeError; -use crate::scripting::result::DropbearNativeResult; #[repr(C)] #[derive(Clone, Copy, Debug)] @@ -75,9 +75,7 @@ pub struct NVector3 { impl NVector3 { pub fn new(x: f64, y: f64, z: f64) -> NVector3 { - NVector3 { - x, y, z - } + NVector3 { x, y, z } } pub fn to_array(&self) -> [f64; 3] { @@ -90,19 +88,31 @@ impl NVector3 { impl From<glam::DVec3> for NVector3 { fn from(v: glam::DVec3) -> Self { - Self { x: v.x, y: v.y, z: v.z } + Self { + x: v.x, + y: v.y, + z: v.z, + } } } impl From<&glam::DVec3> for NVector3 { fn from(v: &glam::DVec3) -> Self { - Self { x: v.x, y: v.y, z: v.z } + Self { + x: v.x, + y: v.y, + z: v.z, + } } } impl From<&NVector3> for glam::DVec3 { fn from(v: &NVector3) -> Self { - Self { x: v.x, y: v.y, z: v.z } + Self { + x: v.x, + y: v.y, + z: v.z, + } } } @@ -145,28 +155,33 @@ impl From<NVector3> for glam::DVec3 { impl FromJObject for NVector3 { fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> where - Self: Sized + Self: Sized, { - let class = env.find_class("com/dropbear/math/Vector3d") + let class = env + .find_class("com/dropbear/math/Vector3d") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - if !env.is_instance_of(obj, &class) + if !env + .is_instance_of(obj, &class) .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? { return Err(DropbearNativeError::InvalidArgument); } - let x = env.get_field(obj, "x", "D") + let x = env + .get_field(obj, "x", "D") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let y = env.get_field(obj, "y", "D") + let y = env + .get_field(obj, "y", "D") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let z = env.get_field(obj, "z", "D") + let z = env + .get_field(obj, "z", "D") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -177,7 +192,8 @@ impl FromJObject for NVector3 { impl ToJObject for NVector3 { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/math/Vector3d") + let class = env + .find_class("com/dropbear/math/Vector3d") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let constructor_sig = "(DDD)V"; @@ -188,14 +204,14 @@ impl ToJObject for NVector3 { jni::objects::JValue::Double(self.z), ]; - let obj = env.new_object(&class, constructor_sig, &args) + let obj = env + .new_object(&class, constructor_sig, &args) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) } } - #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct NVector4 { @@ -207,9 +223,7 @@ pub struct NVector4 { impl NVector4 { pub fn new(x: f64, y: f64, z: f64, w: f64) -> NVector4 { - NVector4 { - x, y, z, w - } + NVector4 { x, y, z, w } } pub fn to_array(&self) -> [f64; 3] { @@ -222,7 +236,12 @@ impl NVector4 { impl From<glam::DVec4> for NVector4 { fn from(v: glam::DVec4) -> Self { - Self { x: v.x, y: v.y, z: v.z, w: v.w } + Self { + x: v.x, + y: v.y, + z: v.z, + w: v.w, + } } } @@ -257,33 +276,39 @@ impl From<NVector4> for glam::DVec4 { impl FromJObject for NVector4 { fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> where - Self: Sized + Self: Sized, { - let class = env.find_class("com/dropbear/math/Vector4d") + let class = env + .find_class("com/dropbear/math/Vector4d") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - if !env.is_instance_of(obj, &class) + if !env + .is_instance_of(obj, &class) .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? { return Err(DropbearNativeError::InvalidArgument); } - let x = env.get_field(obj, "x", "D") + let x = env + .get_field(obj, "x", "D") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let y = env.get_field(obj, "y", "D") + let y = env + .get_field(obj, "y", "D") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let z = env.get_field(obj, "z", "D") + let z = env + .get_field(obj, "z", "D") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let w = env.get_field(obj, "w", "D") + let w = env + .get_field(obj, "w", "D") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -294,7 +319,8 @@ impl FromJObject for NVector4 { impl ToJObject for NVector4 { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/math/Vector3d") + let class = env + .find_class("com/dropbear/math/Vector3d") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let constructor_sig = "(DDD)V"; @@ -306,7 +332,8 @@ impl ToJObject for NVector4 { jni::objects::JValue::Double(self.w), ]; - let obj = env.new_object(&class, constructor_sig, &args) + let obj = env + .new_object(&class, constructor_sig, &args) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) @@ -373,7 +400,6 @@ impl From<glam::Quat> for NQuaternion { y: value.y as f64, z: value.z as f64, w: value.w as f64, - } } } @@ -391,7 +417,8 @@ impl From<NVector4> for NQuaternion { impl ToJObject for NQuaternion { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/math/Quaterniond") + let class = env + .find_class("com/dropbear/math/Quaterniond") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let args = [ @@ -409,33 +436,39 @@ impl ToJObject for NQuaternion { impl FromJObject for NQuaternion { fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> where - Self: Sized + Self: Sized, { - let class = env.find_class("com/dropbear/math/Quaterniond") + let class = env + .find_class("com/dropbear/math/Quaterniond") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - if !env.is_instance_of(obj, &class) + if !env + .is_instance_of(obj, &class) .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? { return Err(DropbearNativeError::InvalidArgument); } - let x = env.get_field(obj, "x", "D") + let x = env + .get_field(obj, "x", "D") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let y = env.get_field(obj, "y", "D") + let y = env + .get_field(obj, "y", "D") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let z = env.get_field(obj, "z", "D") + let z = env + .get_field(obj, "z", "D") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let w = env.get_field(obj, "w", "D") + let w = env + .get_field(obj, "w", "D") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -474,22 +507,28 @@ impl From<NTransform> for Transform { impl FromJObject for NTransform { fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { - let pos_val = env.get_field(obj, "position", "Lcom/dropbear/math/Vector3d;") + let pos_val = env + .get_field(obj, "position", "Lcom/dropbear/math/Vector3d;") .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - let pos_obj = pos_val.l() + let pos_obj = pos_val + .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let rot_val = env.get_field(obj, "rotation", "Lcom/dropbear/math/Quaterniond;") + let rot_val = env + .get_field(obj, "rotation", "Lcom/dropbear/math/Quaterniond;") .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - let rot_obj = rot_val.l() + let rot_obj = rot_val + .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let scale_val = env.get_field(obj, "scale", "Lcom/dropbear/math/Vector3d;") + let scale_val = env + .get_field(obj, "scale", "Lcom/dropbear/math/Vector3d;") .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; - let scale_obj = scale_val.l() + let scale_obj = scale_val + .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let position: DVec3 = NVector3::from_jobject(env, &pos_obj)?.into(); @@ -551,39 +590,44 @@ pub struct NCollider { impl ToJObject for NCollider { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let collider_cls = env.find_class("com/dropbear/physics/Collider") + let collider_cls = env + .find_class("com/dropbear/physics/Collider") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let index_cls = env.find_class("com/dropbear/physics/Index") + let index_cls = env + .find_class("com/dropbear/physics/Index") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let entity_cls = env.find_class("com/dropbear/EntityId") + let entity_cls = env + .find_class("com/dropbear/EntityId") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let entity_obj = env.new_object( - &entity_cls, - "(J)V", - &[JValue::Long(self.entity_id as i64)] - ).map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - - let index_obj = env.new_object( - &index_cls, - "(II)V", - &[ - 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, - "(Lcom/dropbear/physics/Index;Lcom/dropbear/EntityId;I)V", - &[ - JValue::Object(&index_obj), - JValue::Object(&entity_obj), - JValue::Int(self.id as i32) - ] - ).map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + let entity_obj = env + .new_object(&entity_cls, "(J)V", &[JValue::Long(self.entity_id as i64)]) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + + let index_obj = env + .new_object( + &index_cls, + "(II)V", + &[ + 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, + "(Lcom/dropbear/physics/Index;Lcom/dropbear/EntityId;I)V", + &[ + JValue::Object(&index_obj), + JValue::Object(&entity_obj), + JValue::Int(self.id as i32), + ], + ) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(collider_obj) } @@ -592,34 +636,40 @@ impl ToJObject for NCollider { impl FromJObject for NCollider { fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> where - Self: Sized + Self: Sized, { - let index_obj = env.get_field(obj, "index", "Lcom/dropbear/physics/Index;") + let index_obj = env + .get_field(obj, "index", "Lcom/dropbear/physics/Index;") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let entity_obj = env.get_field(obj, "entity", "Lcom/dropbear/EntityId;") + let entity_obj = env + .get_field(obj, "entity", "Lcom/dropbear/EntityId;") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let id_val = env.get_field(obj, "id", "I") + let id_val = env + .get_field(obj, "id", "I") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .i() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let entity_raw = env.get_field(&entity_obj, "raw", "J") + let entity_raw = env + .get_field(&entity_obj, "raw", "J") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .j() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let idx_val = env.get_field(&index_obj, "index", "I") + let idx_val = env + .get_field(&index_obj, "index", "I") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .i() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let gen_val = env.get_field(&index_obj, "generation", "I") + let gen_val = env + .get_field(&index_obj, "generation", "I") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .i() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -660,24 +710,25 @@ impl From<IndexNative> for Index { impl ToJObject for IndexNative { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let cls = env.find_class("com/dropbear/physics/Index") + let cls = env.find_class("com/dropbear/physics/Index").map_err(|e| { + eprintln!("[JNI Error] Could not find Index class: {:?}", e); + DropbearNativeError::GenericError + })?; + + let obj = env + .new_object( + cls, + "(II)V", + &[ + JValue::Int(self.index as i32), + JValue::Int(self.generation as i32), + ], + ) .map_err(|e| { - eprintln!("[JNI Error] Could not find Index class: {:?}", e); + eprintln!("[JNI Error] Failed to create Index object: {:?}", e); DropbearNativeError::GenericError })?; - let obj = env.new_object( - cls, - "(II)V", - &[ - 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) } } @@ -694,14 +745,16 @@ impl ToJObject for Option<IndexNative> { impl FromJObject for IndexNative { fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> where - Self: Sized + Self: Sized, { - let idx_val = env.get_field(obj, "index", "I") + let idx_val = env + .get_field(obj, "index", "I") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .i() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let gen_val = env.get_field(obj, "generation", "I") + let gen_val = env + .get_field(obj, "generation", "I") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .i() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -723,29 +776,34 @@ pub struct RigidBodyContext { impl FromJObject for RigidBodyContext { fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> where - Self: Sized + Self: Sized, { - let index_obj = env.get_field(obj, "index", "Lcom/dropbear/physics/Index;") + let index_obj = env + .get_field(obj, "index", "Lcom/dropbear/physics/Index;") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let idx_val = env.get_field(&index_obj, "index", "I") + let idx_val = env + .get_field(&index_obj, "index", "I") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .i() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let gen_val = env.get_field(&index_obj, "generation", "I") + let gen_val = env + .get_field(&index_obj, "generation", "I") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .i() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let entity_obj = env.get_field(obj, "entity", "Lcom/dropbear/EntityId;") + let entity_obj = env + .get_field(obj, "entity", "Lcom/dropbear/EntityId;") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; - let entity_raw = env.get_field(&entity_obj, "raw", "J") + let entity_raw = env + .get_field(&entity_obj, "raw", "J") .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .j() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -762,56 +820,63 @@ impl FromJObject for RigidBodyContext { impl ToJObject for RigidBodyContext { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let index_cls = env.find_class("com/dropbear/physics/Index") + let index_cls = env.find_class("com/dropbear/physics/Index").map_err(|e| { + eprintln!( + "[JNI Error] Class 'com/dropbear/physics/Index' not found: {:?}", + e + ); + DropbearNativeError::JNIClassNotFound + })?; + + let entity_cls = env.find_class("com/dropbear/EntityId").map_err(|e| { + eprintln!( + "[JNI Error] Class 'com/dropbear/EntityId' not found: {:?}", + e + ); + DropbearNativeError::JNIClassNotFound + })?; + + let rb_cls = env + .find_class("com/dropbear/physics/RigidBody") .map_err(|e| { - eprintln!("[JNI Error] Class 'com/dropbear/physics/Index' not found: {:?}", e); + eprintln!( + "[JNI Error] Class 'com/dropbear/physics/RigidBody' not found: {:?}", + e + ); DropbearNativeError::JNIClassNotFound })?; - let entity_cls = env.find_class("com/dropbear/EntityId") + let index_obj = env + .new_object( + &index_cls, + "(II)V", + &[ + JValue::Int(self.index.index as i32), + JValue::Int(self.index.generation as i32), + ], + ) .map_err(|e| { - eprintln!("[JNI Error] Class 'com/dropbear/EntityId' not found: {:?}", e); - DropbearNativeError::JNIClassNotFound + eprintln!("[JNI Error] Failed to create Index object: {:?}", e); + DropbearNativeError::JNIFailedToCreateObject })?; - let rb_cls = env.find_class("com/dropbear/physics/RigidBody") + let entity_obj = env + .new_object(&entity_cls, "(J)V", &[JValue::Long(self.entity_id as i64)]) .map_err(|e| { - eprintln!("[JNI Error] Class 'com/dropbear/physics/RigidBody' not found: {:?}", e); - DropbearNativeError::JNIClassNotFound + eprintln!("[JNI Error] Failed to create EntityId object: {:?}", e); + DropbearNativeError::JNIFailedToCreateObject })?; - let index_obj = env.new_object( - &index_cls, - "(II)V", - &[ - 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, - "(J)V", - &[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, - "(Lcom/dropbear/physics/Index;Lcom/dropbear/EntityId;)V", - &[ - JValue::Object(&index_obj), - JValue::Object(&entity_obj) - ] - ).map_err(|e| { - eprintln!("[JNI Error] Failed to create RigidBody object: {:?}", e); - DropbearNativeError::JNIFailedToCreateObject - })?; + let rb_obj = env + .new_object( + rb_cls, + "(Lcom/dropbear/physics/Index;Lcom/dropbear/EntityId;)V", + &[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) } @@ -834,17 +899,16 @@ impl ToJObject for RayHit { DropbearNativeError::JNIClassNotFound })?; - let object = env.new_object( - class, - "(Lcom/dropbear/physics/Collider;D)V", - &[ - JValue::Object(&collider), - JValue::Double(distance), - ] - ).map_err(|e| { - eprintln!("[JNI Error] Failed to create RayHit object: {:?}", e); - DropbearNativeError::JNIFailedToCreateObject - })?; + let object = env + .new_object( + class, + "(Lcom/dropbear/physics/Collider;D)V", + &[JValue::Object(&collider), JValue::Double(distance)], + ) + .map_err(|e| { + eprintln!("[JNI Error] Failed to create RayHit object: {:?}", e); + DropbearNativeError::JNIFailedToCreateObject + })?; Ok(object) } @@ -866,7 +930,9 @@ impl Into<ShapeCastStatus> for NShapeCastStatus { NShapeCastStatus::OutOfIterations => ShapeCastStatus::OutOfIterations, NShapeCastStatus::Converged => ShapeCastStatus::Converged, NShapeCastStatus::Failed => ShapeCastStatus::Failed, - NShapeCastStatus::PenetratingOrWithinTargetDist => ShapeCastStatus::PenetratingOrWithinTargetDist, + NShapeCastStatus::PenetratingOrWithinTargetDist => { + ShapeCastStatus::PenetratingOrWithinTargetDist + } } } } @@ -877,7 +943,9 @@ impl Into<NShapeCastStatus> for ShapeCastStatus { ShapeCastStatus::OutOfIterations => NShapeCastStatus::OutOfIterations, ShapeCastStatus::Converged => NShapeCastStatus::Converged, ShapeCastStatus::Failed => NShapeCastStatus::Failed, - ShapeCastStatus::PenetratingOrWithinTargetDist => NShapeCastStatus::PenetratingOrWithinTargetDist, + ShapeCastStatus::PenetratingOrWithinTargetDist => { + NShapeCastStatus::PenetratingOrWithinTargetDist + } } } } @@ -907,10 +975,12 @@ impl ToJObject for NShapeCastHit { let distance = self.distance as jdouble; - let class = env.find_class("com/dropbear/physics/ShapeCastHit").map_err(|e| { - eprintln!("[JNI Error] Failed to find ShapeCastHit class: {:?}", e); - DropbearNativeError::JNIClassNotFound - })?; + let class = env + .find_class("com/dropbear/physics/ShapeCastHit") + .map_err(|e| { + eprintln!("[JNI Error] Failed to find ShapeCastHit class: {:?}", e); + DropbearNativeError::JNIClassNotFound + })?; let object = env .new_object( @@ -940,19 +1010,21 @@ impl ToJObject for NShapeCastHit { /// Class: `com.dropbear.physics.CollisionEventType` pub enum CollisionEventType { Started, - Stopped + Stopped, } impl ToJObject for CollisionEventType { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/physics/CollisionEventType") + let class = env + .find_class("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) + let name_jstring = env + .new_string(name) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; let value = env @@ -996,40 +1068,42 @@ impl CollisionEvent { ) -> Option<CollisionEvent> { 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 collider1_info = physics + .colliders_entity_map + .iter() + .find_map(|(l, s)| { + for (_, h) in s { + if col1 == *h { + return Some(l.clone()); + } } + 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 + .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 }) + })?; + Some(Self { event_type: CollisionEventType::Started, collider1: NCollider { @@ -1046,39 +1120,41 @@ impl CollisionEvent { }) } 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 collider1_info = physics + .colliders_entity_map + .iter() + .find_map(|(l, s)| { + for (_, h) in s { + if col1 == *h { + return Some(l.clone()); + } } + 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 + .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 }) + })?; Some(Self { event_type: CollisionEventType::Stopped, @@ -1101,7 +1177,8 @@ impl CollisionEvent { impl ToJObject for CollisionEvent { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/physics/CollisionEvent") + let class = env + .find_class("com/dropbear/physics/CollisionEvent") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let event_type = self.event_type.to_jobject(env)?; @@ -1153,24 +1230,27 @@ impl ContactForceEvent { 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 { + 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 }) + })?, + ) }; - + Some(Self { collider1: NCollider { index: IndexNative::from(event.collider1.0), @@ -1183,15 +1263,15 @@ 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.into(), + event.total_force.y.into(), + event.total_force.z.into(), ), 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.into(), + event.max_force_direction.y.into(), + event.max_force_direction.z.into(), ), max_force_magnitude: event.max_force_magnitude as f64, }) @@ -1200,7 +1280,8 @@ impl ContactForceEvent { impl ToJObject for ContactForceEvent { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/physics/ContactForceEvent") + let class = env + .find_class("com/dropbear/physics/ContactForceEvent") .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let collider1 = self.collider1.to_jobject(env)?; @@ -1,16 +1,16 @@ //! Utility functions and helpers -pub mod option; pub mod hashmap; +pub mod option; +use crate::scripting::result::DropbearNativeResult; use crate::states::Node; use dropbear_engine::utils::{ResourceReference, ResourceReferenceType, relative_path_from_euca}; -use std::path::{Path, PathBuf}; -use std::time::Duration; use jni::JNIEnv; use jni::objects::{JObject, JValue}; +use std::path::{Path, PathBuf}; +use std::time::Duration; use winit::keyboard::KeyCode; -use crate::scripting::result::DropbearNativeResult; pub const PROTO_TEXTURE: &[u8] = include_bytes!("../../../resources/textures/proto.png"); @@ -718,19 +718,26 @@ impl Default for Progress { impl crate::scripting::jni::utils::ToJObject for crate::utils::Progress { fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let class = env.find_class("com/dropbear/utils/Progress") + let class = env + .find_class("com/dropbear/utils/Progress") .map_err(|_| crate::scripting::native::DropbearNativeError::JNIClassNotFound)?; - let message_jstring = env.new_string(&self.message) + let message_jstring = env + .new_string(&self.message) .map_err(|_| crate::scripting::native::DropbearNativeError::JNIFailedToCreateObject)?; - let obj = env.new_object(&class, "(DDLjava/lang/String;)V", &[ - JValue::Double(self.current as f64), - JValue::Double(self.total as f64), - JValue::Object(&JObject::from(message_jstring)), - ]) + let obj = env + .new_object( + &class, + "(DDLjava/lang/String;)V", + &[ + JValue::Double(self.current as f64), + JValue::Double(self.total as f64), + JValue::Object(&JObject::from(message_jstring)), + ], + ) .map_err(|_| crate::scripting::native::DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) } -} +} @@ -179,10 +179,12 @@ impl<K: Eq + std::hash::Hash, V> StaleTracker<K, V> { /// assert!(!removed.contains(&"fresh")); /// ``` pub fn remove_stale(&mut self, max_age: usize) -> Vec<K> - where K: Clone + where + K: Clone, { let current = self.current_generation; - let stale_keys: Vec<K> = self.map + let stale_keys: Vec<K> = self + .map .iter() .filter(|(_, (_, generation))| current - generation > max_age) .map(|(k, _)| k.clone()) @@ -244,4 +246,4 @@ impl<K: Eq + std::hash::Hash, V> Default for StaleTracker<K, V> { fn default() -> Self { Self::new() } -} +} @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; -/// An optional value that when replaced can store the old value. +/// An optional value that when replaced can store the old value. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct HistoricalOption<T> { _inner: Option<T>, @@ -32,7 +32,7 @@ impl<T> HistoricalOption<T> { self._inner = new_value; } - /// Swaps the old value with the new one. + /// Swaps the old value with the new one. pub fn swap(&mut self) { let old = self._old.take(); let inner = self._inner.take(); @@ -80,7 +80,7 @@ impl<T> HistoricalOption<T> { self._inner } - /// Turn the option ON. + /// Turn the option ON. /// If there is a saved history value, restore it. /// Otherwise, use the provided default. pub fn enable_or(&mut self, default_val: T) { @@ -117,4 +117,4 @@ impl<T> Default for HistoricalOption<T> { fn default() -> Self { Self::none() } -} +} @@ -1,15 +1,15 @@ //! Used for displaying the Help->About window in the editor. -use egui::{CentralPanel}; +use dropbear_engine::input::{Controller, Keyboard, Mouse}; +use dropbear_engine::scene::{Scene, SceneCommand}; +use egui::CentralPanel; +use eucalyptus_core::input::InputState; use gilrs::{Button, GamepadId}; use winit::dpi::PhysicalPosition; use winit::event::MouseButton; use winit::event_loop::ActiveEventLoop; use winit::keyboard::KeyCode; -use winit::window::{WindowId}; -use dropbear_engine::input::{Controller, Keyboard, Mouse}; -use dropbear_engine::scene::{Scene, SceneCommand}; -use eucalyptus_core::input::InputState; +use winit::window::WindowId; pub struct AboutWindow { scene_command: SceneCommand, @@ -32,9 +32,18 @@ impl Scene for AboutWindow { self.window = Some(graphics.window.id()); } - fn physics_update(&mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) {} + fn physics_update( + &mut self, + _dt: f32, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { + } - fn update(&mut self, _dt: f32, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn update( + &mut self, + _dt: f32, + graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { CentralPanel::default().show(&graphics.get_egui_context(), |ui| { ui.centered_and_justified(|ui| { ui.add_space(8.0); @@ -63,9 +72,9 @@ impl Scene for AboutWindow { env!("GIT_HASH"), rustc_version_runtime::version_meta().short_version_string )) - .weak() - .italics() - .small(), + .weak() + .italics() + .small(), ); ui.add_space(8.0); @@ -75,7 +84,10 @@ impl Scene for AboutWindow { self.window = Some(graphics.window.id()); } - fn render<'a>(&mut self, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn render<'a>( + &mut self, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { } fn exit(&mut self, _event_loop: &ActiveEventLoop) {} @@ -154,4 +166,4 @@ impl Controller for AboutWindow { self.input_state.left_stick_position.remove(&id); self.input_state.right_stick_position.remove(&id); } -} +} @@ -1,17 +1,17 @@ -use anyhow::{bail, Context}; -use app_dirs2::{app_root, AppDataType}; +use anyhow::{Context, bail}; +use app_dirs2::{AppDataType, app_root}; use crossbeam_channel::Sender; +use eucalyptus_core::APP_INFO; use eucalyptus_core::config::ProjectConfig; use eucalyptus_core::runtime::RuntimeProjectConfig; use eucalyptus_core::scene::SceneConfig; -use eucalyptus_core::APP_INFO; +use magna_carta::Target; +use ron::ser::PrettyConfig; use semver::Version; use std::fs; use std::path::{Path, PathBuf}; use std::time::UNIX_EPOCH; -use ron::ser::PrettyConfig; use tokio::{fs as tokio_fs, process::Command, task}; -use magna_carta::Target; /// Builds a eucalyptus project into a single bundle. /// @@ -64,13 +64,13 @@ pub fn build(project_config: PathBuf) -> anyhow::Result<PathBuf> { scenes, authors: config.authors.clone(), editor_version: Version::parse(env!("CARGO_PKG_VERSION"))?, - project_version: Version::parse( - config - .project_version - .clone() - .as_str(), - )?, - initial_scene: config.runtime_settings.initial_scene.ok_or(anyhow::anyhow!("Project was expected to be an initial scene"))?, + project_version: Version::parse(config.project_version.clone().as_str())?, + initial_scene: config + .runtime_settings + .initial_scene + .ok_or(anyhow::anyhow!( + "Project was expected to be an initial scene" + ))?, }; log::debug!("Converted to runtime project config"); @@ -112,8 +112,7 @@ fn copy_dir_recursive(src: &Path, dst: &Path) -> anyhow::Result<()> { /// Returns the contents of the project config in a [`ron`] format pub fn read(eupak: PathBuf) -> anyhow::Result<RuntimeProjectConfig> { let bytes = std::fs::read(&eupak)?; - let (content, _): (RuntimeProjectConfig, usize) = - postcard::from_bytes(&bytes)?; + let (content, _): (RuntimeProjectConfig, usize) = postcard::from_bytes(&bytes)?; let str = ron::ser::to_string_pretty(&content, PrettyConfig::default())?; @@ -164,7 +163,10 @@ pub async fn package( &status_tx, PackageStatus::Progress { step: "Locating runtime", - detail: format!("Searching for runtime executable in {}", templates_dir.display()), + detail: format!( + "Searching for runtime executable in {}", + templates_dir.display() + ), }, ); @@ -310,10 +312,14 @@ pub async fn package( for lib in shared_objects { let file_name = lib .file_name() - .ok_or_else(|| anyhow::anyhow!("Runtime dependency missing filename: {}", lib.display()))? + .ok_or_else(|| { + anyhow::anyhow!("Runtime dependency missing filename: {}", lib.display()) + })? .to_owned(); let libs_target = libs_dir.join(&file_name); - if !format!("{}", file_name.display()).contains("eucalyptus_core") { continue; } + if !format!("{}", file_name.display()).contains("eucalyptus_core") { + continue; + } tokio_fs::copy(&lib, &libs_target).await?; let package_target = package_dir.join(&file_name); tokio_fs::copy(&lib, &package_target).await?; @@ -330,9 +336,13 @@ pub async fn package( for import_lib in import_libs { let file_name = import_lib .file_name() - .ok_or_else(|| anyhow::anyhow!("Import library missing filename: {}", import_lib.display()))? + .ok_or_else(|| { + anyhow::anyhow!("Import library missing filename: {}", import_lib.display()) + })? .to_owned(); - if !format!("{}", file_name.display()).contains("eucalyptus_core") { continue; } + if !format!("{}", file_name.display()).contains("eucalyptus_core") { + continue; + } let libs_target = libs_dir.join(&file_name); tokio_fs::copy(&import_lib, &libs_target).await?; log::info!( @@ -381,7 +391,9 @@ fn locate_runtime_binary(templates_dir: &Path) -> anyhow::Result<PathBuf> { } let current_exe = std::env::current_exe()?; - let current_dir = current_exe.parent().ok_or(anyhow::anyhow!("Unable to locate parent folder of current executable"))?; + let current_dir = current_exe.parent().ok_or(anyhow::anyhow!( + "Unable to locate parent folder of current executable" + ))?; for entry in fs::read_dir(current_dir)? { let entry = entry?; let path = entry.path(); @@ -615,4 +627,4 @@ async fn run_gradle_build(project_root: &Path) -> anyhow::Result<()> { } Ok(()) -} +} @@ -1,9 +1,3 @@ -use crate::editor::{Signal, StaticallyKept, UndoableAction}; -use dropbear_engine::camera::Camera; -use egui::{Ui}; -use eucalyptus_core::camera::{CameraComponent}; -use hecs::Entity; - // impl InspectableComponent for Camera { // fn inspect( // &mut self, @@ -25,7 +19,7 @@ use hecs::Entity; // self.eye = glam::DVec3::ZERO; // } // }); -// +// // ui.horizontal(|ui| { // ui.label("Target:"); // ui.label(format!( @@ -39,7 +33,7 @@ use hecs::Entity; // }); // } // } -// +// // impl InspectableComponent for CameraComponent { // fn inspect( // &mut self, @@ -58,7 +52,7 @@ use hecs::Entity; // .speed(0.1), // ); // }); -// +// // ui.horizontal(|ui| { // ui.label("Sensitivity:"); // ui.add( @@ -67,7 +61,7 @@ use hecs::Entity; // .range(0.0001..=1.5), // ); // }); -// +// // ui.horizontal(|ui| { // ui.label("FOV:"); // ui.add( @@ -2,13 +2,13 @@ mod window; -use std::rc::Rc; +use crate::debug::window::DebugWindow; use crate::editor::Signal; +use dropbear_engine::DropbearWindowBuilder; use egui::Ui; use parking_lot::RwLock; +use std::rc::Rc; use winit::window::WindowAttributes; -use dropbear_engine::DropbearWindowBuilder; -use crate::debug::window::DebugWindow; pub(crate) fn show_menu_bar(ui: &mut Ui, signal: &mut Signal) { ui.menu_button("Debug", |ui_debug| { @@ -26,15 +26,15 @@ pub(crate) fn show_menu_bar(ui: &mut Ui, signal: &mut Signal) { if ui_debug.button("Launch new debug test window").clicked() { let debug_window = Rc::new(RwLock::new(DebugWindow::new())); - + let window_data = DropbearWindowBuilder::new() .with_attributes( - WindowAttributes::default().with_title("eucalyptus-editor debug window") + WindowAttributes::default().with_title("eucalyptus-editor debug window"), ) .add_scene_with_input(debug_window, "debug_window") .set_initial_scene("debug_window") .build(); - + *signal = Signal::RequestNewWindow(window_data); } @@ -2,13 +2,13 @@ //! //! Can also be technically used as a template for other windowed scenes (as its essentially the basics) -use egui::{CentralPanel}; +use egui::CentralPanel; use gilrs::{Button, GamepadId}; use winit::dpi::PhysicalPosition; use winit::event::MouseButton; use winit::event_loop::ActiveEventLoop; use winit::keyboard::KeyCode; -use winit::window::{WindowId}; +use winit::window::WindowId; use dropbear_engine::input::{Controller, Keyboard, Mouse}; use dropbear_engine::scene::{Scene, SceneCommand}; @@ -35,9 +35,18 @@ impl Scene for DebugWindow { self.window = Some(graphics.window.id()); } - fn physics_update(&mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) {} + fn physics_update( + &mut self, + _dt: f32, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { + } - fn update(&mut self, _dt: f32, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn update( + &mut self, + _dt: f32, + graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { CentralPanel::default().show(&graphics.get_egui_context(), |ui| { ui.label("Hello Debug Window!"); }); @@ -45,7 +54,10 @@ impl Scene for DebugWindow { self.window = Some(graphics.window.id()); } - fn render<'a>(&mut self, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn render<'a>( + &mut self, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { } fn exit(&mut self, _event_loop: &ActiveEventLoop) {} @@ -124,4 +136,4 @@ impl Controller for DebugWindow { self.input_state.left_stick_position.remove(&id); self.input_state.right_stick_position.remove(&id); } -} +} @@ -1,20 +1,20 @@ -use std::{cmp::Ordering, fs, hash::DefaultHasher, io, path::Path}; -use std::hash::{Hash, Hasher}; -use dropbear_engine::{graphics::NO_TEXTURE, utils::ResourceReference}; use dropbear_engine::asset::ASSET_REGISTRY; use dropbear_engine::entity::MeshRenderer; use dropbear_engine::model::Model; use dropbear_engine::texture::Texture; -use eucalyptus_core::utils::ResolveReference; +use dropbear_engine::{graphics::NO_TEXTURE, utils::ResourceReference}; use egui_ltreeview::{Action, NodeBuilder, TreeViewBuilder}; use eucalyptus_core::states::PROJECT; +use eucalyptus_core::utils::ResolveReference; use hecs::Entity; use log::{info, warn}; +use std::hash::{Hash, Hasher}; +use std::{cmp::Ordering, fs, hash::DefaultHasher, io, path::Path}; use crate::editor::{ AssetDivision, AssetNodeInfo, AssetNodeKind, ComponentNodeSelection, DraggedAsset, - EditorTabViewer, FsEntry, ResourceDivision, SceneDivision, ScriptDivision, StaticallyKept, - Signal, TABS_GLOBAL, + EditorTabViewer, FsEntry, ResourceDivision, SceneDivision, ScriptDivision, Signal, + StaticallyKept, TABS_GLOBAL, }; use eucalyptus_core::component::DRAGGED_ASSET_ID; @@ -44,9 +44,7 @@ impl<'a> EditorTabViewer<'a> { ui.horizontal(|ui| { ui.label("Rename"); - let response = ui.add( - egui::TextEdit::singleline(&mut rename.buffer).id(rename_id), - ); + let response = ui.add(egui::TextEdit::singleline(&mut rename.buffer).id(rename_id)); if rename.just_started { ui.ctx().memory_mut(|m| m.request_focus(rename_id)); rename.just_started = false; @@ -75,13 +73,14 @@ impl<'a> EditorTabViewer<'a> { project.project_path.clone() }; - let (_resp, action) = egui_ltreeview::TreeView::new(egui::Id::new("asset_viewer")).show(ui, |builder| { - builder.node(Self::dir_node("euca://")); - self.build_resource_branch(&mut cfg, builder, &project_root); - self.build_scripts_branch(&mut cfg, builder, &project_root); - self.build_scene_branch(&mut cfg, builder, &project_root); - builder.close_dir(); - }); + let (_resp, action) = + egui_ltreeview::TreeView::new(egui::Id::new("asset_viewer")).show(ui, |builder| { + builder.node(Self::dir_node("euca://")); + self.build_resource_branch(&mut cfg, builder, &project_root); + self.build_scripts_branch(&mut cfg, builder, &project_root); + self.build_scene_branch(&mut cfg, builder, &project_root); + builder.close_dir(); + }); for a in action { match a { @@ -99,7 +98,10 @@ impl<'a> EditorTabViewer<'a> { if let Some(asset) = cfg.asset_node_assets.get(&node_id).cloned() { cfg.dragged_asset = Some(asset.clone()); ui.ctx().data_mut(|d| { - d.insert_temp(egui::Id::new(DRAGGED_ASSET_ID), Some(asset.path.clone())) + d.insert_temp( + egui::Id::new(DRAGGED_ASSET_ID), + Some(asset.path.clone()), + ) }); } } @@ -131,8 +133,9 @@ impl<'a> EditorTabViewer<'a> { }; Self::register_asset_node(cfg, label, root_info.clone()); let node_id = Self::asset_node_id(label); - let menu = Self::dir_node_kind(label, "resources", root_info.kind) - .context_menu(|ui| self.asset_dir_context_menu(cfg, ui, node_id, &root_info, "New Folder")); + let menu = Self::dir_node_kind(label, "resources", root_info.kind).context_menu(|ui| { + self.asset_dir_context_menu(cfg, ui, node_id, &root_info, "New Folder") + }); builder.node(menu); if resources_root.exists() { self.walk_resource_directory(cfg, builder, &resources_root, &resources_root); @@ -180,7 +183,9 @@ impl<'a> EditorTabViewer<'a> { Self::register_asset_node(cfg, &full_label, dir_info.clone()); let node_id = Self::asset_node_id(&full_label); let menu = Self::dir_node_kind(&full_label, &entry.name, dir_info.kind) - .context_menu(|ui| self.asset_dir_context_menu(cfg, ui, node_id, &dir_info, "New Folder")); + .context_menu(|ui| { + self.asset_dir_context_menu(cfg, ui, node_id, &dir_info, "New Folder") + }); builder.node(menu); self.walk_resource_directory(cfg, builder, base_path, &entry.path); builder.close_dir(); @@ -212,21 +217,28 @@ impl<'a> EditorTabViewer<'a> { }; Self::register_asset_node(cfg, &full_label, file_info.clone()); let node_id = Self::asset_node_id(&full_label); - let menu = Self::leaf_node_kind(&full_label, &entry.name, file_info.kind).context_menu(|ui| { + let menu = Self::leaf_node_kind(&full_label, &entry.name, file_info.kind) + .context_menu(|ui| { self.asset_file_context_menu(cfg, ui, node_id, &file_info); ui.separator(); if is_model { if ui.button("Load to memory").clicked() { ui.close(); - self.queue_model_load(reference_for_menu.clone(), entry_name.clone()); + self.queue_model_load( + reference_for_menu.clone(), + entry_name.clone(), + ); } } if is_texture { if ui.button("Load to memory").clicked() { ui.close(); - self.queue_texture_load(reference_for_menu.clone(), entry_name.clone()); + self.queue_texture_load( + reference_for_menu.clone(), + entry_name.clone(), + ); } ui.separator(); @@ -304,8 +316,9 @@ impl<'a> EditorTabViewer<'a> { }; Self::register_asset_node(cfg, label, root_info.clone()); let node_id = Self::asset_node_id(label); - let menu = Self::dir_node_kind(label, "scripts", root_info.kind) - .context_menu(|ui| self.asset_dir_context_menu(cfg, ui, node_id, &root_info, "New Package")); + let menu = Self::dir_node_kind(label, "scripts", root_info.kind).context_menu(|ui| { + self.asset_dir_context_menu(cfg, ui, node_id, &root_info, "New Package") + }); builder.node(menu); if !scripts_root.exists() { Self::add_placeholder_leaf( @@ -347,7 +360,9 @@ impl<'a> EditorTabViewer<'a> { Self::register_asset_node(cfg, &source_label, source_info.clone()); let node_id = Self::asset_node_id(&source_label); let menu = Self::dir_node_kind(&source_label, &entry.name, source_info.kind) - .context_menu(|ui| self.asset_dir_context_menu(cfg, ui, node_id, &source_info, "New Package")); + .context_menu(|ui| { + self.asset_dir_context_menu(cfg, ui, node_id, &source_info, "New Package") + }); builder.node(menu); if self.build_script_source_set(cfg, builder, &entry.path, &source_label) { had_content = true; @@ -429,7 +444,9 @@ impl<'a> EditorTabViewer<'a> { Self::register_asset_node(cfg, &child_label, dir_info.clone()); let node_id = Self::asset_node_id(&child_label); let menu = Self::dir_node_kind(&child_label, &entry.name, dir_info.kind) - .context_menu(|ui| self.asset_dir_context_menu(cfg, ui, node_id, &dir_info, "New Package")); + .context_menu(|ui| { + self.asset_dir_context_menu(cfg, ui, node_id, &dir_info, "New Package") + }); builder.node(menu); self.build_plain_directory( cfg, @@ -529,7 +546,9 @@ impl<'a> EditorTabViewer<'a> { Self::register_asset_node(cfg, &child_label, dir_info.clone()); let node_id = Self::asset_node_id(&child_label); let menu = Self::dir_node_kind(&child_label, &entry.name, dir_info.kind) - .context_menu(|ui| self.asset_dir_context_menu(cfg, ui, node_id, &dir_info, new_folder_label)); + .context_menu(|ui| { + self.asset_dir_context_menu(cfg, ui, node_id, &dir_info, new_folder_label) + }); builder.node(menu); self.build_plain_directory( cfg, @@ -686,7 +705,9 @@ impl<'a> EditorTabViewer<'a> { Self::register_asset_node(cfg, &full_path_str, dir_info.clone()); let node_id = Self::asset_node_id(&full_path_str); let menu = Self::dir_node_kind(&full_path_str, &package_suffix, dir_info.kind) - .context_menu(|ui| self.asset_dir_context_menu(cfg, ui, node_id, &dir_info, "New Package")); + .context_menu(|ui| { + self.asset_dir_context_menu(cfg, ui, node_id, &dir_info, "New Package") + }); builder.node(menu); for file in files { @@ -738,8 +759,9 @@ impl<'a> EditorTabViewer<'a> { }; Self::register_asset_node(cfg, label, root_info.clone()); let node_id = Self::asset_node_id(label); - let menu = Self::dir_node_kind(label, "scenes", root_info.kind) - .context_menu(|ui| self.asset_dir_context_menu(cfg, ui, node_id, &root_info, "New Folder")); + let menu = Self::dir_node_kind(label, "scenes", root_info.kind).context_menu(|ui| { + self.asset_dir_context_menu(cfg, ui, node_id, &root_info, "New Folder") + }); builder.node(menu); if !scenes_root.exists() { Self::add_placeholder_leaf( @@ -786,7 +808,9 @@ impl<'a> EditorTabViewer<'a> { Self::register_asset_node(cfg, &child_label, dir_info.clone()); let node_id = Self::asset_node_id(&child_label); let menu = Self::dir_node_kind(&child_label, &entry.name, dir_info.kind) - .context_menu(|ui| self.asset_dir_context_menu(cfg, ui, node_id, &dir_info, "New Folder")); + .context_menu(|ui| { + self.asset_dir_context_menu(cfg, ui, node_id, &dir_info, "New Folder") + }); builder.node(menu); self.build_plain_directory( cfg, @@ -869,11 +893,7 @@ impl<'a> EditorTabViewer<'a> { id } - fn register_asset_node( - cfg: &mut StaticallyKept, - id_source: &str, - info: AssetNodeInfo, - ) -> u64 { + fn register_asset_node(cfg: &mut StaticallyKept, id_source: &str, info: AssetNodeInfo) -> u64 { let node_id = Self::asset_node_id(id_source); cfg.asset_node_info.insert(node_id, info); node_id @@ -886,21 +906,32 @@ impl<'a> EditorTabViewer<'a> { ) } - fn dir_node_kind<'ui>(id_source: &str, label: &str, kind: AssetNodeKind) -> NodeBuilder<'ui, u64> { + fn dir_node_kind<'ui>( + id_source: &str, + label: &str, + kind: AssetNodeKind, + ) -> NodeBuilder<'ui, u64> { Self::with_icon_kind( NodeBuilder::dir(Self::asset_node_id(id_source)).label(label.to_string()), kind, ) } - fn leaf_node_kind<'ui>(id_source: &str, label: &str, kind: AssetNodeKind) -> NodeBuilder<'ui, u64> { + fn leaf_node_kind<'ui>( + id_source: &str, + label: &str, + kind: AssetNodeKind, + ) -> NodeBuilder<'ui, u64> { Self::with_icon_kind( NodeBuilder::leaf(Self::asset_node_id(id_source)).label(label.to_string()), kind, ) } - fn with_icon_kind<'ui>(builder: NodeBuilder<'ui, u64>, kind: AssetNodeKind) -> NodeBuilder<'ui, u64> { + fn with_icon_kind<'ui>( + builder: NodeBuilder<'ui, u64>, + kind: AssetNodeKind, + ) -> NodeBuilder<'ui, u64> { builder.icon(move |ui| { egui_extras::install_image_loaders(ui.ctx()); Self::draw_asset_icon(ui, kind) @@ -1065,7 +1096,11 @@ impl<'a> EditorTabViewer<'a> { } if let Err(err) = fs::rename(&rename.original_path, &target_path) { - warn!("Failed to rename '{}': {}", rename.original_path.display(), err); + warn!( + "Failed to rename '{}': {}", + rename.original_path.display(), + err + ); } else { info!("Renamed to {}", target_path.display()); } @@ -1120,7 +1155,11 @@ impl<'a> EditorTabViewer<'a> { } } - fn handle_asset_move(&mut self, cfg: &mut StaticallyKept, drag: &egui_ltreeview::DragAndDrop<u64>) { + fn handle_asset_move( + &mut self, + cfg: &mut StaticallyKept, + drag: &egui_ltreeview::DragAndDrop<u64>, + ) { let Some(&source_id) = drag.source.first() else { return; }; @@ -1185,7 +1224,6 @@ impl<'a> EditorTabViewer<'a> { } } - fn is_model_file(name: &str) -> bool { let name = name.to_ascii_lowercase(); name.ends_with(".glb") || name.ends_with(".gltf") @@ -1223,12 +1261,13 @@ impl<'a> EditorTabViewer<'a> { None, ASSET_REGISTRY.clone(), ) - .await { + .await + { Ok(v) => v, Err(e) => { warn!("Unable to load model {}: {}", reference, e); return Err(e); - }, + } }; let mut registry = ASSET_REGISTRY.write(); @@ -1256,12 +1295,12 @@ impl<'a> EditorTabViewer<'a> { let queue = graphics.future_queue.clone(); queue.push(async move { let path = reference.resolve()?; - let texture = match Texture::from_file(graphics.clone(), &path, Some(&label)).await{ + let texture = match Texture::from_file(graphics.clone(), &path, Some(&label)).await { Ok(v) => v, Err(e) => { warn!("Unable to load texture {}: {}", reference, e); return Err(e); - }, + } }; let mut registry = ASSET_REGISTRY.write(); registry.add_texture_with_label(label.clone(), texture); @@ -1413,4 +1452,4 @@ impl<'a> EditorTabViewer<'a> { Entity::from_bits(node_id) } } -} +} @@ -2,142 +2,143 @@ use std::path::PathBuf; use egui::{Margin, RichText}; -use crate::editor::{EditorTabViewer, console_error::{ConsoleItem, ErrorLevel}}; +use crate::editor::{ + EditorTabViewer, + console_error::{ConsoleItem, ErrorLevel}, +}; impl<'a> EditorTabViewer<'a> { pub fn build_console(&mut self, ui: &mut egui::Ui) { fn analyse_error(log: &Vec<String>) -> Vec<ConsoleItem> { - fn parse_compiler_location( - line: &str, - ) -> Option<(ErrorLevel, PathBuf, String)> { - let trimmed = line.trim_start(); - let (error_level, rest) = - if let Some(r) = trimmed.strip_prefix("e: file:///") { - (ErrorLevel::Error, r) - } else if let Some(r) = trimmed.strip_prefix("w: file:///") { - (ErrorLevel::Warn, r) - } else { - return None; - }; + fn parse_compiler_location(line: &str) -> Option<(ErrorLevel, PathBuf, String)> { + let trimmed = line.trim_start(); + let (error_level, rest) = if let Some(r) = trimmed.strip_prefix("e: file:///") { + (ErrorLevel::Error, r) + } else if let Some(r) = trimmed.strip_prefix("w: file:///") { + (ErrorLevel::Warn, r) + } else { + return None; + }; - let location = rest.split_whitespace().next()?; + let location = rest.split_whitespace().next()?; - let mut segments = location.rsplitn(3, ':'); - let column = segments.next()?; - let row = segments.next()?; - let path = segments.next()?; + let mut segments = location.rsplitn(3, ':'); + let column = segments.next()?; + let row = segments.next()?; + let path = segments.next()?; - Some((error_level, PathBuf::from(path), format!("{row}:{column}"))) - } + Some((error_level, PathBuf::from(path), format!("{row}:{column}"))) + } - let mut list: Vec<ConsoleItem> = Vec::new(); - for (index, line) in log.iter().enumerate() { - if line.contains("The required library") { - list.push(ConsoleItem { - error_level: ErrorLevel::Error, - msg: line.clone(), - file_location: None, - line_ref: None, - id: index as u64, - }); - } else if let Some((error_level, path, loc)) = parse_compiler_location(line) { - list.push(ConsoleItem { - error_level, - msg: line.clone(), - file_location: Some(path), - line_ref: Some(loc), - id: index as u64, - }); - } else { - list.push(ConsoleItem { - error_level: ErrorLevel::Info, - msg: line.clone(), - file_location: None, - line_ref: None, - id: index as u64, - }); - } - } - list + let mut list: Vec<ConsoleItem> = Vec::new(); + for (index, line) in log.iter().enumerate() { + if line.contains("The required library") { + list.push(ConsoleItem { + error_level: ErrorLevel::Error, + msg: line.clone(), + file_location: None, + line_ref: None, + id: index as u64, + }); + } else if let Some((error_level, path, loc)) = parse_compiler_location(line) { + list.push(ConsoleItem { + error_level, + msg: line.clone(), + file_location: Some(path), + line_ref: Some(loc), + id: index as u64, + }); + } else { + list.push(ConsoleItem { + error_level: ErrorLevel::Info, + msg: line.clone(), + file_location: None, + line_ref: None, + id: index as u64, + }); } + } + list + } - let logs = analyse_error(&self.build_logs); + let logs = analyse_error(&self.build_logs); - egui::ScrollArea::vertical() - .auto_shrink([false, false]) - .stick_to_bottom(true) - .show(ui, |ui| { - if logs.is_empty() { - ui.label("Build output will appear here once available."); - return; - } + egui::ScrollArea::vertical() + .auto_shrink([false, false]) + .stick_to_bottom(true) + .show(ui, |ui| { + if logs.is_empty() { + ui.label("Build output will appear here once available."); + return; + } - for item in &logs { - let (bg_color, text_color, stroke_color) = match item.error_level { - ErrorLevel::Error => ( - egui::Color32::from_rgb(60, 20, 20), - egui::Color32::from_rgb(255, 200, 200), - egui::Color32::from_rgb(255, 200, 200), - ), - ErrorLevel::Warn => ( - egui::Color32::from_rgb(40, 40, 10), - egui::Color32::from_rgb(255, 255, 200), - egui::Color32::from_rgb(255, 255, 200), - ), - ErrorLevel::Info => ( - egui::Color32::TRANSPARENT, - ui.style().visuals.text_color(), - egui::Color32::TRANSPARENT, - ), - }; + for item in &logs { + let (bg_color, text_color, stroke_color) = match item.error_level { + ErrorLevel::Error => ( + egui::Color32::from_rgb(60, 20, 20), + egui::Color32::from_rgb(255, 200, 200), + egui::Color32::from_rgb(255, 200, 200), + ), + ErrorLevel::Warn => ( + egui::Color32::from_rgb(40, 40, 10), + egui::Color32::from_rgb(255, 255, 200), + egui::Color32::from_rgb(255, 255, 200), + ), + ErrorLevel::Info => ( + egui::Color32::TRANSPARENT, + ui.style().visuals.text_color(), + egui::Color32::TRANSPARENT, + ), + }; - if matches!(item.error_level, ErrorLevel::Info) { - ui.label(RichText::new(&item.msg).monospace()); - } else { - let available_width = ui.available_width(); - let frame = egui::Frame::new() - .inner_margin(Margin::symmetric(8, 6)) - .fill(bg_color) - .stroke(egui::Stroke::new(1.0, stroke_color)); + if matches!(item.error_level, ErrorLevel::Info) { + ui.label(RichText::new(&item.msg).monospace()); + } else { + let available_width = ui.available_width(); + let frame = egui::Frame::new() + .inner_margin(Margin::symmetric(8, 6)) + .fill(bg_color) + .stroke(egui::Stroke::new(1.0, stroke_color)); - let response = frame - .show(ui, |ui| { - ui.set_width(available_width - 10.0); - ui.horizontal(|ui| { - ui.label(RichText::new(&item.msg).color(text_color).monospace()); - }); - }) - .response; + let response = frame + .show(ui, |ui| { + ui.set_width(available_width - 10.0); + ui.horizontal(|ui| { + ui.label( + RichText::new(&item.msg).color(text_color).monospace(), + ); + }); + }) + .response; - if response.clicked() { - log::debug!("Log item clicked: {}", &item.id); - if let (Some(path), Some(loc)) = - (&item.file_location, &item.line_ref) - { - let location_arg = format!("{}:{}", path.display(), loc); + if response.clicked() { + log::debug!("Log item clicked: {}", &item.id); + if let (Some(path), Some(loc)) = (&item.file_location, &item.line_ref) { + let location_arg = format!("{}:{}", path.display(), loc); - match std::process::Command::new("code") - .args(["-g", &location_arg]) - .spawn() - .map(|_| ()) - { - Ok(()) => { - log::info!( - "Launched Visual Studio Code at the error: {}", - &location_arg - ); - } - Err(e) => { - eucalyptus_core::warn!( - "Failed to open '{}' in VS Code: {}", - location_arg, e - ); - } - } + match std::process::Command::new("code") + .args(["-g", &location_arg]) + .spawn() + .map(|_| ()) + { + Ok(()) => { + log::info!( + "Launched Visual Studio Code at the error: {}", + &location_arg + ); + } + Err(e) => { + eucalyptus_core::warn!( + "Failed to open '{}' in VS Code: {}", + location_arg, + e + ); } } } } - }); + } + } + }); } -} +} @@ -1,7 +1,7 @@ +use parking_lot::Mutex; use std::io::{BufRead, BufReader}; use std::net::{TcpListener, TcpStream}; use std::sync::Arc; -use parking_lot::Mutex; pub struct EucalyptusConsole { pub buffer: Arc<Mutex<Vec<String>>>, @@ -68,7 +68,8 @@ impl EucalyptusConsole { buf.lock().push(text); } Err(e) => { - buf.lock().push(format!("Error reading from {}: {}", peer_addr, e)); + buf.lock() + .push(format!("Error reading from {}: {}", peer_addr, e)); break; } } @@ -85,4 +86,4 @@ impl EucalyptusConsole { buf.iter().for_each(|v| self.history.push(v.clone())); buf } -} +} @@ -1,18 +1,11 @@ use super::*; use crate::editor::ViewportMode; -use std::{ - collections::HashMap - , - hash::Hash - , - path::PathBuf, - sync::LazyLock, -}; +use std::{collections::HashMap, hash::Hash, path::PathBuf, sync::LazyLock}; use crate::editor::console::EucalyptusConsole; use crate::plugin::PluginRegistry; -use dropbear_engine::utils::ResourceReference; use dropbear_engine::entity::{EntityTransform, Transform}; +use dropbear_engine::utils::ResourceReference; use egui::{self}; use egui_dock::TabViewer; use hecs::{Entity, World}; @@ -257,7 +250,7 @@ impl<'a> TabViewer for EditorTabViewer<'a> { let scroll = egui::ScrollArea::vertical() .auto_shrink([false, false]) .stick_to_bottom(self.eucalyptus_console.auto_scroll); - + scroll.show(ui, |ui| { for log in &self.eucalyptus_console.history { let is_error = log.contains("[ERROR]") || log.contains("[FATAL]"); @@ -266,11 +259,21 @@ impl<'a> TabViewer for EditorTabViewer<'a> { let is_trace = log.contains("[TRACE]"); let is_info = !is_error && !is_warn && !is_debug && !is_trace; - if is_error && !self.eucalyptus_console.show_error { continue; } - if is_warn && !self.eucalyptus_console.show_warning { continue; } - if is_debug && !self.eucalyptus_console.show_debug { continue; } - if is_trace && !self.eucalyptus_console.show_trace { continue; } - if is_info && !self.eucalyptus_console.show_info { continue; } + if is_error && !self.eucalyptus_console.show_error { + continue; + } + if is_warn && !self.eucalyptus_console.show_warning { + continue; + } + if is_debug && !self.eucalyptus_console.show_debug { + continue; + } + if is_trace && !self.eucalyptus_console.show_trace { + continue; + } + if is_info && !self.eucalyptus_console.show_info { + continue; + } let color = if is_error { egui::Color32::from_rgb(255, 100, 100) @@ -285,9 +288,7 @@ impl<'a> TabViewer for EditorTabViewer<'a> { }; ui.add(egui::Label::new( - egui::RichText::new(log) - .color(color) - .monospace() + egui::RichText::new(log).color(color).monospace(), )); } }); @@ -1,5 +1,10 @@ use egui_ltreeview::{NodeBuilder, TreeViewBuilder}; -use eucalyptus_core::{component::ComponentRegistry, hierarchy::{Children, Hierarchy, Parent}, physics::{collider::ColliderGroup, rigidbody::RigidBody}, states::{Label, PROJECT}}; +use eucalyptus_core::{ + component::ComponentRegistry, + hierarchy::{Children, Hierarchy, Parent}, + physics::{collider::ColliderGroup, rigidbody::RigidBody}, + states::{Label, PROJECT}, +}; use hecs::{Entity, World}; use crate::editor::{Editor, EditorTabViewer, Signal, StaticallyKept, TABS_GLOBAL}; @@ -198,4 +203,4 @@ impl<'a> EditorTabViewer<'a> { } } } -} +} @@ -1,10 +1,10 @@ -use std::process::{Command, Stdio}; use super::*; use dropbear_engine::input::{Controller, Keyboard, Mouse}; use eucalyptus_core::states::Label; use eucalyptus_core::success_without_console; use gilrs::{Button, GamepadId}; use log; +use std::process::{Command, Stdio}; use transform_gizmo_egui::{GizmoMode, GizmoOrientation}; use winit::{ dpi::PhysicalPosition, event::MouseButton, event_loop::ActiveEventLoop, keyboard::KeyCode, @@ -117,9 +117,7 @@ impl Keyboard for Editor { log::info!("Successfully saved project, about to quit..."); success_without_console!("Successfully saved project"); let commands: fn() = || { - let current_dir = { - PROJECT.read().project_path.clone() - }; + let current_dir = { PROJECT.read().project_path.clone() }; #[cfg(unix)] { @@ -151,7 +149,7 @@ impl Keyboard for Editor { log::debug!("Stopping gradle threads"); } }; - + self.scene_command = SceneCommand::Quit(Some(commands)); log::debug!("Sent quit command"); } else if is_playing { @@ -319,7 +317,8 @@ impl Mouse for Editor { if let Some(active_camera) = *self.active_camera.lock() && let Ok((camera, _)) = self .world - .query_one::<(&mut Camera, &CameraComponent)>(active_camera).get() + .query_one::<(&mut Camera, &CameraComponent)>(active_camera) + .get() { if let Some((dx, dy)) = delta { camera.track_mouse_delta(dx, dy); @@ -1,8 +1,8 @@ pub mod asset_viewer; pub mod build_console; // pub mod component; -pub mod console_error; pub mod console; +pub mod console_error; pub mod dock; pub mod entity_list; pub mod input; @@ -13,68 +13,63 @@ pub mod viewport; pub(crate) use crate::editor::dock::*; +use crate::about::AboutWindow; use crate::build::build; use crate::debug; +use crate::editor::console::EucalyptusConsole; +use crate::editor::settings::editor::{EDITOR_SETTINGS, EditorSettingsWindow}; +use crate::editor::settings::project::ProjectSettingsWindow; use crate::plugin::PluginRegistry; use crate::stats::NerdStats; -use crossbeam_channel::{unbounded, Receiver, Sender}; +use crossbeam_channel::{Receiver, Sender, unbounded}; use dropbear_engine::buffer::ResizableBuffer; use dropbear_engine::entity::EntityTransform; use dropbear_engine::graphics::InstanceRaw; +use dropbear_engine::mipmap::MipMapper; +use dropbear_engine::pipelines::DropbearShaderPipeline; +use dropbear_engine::pipelines::GlobalsUniform; use dropbear_engine::pipelines::light_cube::LightCubePipeline; -use dropbear_engine::{camera::Camera, entity::{Transform}, future::FutureHandle, graphics::{SharedGraphicsContext}, scene::SceneCommand, DropbearWindowBuilder, WindowData}; +use dropbear_engine::pipelines::shader::MainRenderPipeline; +use dropbear_engine::sky::{DEFAULT_SKY_TEXTURE, HdrLoader, SkyPipeline}; +use dropbear_engine::{ + DropbearWindowBuilder, WindowData, camera::Camera, entity::Transform, future::FutureHandle, + graphics::SharedGraphicsContext, scene::SceneCommand, +}; use egui::{self, Context}; use egui_dock::{DockArea, DockState, NodeIndex, Style}; use eucalyptus_core::component::{ComponentRegistry, SerializedComponent}; -use eucalyptus_core::{register_components, APP_INFO}; use eucalyptus_core::hierarchy::{Children, SceneHierarchy}; +use eucalyptus_core::physics::PhysicsState; +use eucalyptus_core::physics::collider::shader::ColliderInstanceRaw; +use eucalyptus_core::physics::collider::shader::ColliderWireframePipeline; +use eucalyptus_core::physics::collider::{ColliderShapeKey, WireframeGeometry}; use eucalyptus_core::scene::{SceneConfig, SceneEntity}; use eucalyptus_core::states::Label; -use std::collections::HashSet; +use eucalyptus_core::{APP_INFO, register_components}; use eucalyptus_core::{ camera::{CameraComponent, CameraType, DebugCamera}, fatal, info, input::InputState, scripting::BuildStatus, states, - states::{ - EditorTab, WorldLoadingStatus, PROJECT, SCENES, - }, + states::{EditorTab, PROJECT, SCENES, WorldLoadingStatus}, success, utils::ViewportMode, warn, }; use hecs::{Entity, World}; +use log::{debug, error}; use parking_lot::{Mutex, RwLock}; use rfd::FileDialog; -use std::{ - collections::HashMap, - fs, - path::PathBuf, - sync::Arc, - time::Instant, -}; +use std::collections::HashSet; use std::rc::Rc; -use log::{debug, error}; +use std::{collections::HashMap, fs, path::PathBuf, sync::Arc, time::Instant}; use tokio::sync::oneshot; use transform_gizmo_egui::{EnumSet, Gizmo, GizmoMode, GizmoOrientation}; use wgpu::{Color, Extent3d}; +use winit::dpi::PhysicalSize; use winit::window::{CursorGrabMode, WindowAttributes}; use winit::{keyboard::KeyCode, window::Window}; -use winit::dpi::PhysicalSize; -use dropbear_engine::mipmap::MipMapper; -use dropbear_engine::pipelines::{DropbearShaderPipeline}; -use dropbear_engine::pipelines::shader::MainRenderPipeline; -use dropbear_engine::pipelines::GlobalsUniform; -use dropbear_engine::sky::{HdrLoader, SkyPipeline, DEFAULT_SKY_TEXTURE}; -use eucalyptus_core::physics::PhysicsState; -use eucalyptus_core::physics::collider::{ColliderShapeKey, WireframeGeometry}; -use eucalyptus_core::physics::collider::shader::ColliderInstanceRaw; -use eucalyptus_core::physics::collider::shader::ColliderWireframePipeline; -use crate::about::AboutWindow; -use crate::editor::console::EucalyptusConsole; -use crate::editor::settings::editor::{EditorSettingsWindow, EDITOR_SETTINGS}; -use crate::editor::settings::project::ProjectSettingsWindow; pub struct Editor { pub scene_command: SceneCommand, @@ -128,7 +123,6 @@ pub struct Editor { // might as well save some memory if its not required... // #[allow(unused)] // unused to allow for JVM to startup // pub(crate) script_manager: ScriptManager, - /// State of the input pub(crate) input_state: Box<InputState>, @@ -699,7 +693,10 @@ impl Editor { Ok(()) } - fn cleanup_scene_resources(&mut self, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn cleanup_scene_resources( + &mut self, + graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { if let Some(handle) = self.world_load_handle.take() { graphics.future_queue.cancel(&handle); } @@ -720,11 +717,14 @@ impl Editor { self.light_cube_pipeline = None; } - fn start_async_scene_load(&mut self, mut scene: SceneConfig, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn start_async_scene_load( + &mut self, + mut scene: SceneConfig, + graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { self.cleanup_scene_resources(graphics.clone()); - let (progress_sender, progress_receiver) = - unbounded::<WorldLoadingStatus>(); + let (progress_sender, progress_receiver) = unbounded::<WorldLoadingStatus>(); self.progress_tx = Some(progress_receiver); self.current_state = WorldLoadingStatus::Idle; @@ -865,7 +865,6 @@ impl Editor { egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| { egui::MenuBar::new().ui(ui, |ui| { ui.menu_button("File", |ui| { - if ui.button("New Scene").clicked() { self.open_new_scene_window = true; } @@ -900,9 +899,7 @@ impl Editor { success!("Successfully saved project"); } if ui.button("Reveal project").clicked() { - let project_path = { - PROJECT.read().project_path.clone() - }; + let project_path = { PROJECT.read().project_path.clone() }; match open::that(project_path) { Ok(()) => info!("Revealed project"), Err(e) => warn!("Unable to open project: {}", e), @@ -913,22 +910,33 @@ impl Editor { if ui.button("Editor Settings").clicked() { debug!("Editor settings"); let window_data = DropbearWindowBuilder::new() - .with_attributes(WindowAttributes::default() - .with_title("eucalyptus editor - settings") + .with_attributes( + WindowAttributes::default() + .with_title("eucalyptus editor - settings"), + ) + .add_scene_with_input( + Rc::new(RwLock::new(EditorSettingsWindow::new())), + "editor_settings", ) - .add_scene_with_input(Rc::new(RwLock::new(EditorSettingsWindow::new())), "editor_settings") .set_initial_scene("editor_settings") .build(); self.scene_command = SceneCommand::RequestWindow(window_data); debug!("Requested editor settings window"); }; - if ui.button(format!("{} Settings", PROJECT.read().project_name.clone())).clicked() { + if ui + .button(format!("{} Settings", PROJECT.read().project_name.clone())) + .clicked() + { debug!("Project Settings"); let window_data = DropbearWindowBuilder::new() - .with_attributes(WindowAttributes::default() - .with_title(format!("{} - settings", PROJECT.read().project_name.clone())) + .with_attributes(WindowAttributes::default().with_title(format!( + "{} - settings", + PROJECT.read().project_name.clone() + ))) + .add_scene_with_input( + Rc::new(RwLock::new(ProjectSettingsWindow::new())), + "project_settings_window", ) - .add_scene_with_input(Rc::new(RwLock::new(ProjectSettingsWindow::new())), "project_settings_window") .set_initial_scene("project_settings_window") .build(); self.scene_command = SceneCommand::RequestWindow(window_data); @@ -948,11 +956,21 @@ impl Editor { if ui.button("Build").clicked() { { let proj = PROJECT.read(); - match build(proj.project_path.join(format!("{}.eucp", proj.project_name.clone())).clone()) { - Ok(thingy) => success!("Project output at {}", thingy.display()), + match build( + proj.project_path + .join(format!("{}.eucp", proj.project_name.clone())) + .clone(), + ) { + Ok(thingy) => { + success!("Project output at {}", thingy.display()) + } Err(e) => { - fatal!("Unable to build project [{}]: {}", proj.project_path.clone().display(), e); - }, + fatal!( + "Unable to build project [{}]: {}", + proj.project_path.clone().display(), + e + ); + } } } } @@ -986,8 +1004,12 @@ impl Editor { components.retain(|component| { self.component_registry .id_for_component(component.as_ref()) - .and_then(|id| self.component_registry.get_descriptor_by_numeric_id(id)) - .map(|desc| desc.fqtn != "dropbear_engine::entity::EntityTransform") + .and_then(|id| { + self.component_registry.get_descriptor_by_numeric_id(id) + }) + .map(|desc| { + desc.fqtn != "dropbear_engine::entity::EntityTransform" + }) .unwrap_or(true) }); let s_entity = SceneEntity { @@ -1001,7 +1023,6 @@ impl Editor { } else { warn!("Unable to copy entity: None selected"); } - } if ui.button("Paste").clicked() { @@ -1037,7 +1058,8 @@ impl Editor { self.dock_state.push_to_focused_leaf(EditorTab::Viewport); } if ui_window.button("Open Error Console").clicked() { - self.dock_state.push_to_focused_leaf(EditorTab::ErrorConsole); + self.dock_state + .push_to_focused_leaf(EditorTab::ErrorConsole); } if ui_window.button("Open Debug Console").clicked() { self.dock_state.push_to_focused_leaf(EditorTab::Console); @@ -1045,11 +1067,14 @@ impl Editor { if self.plugin_registry.plugins.len() == 0 { ui_window.label( egui::RichText::new("No plugins") - .color(ui_window.visuals().weak_text_color()) + .color(ui_window.visuals().weak_text_color()), ); } for (i, (_, plugin)) in self.plugin_registry.plugins.iter().enumerate() { - if ui_window.button(format!("Open {}", plugin.display_name())).clicked() { + if ui_window + .button(format!("Open {}", plugin.display_name())) + .clicked() + { self.dock_state.push_to_focused_leaf(EditorTab::Plugin(i)); } } @@ -1058,15 +1083,13 @@ impl Editor { ui.menu_button("Help", |ui| { if ui.button("Show AppData folder").clicked() { match app_dirs2::app_root(app_dirs2::AppDataType::UserData, &APP_INFO) { - Ok(val) => { - match open::that(&val) { - Ok(()) => info!("Opened logs folder"), - Err(e) => fatal!("Unable to open {}: {}", val.display(), e) - } + Ok(val) => match open::that(&val) { + Ok(()) => info!("Opened logs folder"), + Err(e) => fatal!("Unable to open {}: {}", val.display(), e), }, Err(e) => { fatal!("Unable to show logs: {}", e); - }, + } }; } @@ -1096,7 +1119,7 @@ impl Editor { WindowAttributes::default() .with_title("About eucalyptus editor") .with_inner_size(PhysicalSize::new(500, 300)) - .with_resizable(false) + .with_resizable(false), ) .add_scene_with_input(about, "about") .set_initial_scene("about") @@ -1189,7 +1212,7 @@ impl Editor { ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { ui.colored_label(text_color, format!("Viewing through {label}")); }); - }); + }); let editor_ptr = self as *mut Editor; @@ -1340,7 +1363,8 @@ impl Editor { if let Some(active_camera_entity) = *active_camera && let Ok(component) = self .world - .query_one::<&CameraComponent>(active_camera_entity).get() + .query_one::<&CameraComponent>(active_camera_entity) + .get() { return matches!(component.camera_type, CameraType::Debug); } @@ -1350,10 +1374,16 @@ impl Editor { /// Loads all the wgpu resources such as renderer. /// /// **Note**: To be ran AFTER [`Editor::load_project_config`] - pub fn load_wgpu_nerdy_stuff<'a>(&mut self, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + pub fn load_wgpu_nerdy_stuff<'a>( + &mut self, + graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { self.main_render_pipeline = Some(MainRenderPipeline::new(graphics.clone())); self.light_cube_pipeline = Some(LightCubePipeline::new(graphics.clone())); - self.shader_globals = Some(GlobalsUniform::new(graphics.clone(), Some("editor shader globals"))); + self.shader_globals = Some(GlobalsUniform::new( + graphics.clone(), + Some("editor shader globals"), + )); self.collider_wireframe_pipeline = Some(ColliderWireframePipeline::new(graphics.clone())); self.mipmapper = None; @@ -1366,7 +1396,7 @@ impl Editor { &graphics.queue, DEFAULT_SKY_TEXTURE, 1080, - Some("sky texture") + Some("sky texture"), ); match sky_texture { @@ -1393,12 +1423,16 @@ impl Editor { cfg.project_path.clone() }; - let current_scene = self.current_scene_name.clone().ok_or_else(|| { - anyhow::anyhow!("No current scene loaded; cannot launch play mode") - })?; + let current_scene = self + .current_scene_name + .clone() + .ok_or_else(|| anyhow::anyhow!("No current scene loaded; cannot launch play mode"))?; - log::info!("Launching play mode: {} play --project {:?}", - current_exe.display(), project_dir); + log::info!( + "Launching play mode: {} play --project {:?}", + current_exe.display(), + project_dir + ); let mut child = Command::new(¤t_exe) .arg("play") @@ -31,7 +31,10 @@ impl<'a> EditorTabViewer<'a> { } else { "View Through This Camera" }; - if ui.add_enabled(!is_active, egui::Button::new(label)).clicked() { + if ui + .add_enabled(!is_active, egui::Button::new(label)) + .clicked() + { let mut active_camera = self.active_camera.lock(); *active_camera = Some(inspect_entity); } @@ -39,8 +42,12 @@ impl<'a> EditorTabViewer<'a> { ui.separator(); } - self.component_registry - .inspect_components(self.world, inspect_entity, ui, self.graphics.clone()); + self.component_registry.inspect_components( + self.world, + inspect_entity, + ui, + self.graphics.clone(), + ); } } else if !local_scene_settings { ui.label("No entity selected, therefore no info to provide. Go on, what are you waiting for? Click an entity!"); @@ -51,4 +58,4 @@ impl<'a> EditorTabViewer<'a> { self.scene_settings(&mut cfg, ui); } } -} +} @@ -1,30 +1,33 @@ -use crossbeam_channel::unbounded; -use dropbear_engine::animation::AnimationComponent; -use dropbear_engine::buffer::ResizableBuffer; -use glam::{DMat4, Mat4}; -use wgpu::util::DeviceExt; -use std::collections::HashMap; -use std::sync::Arc; -use std::{fs, path::{Path, PathBuf}}; use super::*; use crate::signal::SignalController; use crate::spawn::PendingSpawnController; +use crossbeam_channel::unbounded; +use dropbear_engine::animation::AnimationComponent; use dropbear_engine::asset::{ASSET_REGISTRY, Handle}; +use dropbear_engine::buffer::ResizableBuffer; use dropbear_engine::graphics::{CommandEncoder, InstanceRaw}; use dropbear_engine::{ entity::{EntityTransform, MeshRenderer, Transform}, - lighting::{Light, MAX_LIGHTS}, + lighting::Light, model::{DrawLight, DrawModel}, scene::{Scene, SceneCommand}, }; -use eucalyptus_core::states::{Label, WorldLoadingStatus}; -use log; -use parking_lot::Mutex; -use winit::{event::WindowEvent, event_loop::ActiveEventLoop, keyboard::KeyCode}; use eucalyptus_core::physics::collider::ColliderGroup; use eucalyptus_core::physics::collider::ColliderShapeKey; use eucalyptus_core::physics::collider::shader::{ColliderInstanceRaw, create_wireframe_geometry}; use eucalyptus_core::properties::CustomProperties; +use eucalyptus_core::states::{Label, WorldLoadingStatus}; +use glam::{DMat4, Mat4}; +use log; +use parking_lot::Mutex; +use std::collections::HashMap; +use std::sync::Arc; +use std::{ + fs, + path::{Path, PathBuf}, +}; +use wgpu::util::DeviceExt; +use winit::{event::WindowEvent, event_loop::ActiveEventLoop, keyboard::KeyCode}; impl Scene for Editor { fn load(&mut self, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { @@ -85,9 +88,18 @@ impl Scene for Editor { self.is_world_loaded.mark_scene_loaded(); } - fn physics_update(&mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) {} + fn physics_update( + &mut self, + _dt: f32, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { + } - fn update(&mut self, dt: f32, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn update( + &mut self, + dt: f32, + graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { if let Some(rx) = &self.play_mode_exit_rx { if rx.try_recv().is_ok() { log::info!("Play mode process has exited, returning to editing mode"); @@ -122,8 +134,12 @@ impl Scene for Editor { } } - self.component_registry - .update_components(self.world.as_mut(), &mut self.physics_state, dt, graphics.clone()); + self.component_registry.update_components( + self.world.as_mut(), + &mut self.physics_state, + dt, + graphics.clone(), + ); if !self.is_world_loaded.is_fully_loaded() { log::debug!("Scene is not fully loaded, initialising..."); @@ -137,10 +153,7 @@ impl Scene for Editor { return; } - match self.check_up( - graphics.clone(), - graphics.future_queue.clone(), - ) { + match self.check_up(graphics.clone(), graphics.future_queue.clone()) { Ok(_) => {} Err(e) => { fatal!("{}", e); @@ -167,7 +180,7 @@ impl Scene for Editor { env!("GIT_HASH") ) }; - + graphics.window.set_title(&title); } @@ -184,10 +197,7 @@ impl Scene for Editor { // basic futurequeue spawn queue management. let mut completed = Vec::new(); for (i, handle) in self.light_spawn_queue.iter().enumerate() { - if let Some(l) = graphics - .future_queue - .exchange_owned_as::<Light>(handle) - { + if let Some(l) = graphics.future_queue.exchange_owned_as::<Light>(handle) { let label_component = Label::from(l.label.clone()); self.world.spawn(( label_component, @@ -227,7 +237,8 @@ impl Scene for Editor { if let Some(active_camera) = *active_cam && let Ok((camera, _)) = self .world - .query_one::<(&mut Camera, &CameraComponent)>(active_camera).get() + .query_one::<(&mut Camera, &CameraComponent)>(active_camera) + .get() { for key in &self.input_state.pressed_keys { match key { @@ -244,7 +255,6 @@ impl Scene for Editor { } let _ = self.run_signal(graphics.clone()); - if let Some(e) = self.previously_selected_entity && let Ok(entity) = self.world.query_one::<&mut MeshRenderer>(e).get() @@ -274,14 +284,14 @@ impl Scene for Editor { } } - - if let Some(l) = &mut self.light_cube_pipeline { l.update(graphics.clone(), &self.world); } { - self.nerd_stats.write().record_stats(dt, self.world.len() as u32); + self.nerd_stats + .write() + .record_stats(dt, self.world.len() as u32); } self.input_state.window = self.window.clone(); @@ -296,13 +306,18 @@ impl Scene for Editor { let mut encoder = CommandEncoder::new(graphics.clone(), Some("runtime viewport encoder")); - let active_camera = {self.active_camera.lock().as_ref().cloned()}; + let active_camera = { self.active_camera.lock().as_ref().cloned() }; let Some(active_camera) = active_camera else { return; }; log_once::debug_once!("Active camera found: {:?}", active_camera); - let q = self.world.query_one::<&Camera>(active_camera).get().ok().cloned(); + let q = self + .world + .query_one::<&Camera>(active_camera) + .get() + .ok() + .cloned(); let Some(camera) = q else { return; @@ -359,14 +374,14 @@ impl Scene for Editor { lights }; - if let Some(globals) = &mut self.shader_globals { - let enabled_count = lights - .iter() - .filter(|light| light.component.enabled) - .count() as u32; - globals.set_num_lights(enabled_count); - globals.write(&graphics.queue); - } + if let Some(globals) = &mut self.shader_globals { + let enabled_count = lights + .iter() + .filter(|light| light.component.enabled) + .count() as u32; + globals.set_num_lights(enabled_count); + globals.write(&graphics.queue); + } let mut static_batches: HashMap<u64, Vec<InstanceRaw>> = HashMap::new(); let mut animated_instances: Vec<(u64, InstanceRaw, wgpu::Buffer)> = Vec::new(); @@ -399,46 +414,42 @@ impl Scene for Editor { continue; }; - let instance_buffer = self - .instance_buffer_cache - .entry(handle) - .or_insert_with(|| { - ResizableBuffer::new( - &graphics.device, - instances.len().max(1), - wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - "Runtime Instance Buffer", - ) - }); + let instance_buffer = self.instance_buffer_cache.entry(handle).or_insert_with(|| { + ResizableBuffer::new( + &graphics.device, + instances.len().max(1), + wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + "Runtime Instance Buffer", + ) + }); instance_buffer.write(&graphics.device, &graphics.queue, &instances); prepared_models.push((model, handle, instances.len() as u32)); } { - let mut render_pass = encoder - .begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("light cube render pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), - depth_slice: None, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &graphics.depth_texture.view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }), - stencil_ops: None, + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("light cube render pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: hdr.view(), + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &graphics.depth_texture.view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, }), - occlusion_query_set: None, - timestamp_writes: None, - }); + stencil_ops: None, + }), + occlusion_query_set: None, + timestamp_writes: None, + }); if let Some(light_pipeline) = &self.light_cube_pipeline { render_pass.set_pipeline(light_pipeline.pipeline()); for light in &lights { @@ -455,31 +466,31 @@ impl Scene for Editor { continue; }; - render_pass.draw_light_model( - model, - &camera.bind_group, - &light.bind_group, - ); + render_pass.draw_light_model(model, &camera.bind_group, &light.bind_group); } } } if self.default_skinning_bind_group.is_none() { let identity = [Mat4::IDENTITY.to_cols_array_2d()]; - let buffer = graphics.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("default skinning buffer"), - contents: bytemuck::cast_slice(&identity), - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - }); + let buffer = graphics + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("default skinning buffer"), + contents: bytemuck::cast_slice(&identity), + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + }); - let bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("default skinning bind group"), - layout: &graphics.layouts.skinning_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: buffer.as_entire_binding(), - }], - }); + let bind_group = graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("default skinning bind group"), + layout: &graphics.layouts.skinning_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: buffer.as_entire_binding(), + }], + }); self.default_skinning_buffer = Some(buffer); self.default_skinning_bind_group = Some(bind_group); @@ -497,68 +508,67 @@ impl Scene for Editor { .shader_globals .as_ref() .expect("Shader globals not initialised"); - let globals_camera_bind_group = graphics.device.create_bind_group( - &wgpu::BindGroupDescriptor { - label: Some("scene globals+camera bind group"), - layout: &graphics.layouts.scene_globals_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: globals.buffer.buffer().as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: camera.buffer().as_entire_binding(), - }, - ], - }, - ); - let light_skin_bind_group = graphics.device.create_bind_group( - &wgpu::BindGroupDescriptor { - label: Some("scene light+skin bind group"), - layout: &graphics.layouts.scene_light_skin_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: lcp.light_buffer().as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: default_skinning_buffer.as_entire_binding(), - }, - ], - }, - ); + let globals_camera_bind_group = + graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("scene globals+camera bind group"), + layout: &graphics.layouts.scene_globals_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: globals.buffer.buffer().as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: camera.buffer().as_entire_binding(), + }, + ], + }); + let light_skin_bind_group = + graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("scene light+skin bind group"), + layout: &graphics.layouts.scene_light_skin_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: lcp.light_buffer().as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: default_skinning_buffer.as_entire_binding(), + }, + ], + }); - let mut render_pass = encoder - .begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("model render pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), - depth_slice: None, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &graphics.depth_texture.view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }), - stencil_ops: None, + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("model render pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: hdr.view(), + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &graphics.depth_texture.view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, }), - occlusion_query_set: None, - timestamp_writes: None, - }); + stencil_ops: None, + }), + occlusion_query_set: None, + timestamp_writes: None, + }); render_pass.set_pipeline(pipeline.pipeline()); if let Some(instance_buffer) = self.instance_buffer_cache.get(&handle) { - render_pass.set_vertex_buffer( - 1, - instance_buffer.slice(instance_count as usize), - ); + render_pass + .set_vertex_buffer(1, instance_buffer.slice(instance_count as usize)); } else { continue; } @@ -576,22 +586,23 @@ impl Scene for Editor { .shader_globals .as_ref() .expect("Shader globals not initialised"); - let globals_camera_bind_group = graphics.device.create_bind_group( - &wgpu::BindGroupDescriptor { - label: Some("scene globals+camera bind group"), - layout: &graphics.layouts.scene_globals_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: globals.buffer.buffer().as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: camera.buffer().as_entire_binding(), - }, - ], - }, - ); + let globals_camera_bind_group = + graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("scene globals+camera bind group"), + layout: &graphics.layouts.scene_globals_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: globals.buffer.buffer().as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: camera.buffer().as_entire_binding(), + }, + ], + }); for (handle, instance, skin_buffer) in animated_instances { let Some(model) = registry.get_model(Handle::new(handle)) else { @@ -599,41 +610,38 @@ impl Scene for Editor { continue; }; - let instance_buffer = self - .animated_instance_buffer - .get_or_insert_with(|| { - ResizableBuffer::new( - &graphics.device, - 1, - wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - "Runtime Animated Instance Buffer", - ) - }); + let instance_buffer = self.animated_instance_buffer.get_or_insert_with(|| { + ResizableBuffer::new( + &graphics.device, + 1, + wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + "Runtime Animated Instance Buffer", + ) + }); instance_buffer.write(&graphics.device, &graphics.queue, &[instance]); - let mut render_pass = encoder - .begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("model render pass (animated)"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), - depth_slice: None, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &graphics.depth_texture.view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }), - stencil_ops: None, + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("model render pass (animated)"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: hdr.view(), + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &graphics.depth_texture.view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, }), - occlusion_query_set: None, - timestamp_writes: None, - }); + stencil_ops: None, + }), + occlusion_query_set: None, + timestamp_writes: None, + }); render_pass.set_pipeline(pipeline.pipeline()); render_pass.set_vertex_buffer(1, instance_buffer.slice(1)); @@ -679,39 +687,44 @@ impl Scene for Editor { }) .unwrap_or(false); - log_once::debug_once!("show_hitboxes = {}, current_scene_name = {:?}", show_hitboxes, self.current_scene_name); + log_once::debug_once!( + "show_hitboxes = {}, current_scene_name = {:?}", + show_hitboxes, + self.current_scene_name + ); if show_hitboxes { if let Some(collider_pipeline) = &self.collider_wireframe_pipeline { - let mut render_pass = encoder - .begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("collider wireframe render pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), - depth_slice: None, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &graphics.depth_texture.view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }), - stencil_ops: None, + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("collider wireframe render pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: hdr.view(), + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &graphics.depth_texture.view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, }), - occlusion_query_set: None, - timestamp_writes: None, - }); - + stencil_ops: None, + }), + occlusion_query_set: None, + timestamp_writes: None, + }); + render_pass.set_pipeline(&collider_pipeline.pipeline); render_pass.set_bind_group(0, &camera.bind_group, &[]); - let mut instances_by_shape: HashMap<ColliderShapeKey, Vec<ColliderInstanceRaw>> = - HashMap::new(); + let mut instances_by_shape: HashMap< + ColliderShapeKey, + Vec<ColliderInstanceRaw>, + > = HashMap::new(); let mut q = self.world.query::<(&EntityTransform, &ColliderGroup)>(); let mut entity_count = 0; @@ -734,22 +747,24 @@ impl Scene for Editor { let final_matrix = entity_matrix * offset_matrix; - let color = [0.0, 1.0, 0.0, 1.0]; let instance = ColliderInstanceRaw::from_matrix(final_matrix, color); let key = ColliderShapeKey::from(&collider.shape); instances_by_shape.entry(key).or_default().push(instance); - self.collider_wireframe_geometry_cache.entry(key).or_insert_with(|| { - create_wireframe_geometry( - graphics.clone(), - &collider.shape, - ) - }); + self.collider_wireframe_geometry_cache + .entry(key) + .or_insert_with(|| { + create_wireframe_geometry(graphics.clone(), &collider.shape) + }); } } - log_once::debug_once!("Collider wireframe: {} entities with colliders, {} total colliders", entity_count, collider_count); + log_once::debug_once!( + "Collider wireframe: {} entities with colliders, {} total colliders", + entity_count, + collider_count + ); if !instances_by_shape.is_empty() { let total_instances: usize = @@ -764,47 +779,39 @@ impl Scene for Editor { draws.push((key, start, count)); } - let instance_buffer = self.collider_instance_buffer.get_or_insert_with(|| { - ResizableBuffer::new( - &graphics.device, - all_instances.len().max(10), - wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - "Collider Instance Buffer", - ) - }); - instance_buffer.write( - &graphics.device, - &graphics.queue, - &all_instances, - ); + let instance_buffer = + self.collider_instance_buffer.get_or_insert_with(|| { + ResizableBuffer::new( + &graphics.device, + all_instances.len().max(10), + wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + "Collider Instance Buffer", + ) + }); + instance_buffer.write(&graphics.device, &graphics.queue, &all_instances); for (key, start, count) in draws { - let Some(geometry) = self.collider_wireframe_geometry_cache.get(&key) else { + let Some(geometry) = self.collider_wireframe_geometry_cache.get(&key) + else { continue; }; - let start_bytes = - (start * std::mem::size_of::<ColliderInstanceRaw>()) as wgpu::BufferAddress; - let end_bytes = - ((start + count) * std::mem::size_of::<ColliderInstanceRaw>()) as wgpu::BufferAddress; + let start_bytes = (start * std::mem::size_of::<ColliderInstanceRaw>()) + as wgpu::BufferAddress; + let end_bytes = ((start + count) + * std::mem::size_of::<ColliderInstanceRaw>()) + as wgpu::BufferAddress; render_pass.set_vertex_buffer( 1, instance_buffer.buffer().slice(start_bytes..end_bytes), ); - render_pass.set_vertex_buffer( - 0, - geometry.vertex_buffer.slice(..), - ); + render_pass.set_vertex_buffer(0, geometry.vertex_buffer.slice(..)); render_pass.set_index_buffer( geometry.index_buffer.slice(..), wgpu::IndexFormat::Uint16, ); - render_pass.draw_indexed( - 0..geometry.index_count, - 0, - 0..count as u32, - ); + render_pass.draw_indexed(0..geometry.index_count, 0, 0..count as u32); } } } @@ -855,10 +862,10 @@ impl Scene for Editor { WindowEvent::DroppedFile(path) => { log::debug!("Dropped file: {}", path.display()); self.handle_file_drop(path); - }, + } WindowEvent::HoveredFile(path_buf) => { log_once::debug_once!("Hovering file: {}", path_buf.display()); - }, + } WindowEvent::HoveredFileCancelled => { log_once::debug_once!("Hover cancelled"); } @@ -971,8 +978,8 @@ impl Editor { self.size = graphics.viewport_texture.size; self.texture_id = Some(*graphics.texture_id.clone()); self.window = Some(graphics.window.clone()); - + self.show_ui(&graphics.get_egui_context(), graphics.clone()); eucalyptus_core::logging::render(&graphics.get_egui_context()); } -} +} @@ -10,13 +10,13 @@ impl<'a> EditorTabViewer<'a> { let editor = unsafe { &mut *self.editor }; let current_scene_name = editor.current_scene_name.clone(); - + if let Some(scene_name) = current_scene_name { let mut scenes = SCENES.write(); if let Some(scene) = scenes.iter_mut().find(|s| s.scene_name == scene_name) { ui.label(format!("Scene: {}", scene.scene_name)); ui.separator(); - + let mut preloaded = scene.settings.preloaded; if ui.checkbox(&mut preloaded, "Preload Assets").changed() { scene.settings.preloaded = preloaded; @@ -35,4 +35,4 @@ impl<'a> EditorTabViewer<'a> { ui.label("No scene currently loaded"); } } -} +} @@ -1,24 +1,24 @@ //! The scene for a window that opens up settings related to the eucalyptus-editor. use app_dirs2::AppDataType; +use dropbear_engine::input::{Controller, Keyboard, Mouse}; +use dropbear_engine::scene::{Scene, SceneCommand}; use egui::{CentralPanel, Id, Slider, SliderClamping}; use egui_dock::DockState; use egui_ltreeview::{Action, NodeBuilder}; +use eucalyptus_core::input::InputState; +use eucalyptus_core::states::EditorTab; use eucalyptus_core::utils::option::HistoricalOption; +use eucalyptus_core::{APP_INFO, warn}; use gilrs::{Button, GamepadId}; use hecs::spin::Lazy; use parking_lot::RwLock; +use serde::{Deserialize, Serialize}; use winit::dpi::PhysicalPosition; use winit::event::MouseButton; use winit::event_loop::ActiveEventLoop; use winit::keyboard::KeyCode; -use winit::window::{WindowId}; -use dropbear_engine::input::{Controller, Keyboard, Mouse}; -use dropbear_engine::scene::{Scene, SceneCommand}; -use eucalyptus_core::input::InputState; -use serde::{Deserialize, Serialize}; -use eucalyptus_core::{warn, APP_INFO}; -use eucalyptus_core::states::{EditorTab}; +use winit::window::WindowId; pub static EDITOR_SETTINGS: Lazy<RwLock<EditorSettings>> = Lazy::new(|| RwLock::new(EditorSettings::new())); @@ -60,7 +60,10 @@ impl EditorSettings { let app_data = app_dirs2::app_root(AppDataType::UserData, &APP_INFO)?; let serialized = ron::ser::to_string_pretty(&self, ron::ser::PrettyConfig::default())?; std::fs::write(app_data.join("editor.eucc"), serialized)?; - log::debug!("Saved editor config to {}", app_data.join("editor.eucc").display()); + log::debug!( + "Saved editor config to {}", + app_data.join("editor.eucc").display() + ); Ok(()) } @@ -128,9 +131,18 @@ impl Scene for EditorSettingsWindow { self.window = Some(graphics.window.id()); } - fn physics_update(&mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) {} + fn physics_update( + &mut self, + _dt: f32, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { + } - fn update(&mut self, _dt: f32, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn update( + &mut self, + _dt: f32, + graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { CentralPanel::default().show(&graphics.get_egui_context(), |ui| { let mut editor = EDITOR_SETTINGS.write(); @@ -142,13 +154,12 @@ impl Scene for EditorSettingsWindow { egui::ScrollArea::vertical() .auto_shrink([false; 2]) .show(ui, |ui| { - let (_resp, action) = egui_ltreeview::TreeView::new(Id::from("editor_settings")) - .show(ui, |builder| { - builder.node( - NodeBuilder::leaf("Performance") - .label("Performance") - ); - }); + let (_resp, action) = egui_ltreeview::TreeView::new(Id::from( + "editor_settings", + )) + .show(ui, |builder| { + builder.node(NodeBuilder::leaf("Performance").label("Performance")); + }); for a in action { match a { @@ -157,11 +168,13 @@ impl Scene for EditorSettingsWindow { if let Some(s) = selected { match s { "Performance" => { - self.current_leaf = EditorSettingsCurrentLeaf::Performance; + self.current_leaf = + EditorSettingsCurrentLeaf::Performance; } _ => { - self.current_leaf = EditorSettingsCurrentLeaf::None; - }, + self.current_leaf = + EditorSettingsCurrentLeaf::None; + } } } } @@ -178,34 +191,37 @@ impl Scene for EditorSettingsWindow { egui::CentralPanel::default().show_inside(ui, |ui| { egui::ScrollArea::vertical() .auto_shrink([false; 2]) - .show(ui, |ui| { - match self.current_leaf { - EditorSettingsCurrentLeaf::Performance => { - ui.heading("Performance Settings"); - ui.separator(); - - ui.label("Target FPS:"); - ui.horizontal(|ui| { - let mut local_set_max_fps = editor.target_fps.is_some(); - - if ui.checkbox(&mut local_set_max_fps, "Set max frames-per-second (FPS)").changed() { - if local_set_max_fps { - editor.target_fps.enable_or(120); - } else { - editor.target_fps.disable(); - } + .show(ui, |ui| match self.current_leaf { + EditorSettingsCurrentLeaf::Performance => { + ui.heading("Performance Settings"); + ui.separator(); + + ui.label("Target FPS:"); + ui.horizontal(|ui| { + let mut local_set_max_fps = editor.target_fps.is_some(); + + if ui + .checkbox( + &mut local_set_max_fps, + "Set max frames-per-second (FPS)", + ) + .changed() + { + if local_set_max_fps { + editor.target_fps.enable_or(120); + } else { + editor.target_fps.disable(); } + } - if let Some(v) = editor.target_fps.get_mut() { - ui.add( - Slider::new(v, 1..=1000) - .clamping(SliderClamping::Never) - ); - } - }); - } - _ => {} + if let Some(v) = editor.target_fps.get_mut() { + ui.add( + Slider::new(v, 1..=1000).clamping(SliderClamping::Never), + ); + } + }); } + _ => {} }); }); }); @@ -213,7 +229,10 @@ impl Scene for EditorSettingsWindow { self.window = Some(graphics.window.id()); } - fn render<'a>(&mut self, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn render<'a>( + &mut self, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { } fn exit(&mut self, _event_loop: &ActiveEventLoop) { @@ -296,4 +315,4 @@ impl Controller for EditorSettingsWindow { self.input_state.left_stick_position.remove(&id); self.input_state.right_stick_position.remove(&id); } -} +} @@ -1,19 +1,19 @@ //! The scene for a window that opens up settings related to the project, "Play Mode" runtime and redback-runtime. +use dropbear_engine::input::{Controller, Keyboard, Mouse}; +use dropbear_engine::scene::{Scene, SceneCommand}; use egui::{CentralPanel, Color32, Id, RichText, Slider, SliderClamping}; use egui_ltreeview::{Action, NodeBuilder}; +use eucalyptus_core::input::InputState; +use eucalyptus_core::states::PROJECT; +use eucalyptus_core::warn; use gilrs::{Button, GamepadId}; use semver::Version; use winit::dpi::PhysicalPosition; use winit::event::MouseButton; use winit::event_loop::ActiveEventLoop; use winit::keyboard::KeyCode; -use winit::window::{WindowId}; -use dropbear_engine::input::{Controller, Keyboard, Mouse}; -use dropbear_engine::scene::{Scene, SceneCommand}; -use eucalyptus_core::input::InputState; -use eucalyptus_core::states::PROJECT; -use eucalyptus_core::warn; +use winit::window::WindowId; #[derive(Default)] pub enum ProjectSettingsLeaf { @@ -48,9 +48,18 @@ impl Scene for ProjectSettingsWindow { self.window = Some(graphics.window.id()); } - fn physics_update(&mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) {} + fn physics_update( + &mut self, + _dt: f32, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { + } - fn update(&mut self, _dt: f32, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn update( + &mut self, + _dt: f32, + graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { CentralPanel::default().show(&graphics.get_egui_context(), |ui| { let mut project = PROJECT.write(); @@ -62,31 +71,23 @@ impl Scene for ProjectSettingsWindow { egui::ScrollArea::vertical() .auto_shrink([false; 2]) .show(ui, |ui| { - let (_resp, action) = egui_ltreeview::TreeView::new(Id::from("project_settings")) - .show(ui, |builder| { - builder.node( - NodeBuilder::dir("Publishing") - .label("Publishing") - ); - - { - builder.node( - NodeBuilder::leaf("Versioning") - .label("Versioning") - ); - builder.node( - NodeBuilder::leaf("Authoring") - .label("Authoring") - ); - } + let (_resp, action) = egui_ltreeview::TreeView::new(Id::from( + "project_settings", + )) + .show(ui, |builder| { + builder.node(NodeBuilder::dir("Publishing").label("Publishing")); + + { + builder + .node(NodeBuilder::leaf("Versioning").label("Versioning")); + builder.node(NodeBuilder::leaf("Authoring").label("Authoring")); + } - builder.close_dir(); + builder.close_dir(); - builder.node( - NodeBuilder::leaf("Runtime") - .label("Runtime Settings") - ); - }); + builder + .node(NodeBuilder::leaf("Runtime").label("Runtime Settings")); + }); for a in action { match a { @@ -95,17 +96,20 @@ impl Scene for ProjectSettingsWindow { if let Some(s) = selected { match s { "Versioning" => { - self.current_leaf = ProjectSettingsLeaf::Versioning; + self.current_leaf = + ProjectSettingsLeaf::Versioning; } "Authoring" => { - self.current_leaf = ProjectSettingsLeaf::Authoring; + self.current_leaf = + ProjectSettingsLeaf::Authoring; } "Runtime" => { - self.current_leaf = ProjectSettingsLeaf::Runtime; + self.current_leaf = + ProjectSettingsLeaf::Runtime; } _ => { self.current_leaf = ProjectSettingsLeaf::None; - }, + } } } } @@ -122,60 +126,70 @@ impl Scene for ProjectSettingsWindow { egui::CentralPanel::default().show_inside(ui, |ui| { egui::ScrollArea::vertical() .auto_shrink([false; 2]) - .show(ui, |ui| { - match self.current_leaf { - ProjectSettingsLeaf::Versioning => { - ui.heading("Versioning Settings"); - ui.separator(); - - let version = &mut project.project_version; - - let old = version.clone(); - ui.label("Version (in semantic form)"); - let resp = ui.text_edit_singleline(version); - - if resp.lost_focus() { - if let Err(e) = Version::parse(version.as_str()) { - ui.label(RichText::new(format!("Semver validation for text [{}] failed: {}", version, e)).color(Color32::from_rgb(255, 0, 0))); - *version = old; - } else { - log::debug!("Semver parsing was fine: {}", version); - } + .show(ui, |ui| match self.current_leaf { + ProjectSettingsLeaf::Versioning => { + ui.heading("Versioning Settings"); + ui.separator(); + + let version = &mut project.project_version; + + let old = version.clone(); + ui.label("Version (in semantic form)"); + let resp = ui.text_edit_singleline(version); + + if resp.lost_focus() { + if let Err(e) = Version::parse(version.as_str()) { + ui.label( + RichText::new(format!( + "Semver validation for text [{}] failed: {}", + version, e + )) + .color(Color32::from_rgb(255, 0, 0)), + ); + *version = old; + } else { + log::debug!("Semver parsing was fine: {}", version); } } - ProjectSettingsLeaf::Authoring => { - ui.heading("Authoring Settings"); - ui.separator(); + } + ProjectSettingsLeaf::Authoring => { + ui.heading("Authoring Settings"); + ui.separator(); - ui.label("Authors"); - ui.text_edit_singleline(&mut project.authors.developer); - } - ProjectSettingsLeaf::Runtime => { - ui.heading("Runtime Settings"); - ui.separator(); - - ui.label("Target FPS:"); - ui.horizontal(|ui| { - let mut local_set_max_fps = project.runtime_settings.target_fps.is_some(); - - if ui.checkbox(&mut local_set_max_fps, "Set max frames-per-second (FPS)").changed() { - if local_set_max_fps { - project.runtime_settings.target_fps.enable_or(120); - } else { - project.runtime_settings.target_fps.disable(); - } + ui.label("Authors"); + ui.text_edit_singleline(&mut project.authors.developer); + } + ProjectSettingsLeaf::Runtime => { + ui.heading("Runtime Settings"); + ui.separator(); + + ui.label("Target FPS:"); + ui.horizontal(|ui| { + let mut local_set_max_fps = + project.runtime_settings.target_fps.is_some(); + + if ui + .checkbox( + &mut local_set_max_fps, + "Set max frames-per-second (FPS)", + ) + .changed() + { + if local_set_max_fps { + project.runtime_settings.target_fps.enable_or(120); + } else { + project.runtime_settings.target_fps.disable(); } + } - if let Some(v) = project.runtime_settings.target_fps.get_mut() { - ui.add( - Slider::new(v, 1..=1000) - .clamping(SliderClamping::Never) - ); - } - }); - } - _ => {} + if let Some(v) = project.runtime_settings.target_fps.get_mut() { + ui.add( + Slider::new(v, 1..=1000).clamping(SliderClamping::Never), + ); + } + }); } + _ => {} }); }); }); @@ -183,7 +197,10 @@ impl Scene for ProjectSettingsWindow { self.window = Some(graphics.window.id()); } - fn render<'a>(&mut self, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn render<'a>( + &mut self, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { } fn exit(&mut self, _event_loop: &ActiveEventLoop) { @@ -268,4 +285,4 @@ impl Controller for ProjectSettingsWindow { self.input_state.left_stick_position.remove(&id); self.input_state.right_stick_position.remove(&id); } -} +} @@ -1,16 +1,16 @@ -use transform_gizmo_egui::{GizmoConfig, GizmoExt, GizmoOrientation}; +use crate::editor::{EditorTabViewer, Signal, TABS_GLOBAL, UndoableAction}; use dropbear_engine::camera::Camera; use dropbear_engine::entity::{EntityTransform, Transform}; -use dropbear_engine::lighting::{Light}; +use dropbear_engine::lighting::Light; use eucalyptus_core::camera::CameraComponent; use eucalyptus_core::utils::ViewportMode; -use crate::editor::{EditorTabViewer, Signal, TABS_GLOBAL, UndoableAction}; use glam::DVec3; +use transform_gizmo_egui::{GizmoConfig, GizmoExt, GizmoOrientation}; impl<'a> EditorTabViewer<'a> { pub(crate) fn viewport_tab(&mut self, ui: &mut egui::Ui) { let mut cfg = TABS_GLOBAL.lock(); - + log_once::debug_once!("Viewport focused"); let available_rect = ui.available_rect_before_wrap(); @@ -68,7 +68,8 @@ impl<'a> EditorTabViewer<'a> { let camera_data = { if let Ok((cam, _comp)) = self .world - .query_one::<(&Camera, &CameraComponent)>(active_camera).get() + .query_one::<(&Camera, &CameraComponent)>(active_camera) + .get() { Some(cam.clone()) } else { @@ -96,7 +97,10 @@ impl<'a> EditorTabViewer<'a> { let mut handled = false; let mut updated_light_transform: Option<Transform> = None; - if let Ok(entity_transform) = self.world.query_one::<&mut EntityTransform>(*entity_id).get() + if let Ok(entity_transform) = self + .world + .query_one::<&mut EntityTransform>(*entity_id) + .get() { let was_focused = cfg.is_focused; cfg.is_focused = self.gizmo.is_focused(); @@ -113,8 +117,7 @@ impl<'a> EditorTabViewer<'a> { synced.position, ); - if let Some((_result, new_transforms)) = - self.gizmo.interact(ui, &[gizmo_transform]) + if let Some((_result, new_transforms)) = self.gizmo.interact(ui, &[gizmo_transform]) && let Some(new_transform) = new_transforms.first() { let new_synced_pos: glam::DVec3 = new_transform.translation.into(); @@ -126,9 +129,21 @@ impl<'a> EditorTabViewer<'a> { let local = *entity_transform.local(); let safe_local_scale = glam::DVec3::new( - if local.scale.x.abs() < 1e-6 { 1.0 } else { local.scale.x }, - if local.scale.y.abs() < 1e-6 { 1.0 } else { local.scale.y }, - if local.scale.z.abs() < 1e-6 { 1.0 } else { local.scale.z }, + if local.scale.x.abs() < 1e-6 { + 1.0 + } else { + local.scale.x + }, + if local.scale.y.abs() < 1e-6 { + 1.0 + } else { + local.scale.y + }, + if local.scale.z.abs() < 1e-6 { + 1.0 + } else { + local.scale.z + }, ); let new_world_scale = new_synced_scale / safe_local_scale; @@ -150,9 +165,21 @@ impl<'a> EditorTabViewer<'a> { let world_pos = world_transform.position; let safe_world_scale = glam::DVec3::new( - if world_scale.x.abs() < 1e-6 { 1.0 } else { world_scale.x }, - if world_scale.y.abs() < 1e-6 { 1.0 } else { world_scale.y }, - if world_scale.z.abs() < 1e-6 { 1.0 } else { world_scale.z }, + if world_scale.x.abs() < 1e-6 { + 1.0 + } else { + world_scale.x + }, + if world_scale.y.abs() < 1e-6 { + 1.0 + } else { + world_scale.y + }, + if world_scale.z.abs() < 1e-6 { + 1.0 + } else { + world_scale.z + }, ); let local_transform = entity_transform.local_mut(); @@ -183,8 +210,7 @@ impl<'a> EditorTabViewer<'a> { } if !handled { - if let Ok(transform) = self.world.query_one::<&mut Transform>(*entity_id).get() - { + if let Ok(transform) = self.world.query_one::<&mut Transform>(*entity_id).get() { let was_focused = cfg.is_focused; cfg.is_focused = self.gizmo.is_focused(); @@ -229,9 +255,10 @@ impl<'a> EditorTabViewer<'a> { if let Ok(mut light) = self.world.get::<&mut Light>(*entity_id) { let forward = DVec3::new(0.0, 0.0, -1.0); light.component.position = updated_transform.position; - light.component.direction = (updated_transform.rotation * forward).normalize_or_zero(); + light.component.direction = + (updated_transform.rotation * forward).normalize_or_zero(); } } } } -} +} @@ -1,20 +1,20 @@ +pub mod about; pub mod build; pub mod camera; pub mod debug; pub mod editor; pub mod menu; +pub mod physics; pub mod plugin; +pub mod process; pub mod signal; pub mod spawn; pub mod stats; pub mod utils; -pub mod about; -pub mod process; -pub mod physics; pub use redback_runtime as runtime; dropbear_engine::features! { pub mod features { const ShowComponentTypeIDInEditor = 0b00000001 } -} +} @@ -1,20 +1,24 @@ // #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // note to self: when it becomes release, remember to re-add this back -use anyhow::{bail, Context}; +use anyhow::{Context, bail}; use clap::{Arg, Command}; +use dropbear_engine::DropbearWindowBuilder; use dropbear_engine::future::FutureQueue; use dropbear_engine::texture::DropbearEngineLogo; +use eucalyptus_core::config::ProjectConfig; +use eucalyptus_core::scripting::jni::{RUNTIME_MODE, RuntimeMode}; +use eucalyptus_core::scripting::{AWAIT_JDB, JVM_ARGS}; +use eucalyptus_editor::editor::settings::editor::EditorSettings; use eucalyptus_editor::{build, editor, menu}; use parking_lot::RwLock; use std::sync::Arc; -use std::{fs, path::{Path, PathBuf}, rc::Rc}; +use std::{ + fs, + path::{Path, PathBuf}, + rc::Rc, +}; use winit::window::{Icon, WindowAttributes}; -use dropbear_engine::{DropbearWindowBuilder}; -use eucalyptus_core::config::ProjectConfig; -use eucalyptus_core::scripting::jni::{RuntimeMode, RUNTIME_MODE}; -use eucalyptus_core::scripting::{AWAIT_JDB, JVM_ARGS}; -use eucalyptus_editor::editor::settings::editor::{EditorSettings}; #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -95,10 +99,7 @@ async fn main() -> anyhow::Result<()> { }) .filter_level(LevelFilter::Warn) .filter(Some("dropbear_engine"), LevelFilter::Trace) - .filter( - Some("eucalyptus_editor"), - LevelFilter::Debug, - ) + .filter(Some("eucalyptus_editor"), LevelFilter::Debug) .filter(Some("eucalyptus_core"), LevelFilter::Debug) .filter(Some("dropbear_traits"), LevelFilter::Debug) .filter(Some("kino_ui"), LevelFilter::Debug) @@ -199,11 +200,11 @@ async fn main() -> anyhow::Result<()> { if await_jdb { let _ = AWAIT_JDB.set(true); } - + if tracing { dropbear_engine::feature_list::enable(dropbear_engine::feature_list::EnablePuffinTracer) } - + EditorSettings::read()?; match matches.subcommand() { @@ -228,12 +229,14 @@ async fn main() -> anyhow::Result<()> { }; build::read(eupak)?; - }, + } Some(("play", sub_matches)) => { let _ = RUNTIME_MODE.set(RuntimeMode::PlayMode); let mut path = resolve_project_argument(sub_matches.get_one::<String>("project"))?; - let initial_scene = sub_matches.get_one::<String>("initial_scene").and_then(|s| Some(s.clone())); + let initial_scene = sub_matches + .get_one::<String>("initial_scene") + .and_then(|s| Some(s.clone())); if path.is_dir() { path = find_eucp_in_dir(path.as_path())?; @@ -249,21 +252,26 @@ async fn main() -> anyhow::Result<()> { let scene_to_load = initial_scene .as_ref() .or(config.runtime_settings.initial_scene.as_ref()) - .ok_or_else(|| anyhow::anyhow!("No initial scene specified and no default scene in project config"))?; + .ok_or_else(|| { + anyhow::anyhow!( + "No initial scene specified and no default scene in project config" + ) + })?; eucalyptus_core::states::load_scene_into_memory(scene_to_load)?; log::info!("Loaded initial scene '{}' for play mode", scene_to_load); let future_queue = Arc::new(FutureQueue::new()); - let play_mode = - Rc::new(RwLock::new(eucalyptus_editor::runtime::PlayMode::new(initial_scene).unwrap_or_else(|e| { + let play_mode = Rc::new(RwLock::new( + eucalyptus_editor::runtime::PlayMode::new(initial_scene).unwrap_or_else(|e| { panic!("Unable to initialise eucalyptus play mode session: {}", e) - }))); + }), + )); let window = DropbearWindowBuilder::new() .with_attributes( - WindowAttributes::default().with_title(config.project_name.clone()) + WindowAttributes::default().with_title(config.project_name.clone()), ) .add_scene_with_input(play_mode, "play_mode") .set_initial_scene("play_mode") @@ -272,8 +280,9 @@ async fn main() -> anyhow::Result<()> { dropbear_engine::DropbearAppBuilder::new() .with_future_queue(future_queue) .add_window(window) - .run().await?; - }, + .run() + .await?; + } None => { let _ = RUNTIME_MODE.set(RuntimeMode::Editor); @@ -293,15 +302,14 @@ async fn main() -> anyhow::Result<()> { let window = DropbearWindowBuilder::new() .with_attributes( - WindowAttributes::default().with_title( - format!( + WindowAttributes::default() + .with_title(format!( "Eucalyptus, built with dropbear | Version {} on commit {}", env!("CARGO_PKG_VERSION"), env!("GIT_HASH") - ) - ) + )) .with_maximized(true) - .with_window_icon(window_icon) + .with_window_icon(window_icon), ) .add_scene_with_input(editor, "editor") .add_scene_with_input(main_menu, "main_menu") @@ -311,7 +319,8 @@ async fn main() -> anyhow::Result<()> { dropbear_engine::DropbearAppBuilder::new() .with_future_queue(future_queue) .add_window(window) - .run().await?; + .run() + .await?; } _ => unreachable!(), } @@ -327,7 +336,10 @@ fn resolve_project_argument(arg: Option<&String>) -> anyhow::Result<PathBuf> { } else if provided.exists() { Ok(provided) } else { - bail!("Provided project path does not exist: {}", provided.display()); + bail!( + "Provided project path does not exist: {}", + provided.display() + ); } } None => find_eucp_file(), @@ -1,5 +1,11 @@ +use crate::editor::settings::editor::EditorSettingsWindow; use anyhow::{Context, anyhow}; -use dropbear_engine::{DropbearWindowBuilder, future::{FutureHandle, FutureQueue}, input::{Controller, Keyboard, Mouse}, scene::{Scene, SceneCommand}}; +use dropbear_engine::{ + DropbearWindowBuilder, + future::{FutureHandle, FutureQueue}, + input::{Controller, Keyboard, Mouse}, + scene::{Scene, SceneCommand}, +}; use egui::{self, FontId, Frame, RichText}; use egui_toast::{ToastOptions, Toasts}; use eucalyptus_core::config::ProjectConfig; @@ -7,17 +13,16 @@ use eucalyptus_core::states::PROJECT; use git2::Repository; use log::{self, debug}; use log_once::debug_once; +use parking_lot::RwLock; use rfd::FileDialog; +use std::rc::Rc; use std::sync::Arc; use std::{fs, path::PathBuf}; -use std::rc::Rc; -use parking_lot::RwLock; use tokio::sync::watch; +use winit::window::WindowAttributes; use winit::{ dpi::PhysicalPosition, event::MouseButton, event_loop::ActiveEventLoop, keyboard::KeyCode, }; -use winit::window::WindowAttributes; -use crate::editor::settings::editor::EditorSettingsWindow; #[derive(Debug, Clone)] pub enum ProjectProgress { @@ -235,15 +240,31 @@ impl MainMenu { } impl Scene for MainMenu { - fn load(&mut self, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn load( + &mut self, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { log::info!("Loaded main menu scene"); } - fn physics_update(&mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) {} + fn physics_update( + &mut self, + _dt: f32, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { + } - fn update(&mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) {} + fn update( + &mut self, + _dt: f32, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { + } - fn render<'a>(&mut self, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn render<'a>( + &mut self, + graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { #[allow(clippy::collapsible_if)] if let Some(handle) = self.project_creation_handle.as_ref() { if let Some(result) = graphics @@ -334,10 +355,14 @@ impl Scene for MainMenu { { debug!("Editor settings"); let window_data = DropbearWindowBuilder::new() - .with_attributes(WindowAttributes::default() - .with_title("eucalyptus editor - settings") + .with_attributes( + WindowAttributes::default() + .with_title("eucalyptus editor - settings"), + ) + .add_scene_with_input( + Rc::new(RwLock::new(EditorSettingsWindow::new())), + "editor_settings", ) - .add_scene_with_input(Rc::new(RwLock::new(EditorSettingsWindow::new())), "editor_settings") .set_initial_scene("editor_settings") .build(); self.scene_command = SceneCommand::RequestWindow(window_data); @@ -1,3 +1,3 @@ // pub mod rigidbody; // pub mod collider; -// pub mod kcc; +// pub mod kcc; @@ -6,14 +6,14 @@ use std::process::Command; #[cfg(unix)] pub fn kill_process(pid: u32) -> io::Result<()> { let status = Command::new("kill") - .arg("-15") // SIGTERM + .arg("-15") // SIGTERM .arg(pid.to_string()) .status()?; if !status.success() { return Err(io::Error::new( io::ErrorKind::Other, - format!("Failed to kill process {}", pid) + format!("Failed to kill process {}", pid), )); } @@ -30,9 +30,9 @@ pub fn kill_process(pid: u32) -> io::Result<()> { if !status.success() { return Err(io::Error::new( io::ErrorKind::Other, - format!("Failed to kill process {}", pid) + format!("Failed to kill process {}", pid), )); } Ok(()) -} +} @@ -1,12 +1,10 @@ use crate::editor::{AssetClipboard, Editor, EditorState, Signal}; +use crate::spawn::{PendingSpawn, push_pending_spawn}; use dropbear_engine::graphics::SharedGraphicsContext; -use dropbear_engine::utils::{ - relative_path_from_euca, EUCA_SCHEME, -}; +use dropbear_engine::utils::{EUCA_SCHEME, relative_path_from_euca}; use egui::Align2; -use crate::spawn::{push_pending_spawn, PendingSpawn}; use eucalyptus_core::camera::{CameraComponent, CameraType}; -use eucalyptus_core::scripting::{build_jvm, BuildStatus}; +use eucalyptus_core::scripting::{BuildStatus, build_jvm}; use eucalyptus_core::states::{EditorTab, PROJECT}; use eucalyptus_core::{fatal, info, success, success_without_console, warn, warn_without_console}; use std::fs; @@ -16,8 +14,8 @@ use winit::keyboard::KeyCode; fn resolve_editor_path(uri: &str) -> PathBuf { if uri.starts_with(EUCA_SCHEME) { - let relative = relative_path_from_euca(uri) - .unwrap_or_else(|_| uri.trim_start_matches(EUCA_SCHEME)); + let relative = + relative_path_from_euca(uri).unwrap_or_else(|_| uri.trim_start_matches(EUCA_SCHEME)); let project_path = PROJECT.read().project_path.clone(); project_path.join("resources").join(relative) } else { @@ -53,7 +51,10 @@ impl SignalController for Editor { self.signal = Signal::None; Ok(()) } - Signal::AssetPaste { target_dir, division } => { + Signal::AssetPaste { + target_dir, + division, + } => { let clipboard = self.asset_clipboard.clone(); if clipboard.is_none() { warn!("Nothing copied to paste"); @@ -505,7 +506,10 @@ impl SignalController for Editor { self.editor_state = EditorState::Editing; if let Some(pid) = self.play_mode_pid { - log::debug!("Play mode requested to be exited, killing processes [{}]", pid); + log::debug!( + "Play mode requested to be exited, killing processes [{}]", + pid + ); let _ = crate::process::kill_process(pid); } @@ -552,9 +556,8 @@ impl SignalController for Editor { let current_size = graphics.viewport_texture.size; if current_size.width != width || current_size.height != height { - self.scene_command = dropbear_engine::scene::SceneCommand::ResizeViewport(( - width, height, - )); + self.scene_command = + dropbear_engine::scene::SceneCommand::ResizeViewport((width, height)); } self.signal = Signal::None; Ok(()) @@ -8,7 +8,7 @@ use eucalyptus_core::hierarchy::Parent; use eucalyptus_core::scene::SceneEntity; use eucalyptus_core::states::Label; use eucalyptus_core::{fatal, success, warn}; -use hecs::{Entity, EntityBuilder}; +use hecs::EntityBuilder; use parking_lot::Mutex; use std::sync::{Arc, LazyLock}; @@ -56,8 +56,9 @@ impl PendingSpawnController for Editor { let graphics = graphics.clone(); let future = async move { - let mut appliers: Vec<Box<dyn for<'a> FnOnce(&'a mut EntityBuilder) + Send + Sync>> = - Vec::new(); + let mut appliers: Vec< + Box<dyn for<'a> FnOnce(&'a mut EntityBuilder) + Send + Sync>, + > = Vec::new(); for component in components { if component.as_any().downcast_ref::<Parent>().is_some() { continue; @@ -74,10 +75,13 @@ impl PendingSpawnController for Editor { appliers.push(applier); } - Ok::<(Label, Vec<Box<dyn for<'a> FnOnce(&'a mut EntityBuilder) + Send + Sync>>), anyhow::Error>(( - label, - appliers, - )) + Ok::< + ( + Label, + Vec<Box<dyn for<'a> FnOnce(&'a mut EntityBuilder) + Send + Sync>>, + ), + anyhow::Error, + >((label, appliers)) }; let handle = queue.push(Box::pin(future)); @@ -86,7 +90,10 @@ impl PendingSpawnController for Editor { if let Some(handle) = &spawn.handle { if let Some(result) = queue.exchange_owned(handle) { - if let Ok(r) = result.downcast::<anyhow::Result<(Label, Vec<Box<dyn for<'a> FnOnce(&'a mut EntityBuilder) + Send + Sync>>)>>() { + if let Ok(r) = result.downcast::<anyhow::Result<( + Label, + Vec<Box<dyn for<'a> FnOnce(&'a mut EntityBuilder) + Send + Sync>>, + )>>() { match Arc::try_unwrap(r) { Ok(Ok((label, appliers))) => { let mut builder = EntityBuilder::new(); @@ -97,14 +104,19 @@ impl PendingSpawnController for Editor { let entity = self.world.spawn(builder.build()); if self.world.get::<&EntityTransform>(entity).is_err() { - let _ = self.world.insert_one(entity, EntityTransform::default()); + let _ = + self.world.insert_one(entity, EntityTransform::default()); } success!("Spawned '{}' from pending queue", label); completed.push(index); } Ok(Err(err)) => { - fatal!("Unable to init components for '{}': {}", spawn.scene_entity.label, err); + fatal!( + "Unable to init components for '{}': {}", + spawn.scene_entity.label, + err + ); completed.push(index); } Err(_) => { @@ -1,16 +1,16 @@ use std::{collections::VecDeque, time::Instant}; use dropbear_engine::WGPU_BACKEND; +use dropbear_engine::input::{Controller, Keyboard, Mouse}; +use dropbear_engine::scene::Scene; use egui::{Color32, Context, RichText, Ui}; use egui_plot::{Legend, Line, Plot, PlotPoints}; -use dropbear_engine::scene::Scene; -use dropbear_engine::input::{Keyboard, Mouse, Controller}; +use dropbear_engine::gilrs; +use winit::dpi::PhysicalPosition; +use winit::event::MouseButton; use winit::event_loop::ActiveEventLoop; use winit::keyboard::KeyCode; -use winit::event::MouseButton; -use winit::dpi::PhysicalPosition; -use dropbear_engine::gilrs; pub const EGUI_VERSION: &str = "0.33"; pub const WGPU_VERSION: &str = "27"; @@ -157,11 +157,8 @@ impl NerdStats { ui.vertical(|ui| { ui.label(RichText::new("Frame Time").strong()); ui.label( - RichText::new(format!( - "{:.2} ms", - 1000.0 / self.current_fps.max(1.0) - )) - .size(24.0), + RichText::new(format!("{:.2} ms", 1000.0 / self.current_fps.max(1.0))) + .size(24.0), ); }); @@ -176,9 +173,7 @@ impl NerdStats { ui.vertical(|ui| { ui.label(RichText::new("Entity Count").strong()); - ui.label( - RichText::new(format!("{} entities", self.entity_count)).size(24.0), - ); + ui.label(RichText::new(format!("{} entities", self.entity_count)).size(24.0)); }); }); @@ -207,8 +202,7 @@ impl NerdStats { .legend(Legend::default()) .show(ui, |plot_ui| { if !self.fps_history.is_empty() { - let points: Vec<[f64; 2]> = - self.fps_history.iter().cloned().collect(); + let points: Vec<[f64; 2]> = self.fps_history.iter().cloned().collect(); plot_ui.line( Line::new("fps", PlotPoints::from(points)) .color(Color32::from_rgb(100, 200, 100)) @@ -287,8 +281,7 @@ impl NerdStats { .legend(Legend::default()) .show(ui, |plot_ui| { if !self.memory_history.is_empty() { - let points: Vec<[f64; 2]> = - self.memory_history.iter().cloned().collect(); + let points: Vec<[f64; 2]> = self.memory_history.iter().cloned().collect(); plot_ui.line( Line::new("memory", PlotPoints::from(points)) .color(Color32::from_rgb(255, 150, 100)) @@ -319,7 +312,7 @@ impl NerdStats { }); } - /// Shows the egui window as a CentralPanel, typically used for another window. + /// Shows the egui window as a CentralPanel, typically used for another window. pub fn show_window(&mut self, ctx: &Context) { egui::CentralPanel::default().show(ctx, |ui| { self.content(ui); @@ -330,12 +323,28 @@ impl NerdStats { } impl Scene for NerdStats { - fn load(&mut self, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) {} - fn physics_update(&mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) {} - fn update(&mut self, dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn load( + &mut self, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { + } + fn physics_update( + &mut self, + _dt: f32, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { + } + fn update( + &mut self, + dt: f32, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { self.record_stats(dt, self.entity_count); } - fn render<'a>(&mut self, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn render<'a>( + &mut self, + graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ) { self.show_window(&graphics.get_egui_context()); } fn exit(&mut self, _event_loop: &ActiveEventLoop) {} @@ -177,4 +177,4 @@ pub fn start_project_creation( }); Some(rx) -} +} @@ -122,9 +122,15 @@ fn extract_exports_from_file( if out_type.is_some() { let out_ty = out_type.clone().unwrap(); - params.push(ExportParam { name: "out0".to_string(), ty: format!("{}*", out_ty) }); + params.push(ExportParam { + name: "out0".to_string(), + ty: format!("{}*", out_ty), + }); if out_is_optional { - params.push(ExportParam { name: "out0_present".to_string(), ty: "bool*".to_string() }); + params.push(ExportParam { + name: "out0_present".to_string(), + ty: "bool*".to_string(), + }); } } @@ -216,9 +222,15 @@ fn extract_repr_c_enums_from_file( } syn::Fields::Unit => {} } - variants.push(EnumVariantDef { name: variant.ident.to_string(), fields }); + variants.push(EnumVariantDef { + name: variant.ident.to_string(), + fields, + }); } - enums.push(EnumDef { name: enm.ident.to_string(), variants }); + enums.push(EnumDef { + name: enm.ident.to_string(), + variants, + }); } } Ok(()) @@ -236,13 +248,27 @@ fn extract_structs_from_file( if let syn::Fields::Named(named) = &strct.fields { for field in &named.named { - let field_name = field.ident.as_ref().map(|i| i.to_string()).unwrap_or_else(|| "field".to_string()); + let field_name = field + .ident + .as_ref() + .map(|i| i.to_string()) + .unwrap_or_else(|| "field".to_string()); let ty = type_to_c(&field.ty, false); - fields.push(ExportParam { name: field_name, ty }); + fields.push(ExportParam { + name: field_name, + ty, + }); } } - structs.insert(name.clone(), StructDef { name, fields, is_repr_c }); + structs.insert( + name.clone(), + StructDef { + name, + fields, + is_repr_c, + }, + ); } } @@ -262,9 +288,17 @@ struct CArgs { fn parse_export_attr(attrs: &[syn::Attribute]) -> anyhow::Result<Option<ExportAttr>> { for attr in attrs { let path = attr.path(); - let is_export = path.segments.last().map(|s| s.ident == "export").unwrap_or(false) + let is_export = path + .segments + .last() + .map(|s| s.ident == "export") + .unwrap_or(false) || (path.segments.iter().any(|s| s.ident == "dropbear_macro") - && path.segments.last().map(|s| s.ident == "export").unwrap_or(false)); + && path + .segments + .last() + .map(|s| s.ident == "export") + .unwrap_or(false)); if !is_export { continue; } @@ -285,7 +319,8 @@ struct ExportArgs { impl syn::parse::Parse for ExportArgs { fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { - let items = syn::punctuated::Punctuated::<ExportItem, syn::Token![,]>::parse_terminated(input)?; + let items = + syn::punctuated::Punctuated::<ExportItem, syn::Token![,]>::parse_terminated(input)?; let mut args = ExportArgs { c: None }; for item in items { @@ -354,7 +389,11 @@ fn extract_arg_markers(attrs: &[syn::Attribute]) -> (Option<syn::Type>, bool) { let mut is_entity = false; for attr in attrs { let path = attr.path(); - let ident = path.segments.last().map(|s| s.ident.to_string()).unwrap_or_default(); + let ident = path + .segments + .last() + .map(|s| s.ident.to_string()) + .unwrap_or_default(); if ident == "define" { if let Ok(ty) = attr.parse_args::<syn::Type>() { define_ty = Some(ty); @@ -375,15 +414,23 @@ fn extract_result_type(output: &syn::ReturnType) -> anyhow::Result<(Option<Strin let inner = match &**ty { syn::Type::Path(path) => { - let last = path.path.segments.last().ok_or_else(|| anyhow::anyhow!("Invalid return type"))?; + let last = path + .path + .segments + .last() + .ok_or_else(|| anyhow::anyhow!("Invalid return type"))?; if last.ident != "DropbearNativeResult" { return Ok((None, false)); } match &last.arguments { - syn::PathArguments::AngleBracketed(args) => args.args.first().and_then(|a| match a { - syn::GenericArgument::Type(t) => Some(t.clone()), - _ => None, - }).ok_or_else(|| anyhow::anyhow!("DropbearNativeResult missing type"))?, + syn::PathArguments::AngleBracketed(args) => args + .args + .first() + .and_then(|a| match a { + syn::GenericArgument::Type(t) => Some(t.clone()), + _ => None, + }) + .ok_or_else(|| anyhow::anyhow!("DropbearNativeResult missing type"))?, _ => return Ok((None, false)), } } @@ -423,7 +470,11 @@ fn is_unit_type(ty: &syn::Type) -> bool { fn type_to_c(ty: &syn::Type, for_output: bool) -> String { if let syn::Type::Reference(reference) = ty { let inner = type_to_c(&reference.elem, for_output); - let mutability = if reference.mutability.is_some() { "" } else { "const " }; + let mutability = if reference.mutability.is_some() { + "" + } else { + "const " + }; return format!("{}{}*", mutability, inner); } if let Some(inner) = extract_option_inner(ty) { @@ -436,7 +487,12 @@ fn type_to_c(ty: &syn::Type, for_output: bool) -> String { } if let syn::Type::Path(path) = ty { - let ident = path.path.segments.last().map(|s| s.ident.to_string()).unwrap_or_else(|| "void".to_string()); + let ident = path + .path + .segments + .last() + .map(|s| s.ident.to_string()) + .unwrap_or_else(|| "void".to_string()); return match ident.as_str() { "i8" => "int8_t".to_string(), "u8" => "uint8_t".to_string(), @@ -559,7 +615,8 @@ fn render_header( let params = if func.params.is_empty() { "void".to_string() } else { - func.params.iter() + func.params + .iter() .map(|p| format!("{} {}", p.ty, p.name)) .collect::<Vec<_>>() .join(", ") @@ -626,11 +683,20 @@ fn emit_repr_c_enum( fn has_repr_c_enum_attr(attrs: &[syn::Attribute]) -> bool { for attr in attrs { let path = attr.path(); - if path.segments.last().map(|s| s.ident == "repr_c_enum").unwrap_or(false) { + if path + .segments + .last() + .map(|s| s.ident == "repr_c_enum") + .unwrap_or(false) + { return true; } if path.segments.iter().any(|s| s.ident == "dropbear_macro") - && path.segments.last().map(|s| s.ident == "repr_c_enum").unwrap_or(false) + && path + .segments + .last() + .map(|s| s.ident == "repr_c_enum") + .unwrap_or(false) { return true; } @@ -687,7 +753,11 @@ fn object_input_to_c(ty: &syn::Type) -> String { match ty { syn::Type::Reference(reference) => { let inner = type_to_c(&reference.elem, false); - let mutability = if reference.mutability.is_some() { "" } else { "const " }; + let mutability = if reference.mutability.is_some() { + "" + } else { + "const " + }; format!("{}{}*", mutability, inner) } _ => type_to_c(ty, false), @@ -724,9 +794,24 @@ fn peel_reference<'a>(ty: &'a syn::Type) -> &'a syn::Type { fn is_builtin_c_type(ty: &str) -> bool { matches!( ty, - "int8_t" | "uint8_t" | "int16_t" | "uint16_t" | "int32_t" | "uint32_t" | - "int64_t" | "uint64_t" | "intptr_t" | "uintptr_t" | "bool" | "float" | - "double" | "char" | "char*" | "const char*" | "void*" | "size_t" + "int8_t" + | "uint8_t" + | "int16_t" + | "uint16_t" + | "int32_t" + | "uint32_t" + | "int64_t" + | "uint64_t" + | "intptr_t" + | "uintptr_t" + | "bool" + | "float" + | "double" + | "char" + | "char*" + | "const char*" + | "void*" + | "size_t" ) } @@ -734,10 +819,7 @@ fn is_opaque_ptr_name(name: &str) -> bool { name.ends_with("Ptr") } -fn emit_string_typedef( - emitted: &mut std::collections::HashSet<String>, - out: &mut String, -) { +fn emit_string_typedef(emitted: &mut std::collections::HashSet<String>, out: &mut String) { if emitted.contains("String") { return; } @@ -867,8 +949,18 @@ fn emit_array_struct( fn is_builtin_rust_primitive(name: &str) -> bool { matches!( name, - "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" | - "isize" | "usize" | "f32" | "f64" | "bool" + "i8" | "u8" + | "i16" + | "u16" + | "i32" + | "u32" + | "i64" + | "u64" + | "isize" + | "usize" + | "f32" + | "f64" + | "bool" ) } @@ -1,14 +1,14 @@ +use crate::rendering::texture::Texture; use std::collections::HashMap; -use std::hash::{Hash, Hasher}; use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; use std::marker::PhantomData; -use crate::rendering::texture::Texture; /// A handle with type [`T`] that provides an index to the [AssetServer] contents. #[derive(Hash, Eq, PartialEq, Debug)] pub struct Handle<T> { pub id: u64, - _phanton: PhantomData<T> + _phanton: PhantomData<T>, } impl<T> Copy for Handle<T> {} @@ -21,7 +21,10 @@ impl<T> Clone for Handle<T> { impl<T> Handle<T> { pub fn new(id: u64) -> Self { - Self { id, _phanton: Default::default() } + Self { + id, + _phanton: Default::default(), + } } } @@ -53,7 +56,11 @@ impl AssetServer { /// Adds a texture with a label. If the texture already exists (by hash), /// returns the existing handle and updates the label to point at it. - pub fn add_texture_with_label(&mut self, label: impl Into<String>, texture: Texture) -> Handle<Texture> { + pub fn add_texture_with_label( + &mut self, + label: impl Into<String>, + texture: Texture, + ) -> Handle<Texture> { let handle = self.add_texture(texture); self.texture_labels.insert(label.into(), handle.clone()); handle @@ -93,4 +100,4 @@ impl AssetServer { data.hash(&mut hasher); hasher.finish() } -} +} @@ -1,6 +1,7 @@ - use bytemuck::{Pod, Zeroable}; -use glam::{Mat4, Vec2, Vec3}; + use crate::math::Rect; +use bytemuck::{Pod, Zeroable}; +use glam::{Mat4, Vec2, Vec3}; pub struct Camera2D { position: Vec2, @@ -27,14 +28,7 @@ impl Camera2D { Vec3::Y, ); - let proj = Mat4::orthographic_rh( - 0.0, - width, - height, - 0.0, - -1.0, - 1.0, - ); + let proj = Mat4::orthographic_rh(0.0, width, height, 0.0, -1.0, 1.0); proj * view } @@ -80,4 +74,4 @@ pub struct CameraRendering { #[derive(Copy, Clone, Pod, Zeroable)] pub struct CameraUniform { pub view_proj: [[f32; 4]; 4], -} +} @@ -1,38 +1,38 @@ #![doc = include_str!("../README.md")] -pub mod resp; -pub mod widgets; -pub mod rendering; -pub mod camera; pub mod asset; +pub mod camera; pub mod math; +pub mod rendering; +pub mod resp; +pub mod widgets; pub mod windowing; pub mod crates { + pub use glyphon; pub use wgpu; pub use winit; - pub use glyphon; } pub use widgets::shorthand::*; use crate::asset::{AssetServer, Handle}; use crate::camera::Camera2D; +use crate::math::Rect; +use crate::rendering::KinoWGPURenderer; +use crate::rendering::text::KinoTextRenderer; use crate::rendering::texture::Texture; use crate::rendering::vertex::Vertex; -use crate::rendering::{KinoWGPURenderer}; use crate::resp::WidgetResponse; use crate::widgets::{ContaineredWidget, NativeWidget}; -use crate::math::Rect; +use crate::windowing::KinoWinitWindowing; +use glam::Vec2; +use rendering::batching::VertexBatch; use std::borrow::Cow; use std::collections::{HashMap, VecDeque}; use std::fmt::Debug; use std::hash::{DefaultHasher, Hash, Hasher}; use wgpu::{LoadOp, StoreOp}; -use rendering::batching::VertexBatch; -use crate::windowing::KinoWinitWindowing; -use glam::Vec2; -use crate::rendering::text::KinoTextRenderer; /// Holds the state of all the instructions, and the vertices+indices for rendering as well /// as the responses. @@ -50,31 +50,31 @@ pub struct KinoState { // public stuff impl KinoState { /// Returns a mutable reference to the current [`KinoTextRenderer`], used for text and font - /// management. + /// management. pub fn text(&mut self) -> &mut KinoTextRenderer { &mut self.renderer.text } - - /// Returns a mutable reference to the current [`KinoWGPURenderer`], used for rendering and - /// pipelines. + + /// Returns a mutable reference to the current [`KinoWGPURenderer`], used for rendering and + /// pipelines. pub fn renderer(&mut self) -> &mut KinoWGPURenderer { &mut self.renderer } - - /// Returns a mutable reference to the current [`KinoWinitWindowing`], used for handling events - /// and windowing operations. + + /// Returns a mutable reference to the current [`KinoWinitWindowing`], used for handling events + /// and windowing operations. pub fn windowing(&mut self) -> &mut KinoWinitWindowing { &mut self.windowing } - - /// Returns a mutable reference to the current [`Camera2D`], used for displaying the current - /// viewport. + + /// Returns a mutable reference to the current [`Camera2D`], used for displaying the current + /// viewport. pub fn camera(&mut self) -> &mut Camera2D { &mut self.camera } - + /// Returns a mutable reference to the [`AssetServer`], used for storing textures and - /// other assets. + /// other assets. pub fn assets(&mut self) -> &mut AssetServer { &mut self.assets } @@ -103,7 +103,8 @@ impl KinoState { /// checking. pub fn add_widget(&mut self, widget: Box<dyn NativeWidget>) -> WidgetId { let id = widget.id(); - self.instruction_set.push_back(UiInstructionType::Widget(widget)); + self.instruction_set + .push_back(UiInstructionType::Widget(widget)); id } @@ -112,20 +113,22 @@ impl KinoState { /// checking. pub fn add_container(&mut self, container: Box<dyn ContaineredWidget>) -> WidgetId { let id = container.id(); - self.instruction_set.push_back(UiInstructionType::Containered( - ContaineredWidgetType::Start { - id, - widget: container, - } - )); + self.instruction_set + .push_back(UiInstructionType::Containered( + ContaineredWidgetType::Start { + id, + widget: container, + }, + )); id } /// Ends the current container block. pub fn end_container(&mut self, id: WidgetId) { - self.instruction_set.push_back(UiInstructionType::Containered( - ContaineredWidgetType::End { id } - )); + self.instruction_set + .push_back(UiInstructionType::Containered(ContaineredWidgetType::End { + id, + })); } /// Adds a [UiInstructionType] to the instruction set. @@ -143,9 +146,7 @@ impl KinoState { /// This sits inside your `update()` loop. pub fn poll(&mut self) { log::trace!("polling kinostate"); - let current_instructions = { - self.instruction_set.drain(..).collect::<Vec<_>>() - }; + let current_instructions = { self.instruction_set.drain(..).collect::<Vec<_>>() }; self.widget_states.clear(); @@ -159,7 +160,10 @@ impl KinoState { /// This will only provide the proper information **after** you have /// polled with [`KinoState::poll`]. pub fn response(&self, id: impl Into<WidgetId>) -> WidgetResponse { - self.widget_states.get(&id.into()).copied().unwrap_or_default() + self.widget_states + .get(&id.into()) + .copied() + .unwrap_or_default() } pub fn set_viewport_offset(&mut self, offset: Vec2) { @@ -188,9 +192,7 @@ impl KinoState { log::trace!("rendering kinostate"); self.renderer.upload_camera_matrix( queue, - self.camera - .view_proj(self.renderer.size) - .to_cols_array_2d(), + self.camera.view_proj(self.renderer.size).to_cols_array_2d(), ); let batch = self.batch.take(); @@ -220,10 +222,9 @@ impl KinoState { for mut tg in batch { // log::debug!("Rendering textured geometry: {:?}", tg); - let texture = tg.texture_id.and_then(|v| { - self.assets.get_texture(v) - }); - self.renderer.draw_batch(&mut pass, device, queue, &mut tg.batch, texture); + let texture = tg.texture_id.and_then(|v| self.assets.get_texture(v)); + self.renderer + .draw_batch(&mut pass, device, queue, &mut tg.batch, texture); } self.renderer.text.render(&mut pass); @@ -244,18 +245,15 @@ impl KinoState { ) { self.renderer.upload_camera_matrix( queue, - self.camera - .view_proj(self.renderer.size) - .to_cols_array_2d(), + self.camera.view_proj(self.renderer.size).to_cols_array_2d(), ); let batch = self.batch.take(); for mut tg in batch { // log::debug!("Rendering textured geometry: {:?}", tg); - let texture = tg.texture_id.and_then(|v| { - self.assets.get_texture(v) - }); - self.renderer.draw_batch(pass, device, queue, &mut tg.batch, texture); + let texture = tg.texture_id.and_then(|v| self.assets.get_texture(v)); + self.renderer + .draw_batch(pass, device, queue, &mut tg.batch, texture); } self.renderer.text.render(pass); @@ -379,25 +377,23 @@ impl KinoState { for instruction in instructions { match &instruction { - UiInstructionType::Containered(container_ty) => { - match container_ty { - ContaineredWidgetType::Start { .. } => { - stack.push(UiNode { - instruction, - children: Vec::new(), - }); - } - ContaineredWidgetType::End { .. } => { - if let Some(node) = stack.pop() { - if let Some(parent) = stack.last_mut() { - parent.children.push(node); - } else { - root.push(node); - } + UiInstructionType::Containered(container_ty) => match container_ty { + ContaineredWidgetType::Start { .. } => { + stack.push(UiNode { + instruction, + children: Vec::new(), + }); + } + ContaineredWidgetType::End { .. } => { + if let Some(node) = stack.pop() { + if let Some(parent) = stack.last_mut() { + parent.children.push(node); + } else { + root.push(node); } } } - } + }, UiInstructionType::Widget(_) => { let node = UiNode { instruction, @@ -511,7 +507,7 @@ pub enum ContaineredWidgetType { }, End { id: WidgetId, - } + }, } pub struct UiNode { @@ -564,4 +560,4 @@ fn intersect_rects(a: Rect, b: Rect) -> Option<Rect> { } else { None } -} +} @@ -1,6 +1,6 @@ -use glam::{vec2, Vec2}; +use glam::{Vec2, vec2}; -/// A rectangular shape consisting of a `position` and a `size`. +/// A rectangular shape consisting of a `position` and a `size`. #[derive(Copy, Clone, PartialEq, Debug)] pub struct Rect { pub position: Vec2, @@ -46,4 +46,4 @@ impl Rect { let bl = vec2(tl.x, tl.y + self.size.y); [tl, tr, br, bl] } -} +} @@ -1,15 +1,15 @@ -use glam::Vec2; -use wgpu::util::{BufferInitDescriptor, DeviceExt}; -use batching::VertexBatch; use crate::camera::{CameraRendering, CameraUniform}; use crate::rendering::pipeline::KinoRendererPipeline; use crate::rendering::text::KinoTextRenderer; +use batching::VertexBatch; +use glam::Vec2; +use wgpu::util::{BufferInitDescriptor, DeviceExt}; +pub mod batching; pub mod pipeline; +pub mod text; pub mod texture; pub mod vertex; -pub mod batching; -pub mod text; pub struct KinoWGPURenderer { pipeline: KinoRendererPipeline, @@ -119,4 +119,3 @@ impl KinoWGPURenderer { &self.pipeline.texture_bind_group_layout } } - @@ -1,5 +1,5 @@ -use wgpu::{BufferUsages, IndexFormat}; use crate::rendering::vertex::Vertex; +use wgpu::{BufferUsages, IndexFormat}; /// Describes a primitive shape. #[derive(Debug)] @@ -117,4 +117,4 @@ impl VertexBatch { ); pass.draw_indexed(0..self.indices.len() as u32, 0, 0..1); } -} +} @@ -7,10 +7,7 @@ pub struct KinoRendererPipeline { } impl KinoRendererPipeline { - pub fn new( - device: &wgpu::Device, - surface_format: wgpu::TextureFormat, - ) -> Self { + pub fn new(device: &wgpu::Device, surface_format: wgpu::TextureFormat) -> Self { log::debug!("Initialising pipeline with format {:?}", surface_format); let shader = device.create_shader_module(wgpu::include_wgsl!("../shaders/ui.wgsl")); @@ -19,10 +16,7 @@ impl KinoRendererPipeline { let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("kino pipeline layout descriptor"), - bind_group_layouts: &[ - &texture_bind_group_layout, - &camera_bind_group_layout, - ], + bind_group_layouts: &[&texture_bind_group_layout, &camera_bind_group_layout], push_constant_ranges: &[], }); @@ -55,7 +49,7 @@ impl KinoRendererPipeline { multiview: None, cache: None, }); - + log::debug!("Created pipeline"); Self { @@ -80,7 +74,6 @@ impl KinoRendererPipeline { }, count: None, }, - // texture sampler wgpu::BindGroupLayoutEntry { binding: 1, @@ -106,8 +99,8 @@ impl KinoRendererPipeline { min_binding_size: None, }, count: None, - } + }, ], }) } -} +} @@ -1,5 +1,8 @@ use glam::Vec2; -use glyphon::{Buffer, Cache, Color, FontSystem, Resolution, SwashCache, TextArea, TextAtlas, TextBounds, TextRenderer, Viewport}; +use glyphon::{ + Buffer, Cache, Color, FontSystem, Resolution, SwashCache, TextArea, TextAtlas, TextBounds, + TextRenderer, Viewport, +}; pub struct TextEntry { pub buffer: Buffer, @@ -16,17 +19,21 @@ pub struct KinoTextRenderer { } impl KinoTextRenderer { - pub(crate) fn new(device: &wgpu::Device, queue: &wgpu::Queue, format: wgpu::TextureFormat) -> Self { + pub(crate) fn new( + device: &wgpu::Device, + queue: &wgpu::Queue, + format: wgpu::TextureFormat, + ) -> Self { log::debug!("Creating KinoTextRenderer"); log::debug!("Loading system fonts"); let mut font_system = FontSystem::new(); - font_system - .db_mut() - .load_system_fonts(); + font_system.db_mut().load_system_fonts(); log::debug!("Loaded system fonts"); log::debug!("Loading \"Roboto-Regular.ttf\" as fallback"); - font_system.db_mut().load_font_data(include_bytes!("../../../../resources/fonts/Roboto-Regular.ttf").to_vec()); + font_system.db_mut().load_font_data( + include_bytes!("../../../../resources/fonts/Roboto-Regular.ttf").to_vec(), + ); log::debug!("Loaded fallback"); let swash_cache = SwashCache::new(); let cache = Cache::new(device); @@ -26,9 +26,9 @@ impl Texture { | TextureFormat::Rgba16Snorm | TextureFormat::Rgba16Uint | TextureFormat::Rgba16Sint => Some(8), - TextureFormat::Rgba32Float - | TextureFormat::Rgba32Uint - | TextureFormat::Rgba32Sint => Some(16), + TextureFormat::Rgba32Float | TextureFormat::Rgba32Uint | TextureFormat::Rgba32Sint => { + Some(16) + } _ => None, } } @@ -61,7 +61,7 @@ impl Texture { texture_format ); } - + let texture = device.create_texture(&TextureDescriptor { label: None, size: Extent3d { @@ -117,18 +117,31 @@ impl Texture { let mut hasher = DefaultHasher::new(); data.hash(&mut hasher); let hash = hasher.finish(); - + log::debug!("Created new texture [{}]", hash); - + Self { hash, bind_group } } /// Creates a 1×1 white fallback texture /// /// Used when no valid texture is provided for a draw call - pub fn create_default(device: &Device, queue: &Queue, layout: &BindGroupLayout, texture_format: TextureFormat) -> Self { + pub fn create_default( + device: &Device, + queue: &Queue, + layout: &BindGroupLayout, + texture_format: TextureFormat, + ) -> Self { log::debug!("Creating standard white texture"); - Self::from_bytes(device, queue, layout, &[255u8, 255, 255, 255], 1, 1, texture_format) + Self::from_bytes( + device, + queue, + layout, + &[255u8, 255, 255, 255], + 1, + 1, + texture_format, + ) } /// Binds this texture at the given index in the render pass @@ -137,4 +150,4 @@ impl Texture { pub fn bind(&self, pass: &mut RenderPass, index: u32) { pass.set_bind_group(index, &self.bind_group, &[]); } -} +} @@ -9,14 +9,18 @@ pub struct Vertex { } impl Vertex { - pub fn new(position: impl Into<[f32; 2]>, colour: impl Into<[f32; 4]>, tex_coord: impl Into<[f32; 2]>) -> Self { + pub fn new( + position: impl Into<[f32; 2]>, + colour: impl Into<[f32; 4]>, + tex_coord: impl Into<[f32; 2]>, + ) -> Self { Self { position: position.into(), colour: colour.into(), tex_coord: tex_coord.into(), } } - + pub fn desc() -> wgpu::VertexBufferLayout<'static> { wgpu::VertexBufferLayout { array_stride: size_of::<Vertex>() as wgpu::BufferAddress, @@ -28,21 +32,19 @@ impl Vertex { offset: 0, shader_location: 0, }, - // colour wgpu::VertexAttribute { format: wgpu::VertexFormat::Float32x4, offset: size_of::<[f32; 2]>() as wgpu::BufferAddress, shader_location: 1, }, - // tex_coords wgpu::VertexAttribute { format: wgpu::VertexFormat::Float32x2, offset: size_of::<[f32; 6]>() as wgpu::BufferAddress, shader_location: 2, - } + }, ], } } -} +} @@ -1,8 +1,8 @@ -use crate::{WidgetId}; +use crate::WidgetId; #[derive(Clone, Copy, Debug, Default)] pub struct WidgetResponse { pub queried: WidgetId, pub clicked: bool, pub hovering: bool, -} +} @@ -1,9 +1,9 @@ // In widgets/layout.rs -use std::any::Any; -use glam::{vec2, Vec2}; -use crate::{KinoState, UiNode, UiInstructionType, ContaineredWidgetType, WidgetId}; use crate::widgets::{Anchor, ContaineredWidget}; +use crate::{ContaineredWidgetType, KinoState, UiInstructionType, UiNode, WidgetId}; +use glam::{Vec2, vec2}; +use std::any::Any; fn calculate_node_size(node: &UiNode) -> Vec2 { match &node.instruction { @@ -117,7 +117,7 @@ impl ContaineredWidget for Row { state.push_container(crate::math::Rect::new( start_pos + vec2(x_offset, 0.0), - vec2(child_size.x, total_size.y) + vec2(child_size.x, total_size.y), )); state.render_tree(vec![child]); @@ -194,7 +194,7 @@ impl ContaineredWidget for Column { state.push_container(crate::math::Rect::new( start_pos + vec2(0.0, y_offset), - vec2(total_size.x, child_size.y) + vec2(total_size.x, child_size.y), )); state.render_tree(vec![child]); @@ -215,4 +215,4 @@ impl ContaineredWidget for Column { fn as_any(&self) -> &dyn Any { self } -} +} @@ -1,11 +1,11 @@ +pub mod layout; pub mod rect; pub mod shorthand; pub mod text; -pub mod layout; -use std::any::Any; -use glam::Vec2; use crate::{KinoState, UiNode, WidgetId}; +use glam::Vec2; +use std::any::Any; /// Determines how the object is anchored. pub enum Anchor { @@ -16,12 +16,12 @@ pub enum Anchor { TopLeft, } -/// Defines a widget with no children. +/// Defines a widget with no children. pub trait NativeWidget: Send + Sync { - /// Renders the widget. - /// + /// Renders the widget. + /// /// The state is provided for you to manipulate, such as adding a new response based on the - /// [`WidgetId`]. + /// [`WidgetId`]. fn render(self: Box<Self>, state: &mut KinoState); fn size(&self) -> Vec2; fn id(&self) -> WidgetId; @@ -35,7 +35,7 @@ pub trait ContaineredWidget: Send + Sync { fn as_any(&self) -> &dyn Any; } -/// Describes the colour that the widget will be filled in with. +/// Describes the colour that the widget will be filled in with. #[derive(Copy, Clone, Debug, PartialEq)] pub struct Fill { /// The colour of the fill described as RGBA between the range `0.0` <-> `1.0`. @@ -53,23 +53,25 @@ impl Fill { impl Default for Fill { fn default() -> Self { - Fill { colour: [1.0, 1.0, 1.0, 1.0] } + Fill { + colour: [1.0, 1.0, 1.0, 1.0], + } } } -/// Describes the properties of the border/stroke of the widget. +/// Describes the properties of the border/stroke of the widget. #[derive(Copy, Clone, Debug, PartialEq)] pub struct Border { /// The colour of the border described as RGBA between the range `0.0` <-> `1.0`. pub colour: [f32; 4], - - /// The width of the border. + + /// The width of the border. pub width: f32, } impl Border { - /// Creates a new [`Border`]. + /// Creates a new [`Border`]. pub fn new(colour: [f32; 4], width: f32) -> Self { Self { colour, width } } -} +} @@ -1,14 +1,14 @@ //! Defines the primitive [`Rectangle`] widget. -use std::any::Any; -use glam::{vec2, Mat2, Vec2}; -use crate::{KinoState, UiNode, WidgetId}; use crate::asset::Handle; use crate::math::Rect; use crate::rendering::texture::Texture; use crate::rendering::vertex::Vertex; -use crate::widgets::{Anchor, Border, ContaineredWidget, Fill, NativeWidget}; use crate::resp::WidgetResponse; +use crate::widgets::{Anchor, Border, ContaineredWidget, Fill, NativeWidget}; +use crate::{KinoState, UiNode, WidgetId}; +use glam::{Mat2, Vec2, vec2}; +use std::any::Any; use winit::event::{ElementState, MouseButton}; /// A simple and humble rectangle. @@ -153,8 +153,8 @@ impl Rectangle { fn render_body(&self, state: &mut KinoState, rect: &Rect, rot: Mat2) { let input = state.input(); - let hovering = rect.contains(input.mouse_position) - && state.clip_contains(input.mouse_position); + let hovering = + rect.contains(input.mouse_position) && state.clip_contains(input.mouse_position); let clicked = hovering && input.mouse_button == MouseButton::Left && input.mouse_press_state == ElementState::Pressed; @@ -177,17 +177,19 @@ impl Rectangle { }) .collect(); - state.batch.push(&fill_verts, &[0, 1, 2, 2, 3, 0], self.texture); + state + .batch + .push(&fill_verts, &[0, 1, 2, 2, 3, 0], self.texture); if let Some(border) = self.border { let half_width = border.width / 2.0; let outer_rect = Rect::new( rect.position - Vec2::splat(half_width), - rect.size + Vec2::splat(border.width) + rect.size + Vec2::splat(border.width), ); let inner_rect = Rect::new( rect.position + Vec2::splat(half_width), - rect.size - Vec2::splat(border.width) + rect.size - Vec2::splat(border.width), ); let outer_corners = outer_rect.corners(); @@ -196,17 +198,22 @@ impl Rectangle { let mut border_verts = Vec::with_capacity(8); for i in 0..4 { let outer_world = rot * (outer_corners[i] - rect.center()) + rect.center(); - border_verts.push(Vertex::new(outer_world.to_array(), border.colour, [0.0, 0.0])); + border_verts.push(Vertex::new( + outer_world.to_array(), + border.colour, + [0.0, 0.0], + )); let inner_world = rot * (inner_corners[i] - rect.center()) + rect.center(); - border_verts.push(Vertex::new(inner_world.to_array(), border.colour, [0.0, 0.0])); + border_verts.push(Vertex::new( + inner_world.to_array(), + border.colour, + [0.0, 0.0], + )); } let border_indices = [ - 0, 1, 3, 3, 2, 0, - 2, 3, 5, 5, 4, 2, - 4, 5, 7, 7, 6, 4, - 6, 7, 1, 1, 0, 6, + 0, 1, 3, 3, 2, 0, 2, 3, 5, 5, 4, 2, 4, 5, 7, 7, 6, 4, 6, 7, 1, 1, 0, 6, ]; state.batch.push(&border_verts, &border_indices, None); @@ -253,4 +260,4 @@ impl ContaineredWidget for Rectangle { fn as_any(&self) -> &dyn Any { self } -} +} @@ -1,7 +1,7 @@ -use crate::{KinoState, WidgetId}; use crate::widgets::layout::{Column, Row}; use crate::widgets::rect::Rectangle; use crate::widgets::text::Text; +use crate::{KinoState, WidgetId}; /// Shorthand for a standard rectangle widget. pub fn rect<F>(kino: &mut KinoState, id: impl Into<WidgetId>, configure: F) -> WidgetId @@ -19,11 +19,7 @@ where /// /// `configure` sets up the rectangle, and `contents` emits child widgets between /// start/end instructions. -pub fn rect_container<C>( - kino: &mut KinoState, - rect: Rectangle, - contents: C, -) -> WidgetId +pub fn rect_container<C>(kino: &mut KinoState, rect: Rectangle, contents: C) -> WidgetId where C: FnOnce(&mut KinoState), { @@ -35,7 +31,7 @@ where id } -/// Shorthand for a standard label. +/// Shorthand for a standard label. pub fn label<F>(kino: &mut KinoState, text: impl ToString, configure: F) -> WidgetId where F: FnOnce(&mut Text), @@ -46,11 +42,7 @@ where } /// Shorthand for [`Row`], used for displaying items that are displayed horizontally. -pub fn row<C>( - kino: &mut KinoState, - row: Row, - contents: C, -) -> WidgetId +pub fn row<C>(kino: &mut KinoState, row: Row, contents: C) -> WidgetId where C: FnOnce(&mut KinoState), { @@ -63,11 +55,7 @@ where } /// Shorthand for [`Column`], used for displaying items that are displayed vertically. -pub fn column<C>( - kino: &mut KinoState, - column: Column, - contents: C, -) -> WidgetId +pub fn column<C>(kino: &mut KinoState, column: Column, contents: C) -> WidgetId where C: FnOnce(&mut KinoState), { @@ -77,4 +65,4 @@ where contents(kino); kino.end_container(id); id -} +} @@ -1,19 +1,19 @@ -use std::any::Any; -use glam::Vec2; -use glyphon::{Attrs, AttrsOwned, Buffer, Color, Metrics, Shaping}; -use crate::{KinoState, WidgetId}; use crate::math::Rect; use crate::rendering::text::TextEntry; use crate::resp::WidgetResponse; use crate::widgets::NativeWidget; +use crate::{KinoState, WidgetId}; +use glam::Vec2; +use glyphon::{Attrs, AttrsOwned, Buffer, Color, Metrics, Shaping}; +use std::any::Any; use winit::event::{ElementState, MouseButton}; -/// Creates a label with the specified text and properties. -/// +/// Creates a label with the specified text and properties. +/// /// # Input -/// Responses are weird for text, as it recognises the input when you touch the text itself. -/// -/// If you want an area, you might be interested in [`crate::rect_container`] (with a transparent colour). +/// Responses are weird for text, as it recognises the input when you touch the text itself. +/// +/// If you want an area, you might be interested in [`crate::rect_container`] (with a transparent colour). pub struct Text { pub id: WidgetId, pub text: String, @@ -64,7 +64,6 @@ impl Text { self.metrics = metrics; self } - } impl NativeWidget for Text { @@ -92,10 +91,7 @@ impl NativeWidget for Text { } max_y = max_y.max(run.line_top + run.line_height); } - let intrinsic_size = Vec2::new( - max_x.max(1.0), - max_y.max(self.metrics.line_height), - ); + let intrinsic_size = Vec2::new(max_x.max(1.0), max_y.max(self.metrics.line_height)); let size = if self.size == Vec2::ZERO { intrinsic_size } else { @@ -106,8 +102,8 @@ impl NativeWidget for Text { let rect = Rect::new(top_left, size); let input = state.input(); - let hovering = rect.contains(input.mouse_position) - && state.clip_contains(input.mouse_position); + let hovering = + rect.contains(input.mouse_position) && state.clip_contains(input.mouse_position); let clicked = hovering && input.mouse_button == MouseButton::Left && input.mouse_press_state == ElementState::Pressed; @@ -137,4 +133,4 @@ impl NativeWidget for Text { fn as_any(&self) -> &dyn Any { self } -} +} @@ -1,8 +1,8 @@ -use std::sync::Arc; +use crate::KinoState; use glam::Vec2; +use std::sync::Arc; use winit::event::{ElementState, MouseButton, WindowEvent}; use winit::window::Window; -use crate::KinoState; pub struct KinoWinitWindowing { // mouse @@ -62,7 +62,9 @@ impl KinoWinitWindowing { self.mouse_press_state = *state; } WindowEvent::ScaleFactorChanged { scale_factor, .. } => { - if self.auto_scale { return; } + if self.auto_scale { + return; + } self.scale_factor = *scale_factor as f32; } WindowEvent::Resized(_) => {} @@ -107,4 +109,4 @@ impl KinoState { self.windowing.auto_scale = true; } } -} +} @@ -723,4 +723,4 @@ fun dropbear_set_last_error(err: String?) {{ Ok(output) } -} +} @@ -1,12 +1,12 @@ pub mod generator; -use std::fs; -use std::path::{Path, PathBuf}; -use clap::ValueEnum; -use tree_sitter::{Parser, Query, QueryCursor}; use crate::generator::Generator; use crate::generator::jvm::KotlinJVMGenerator; use crate::generator::native::KotlinNativeGenerator; +use clap::ValueEnum; +use std::fs; +use std::path::{Path, PathBuf}; +use tree_sitter::{Parser, Query, QueryCursor}; /// A group of manifests. #[derive(Debug, Clone)] @@ -311,7 +311,11 @@ pub enum Target { /// # Target Behaviours /// - [Target::Jvm] - Stores the manifest in `{output}/RunnableRegistry.kt` /// - [Target::Native] - Stored the manifest in `{output}/ScriptManifest.kt` -pub fn parse(input: impl AsRef<Path>, target: Target, output: impl AsRef<Path>) -> anyhow::Result<()> { +pub fn parse( + input: impl AsRef<Path>, + target: Target, + output: impl AsRef<Path>, +) -> anyhow::Result<()> { let input = input.as_ref().to_path_buf(); let output = output.as_ref().to_path_buf(); @@ -383,7 +387,6 @@ pub fn visit_kotlin_files( Ok(()) } - #[cfg(test)] mod tests { use super::*; @@ -1,4 +1,4 @@ -use clap::{Parser}; +use clap::Parser; use magna_carta::generator::{Generator, jvm::KotlinJVMGenerator, native::KotlinNativeGenerator}; use magna_carta::{KotlinProcessor, ScriptManifest, Target}; use std::fs; @@ -1,11 +1,11 @@ use std::sync::Arc; -use dropbear_engine::{scene::SceneCommand}; -use eucalyptus_core::command::{CommandBufferPoller, COMMAND_BUFFER, CommandBuffer, WindowCommand}; -use winit::window::CursorGrabMode; use crate::PlayMode; -use eucalyptus_core::scene::loading::IsSceneLoaded; use dropbear_engine::graphics::SharedGraphicsContext; +use dropbear_engine::scene::SceneCommand; +use eucalyptus_core::command::{COMMAND_BUFFER, CommandBuffer, CommandBufferPoller, WindowCommand}; +use eucalyptus_core::scene::loading::IsSceneLoaded; +use winit::window::CursorGrabMode; impl CommandBufferPoller for PlayMode { fn poll(&mut self, graphics: Arc<SharedGraphicsContext>) { @@ -17,13 +17,21 @@ impl CommandBufferPoller for PlayMode { if lock { let window = &graphics.window; window.set_cursor_visible(false); - if let Err(e) = window.set_cursor_grab(CursorGrabMode::Locked).or_else(|_| { - log_once::warn_once!("Using cursor grab fallback: CursorGrabMode::Confined"); - window.set_cursor_grab(CursorGrabMode::Confined) - }) { + if let Err(e) = + window.set_cursor_grab(CursorGrabMode::Locked).or_else(|_| { + log_once::warn_once!( + "Using cursor grab fallback: CursorGrabMode::Confined" + ); + window.set_cursor_grab(CursorGrabMode::Confined) + }) + { log_once::error_once!("Unable to grab mouse: {}", e); } - } else if let Err(e) = graphics.clone().window.set_cursor_grab(CursorGrabMode::None) { + } else if let Err(e) = graphics + .clone() + .window + .set_cursor_grab(CursorGrabMode::None) + { log_once::warn_once!("Failed to release cursor: {:?}", e); } else { log_once::info_once!("Released cursor"); @@ -39,7 +47,7 @@ impl CommandBufferPoller for PlayMode { }, CommandBuffer::Quit => { self.scene_command = SceneCommand::CloseWindow(graphics.window.id()); - }, + } CommandBuffer::SwitchSceneImmediate(scene_name) => { log::debug!("Immediate scene switch requested: {}", scene_name); let scene_to_load = IsSceneLoaded::new(scene_name); @@ -52,7 +60,9 @@ impl CommandBufferPoller for PlayMode { } CommandBuffer::SwitchToAsync(handle) => { if let Some(ref progress) = self.scene_progress { - if progress.requested_scene == handle.scene_name && progress.is_everything_loaded() { + if progress.requested_scene == handle.scene_name + && progress.is_everything_loaded() + { self.switch_to(progress.clone(), graphics.clone()); } } @@ -60,4 +70,4 @@ impl CommandBufferPoller for PlayMode { } } } -} +} @@ -1,10 +1,10 @@ +use crate::PlayMode; +use dropbear_engine::input::{Controller, Keyboard, Mouse}; use gilrs::{Button, GamepadId}; use winit::dpi::PhysicalPosition; use winit::event::MouseButton; use winit::event_loop::ActiveEventLoop; use winit::keyboard::KeyCode; -use dropbear_engine::input::{Controller, Keyboard, Mouse}; -use crate::PlayMode; impl Keyboard for PlayMode { fn key_down(&mut self, key: KeyCode, _event_loop: &ActiveEventLoop) { @@ -75,4 +75,4 @@ impl Controller for PlayMode { self.input_state.left_stick_position.remove(&id); self.input_state.right_stick_position.remove(&id); } -} +} @@ -1,44 +1,46 @@ //! Allows you to a launch play mode as another window. -use std::sync::Arc; -use crossbeam_channel::{unbounded, Receiver}; +use crossbeam_channel::{Receiver, unbounded}; use dropbear_engine::buffer::ResizableBuffer; +use dropbear_engine::future::{FutureHandle, FutureQueue}; +use dropbear_engine::graphics::{InstanceRaw, SharedGraphicsContext}; use dropbear_engine::pipelines::DropbearShaderPipeline; use dropbear_engine::pipelines::GlobalsUniform; use dropbear_engine::pipelines::light_cube::LightCubePipeline; use dropbear_engine::pipelines::shader::MainRenderPipeline; +use dropbear_engine::scene::SceneCommand; +use dropbear_engine::sky::{DEFAULT_SKY_TEXTURE, HdrLoader, SkyPipeline}; +use eucalyptus_core::command::COMMAND_BUFFER; use eucalyptus_core::component::ComponentRegistry; -use eucalyptus_core::physics::collider::shader::ColliderWireframePipeline; +use eucalyptus_core::input::InputState; +use eucalyptus_core::physics::PhysicsState; use eucalyptus_core::physics::collider::shader::ColliderInstanceRaw; +use eucalyptus_core::physics::collider::shader::ColliderWireframePipeline; use eucalyptus_core::physics::collider::{ColliderShapeKey, WireframeGeometry}; +use eucalyptus_core::ptr::{ + CommandBufferPtr, GraphicsContextPtr, InputStatePtr, PhysicsStatePtr, UiBufferPtr, WorldPtr, +}; +use eucalyptus_core::rapier3d::prelude::*; +use eucalyptus_core::register_components; +use eucalyptus_core::scene::loading::IsSceneLoaded; +use eucalyptus_core::scene::loading::{SCENE_LOADER, SceneLoadResult}; +use eucalyptus_core::scripting::{ScriptManager, ScriptTarget}; +use eucalyptus_core::states::{SCENES, Script, WorldLoadingStatus}; use futures::executor; use hecs::{Entity, World}; -use dropbear_engine::future::{FutureHandle, FutureQueue}; -use dropbear_engine::graphics::{InstanceRaw, SharedGraphicsContext}; -use dropbear_engine::scene::SceneCommand; -use eucalyptus_core::input::InputState; -use eucalyptus_core::scripting::{ScriptManager, ScriptTarget}; -use eucalyptus_core::states::{WorldLoadingStatus, SCENES, Script}; -use eucalyptus_core::scene::loading::{SceneLoadResult, SCENE_LOADER}; -use eucalyptus_core::ptr::{CommandBufferPtr, GraphicsContextPtr, InputStatePtr, PhysicsStatePtr, UiBufferPtr, WorldPtr}; -use eucalyptus_core::command::COMMAND_BUFFER; -use eucalyptus_core::scene::loading::IsSceneLoaded; +use kino_ui::KinoState; +use kino_ui::rendering::KinoWGPURenderer; +use kino_ui::windowing::KinoWinitWindowing; +use log::error; use std::collections::HashMap; use std::path::PathBuf; -use log::error; +use std::sync::Arc; use wgpu::SurfaceConfiguration; use winit::window::Fullscreen; -use dropbear_engine::sky::{HdrLoader, SkyPipeline, DEFAULT_SKY_TEXTURE}; -use eucalyptus_core::physics::PhysicsState; -use eucalyptus_core::rapier3d::prelude::*; -use eucalyptus_core::register_components; -use kino_ui::KinoState; -use kino_ui::windowing::KinoWinitWindowing; -use kino_ui::rendering::KinoWGPURenderer; -mod scene; -mod input; mod command; +mod input; +mod scene; #[cfg(feature = "debug")] fn find_jvm_library_path() -> PathBuf { @@ -51,7 +53,7 @@ fn find_jvm_library_path() -> PathBuf { } else { proj.project_path.clone() } - .join("build/libs"); + .join("build/libs"); let mut latest_jar: Option<(PathBuf, std::time::SystemTime)> = None; @@ -62,7 +64,9 @@ fn find_jvm_library_path() -> PathBuf { if let Some(filename) = path.file_name().and_then(|n| n.to_str()) { if filename.ends_with("-all.jar") { let metadata = entry.metadata().expect("Unable to get file metadata"); - let modified = metadata.modified().expect("Unable to get file modified time"); + let modified = metadata + .modified() + .expect("Unable to get file modified time"); match latest_jar { None => latest_jar = Some((path.clone(), modified)), @@ -84,14 +88,18 @@ fn find_jvm_library_path() -> PathBuf { fn find_jvm_library_path() -> PathBuf { let mut latest_jar: Option<(PathBuf, std::time::SystemTime)> = None; - for entry in std::fs::read_dir(std::env::current_exe().unwrap().parent().unwrap()).expect("Unable to read directory") { + for entry in std::fs::read_dir(std::env::current_exe().unwrap().parent().unwrap()) + .expect("Unable to read directory") + { let entry = entry.expect("Unable to get directory entry"); let path = entry.path(); if let Some(filename) = path.file_name().and_then(|n| n.to_str()) { if filename.ends_with("-all.jar") { let metadata = entry.metadata().expect("Unable to get file metadata"); - let modified = metadata.modified().expect("Unable to get file modified time"); + let modified = metadata + .modified() + .expect("Unable to get file modified time"); match latest_jar { None => latest_jar = Some((path.clone(), modified)), @@ -117,7 +125,7 @@ pub struct PlayMode { component_registry: Arc<ComponentRegistry>, active_camera: Option<Entity>, display_settings: DisplaySettings, - + // rendering light_cube_pipeline: Option<LightCubePipeline>, main_pipeline: Option<MainRenderPipeline>, @@ -170,7 +178,8 @@ impl PlayMode { let (collision_event_sender, ce_r) = std::sync::mpsc::channel::<CollisionEvent>(); let (contact_force_event_sender, cfe_r) = std::sync::mpsc::channel::<ContactForceEvent>(); - let event_collector = ChannelEventCollector::new(collision_event_sender, contact_force_event_sender); + let event_collector = + ChannelEventCollector::new(collision_event_sender, contact_force_event_sender); let result = Self { scene_command: SceneCommand::None, @@ -229,9 +238,12 @@ impl PlayMode { pub fn load_wgpu_nerdy_stuff<'a>(&mut self, graphics: Arc<SharedGraphicsContext>) { self.light_cube_pipeline = Some(LightCubePipeline::new(graphics.clone())); self.main_pipeline = Some(MainRenderPipeline::new(graphics.clone())); - self.shader_globals = Some(GlobalsUniform::new(graphics.clone(), Some("runtime shader globals"))); + self.shader_globals = Some(GlobalsUniform::new( + graphics.clone(), + Some("runtime shader globals"), + )); self.collider_wireframe_pipeline = Some(ColliderWireframePipeline::new(graphics.clone())); - + self.kino = Some(KinoState::new( KinoWGPURenderer::new( &graphics.device, @@ -250,7 +262,7 @@ impl PlayMode { &graphics.queue, DEFAULT_SKY_TEXTURE, 1080, - Some("sky texture") + Some("sky texture"), ); match sky_texture { @@ -267,7 +279,10 @@ impl PlayMode { let mut entity_tag_map: HashMap<String, Vec<Entity>> = HashMap::new(); for (entity_id, script) in self.world.query::<(Entity, &Script)>().iter() { for tag in &script.tags { - entity_tag_map.entry(tag.clone()).or_default().push(entity_id); + entity_tag_map + .entry(tag.clone()) + .or_default() + .push(entity_id); } } @@ -277,9 +292,9 @@ impl PlayMode { self.scripts_ready = false; - if let Err(e) = self - .script_manager - .init_script(None, entity_tag_map.clone(), target.clone()) + if let Err(e) = + self.script_manager + .init_script(None, entity_tag_map.clone(), target.clone()) { panic!("Failed to initialise scripts: {}", e); } else { @@ -296,18 +311,15 @@ impl PlayMode { .as_mut() .map(|kino| kino as *mut KinoState as UiBufferPtr) .unwrap_or(std::ptr::null_mut()); - - if let Err(e) = self - .script_manager - .load_script( - world_ptr, - input_ptr, - graphics_ptr, - graphics_context_ptr, - physics_ptr, - ui_ptr, - ) - { + + if let Err(e) = self.script_manager.load_script( + world_ptr, + input_ptr, + graphics_ptr, + graphics_context_ptr, + physics_ptr, + ui_ptr, + ) { panic!("Failed to load scripts: {}", e); } else { log::debug!("Loaded scripts successfully!"); @@ -320,8 +332,15 @@ impl PlayMode { /// Requests an asynchronous scene load, returning immediately and loading the scene in the background. /// /// It will not request the scene load if the currently rendered scene is the same as the requested scene. - pub fn request_async_scene_load(&mut self, graphics: Arc<SharedGraphicsContext>, requested_scene: IsSceneLoaded) { - log::debug!("Requested async scene load: {}", requested_scene.requested_scene); + pub fn request_async_scene_load( + &mut self, + graphics: Arc<SharedGraphicsContext>, + requested_scene: IsSceneLoaded, + ) { + log::debug!( + "Requested async scene load: {}", + requested_scene.requested_scene + ); let scene_name = requested_scene.requested_scene.clone(); self.scene_progress = Some(requested_scene); @@ -337,14 +356,21 @@ impl PlayMode { if let Some(id) = progress.id { let mut loader = SCENE_LOADER.lock(); if let Some(entry) = loader.get_entry_mut(id) { - if let Some(scene) = &self.current_scene && scene == &self.scene_progress.as_ref().unwrap().requested_scene { - log::debug!("Load scene async request cancelled because scene name is current"); - entry.result = SceneLoadResult::Error("Currently rendered scene name is the same as the requested scene".to_string()); + if let Some(scene) = &self.current_scene + && scene == &self.scene_progress.as_ref().unwrap().requested_scene + { + log::debug!( + "Load scene async request cancelled because scene name is current" + ); + entry.result = SceneLoadResult::Error( + "Currently rendered scene name is the same as the requested scene" + .to_string(), + ); self.world_loading_progress = None; self.world_receiver = None; self.physics_receiver = None; self.scene_progress = None; - return + return; } else { if entry.status.is_none() { entry.status = self.world_loading_progress.as_ref().cloned(); @@ -356,7 +382,11 @@ impl PlayMode { let mut scene_to_load = { let scenes = SCENES.read(); - let scene = scenes.iter().find(|s| s.scene_name == scene_name).unwrap().clone(); + let scene = scenes + .iter() + .find(|s| s.scene_name == scene_name) + .unwrap() + .clone(); scene }; @@ -365,26 +395,35 @@ impl PlayMode { let handle = FutureQueue::push(&graphics.future_queue, async move { let mut temp_world = World::new(); - let load_status = scene_to_load.load_into_world( - &mut temp_world, - graphics_cloned, - &component_registry.clone(), - Some(tx), - true, - ).await; + let load_status = scene_to_load + .load_into_world( + &mut temp_world, + graphics_cloned, + &component_registry.clone(), + Some(tx), + true, + ) + .await; match load_status { Ok(v) => { if world_tx.send(temp_world).is_err() { - log::warn!("Unable to send world: Receiver has been deallocated. This usually means a new scene load was requested before this one finished."); + log::warn!( + "Unable to send world: Receiver has been deallocated. This usually means a new scene load was requested before this one finished." + ); }; - if physics_tx.send(scene_to_load.physics_state.clone()).is_err() { + if physics_tx + .send(scene_to_load.physics_state.clone()) + .is_err() + { log::warn!("Unable to send physics state: Receiver has been deallocated"); } v } - Err(e) => { panic!("Failed to load scene [{}]: {}", scene_to_load.scene_name, e); } + Err(e) => { + panic!("Failed to load scene [{}]: {}", scene_to_load.scene_name, e); + } } }); @@ -397,10 +436,16 @@ impl PlayMode { } /// Requests an immediate scene load, blocking the current thread until the scene is fully loaded. - pub fn request_immediate_scene_load(&mut self, graphics: Arc<SharedGraphicsContext>, requested_scene: IsSceneLoaded) { - if let Some(scene) = &self.current_scene && scene == &requested_scene.requested_scene { + pub fn request_immediate_scene_load( + &mut self, + graphics: Arc<SharedGraphicsContext>, + requested_scene: IsSceneLoaded, + ) { + if let Some(scene) = &self.current_scene + && scene == &requested_scene.requested_scene + { log::debug!("Immediate scene load request cancelled because scene name is current"); - return + return; } let scene_name = requested_scene.requested_scene.clone(); @@ -420,7 +465,8 @@ impl PlayMode { let mut scene_to_load = { let scenes = SCENES.read(); - scenes.iter() + scenes + .iter() .find(|s| s.scene_name == scene_name) .cloned() .expect(&format!("Scene '{}' not found", scene_name)) @@ -433,17 +479,22 @@ impl PlayMode { let (loaded_world, camera_entity, physics_state) = executor::block_on(async move { let mut temp_world = World::new(); - let camera = scene_to_load.load_into_world( - &mut temp_world, - graphics_cloned, - &component_registry.clone(), - Some(tx), - true, - ).await; + let camera = scene_to_load + .load_into_world( + &mut temp_world, + graphics_cloned, + &component_registry.clone(), + Some(tx), + true, + ) + .await; match camera { Ok(cam) => (temp_world, cam, scene_to_load.physics_state), - Err(e) => panic!("Failed to immediately load scene [{}]: {}", scene_to_load.scene_name, e), + Err(e) => panic!( + "Failed to immediately load scene [{}]: {}", + scene_to_load.scene_name, e + ), } }); @@ -466,8 +517,15 @@ impl PlayMode { } /// Switches to a new scene, clearing the current world and preparing to load the new scene. - pub fn switch_to(&mut self, scene_progress: IsSceneLoaded, graphics: Arc<SharedGraphicsContext>) { - log::debug!("Switching to new scene requested: {}", scene_progress.requested_scene); + pub fn switch_to( + &mut self, + scene_progress: IsSceneLoaded, + graphics: Arc<SharedGraphicsContext>, + ) { + log::debug!( + "Switching to new scene requested: {}", + scene_progress.requested_scene + ); if scene_progress.is_everything_loaded() { if let Some(new_world) = self.pending_world.take() { @@ -573,4 +631,4 @@ pub enum WindowMode { Maximized, Fullscreen, BorderlessFullscreen, -} +} @@ -1,39 +1,38 @@ use std::sync::Arc; -use std::collections::HashMap; -use dropbear_engine::pipelines::DropbearShaderPipeline; -use dropbear_engine::graphics::CommandEncoder; -use eucalyptus_core::egui::CentralPanel; -use eucalyptus_core::physics::collider::ColliderGroup; -use eucalyptus_core::physics::collider::ColliderShapeKey; -use eucalyptus_core::physics::collider::shader::ColliderInstanceRaw; -use glam::{vec2, DMat4, DQuat, DVec3, Mat4, Quat, Vec2}; -use hecs::Entity; -use wgpu::util::DeviceExt; -use winit::event_loop::ActiveEventLoop; -use winit::event::WindowEvent; +use crate::PlayMode; use dropbear_engine::animation::AnimationComponent; use dropbear_engine::asset::{ASSET_REGISTRY, Handle}; -use dropbear_engine::camera::Camera; use dropbear_engine::buffer::ResizableBuffer; +use dropbear_engine::camera::Camera; use dropbear_engine::entity::{EntityTransform, MeshRenderer, Transform}; +use dropbear_engine::graphics::CommandEncoder; use dropbear_engine::graphics::{InstanceRaw, SharedGraphicsContext}; -use dropbear_engine::lighting::{Light}; -use dropbear_engine::lighting::MAX_LIGHTS; +use dropbear_engine::lighting::Light; use dropbear_engine::model::{DrawLight, DrawModel}; +use dropbear_engine::pipelines::DropbearShaderPipeline; use dropbear_engine::scene::{Scene, SceneCommand}; use eucalyptus_core::command::CommandBufferPoller; +use eucalyptus_core::egui::CentralPanel; use eucalyptus_core::hierarchy::{EntityTransformExt, Parent}; +use eucalyptus_core::physics::collider::ColliderGroup; +use eucalyptus_core::physics::collider::ColliderShapeKey; +use eucalyptus_core::physics::collider::shader::ColliderInstanceRaw; +use eucalyptus_core::physics::collider::shader::create_wireframe_geometry; use eucalyptus_core::physics::kcc::KCC; -use eucalyptus_core::rapier3d::prelude::QueryFilter; use eucalyptus_core::rapier3d::geometry::SharedShape; -use eucalyptus_core::states::{Label, PROJECT}; +use eucalyptus_core::rapier3d::prelude::QueryFilter; +use eucalyptus_core::scene::loading::{IsSceneLoaded, SCENE_LOADER, SceneLoadResult}; use eucalyptus_core::states::SCENES; -use eucalyptus_core::scene::loading::{IsSceneLoaded, SceneLoadResult, SCENE_LOADER}; -use crate::PlayMode; -use eucalyptus_core::physics::collider::shader::create_wireframe_geometry; -use kino_ui::widgets::{Border, Fill}; +use eucalyptus_core::states::{Label, PROJECT}; +use glam::{DMat4, DQuat, DVec3, Mat4, Quat, Vec2, vec2}; +use hecs::Entity; use kino_ui::widgets::rect::Rectangle; +use kino_ui::widgets::{Border, Fill}; +use std::collections::HashMap; +use wgpu::util::DeviceExt; +use winit::event::WindowEvent; +use winit::event_loop::ActiveEventLoop; impl Scene for PlayMode { fn load(&mut self, graphics: Arc<SharedGraphicsContext>) { @@ -41,13 +40,16 @@ impl Scene for PlayMode { // yak.set_automatic_viewport(false); // yak.set_automatic_scale_factor(false); // self.yakui_winit = Some(yak); - + if self.current_scene.is_none() { let initial_scene = if let Some(s) = &self.initial_scene { s.clone() } else { let proj = PROJECT.read(); - proj.runtime_settings.initial_scene.clone().expect("No initial scene set in project settings") + proj.runtime_settings + .initial_scene + .clone() + .expect("No initial scene set in project settings") }; log::debug!("Loading initial scene: {}", initial_scene); @@ -60,10 +62,16 @@ impl Scene for PlayMode { fn physics_update(&mut self, dt: f32, _graphics: Arc<SharedGraphicsContext>) { if self.scripts_ready { - let _ = self.script_manager.physics_update_script(self.world.as_mut(), dt as f64); + let _ = self + .script_manager + .physics_update_script(self.world.as_mut(), dt as f64); } - let world = self.world.iter().map(|e| (e.get::<&Label>().unwrap().to_string(), e.entity())).collect::<Vec<_>>(); + let world = self + .world + .iter() + .map(|e| (e.get::<&Label>().unwrap().to_string(), e.entity())) + .collect::<Vec<_>>(); log::info!("World contents [len={}]: ", world.len()); for (l, e) in world { log::info!("{} -> {:?}", l, e); @@ -74,7 +82,11 @@ impl Scene for PlayMode { } for (e, l, _) in self.world.query::<(Entity, &Label, &KCC)>().iter() { - log_once::debug_once!("This entity [{:?}, label = {}] has the KCC (KinematicCharacterController) component attached", e, l); + log_once::debug_once!( + "This entity [{:?}, label = {}] has the KCC (KinematicCharacterController) component attached", + e, + l + ); } if !self.physics_state.collision_events_to_deal_with.is_empty() { @@ -86,7 +98,11 @@ impl Scene for PlayMode { .collect(); for entity in entities_with_collisions { - let Some(collisions) = self.physics_state.collision_events_to_deal_with.remove(&entity) else { + let Some(collisions) = self + .physics_state + .collision_events_to_deal_with + .remove(&entity) + else { continue; }; if collisions.is_empty() { @@ -97,17 +113,18 @@ impl Scene for PlayMode { kcc.collisions = collisions.clone(); } - let (label, kcc_controller) = match self.world.query_one::<(&Label, &KCC)>(entity).get() { - Ok(v) => { - (v.0.clone(), v.1.clone()) - } - Err(e) => { - log_once::warn_once!("Unable to query {:?}: {}", entity, e); - continue; - } - }; + let (label, kcc_controller) = + match self.world.query_one::<(&Label, &KCC)>(entity).get() { + Ok(v) => (v.0.clone(), v.1.clone()), + Err(e) => { + log_once::warn_once!("Unable to query {:?}: {}", entity, e); + continue; + } + }; - let Some(rigid_body_handle) = self.physics_state.bodies_entity_map.get(&label).copied() else { + let Some(rigid_body_handle) = + self.physics_state.bodies_entity_map.get(&label).copied() + else { continue; }; @@ -129,7 +146,11 @@ impl Scene for PlayMode { (collider.shared_shape().clone(), collider.mass()) }; - let character_mass = if character_mass > 0.0 { character_mass } else { 1.0 }; + let character_mass = if character_mass > 0.0 { + character_mass + } else { + 1.0 + }; let filter = QueryFilter::default().exclude_rigid_body(rigid_body_handle); let dispatcher = self.physics_state.narrow_phase.query_dispatcher(); @@ -138,37 +159,49 @@ impl Scene for PlayMode { let bodies = &mut self.physics_state.bodies; let colliders = &mut self.physics_state.colliders; - let mut query_pipeline_mut = broad_phase.as_query_pipeline_mut( - dispatcher, - bodies, - colliders, - filter, - ); - - kcc_controller.controller.solve_character_collision_impulses( - dt, - &mut query_pipeline_mut, - character_shape.as_ref(), - character_mass, - &collisions, - ); + let mut query_pipeline_mut = + broad_phase.as_query_pipeline_mut(dispatcher, bodies, colliders, filter); + + kcc_controller + .controller + .solve_character_collision_impulses( + dt, + &mut query_pipeline_mut, + character_shape.as_ref(), + character_mass, + &collisions, + ); } } - + let mut entity_label_map = HashMap::new(); for (entity, label) in self.world.query::<(Entity, &Label)>().iter() { entity_label_map.insert(entity, label.clone()); } - - self.physics_state.step(entity_label_map, &mut self.physics_pipeline, &(), &self.event_collector); + + self.physics_state.step( + entity_label_map, + &mut self.physics_pipeline, + &(), + &self.event_collector, + ); if self.scripts_ready { - if let (Some(ce_r), Some(cfe_r)) = (&self.collision_event_receiver, &self.collision_force_event_receiver) { + if let (Some(ce_r), Some(cfe_r)) = ( + &self.collision_event_receiver, + &self.collision_force_event_receiver, + ) { // both are not crucial, so no need to panic while let Ok(event) = ce_r.try_recv() { log_once::debug_once!("Collision event received"); - if let Some(evt) = eucalyptus_core::types::CollisionEvent::from_rapier3d(&self.physics_state, event) { - if let Err(err) = self.script_manager.collision_event_script(self.world.as_mut(), &evt) { + if let Some(evt) = eucalyptus_core::types::CollisionEvent::from_rapier3d( + &self.physics_state, + event, + ) { + if let Err(err) = self + .script_manager + .collision_event_script(self.world.as_mut(), &evt) + { log::error!("Script collision event error: {}", err); } } @@ -176,8 +209,14 @@ impl Scene for PlayMode { while let Ok(event) = cfe_r.try_recv() { log_once::debug_once!("Contact force event received"); - if let Some(evt) = eucalyptus_core::types::ContactForceEvent::from_rapier3d(&self.physics_state, event) { - if let Err(err) = self.script_manager.contact_force_event_script(self.world.as_mut(), &evt) { + if let Some(evt) = eucalyptus_core::types::ContactForceEvent::from_rapier3d( + &self.physics_state, + event, + ) { + if let Err(err) = self + .script_manager + .contact_force_event_script(self.world.as_mut(), &evt) + { log::error!("Script contact force event error: {}", err); } } @@ -187,7 +226,11 @@ impl Scene for PlayMode { let mut sync_updates = Vec::new(); - for (entity, label, _) in self.world.query::<(Entity, &Label, &EntityTransform)>().iter() { + for (entity, label, _) in self + .world + .query::<(Entity, &Label, &EntityTransform)>() + .iter() + { if let Some(handle) = self.physics_state.bodies_entity_map.get(label) { if let Some(body) = self.physics_state.bodies.get(*handle) { let p = body.translation(); @@ -196,14 +239,13 @@ impl Scene for PlayMode { sync_updates.push(( entity, DVec3::new(p.x as f64, p.y as f64, p.z as f64), - Quat::from(r.clone()).as_dquat() + Quat::from(r.clone()).as_dquat(), )); } } } for (entity, new_world_pos, new_world_rot) in sync_updates { - let parent_world = if let Ok(parent_comp) = self.world.get::<&Parent>(entity) { let parent_entity = parent_comp.parent(); if let Ok(p_transform) = self.world.get::<&EntityTransform>(parent_entity) { @@ -259,8 +301,14 @@ impl Scene for PlayMode { } if let Some(ref progress) = self.scene_progress { - if !progress.scene_handle_requested && self.world_receiver.is_none() && self.scene_loading_handle.is_none() { - log::debug!("Starting async load for scene: {}", progress.requested_scene); + if !progress.scene_handle_requested + && self.world_receiver.is_none() + && self.scene_loading_handle.is_none() + { + log::debug!( + "Starting async load for scene: {}", + progress.requested_scene + ); let scene_to_load = IsSceneLoaded::new(progress.requested_scene.clone()); self.request_async_scene_load(graphics.clone(), scene_to_load); } @@ -326,15 +374,20 @@ impl Scene for PlayMode { } if self.scripts_ready { - if let Err(e) = self.script_manager.update_script(self.world.as_mut(), dt as f64) { + if let Err(e) = self + .script_manager + .update_script(self.world.as_mut(), dt as f64) + { panic!("Script update error: {}", e); } } - self.component_registry - .update_components(self.world.as_mut(), &mut self.physics_state, dt, graphics.clone()); - - + self.component_registry.update_components( + self.world.as_mut(), + &mut self.physics_state, + dt, + graphics.clone(), + ); if let Some(l) = &mut self.light_cube_pipeline { l.update(graphics.clone(), &self.world); @@ -346,26 +399,35 @@ impl Scene for PlayMode { use crate::WindowMode; ui.menu_button("Window", |ui| { ui.menu_button("Window Mode", |ui| { - let is_windowed = matches!(self.display_settings.window_mode, WindowMode::Windowed); + let is_windowed = + matches!(self.display_settings.window_mode, WindowMode::Windowed); if ui.selectable_label(is_windowed, "Windowed").clicked() { self.display_settings.window_mode = WindowMode::Windowed; ui.close(); } - let is_maximized = matches!(self.display_settings.window_mode, WindowMode::Maximized); + let is_maximized = + matches!(self.display_settings.window_mode, WindowMode::Maximized); if ui.selectable_label(is_maximized, "Maximized").clicked() { self.display_settings.window_mode = WindowMode::Maximized; ui.close(); } - let is_fullscreen = matches!(self.display_settings.window_mode, WindowMode::Fullscreen); + let is_fullscreen = + matches!(self.display_settings.window_mode, WindowMode::Fullscreen); if ui.selectable_label(is_fullscreen, "Fullscreen").clicked() { self.display_settings.window_mode = WindowMode::Fullscreen; ui.close(); } - let is_borderless = matches!(self.display_settings.window_mode, WindowMode::BorderlessFullscreen); - if ui.selectable_label(is_borderless, "Borderless Fullscreen").clicked() { + let is_borderless = matches!( + self.display_settings.window_mode, + WindowMode::BorderlessFullscreen + ); + if ui + .selectable_label(is_borderless, "Borderless Fullscreen") + .clicked() + { self.display_settings.window_mode = WindowMode::BorderlessFullscreen; ui.close(); } @@ -373,8 +435,12 @@ impl Scene for PlayMode { ui.separator(); - ui.checkbox(&mut self.display_settings.maintain_aspect_ratio, "Maintain aspect ratio"); - ui.checkbox(&mut self.display_settings.vsync, "VSync").clicked(); + ui.checkbox( + &mut self.display_settings.maintain_aspect_ratio, + "Maintain aspect ratio", + ); + ui.checkbox(&mut self.display_settings.vsync, "VSync") + .clicked(); }); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { @@ -382,7 +448,8 @@ impl Scene for PlayMode { ui.add_enabled_ui(true, |ui| { if ui.button("⏹").clicked() { log::debug!("Menu button Stop button pressed"); - self.scene_command = SceneCommand::CloseWindow(graphics.window.id()); + self.scene_command = + SceneCommand::CloseWindow(graphics.window.id()); } }); @@ -402,8 +469,10 @@ impl Scene for PlayMode { ui.centered_and_justified(|ui| { egui_extras::install_image_loaders(&graphics.get_egui_context()); ui.add( - egui::Image::new(egui::include_image!("../../../resources/eucalyptus-editor.png")) - .max_width(128.0) + egui::Image::new(egui::include_image!( + "../../../resources/eucalyptus-editor.png" + )) + .max_width(128.0), ) }); return; @@ -429,13 +498,14 @@ impl Scene for PlayMode { cam.update_view_proj(); cam.update(graphics.clone()); - let (display_width, display_height) = if self.display_settings.maintain_aspect_ratio { - let width = available_size.x; - let height = width / cam.aspect as f32; - (width, height) - } else { - (available_size.x, available_size.y) - }; + let (display_width, display_height) = + if self.display_settings.maintain_aspect_ratio { + let width = available_size.x; + let height = width / cam.aspect as f32; + (width, height) + } else { + (available_size.x, available_size.y) + }; let center_x = available_rect.center().x; let center_y = available_rect.center().y; @@ -476,7 +546,7 @@ impl Scene for PlayMode { // UI_CONTEXT.with(|yakui_cell| { // let yak = yakui_cell.borrow(); // let mut yakui = yak.yakui_state.lock(); - // + // // let tex_size = graphics.viewport_texture.size; // let viewport_size = yakui::geometry::Vec2::new( // tex_size.width as f32, @@ -488,21 +558,23 @@ impl Scene for PlayMode { // viewport_size, // )); // yakui.set_scale_factor(graphics.window.scale_factor() as f32); - // + // // yakui.start(); - // + // // // eucalyptus_core::ui::poll(); - // + // // yakui.finish(); // }); if let Some(kino) = &mut self.kino { // #[allow(dead_code)] let no_texture = kino.add_texture_from_bytes( - &graphics.device, &graphics.queue, + &graphics.device, + &graphics.queue, "no texture", include_bytes!("../../../resources/textures/no-texture.png"), - 256, 256 + 256, + 256, ); let parent = kino_ui::rect_container( @@ -511,19 +583,23 @@ impl Scene for PlayMode { .fill(Fill::new([1.0, 1.0, 1.0, 1.0])) .size(vec2(400.0, 400.0)), |kino| { - kino.add_widget(Rectangle::new("rect") - .texture(no_texture) - .size(vec2(128.0, 100.0)) - .border(Border::new([1.0, 0.0, 0.0, 1.0], 3.0)) - .fill(Fill::new([1.0, 1.0, 1.0, 1.0])) - .texture(no_texture) - .build() + kino.add_widget( + Rectangle::new("rect") + .texture(no_texture) + .size(vec2(128.0, 100.0)) + .border(Border::new([1.0, 0.0, 0.0, 1.0], 3.0)) + .fill(Fill::new([1.0, 1.0, 1.0, 1.0])) + .texture(no_texture) + .build(), ); - } + }, ); kino_ui::label(kino, "Hello World!", |l| { - l.position = vec2(graphics.viewport_texture.size.width as f32 / 2.0, graphics.viewport_texture.size.height as f32 / 2.0); + l.position = vec2( + graphics.viewport_texture.size.width as f32 / 2.0, + graphics.viewport_texture.size.height as f32 / 2.0, + ); l.metrics.font_size = 30.0; }); @@ -574,7 +650,12 @@ impl Scene for PlayMode { }; log_once::debug_once!("Active camera found: {:?}", active_camera); - let q = self.world.query_one::<&Camera>(active_camera).get().ok().cloned(); + let q = self + .world + .query_one::<&Camera>(active_camera) + .get() + .ok() + .cloned(); let Some(camera) = q else { return; @@ -631,14 +712,14 @@ impl Scene for PlayMode { lights }; - if let Some(globals) = &mut self.shader_globals { - let enabled_count = lights - .iter() - .filter(|light| light.component.enabled) - .count() as u32; - globals.set_num_lights(enabled_count); - globals.write(&graphics.queue); - } + if let Some(globals) = &mut self.shader_globals { + let enabled_count = lights + .iter() + .filter(|light| light.component.enabled) + .count() as u32; + globals.set_num_lights(enabled_count); + globals.write(&graphics.queue); + } let mut static_batches: HashMap<u64, Vec<InstanceRaw>> = HashMap::new(); let mut animated_instances: Vec<(u64, InstanceRaw, wgpu::Buffer)> = Vec::new(); @@ -671,17 +752,14 @@ impl Scene for PlayMode { continue; }; - let instance_buffer = self - .instance_buffer_cache - .entry(handle) - .or_insert_with(|| { - ResizableBuffer::new( - &graphics.device, - instances.len().max(1), - wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - "Runtime Instance Buffer", - ) - }); + let instance_buffer = self.instance_buffer_cache.entry(handle).or_insert_with(|| { + ResizableBuffer::new( + &graphics.device, + instances.len().max(1), + wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + "Runtime Instance Buffer", + ) + }); instance_buffer.write(&graphics.device, &graphics.queue, &instances); prepared_models.push((model, handle, instances.len() as u32)); @@ -689,29 +767,28 @@ impl Scene for PlayMode { let registry = ASSET_REGISTRY.read(); { - let mut render_pass = encoder - .begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("light cube render pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), - depth_slice: None, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &graphics.depth_texture.view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }), - stencil_ops: None, + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("light cube render pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: hdr.view(), + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &graphics.depth_texture.view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, }), - occlusion_query_set: None, - timestamp_writes: None, - }); + stencil_ops: None, + }), + occlusion_query_set: None, + timestamp_writes: None, + }); if let Some(light_pipeline) = &self.light_cube_pipeline { render_pass.set_pipeline(light_pipeline.pipeline()); for light in &lights { @@ -728,31 +805,31 @@ impl Scene for PlayMode { continue; }; - render_pass.draw_light_model( - model, - &camera.bind_group, - &light.bind_group, - ); + render_pass.draw_light_model(model, &camera.bind_group, &light.bind_group); } } } if self.default_skinning_bind_group.is_none() { let identity = [Mat4::IDENTITY.to_cols_array_2d()]; - let buffer = graphics.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("default skinning buffer"), - contents: bytemuck::cast_slice(&identity), - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - }); + let buffer = graphics + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("default skinning buffer"), + contents: bytemuck::cast_slice(&identity), + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + }); - let bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("default skinning bind group"), - layout: &graphics.layouts.skinning_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: buffer.as_entire_binding(), - }], - }); + let bind_group = graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("default skinning bind group"), + layout: &graphics.layouts.skinning_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: buffer.as_entire_binding(), + }], + }); self.default_skinning_buffer = Some(buffer); self.default_skinning_bind_group = Some(bind_group); @@ -770,68 +847,67 @@ impl Scene for PlayMode { .shader_globals .as_ref() .expect("Shader globals not initialised"); - let globals_camera_bind_group = graphics.device.create_bind_group( - &wgpu::BindGroupDescriptor { - label: Some("scene globals+camera bind group"), - layout: &graphics.layouts.scene_globals_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: globals.buffer.buffer().as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: camera.buffer().as_entire_binding(), - }, - ], - }, - ); - let light_skin_bind_group = graphics.device.create_bind_group( - &wgpu::BindGroupDescriptor { - label: Some("scene light+skin bind group"), - layout: &graphics.layouts.scene_light_skin_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: lcp.light_buffer().as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: default_skinning_buffer.as_entire_binding(), - }, - ], - }, - ); + let globals_camera_bind_group = + graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("scene globals+camera bind group"), + layout: &graphics.layouts.scene_globals_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: globals.buffer.buffer().as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: camera.buffer().as_entire_binding(), + }, + ], + }); + let light_skin_bind_group = + graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("scene light+skin bind group"), + layout: &graphics.layouts.scene_light_skin_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: lcp.light_buffer().as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: default_skinning_buffer.as_entire_binding(), + }, + ], + }); - let mut render_pass = encoder - .begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("model render pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), - depth_slice: None, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &graphics.depth_texture.view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }), - stencil_ops: None, + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("model render pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: hdr.view(), + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &graphics.depth_texture.view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, }), - occlusion_query_set: None, - timestamp_writes: None, - }); + stencil_ops: None, + }), + occlusion_query_set: None, + timestamp_writes: None, + }); render_pass.set_pipeline(pipeline.pipeline()); if let Some(instance_buffer) = self.instance_buffer_cache.get(&handle) { - render_pass.set_vertex_buffer( - 1, - instance_buffer.slice(instance_count as usize), - ); + render_pass + .set_vertex_buffer(1, instance_buffer.slice(instance_count as usize)); } else { continue; } @@ -849,22 +925,23 @@ impl Scene for PlayMode { .shader_globals .as_ref() .expect("Shader globals not initialised"); - let globals_camera_bind_group = graphics.device.create_bind_group( - &wgpu::BindGroupDescriptor { - label: Some("scene globals+camera bind group"), - layout: &graphics.layouts.scene_globals_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: globals.buffer.buffer().as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: camera.buffer().as_entire_binding(), - }, - ], - }, - ); + let globals_camera_bind_group = + graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("scene globals+camera bind group"), + layout: &graphics.layouts.scene_globals_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: globals.buffer.buffer().as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: camera.buffer().as_entire_binding(), + }, + ], + }); for (handle, instance, skin_buffer) in animated_instances { let Some(model) = registry.get_model(Handle::new(handle)) else { @@ -872,41 +949,38 @@ impl Scene for PlayMode { continue; }; - let instance_buffer = self - .animated_instance_buffer - .get_or_insert_with(|| { - ResizableBuffer::new( - &graphics.device, - 1, - wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - "Runtime Animated Instance Buffer", - ) - }); + let instance_buffer = self.animated_instance_buffer.get_or_insert_with(|| { + ResizableBuffer::new( + &graphics.device, + 1, + wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + "Runtime Animated Instance Buffer", + ) + }); instance_buffer.write(&graphics.device, &graphics.queue, &[instance]); - let mut render_pass = encoder - .begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("model render pass (animated)"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), - depth_slice: None, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &graphics.depth_texture.view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }), - stencil_ops: None, + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("model render pass (animated)"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: hdr.view(), + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &graphics.depth_texture.view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, }), - occlusion_query_set: None, - timestamp_writes: None, - }); + stencil_ops: None, + }), + occlusion_query_set: None, + timestamp_writes: None, + }); render_pass.set_pipeline(pipeline.pipeline()); render_pass.set_vertex_buffer(1, instance_buffer.slice(1)); @@ -954,35 +1028,36 @@ impl Scene for PlayMode { if show_hitboxes { if let Some(collider_pipeline) = &self.collider_wireframe_pipeline { - let mut render_pass = encoder - .begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("model render pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), - depth_slice: None, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &graphics.depth_texture.view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }), - stencil_ops: None, + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("model render pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: hdr.view(), + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &graphics.depth_texture.view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, }), - occlusion_query_set: None, - timestamp_writes: None, - }); - + stencil_ops: None, + }), + occlusion_query_set: None, + timestamp_writes: None, + }); + render_pass.set_pipeline(&collider_pipeline.pipeline); render_pass.set_bind_group(0, &camera.bind_group, &[]); - let mut instances_by_shape: HashMap<ColliderShapeKey, Vec<ColliderInstanceRaw>> = - HashMap::new(); + let mut instances_by_shape: HashMap< + ColliderShapeKey, + Vec<ColliderInstanceRaw>, + > = HashMap::new(); let mut q = self.world.query::<(&EntityTransform, &ColliderGroup)>(); for (entity_transform, group) in q.iter() { @@ -1007,12 +1082,11 @@ impl Scene for PlayMode { let key = ColliderShapeKey::from(&collider.shape); instances_by_shape.entry(key).or_default().push(instance); - self.collider_wireframe_geometry_cache.entry(key).or_insert_with(|| { - create_wireframe_geometry( - graphics.clone(), - &collider.shape, - ) - }); + self.collider_wireframe_geometry_cache + .entry(key) + .or_insert_with(|| { + create_wireframe_geometry(graphics.clone(), &collider.shape) + }); } } @@ -1029,47 +1103,39 @@ impl Scene for PlayMode { draws.push((key, start, count)); } - let instance_buffer = self.collider_instance_buffer.get_or_insert_with(|| { - ResizableBuffer::new( - &graphics.device, - all_instances.len().max(10), - wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - "Collider Instance Buffer", - ) - }); - instance_buffer.write( - &graphics.device, - &graphics.queue, - &all_instances, - ); + let instance_buffer = + self.collider_instance_buffer.get_or_insert_with(|| { + ResizableBuffer::new( + &graphics.device, + all_instances.len().max(10), + wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + "Collider Instance Buffer", + ) + }); + instance_buffer.write(&graphics.device, &graphics.queue, &all_instances); for (key, start, count) in draws { - let Some(geometry) = self.collider_wireframe_geometry_cache.get(&key) else { + let Some(geometry) = self.collider_wireframe_geometry_cache.get(&key) + else { continue; }; - let start_bytes = - (start * std::mem::size_of::<ColliderInstanceRaw>()) as wgpu::BufferAddress; - let end_bytes = - ((start + count) * std::mem::size_of::<ColliderInstanceRaw>()) as wgpu::BufferAddress; + let start_bytes = (start * std::mem::size_of::<ColliderInstanceRaw>()) + as wgpu::BufferAddress; + let end_bytes = ((start + count) + * std::mem::size_of::<ColliderInstanceRaw>()) + as wgpu::BufferAddress; render_pass.set_vertex_buffer( 1, instance_buffer.buffer().slice(start_bytes..end_bytes), ); - render_pass.set_vertex_buffer( - 0, - geometry.vertex_buffer.slice(..), - ); + render_pass.set_vertex_buffer(0, geometry.vertex_buffer.slice(..)); render_pass.set_index_buffer( geometry.index_buffer.slice(..), wgpu::IndexFormat::Uint16, ); - render_pass.draw_indexed( - 0..geometry.index_count, - 0, - 0..count as u32, - ); + render_pass.draw_indexed(0..geometry.index_count, 0, 0..count as u32); } } } @@ -1158,4 +1224,3 @@ impl Scene for PlayMode { std::mem::replace(&mut self.scene_command, SceneCommand::None) } } - @@ -93,14 +93,13 @@ fn find_in_path(name: &str) -> Option<PathBuf> { fn check_cached_download() -> Option<PathBuf> { if let Ok(out_dir) = std::env::var("OUT_DIR") { - let cached = PathBuf::from(out_dir) - .join("slangc") - .join("bin") - .join(if cfg!(target_os = "windows") { + let cached = PathBuf::from(out_dir).join("slangc").join("bin").join( + if cfg!(target_os = "windows") { "slangc.exe" } else { "slangc" - }); + }, + ); if cached.exists() { return Some(cached); @@ -108,15 +107,13 @@ fn check_cached_download() -> Option<PathBuf> { } if let Some(cache_dir) = dirs::cache_dir() { - let cached = cache_dir - .join("slank") - .join("slangc") - .join("bin") - .join(if cfg!(target_os = "windows") { + let cached = cache_dir.join("slank").join("slangc").join("bin").join( + if cfg!(target_os = "windows") { "slangc.exe" } else { "slangc" - }); + }, + ); if cached.exists() { return Some(cached); @@ -128,7 +125,6 @@ fn check_cached_download() -> Option<PathBuf> { #[cfg(feature = "download-slang")] fn download_slang() -> anyhow::Result<PathBuf> { - let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); @@ -152,16 +148,17 @@ fn download_slang() -> anyhow::Result<PathBuf> { extract_archive(&archive_path, &cache_dir)?; - let slangc_exe = cache_dir - .join("bin") - .join(if cfg!(target_os = "windows") { - "slangc.exe" - } else { - "slangc" - }); + let slangc_exe = cache_dir.join("bin").join(if cfg!(target_os = "windows") { + "slangc.exe" + } else { + "slangc" + }); if !slangc_exe.exists() { - return Err(anyhow::anyhow!("slangc not found after extraction at: {}", slangc_exe.display())); + return Err(anyhow::anyhow!( + "slangc not found after extraction at: {}", + slangc_exe.display() + )); } #[cfg(unix)] @@ -208,7 +205,8 @@ fn get_latest_slang_version() -> anyhow::Result<String> { )); } - let release: Release = response.json() + let release: Release = response + .json() .map_err(|e| anyhow::anyhow!("Failed to parse GitHub response: {}", e))?; Ok(release.tag_name.trim_start_matches('v').to_string()) @@ -242,19 +240,24 @@ fn download_file(url: &str, dest: &PathBuf) -> anyhow::Result<()> { .build() .map_err(|e| anyhow::anyhow!("Failed to create HTTP client: {}", e))?; - let mut response = client.get(url) + let mut response = client + .get(url) .header(reqwest::header::USER_AGENT, "dropbear-slank-build") .send() .map_err(|e| anyhow::anyhow!("Failed to download: {}", e))?; if !response.status().is_success() { - return Err(anyhow::anyhow!("Download failed with status: {}", response.status())); + return Err(anyhow::anyhow!( + "Download failed with status: {}", + response.status() + )); } - let mut file = std::fs::File::create(dest) - .map_err(|e| anyhow::anyhow!("Failed to create file: {}", e))?; + let mut file = + std::fs::File::create(dest).map_err(|e| anyhow::anyhow!("Failed to create file: {}", e))?; - response.copy_to(&mut file) + response + .copy_to(&mut file) .map_err(|e| anyhow::anyhow!("Failed to save file content at {}: {}", dest.display(), e))?; Ok(()) @@ -266,18 +269,20 @@ fn extract_archive(archive: &PathBuf, dest: &PathBuf) -> anyhow::Result<()> { .map_err(|e| anyhow::anyhow!("Failed to open archive: {}", e))?; if archive.extension().and_then(|s| s.to_str()) == Some("zip") { - let mut archive = zip::ZipArchive::new(file) - .map_err(|e| anyhow::anyhow!("Failed to read zip: {}", e))?; + let mut archive = + zip::ZipArchive::new(file).map_err(|e| anyhow::anyhow!("Failed to read zip: {}", e))?; - archive.extract(dest) + archive + .extract(dest) .map_err(|e| anyhow::anyhow!("Failed to extract zip: {}", e))?; } else { let tar = flate2::read::GzDecoder::new(file); let mut archive = tar::Archive::new(tar); - archive.unpack(dest) + archive + .unpack(dest) .map_err(|e| anyhow::anyhow!("Failed to extract tar.gz: {}", e))?; } Ok(()) -} +} @@ -21,10 +21,10 @@ impl CompiledSlangShader { pub fn args(&self) -> String { self.args.clone() } - + /// Creates a [CompiledSlangShader] from raw bytes. /// - /// This is useful when loading shaders compiled at build time using the + /// This is useful when loading shaders compiled at build time using the /// `include_slang!` macro. pub fn from_bytes(label: &str, source: &[u8]) -> Self { Self { @@ -34,7 +34,7 @@ impl CompiledSlangShader { } } - /// Returns the label of the shader. + /// Returns the label of the shader. pub fn label(&self) -> String { self.label.clone() } @@ -45,4 +45,4 @@ impl CompiledSlangShader { std::fs::write(path, &self.source) } -} +} @@ -1,11 +1,11 @@ -//! slank - slangc for rust. -//! +//! slank - slangc for rust. +//! //! Compiles slang code during build and stores the shaders locally (or in the crate with [`include_slang`]) //! //! Check out [`SlangShaderBuilder`] to get started. -/// Fetches the slang file (located in {OUT_DIR}/{label}.spv) (assuming it is compiled as .spv) -/// and includes the bytes of the file. +/// Fetches the slang file (located in {OUT_DIR}/{label}.spv) (assuming it is compiled as .spv) +/// and includes the bytes of the file. #[macro_export] macro_rules! include_slang { ($label:expr) => { @@ -13,7 +13,7 @@ macro_rules! include_slang { }; } -/// Fetches the path of the shader (with the same label) and returns it to you. +/// Fetches the path of the shader (with the same label) and returns it to you. #[macro_export] macro_rules! include_slang_path { ($label:expr) => { @@ -26,7 +26,10 @@ pub mod utils; pub use crate::compiled::*; -use std::{fmt::Display, path::{Path, PathBuf}}; +use std::{ + fmt::Display, + path::{Path, PathBuf}, +}; #[derive(Debug, Clone)] pub struct SourceFile { @@ -47,7 +50,7 @@ pub struct EntryPoint { /// /// This is the entry point of the library. /// # Usage -/// +/// /// Add `slank` to your `[build-dependencies]`. /// /// In your `build.rs`: @@ -65,7 +68,7 @@ pub struct EntryPoint { /// .entry_with_stage("fs_main", ShaderStage::Fragment) /// .build(SlangTarget::SpirV).unwrap() /// .output(&dest_path).unwrap(); -/// +/// /// println!("cargo:rerun-if-changed=src/shader.slang"); /// } /// ``` @@ -207,7 +210,7 @@ impl SlangShaderBuilder { mut self, name: &str, source_index: usize, - stage: ShaderStage + stage: ShaderStage, ) -> Self { self.entries.push(EntryPoint { name: name.to_string(), @@ -223,16 +226,20 @@ impl SlangShaderBuilder { } /// In the case that there was an argument not available to this builder, you can - /// manually provide it here. + /// manually provide it here. pub fn with_additional_args(mut self, args: &[&str]) -> Self { - self.additional_args = args.to_vec().iter().map(|v| v.to_string()).collect::<Vec<_>>(); + self.additional_args = args + .to_vec() + .iter() + .map(|v| v.to_string()) + .collect::<Vec<_>>(); self } - /// Compiles to the `OUT_DIR` env variable. It will return an [Err] if it is not ran in - /// a `build.rs` script. - /// - /// Also assumes that this is for wgpu and a [`SlangTarget::SpirV`] target. + /// Compiles to the `OUT_DIR` env variable. It will return an [Err] if it is not ran in + /// a `build.rs` script. + /// + /// Also assumes that this is for wgpu and a [`SlangTarget::SpirV`] target. pub fn compile_to_out_dir(self, target: SlangTarget) -> anyhow::Result<()> { let label = self.label.clone(); let compiled = self.build(target)?; @@ -242,10 +249,14 @@ impl SlangShaderBuilder { compiled.output(&dest).map_err(Into::into) } - /// Builds the slang shader with the arguments provided. + /// Builds the slang shader with the arguments provided. pub fn build(self, target: SlangTarget) -> anyhow::Result<CompiledSlangShader> { let slang_dir = PathBuf::from(env!("SLANG_DIR")); - let slangc_path = slang_dir.join("bin").join(if cfg!(windows) { "slangc.exe" } else { "slangc" }); + let slangc_path = slang_dir.join("bin").join(if cfg!(windows) { + "slangc.exe" + } else { + "slangc" + }); if !slangc_path.exists() { anyhow::bail!("slangc executable not found at {}", slangc_path.display()); @@ -265,9 +276,7 @@ impl SlangShaderBuilder { let path_to_use = if let Some(path) = &source.path { path.clone() } else { - let mut temp = tempfile::Builder::new() - .suffix(".slang") - .tempfile()?; + let mut temp = tempfile::Builder::new().suffix(".slang").tempfile()?; use std::io::Write as IoWrite; temp.write_all(source.content.as_bytes())?; @@ -309,10 +318,13 @@ impl SlangShaderBuilder { return Err(anyhow::anyhow!("Compilation error: {}", stderr)); } - let binary_output = std::fs::read(&output_path)?; - Ok(CompiledSlangShader::new(self.label, args_record, binary_output)) + Ok(CompiledSlangShader::new( + self.label, + args_record, + binary_output, + )) } } @@ -373,7 +385,7 @@ pub enum SlangTarget { // GLSL/Vulkan/SPIR-V Glsl, - /// Most optimal for WGPU and Vulkan/ + /// Most optimal for WGPU and Vulkan/ SpirV, SpirVAssembly, @@ -477,10 +489,14 @@ impl SlangTarget { "c" => Some(Self::C), "cpp" | "c++" | "cxx" => Some(Self::Cpp), "hpp" => Some(Self::CppHeader), - "torch" | "torch-binding" | "torch-cpp" | "torch-cpp-binding" => Some(Self::TorchBinding), + "torch" | "torch-binding" | "torch-cpp" | "torch-cpp-binding" => { + Some(Self::TorchBinding) + } "host-cpp" | "host-c++" | "host-cxx" => Some(Self::HostCpp), "exe" | "executable" => Some(Self::Executable), - "shader-sharedlib" | "shader-sharedlibrary" | "shader-dll" => Some(Self::ShaderSharedLibrary), + "shader-sharedlib" | "shader-sharedlibrary" | "shader-dll" => { + Some(Self::ShaderSharedLibrary) + } "sharedlib" | "sharedlibrary" | "dll" => Some(Self::SharedLibrary), "cuda" | "cu" => Some(Self::Cuda), "cuh" => Some(Self::CudaHeader), @@ -507,10 +523,18 @@ impl SlangTarget { pub fn is_binary(&self) -> bool { matches!( self, - Self::Dxbc | Self::Dxil | Self::SpirV | Self::Executable - | Self::ShaderSharedLibrary | Self::SharedLibrary | Self::CuBin - | Self::MetalLib | Self::WgslSpirV | Self::SlangVm - | Self::HostObjectCode | Self::ShaderObjectCode + Self::Dxbc + | Self::Dxil + | Self::SpirV + | Self::Executable + | Self::ShaderSharedLibrary + | Self::SharedLibrary + | Self::CuBin + | Self::MetalLib + | Self::WgslSpirV + | Self::SlangVm + | Self::HostObjectCode + | Self::ShaderObjectCode ) } @@ -580,4 +604,4 @@ mod tests { assert_eq!(shader.source, bytes); assert_eq!(shader.label(), "idk"); } -} +} @@ -13,4 +13,4 @@ impl WgpuUtils for crate::CompiledSlangShader { source: wgpu::util::make_spirv(self.source.as_ref()), } } -} +}