tirbofish/dropbear · diff
perf: updated dependencies
style: ran `cargo fmt`
- important updated deps:
- gradle to 9.4.1 (java 25 support)
- egui to 0.34 + related dependencies
- wgpu to 29.0
- jni to 0.22
Signature present but could not be verified.
Unverified
@@ -1,3 +1,3 @@ # Enable auto-env through the sdkman_auto_env config # Add key=value pairs of SDKs to use below -java=21.0.9-tem +java=25.0.2-tem @@ -20,13 +20,19 @@ clap = { version = "4.5", features = ["derive"] } colored = "3.0" crossbeam-channel = "0.5" dropbear-engine = { path = "crates/dropbear-engine" } -egui = "0.33" -egui-toast = { version = "0.19" } -egui-wgpu = { version = "0.33" } -egui-winit = { version = "0.33" } -egui_dnd = "0.14" -egui_dock = { version = "0.18", features = ["serde"] } -egui_extras = { version = "0.33", features = ["all_loaders"] } + +egui = "0.34" +egui-toast = { version = "0.20" } +egui-wgpu = { version = "0.34" } +egui-winit = { version = "0.34" } +egui_dnd = "0.15" +#egui_dock = { version = "0.18", features = ["serde"] } +egui_dock = { git = "https://github.com/enomado/egui_dock", branch = "update-egui-0.34", features = ["serde"]} +egui_extras = { version = "0.34", features = ["all_loaders"] } +egui_ltreeview = { git = "https://github.com/LennysLounge/egui_ltreeview", features = ["doc"] } +egui_plot = "0.35" +transform-gizmo-egui = { git = "https://github.com/tirbofish/transform-gizmo" } + env_logger = "0.11" futures = "0.3" gilrs = "0.11" @@ -42,9 +48,8 @@ rfd = "0.17" ron = "0.12" serde = { version = "1.0", features = ["derive"] } spin_sleep = "1.3" -transform-gizmo-egui = { version = "0.8" } tokio = { version = "1", features = ["full"] } -wgpu = "27" +wgpu = "29" winit = { version = "0.30", features = [] } zip = "8.4" walkdir = "2.5" @@ -53,22 +58,21 @@ backtrace = "0.3" gltf = "1" os_info = "3.12" rustc_version_runtime = "0.3" -jni = { version = "0.21", features = ["invocation"] } +jni = { version = "0.22", features = ["invocation"] } tree-sitter = "0.22" # must be kept as 0.22 because tree-sitter-kotlin has not been updated tree-sitter-kotlin = "0.3" libloading = "0.9" indexmap = "2.11" -sha2 = "0.10" +sha2 = "0.11" wesl = "0.3" dashmap = "6.1" open = "5.3" -egui_plot = "0.34" memory-stats = "1.2" typetag = "0.2" syn = { version = "2.0", features = ["full"] } quote = "1.0" proc-macro2 = "1.0" -egui_ltreeview = { version = "0.6", features = ["doc"] } + dyn-hash = "1.0" semver = { version = "1.0", features = ["serde"] } rapier3d = { version = "0.32", features = [ "simd-stable", "serde-serialize" ] } @@ -78,7 +82,7 @@ pollster = "0.4" thiserror = "2.0" tempfile = "3.24" combine = "4.6" -glyphon = { git = "https://github.com/grovesNL/glyphon", rev = "9dd9376" } +glyphon = { git = "https://github.com/grovesNL/glyphon" } puffin = "0.20" bitflags = "2.10" features = "0.10" @@ -1,5 +1,5 @@ -use std::path::Path; use slank::{SlangShaderBuilder, SlangTarget}; +use std::path::Path; fn main() -> anyhow::Result<()> { // to copy paste: @@ -14,7 +14,7 @@ fn main() -> anyhow::Result<()> { .compile_to_out_dir(SlangTarget::SpirV)?; println!("cargo:rerun-if-changed=src/shaders"); - + validate_wgsl()?; Ok(()) @@ -35,7 +35,11 @@ fn validate_wgsl() -> anyhow::Result<()> { let mut frontend = naga::front::wgsl::Frontend::new(); let module = frontend.parse(&src).unwrap_or_else(|e| { - panic!("WGSL parse error in {}:\n{}", path.display(), e.emit_to_string(&src)); + panic!( + "WGSL parse error in {}:\n{}", + path.display(), + e.emit_to_string(&src) + ); }); let mut validator = naga::valid::Validator::new( @@ -1,10 +1,10 @@ use crate::buffer::{ResizableBuffer, UniformBuffer}; use crate::graphics::SharedGraphicsContext; use crate::model::{AnimationInterpolation, ChannelValues, Model, NodeTransform}; +use dropbear_utils::Dirty; use glam::Mat4; use std::collections::HashMap; use std::sync::Arc; -use dropbear_utils::Dirty; #[repr(C)] #[derive(Copy, Clone, Default, Debug, bytemuck::Pod, bytemuck::Zeroable)] @@ -198,11 +198,23 @@ impl AnimationComponent { } if count == 1 || settings.time <= channel.times[0] { - Self::apply_single_keyframe(channel, 0, &mut self.local_pose, &mut self.morph_weights, model); + Self::apply_single_keyframe( + channel, + 0, + &mut self.local_pose, + &mut self.morph_weights, + model, + ); continue; } if settings.time >= channel.times[count - 1] { - Self::apply_single_keyframe(channel, count - 1, &mut self.local_pose, &mut self.morph_weights, model); + Self::apply_single_keyframe( + channel, + count - 1, + &mut self.local_pose, + &mut self.morph_weights, + model, + ); continue; } @@ -337,7 +349,8 @@ impl AnimationComponent { }; } ChannelValues::MorphWeights(values) => { - let weights = self.morph_weights + let weights = self + .morph_weights .entry(channel.target_node) .or_insert_with(|| vec![0.0; values[0].len()]); @@ -346,7 +359,8 @@ impl AnimationComponent { AnimationInterpolation::Linear => { let a = &values[prev_idx]; let b = &values[next_idx]; - a.iter().zip(b.iter()) + a.iter() + .zip(b.iter()) .map(|(a, b)| a + (b - a) * factor) .collect() } @@ -363,7 +377,8 @@ impl AnimationComponent { let h10 = t3 - 2.0 * t2 + t; let h01 = -2.0 * t3 + 3.0 * t2; let h11 = t3 - t2; - p0.iter().enumerate() + p0.iter() + .enumerate() .map(|(i, p0i)| { p0i * h00 + m0[i] * dt * h10 + p1[i] * h01 + m1[i] * dt * h11 }) @@ -424,7 +439,7 @@ impl AnimationComponent { if let Some(frame) = v.get(actual_index) { morph_weights.insert(channel.target_node, frame.clone()); } - }, + } } } @@ -548,9 +563,9 @@ impl AnimationComponent { _padding: Default::default(), }; - let info_buffer = self.morph_info_buffer.get_or_insert_with(|| { - UniformBuffer::new(&graphics.device, "morph info buffer") - }); + let info_buffer = self + .morph_info_buffer + .get_or_insert_with(|| UniformBuffer::new(&graphics.device, "morph info buffer")); info_buffer.write(&graphics.queue, &info); @@ -559,4 +574,4 @@ impl AnimationComponent { } } -pub const MAX_MORPH_WEIGHTS: usize = 4096; +pub const MAX_MORPH_WEIGHTS: usize = 4096; @@ -143,10 +143,18 @@ impl AssetRegistry { if is_null || is_protected || is_live || has_external_arc { for mat in &arc.materials { live_texture_ids.insert(mat.diffuse_texture.id); - if let Some(h) = mat.normal_texture { live_texture_ids.insert(h.id); } - if let Some(h) = mat.emissive_texture { live_texture_ids.insert(h.id); } - if let Some(h) = mat.metallic_roughness_texture { live_texture_ids.insert(h.id); } - if let Some(h) = mat.occlusion_texture { live_texture_ids.insert(h.id); } + if let Some(h) = mat.normal_texture { + live_texture_ids.insert(h.id); + } + if let Some(h) = mat.emissive_texture { + live_texture_ids.insert(h.id); + } + if let Some(h) = mat.metallic_roughness_texture { + live_texture_ids.insert(h.id); + } + if let Some(h) = mat.occlusion_texture { + live_texture_ids.insert(h.id); + } } return true; } @@ -154,7 +162,8 @@ impl AssetRegistry { counter += 1; false }); - self.model_labels.retain(|_, handle| self.models.contains_key(&handle.id)); + self.model_labels + .retain(|_, handle| self.models.contains_key(&handle.id)); self.textures.retain(|id, arc| { let is_null = *id == 0; @@ -168,7 +177,8 @@ impl AssetRegistry { counter += 1; false }); - self.texture_labels.retain(|_, handle| self.textures.contains_key(&handle.id)); + self.texture_labels + .retain(|_, handle| self.textures.contains_key(&handle.id)); counter } @@ -1,11 +1,11 @@ -use std::sync::Arc; -use glam::Mat4; -use wgpu::MultisampleState; -use wgpu::util::{BufferInitDescriptor, DeviceExt}; use crate::asset::ASSET_REGISTRY; use crate::buffer::UniformBuffer; use crate::graphics::SharedGraphicsContext; use crate::shader::Shader; +use glam::Mat4; +use std::sync::Arc; +use wgpu::MultisampleState; +use wgpu::util::{BufferInitDescriptor, DeviceExt}; pub struct BillboardPipeline { pipeline: wgpu::RenderPipeline, @@ -18,9 +18,7 @@ pub struct BillboardPipeline { } impl BillboardPipeline { - pub fn new( - graphics: Arc<SharedGraphicsContext>, - ) -> Self { + pub fn new(graphics: Arc<SharedGraphicsContext>) -> Self { puffin::profile_function!(); log::debug!("Initialising billboard pipeline"); let shader = Shader::new( @@ -42,12 +40,7 @@ impl BillboardPipeline { usage: wgpu::BufferUsages::VERTEX, }); - let tex_coords: [[f32; 2]; 4] = [ - [1.0, 1.0], - [1.0, 0.0], - [0.0, 1.0], - [0.0, 0.0], - ]; + let tex_coords: [[f32; 2]; 4] = [[1.0, 1.0], [1.0, 0.0], [0.0, 1.0], [0.0, 0.0]]; let tex_coord_buffer = graphics.device.create_buffer_init(&BufferInitDescriptor { label: Some("billboard tex coords buffer"), @@ -61,59 +54,63 @@ impl BillboardPipeline { address_mode_v: wgpu::AddressMode::Repeat, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::MipmapFilterMode::Linear, ..Default::default() }); let uniform_bind_group_layout = - graphics.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("billboard group layout"), - entries: &[ - // transform - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, + graphics + .device + .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("billboard group layout"), + entries: &[ + // transform + 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, }, - count: None, - }, - // projection - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, + // projection + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::VERTEX, + 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: 2, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, + // t_diffuse + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, }, - count: None, - }, - // s_diffuse - wgpu::BindGroupLayoutEntry { - binding: 3, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }); + // s_diffuse + wgpu::BindGroupLayoutEntry { + binding: 3, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + }); - let transform_buffer: UniformBuffer<Mat4> = UniformBuffer::new(&graphics.device, "billboard transform buffer"); - let projection_buffer: UniformBuffer<Mat4> = UniformBuffer::new(&graphics.device, "billboard projection buffer"); + let transform_buffer: UniformBuffer<Mat4> = + UniformBuffer::new(&graphics.device, "billboard transform buffer"); + let projection_buffer: UniformBuffer<Mat4> = + UniformBuffer::new(&graphics.device, "billboard projection buffer"); { let mut registry = ASSET_REGISTRY.write(); @@ -122,69 +119,71 @@ impl BillboardPipeline { } let pipeline_layout = - graphics.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("billboard pipeline layout"), - bind_group_layouts: &[&uniform_bind_group_layout], - push_constant_ranges: &[], + graphics + .device + .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("billboard pipeline layout"), + bind_group_layouts: &[Some(&uniform_bind_group_layout)], + immediate_size: 0, + }); + + let format = { graphics.hdr.read().format().clone() }; + let pipeline = graphics + .device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("billboard render pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: Some("vs_main"), + compilation_options: Default::default(), + buffers: &[ + // positions + wgpu::VertexBufferLayout { + array_stride: (std::mem::size_of::<f32>() * 3) as u64, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &wgpu::vertex_attr_array![0 => Float32x3], + }, + // tex coords + wgpu::VertexBufferLayout { + array_stride: (std::mem::size_of::<f32>() * 2) as u64, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &wgpu::vertex_attr_array![1 => Float32x2], + }, + ], + }, + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleStrip, + front_face: wgpu::FrontFace::Cw, + cull_mode: None, + ..Default::default() + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: graphics.depth_texture.texture.format(), + depth_write_enabled: Some(false), + depth_compare: Some(wgpu::CompareFunction::Greater), + stencil: Default::default(), + bias: Default::default(), + }), + multisample: MultisampleState { + count: (*graphics.antialiasing.read()).into(), + mask: !0, + alpha_to_coverage_enabled: false, + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: Some("fs_main"), + compilation_options: Default::default(), + targets: &[Some(wgpu::ColorTargetState { + format, + blend: Some(wgpu::BlendState::ALPHA_BLENDING), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + cache: None, + multiview_mask: None, }); - let format = { - graphics.hdr.read().format().clone() - }; - let pipeline = graphics.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("billboard render pipeline"), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: Some("vs_main"), - compilation_options: Default::default(), - buffers: &[ - // positions - wgpu::VertexBufferLayout { - array_stride: (std::mem::size_of::<f32>() * 3) as u64, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &wgpu::vertex_attr_array![0 => Float32x3], - }, - // tex coords - wgpu::VertexBufferLayout { - array_stride: (std::mem::size_of::<f32>() * 2) as u64, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &wgpu::vertex_attr_array![1 => Float32x2], - }, - ], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleStrip, - front_face: wgpu::FrontFace::Cw, - cull_mode: None, - ..Default::default() - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: graphics.depth_texture.texture.format(), - depth_write_enabled: false, - depth_compare: wgpu::CompareFunction::Greater, - stencil: Default::default(), - bias: Default::default(), - }), - multisample: MultisampleState { - count: (*graphics.antialiasing.read()).into(), - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: Some("fs_main"), - compilation_options: Default::default(), - targets: &[Some(wgpu::ColorTargetState { - format, - blend: Some(wgpu::BlendState::ALPHA_BLENDING), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - multiview: None, - cache: None, - }); - log::debug!("Created billboard pipeline"); Self { @@ -209,28 +208,30 @@ impl BillboardPipeline { self.transform_buffer.write(&graphics.queue, &transform); self.projection_buffer.write(&graphics.queue, &projection); - let uniform_bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("billboard bind group"), - layout: &self.uniform_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: self.transform_buffer.buffer().as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: self.projection_buffer.buffer().as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::TextureView(texture_view), - }, - wgpu::BindGroupEntry { - binding: 3, - resource: wgpu::BindingResource::Sampler(&self.sampler), - }, - ], - }); + let uniform_bind_group = graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("billboard bind group"), + layout: &self.uniform_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: self.transform_buffer.buffer().as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: self.projection_buffer.buffer().as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::TextureView(texture_view), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::Sampler(&self.sampler), + }, + ], + }); render_pass.set_pipeline(&self.pipeline); render_pass.set_vertex_buffer(0, self.position_buffer.slice(..)); @@ -238,4 +239,4 @@ impl BillboardPipeline { render_pass.set_bind_group(0, &uniform_bind_group, &[]); render_pass.draw(0..4, 0..1); } -} +} @@ -1,3 +1 @@ -pub struct PerFrameBindGroup { - -} +pub struct PerFrameBindGroup {} @@ -26,7 +26,11 @@ impl<T: bytemuck::Pod> ResizableBuffer<T> { mapped_at_creation: false, }); - log::debug!("Registered new resizable buffer: {:?} (usage={:?})", label, usage); + log::debug!( + "Registered new resizable buffer: {:?} (usage={:?})", + label, + usage + ); Self { buffer, capacity: initial_capacity, @@ -134,7 +138,9 @@ impl<T: bytemuck::Pod> StorageBuffer<T> { let usage = if read_only { wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST } else { - wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::COPY_SRC + wgpu::BufferUsages::STORAGE + | wgpu::BufferUsages::COPY_DST + | wgpu::BufferUsages::COPY_SRC }; let size = (std::mem::size_of::<T>() as wgpu::BufferAddress).max(16); @@ -145,7 +151,11 @@ impl<T: bytemuck::Pod> StorageBuffer<T> { mapped_at_creation: false, }); - log::debug!("Registered new storage buffer: {:?} (read_only: {})", label, read_only); + log::debug!( + "Registered new storage buffer: {:?} (read_only: {})", + label, + read_only + ); Self { buffer, label: label.to_string(), @@ -165,4 +175,4 @@ impl<T: bytemuck::Pod> StorageBuffer<T> { pub fn label(&self) -> &str { &self.label } -} +} @@ -117,14 +117,16 @@ impl Camera { uniform.view_proj = mvp.as_mat4().to_cols_array_2d(); let buffer = UniformBuffer::new(&graphics.device, "camera uniform"); - let bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("editor camera bind group"), - layout: &graphics.layouts.camera_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: buffer.buffer().as_entire_binding(), - }], - }); + let bind_group = graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("editor camera bind group"), + layout: &graphics.layouts.camera_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: buffer.buffer().as_entire_binding(), + }], + }); let camera = Self { eye: builder.eye, @@ -1,9 +1,16 @@ -use std::sync::Arc; -use glam::{Mat4, Quat, Vec3, Vec4}; -use wgpu::{BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, BufferBindingType, BufferUsages, CompareFunction, DepthStencilState, LoadOp, MultisampleState, Operations, PrimitiveState, PrimitiveTopology, RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor, RenderPipeline, RenderPipelineDescriptor, ShaderStages, StoreOp, TextureFormat, VertexBufferLayout, VertexState}; use crate::buffer::{ResizableBuffer, UniformBuffer}; use crate::graphics::{CommandEncoder, SharedGraphicsContext}; use crate::shader::Shader; +use glam::{Mat4, Quat, Vec3, Vec4}; +use std::sync::Arc; +use wgpu::{ + BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, + BindingResource, BindingType, BufferBindingType, BufferUsages, CompareFunction, + DepthStencilState, LoadOp, MultisampleState, Operations, PrimitiveState, PrimitiveTopology, + RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor, + RenderPipeline, RenderPipelineDescriptor, ShaderStages, StoreOp, TextureFormat, + VertexBufferLayout, VertexState, +}; pub struct DebugDraw { pipeline: Arc<DebugDrawPipeline>, @@ -22,7 +29,7 @@ impl DebugDraw { &graphics.device, 1024, BufferUsages::VERTEX | BufferUsages::COPY_DST, - "debug draw vertex buffer" + "debug draw vertex buffer", ); Self { @@ -33,7 +40,12 @@ impl DebugDraw { } /// Flushes away all of the vertices to be drawn, and renders them at that instant. - pub fn flush(&mut self, graphics: Arc<SharedGraphicsContext>, encoder: &mut CommandEncoder, view_proj: Mat4) { + pub fn flush( + &mut self, + graphics: Arc<SharedGraphicsContext>, + encoder: &mut CommandEncoder, + view_proj: Mat4, + ) { if self.vertices.is_empty() { return; } @@ -41,7 +53,7 @@ impl DebugDraw { self.vertex_buffer.write( &graphics.device, &graphics.queue, - bytemuck::cast_slice(&self.vertices) + bytemuck::cast_slice(&self.vertices), ); self.pipeline.draw( @@ -63,12 +75,18 @@ impl DebugDraw { pub fn draw_line(&mut self, a: Vec3, b: Vec3, colour: [f32; 4]) { let a = a.to_array(); let b = b.to_array(); - self.vertices.push(DebugVertex { position: [a[0], a[1], a[2], 0.0], colour }); - self.vertices.push(DebugVertex { position: [b[0], b[1], b[2], 0.0], colour }); + self.vertices.push(DebugVertex { + position: [a[0], a[1], a[2], 0.0], + colour, + }); + self.vertices.push(DebugVertex { + position: [b[0], b[1], b[2], 0.0], + colour, + }); } /// A wrapper for [draw_line](Self::draw_line), which draws a line from an origin and at a direction - /// with a specific colour. + /// with a specific colour. pub fn draw_ray(&mut self, origin: Vec3, dir: Vec3, colour: [f32; 4]) { self.draw_line(origin, origin + dir, colour); } @@ -82,7 +100,11 @@ impl DebugDraw { let len = (b - a).length() * 0.15; // find a perpendicular axis - let up = if dir.dot(Vec3::Y).abs() < 0.99 { Vec3::Y } else { Vec3::Z }; + let up = if dir.dot(Vec3::Y).abs() < 0.99 { + Vec3::Y + } else { + Vec3::Z + }; let right = dir.cross(up).normalize(); let tip = b; @@ -107,7 +129,11 @@ impl DebugDraw { let segments = 32; // build tangent frame from normal - let up = if normal.dot(Vec3::Y).abs() < 0.99 { Vec3::Y } else { Vec3::Z }; + let up = if normal.dot(Vec3::Y).abs() < 0.99 { + Vec3::Y + } else { + Vec3::Z + }; let tangent = normal.cross(up).normalize(); let bitangent = normal.cross(tangent).normalize(); @@ -133,7 +159,14 @@ impl DebugDraw { /// /// `lat_lines` controls the number of horizontal rings (latitude), /// `lon_lines` controls the number of vertical rings (longitude). - pub fn draw_globe(&mut self, center: Vec3, radius: f32, lat_lines: u32, lon_lines: u32, color: [f32; 4]) { + pub fn draw_globe( + &mut self, + center: Vec3, + radius: f32, + lat_lines: u32, + lon_lines: u32, + color: [f32; 4], + ) { // latitude rings (horizontal circles stacked along Y axis) for i in 1..lat_lines { let angle = std::f32::consts::PI * (i as f32 / lat_lines as f32); // 0..PI @@ -190,13 +223,13 @@ impl DebugDraw { // rotate the 8 unit corners then scale by half_extents let corners_local = [ Vec3::new(-1.0, -1.0, -1.0), - Vec3::new( 1.0, -1.0, -1.0), - Vec3::new( 1.0, 1.0, -1.0), - Vec3::new(-1.0, 1.0, -1.0), - Vec3::new(-1.0, -1.0, 1.0), - Vec3::new( 1.0, -1.0, 1.0), - Vec3::new( 1.0, 1.0, 1.0), - Vec3::new(-1.0, 1.0, 1.0), + Vec3::new(1.0, -1.0, -1.0), + Vec3::new(1.0, 1.0, -1.0), + Vec3::new(-1.0, 1.0, -1.0), + Vec3::new(-1.0, -1.0, 1.0), + Vec3::new(1.0, -1.0, 1.0), + Vec3::new(1.0, 1.0, 1.0), + Vec3::new(-1.0, 1.0, 1.0), ]; let corners: Vec<Vec3> = corners_local @@ -230,7 +263,11 @@ impl DebugDraw { self.draw_circle(b, radius, axis, colour); // 4 connecting lines along the sides - let up = if axis.dot(Vec3::Y).abs() < 0.99 { Vec3::Y } else { Vec3::Z }; + let up = if axis.dot(Vec3::Y).abs() < 0.99 { + Vec3::Y + } else { + Vec3::Z + }; let tangent = axis.cross(up).normalize(); let bitangent = axis.cross(tangent).normalize(); @@ -240,7 +277,14 @@ impl DebugDraw { } /// Draws a wireframe cylinder centered at `center`, aligned to `axis`. - pub fn draw_cylinder(&mut self, center: Vec3, half_height: f32, radius: f32, axis: Vec3, colour: [f32; 4]) { + pub fn draw_cylinder( + &mut self, + center: Vec3, + half_height: f32, + radius: f32, + axis: Vec3, + colour: [f32; 4], + ) { let axis = axis.normalize(); let top = center + axis * half_height; let bottom = center - axis * half_height; @@ -248,7 +292,11 @@ impl DebugDraw { self.draw_circle(top, radius, axis, colour); self.draw_circle(bottom, radius, axis, colour); - let up = if axis.dot(Vec3::Y).abs() < 0.99 { Vec3::Y } else { Vec3::Z }; + let up = if axis.dot(Vec3::Y).abs() < 0.99 { + Vec3::Y + } else { + Vec3::Z + }; let tangent = axis.cross(up).normalize(); let bitangent = axis.cross(tangent).normalize(); @@ -270,7 +318,11 @@ impl DebugDraw { self.draw_circle(base_center, base_radius, dir, colour); // 4 lines from apex to base rim - let up = if dir.normalize().dot(Vec3::Y).abs() < 0.99 { Vec3::Y } else { Vec3::Z }; + let up = if dir.normalize().dot(Vec3::Y).abs() < 0.99 { + Vec3::Y + } else { + Vec3::Z + }; let tangent = dir.cross(up).normalize(); let bitangent = dir.cross(tangent).normalize(); @@ -286,22 +338,25 @@ impl DebugDraw { // NDC corners, unproject back to world space let ndc_corners = [ Vec3::new(-1.0, -1.0, 0.0), // near - Vec3::new( 1.0, -1.0, 0.0), - Vec3::new( 1.0, 1.0, 0.0), - Vec3::new(-1.0, 1.0, 0.0), + Vec3::new(1.0, -1.0, 0.0), + Vec3::new(1.0, 1.0, 0.0), + Vec3::new(-1.0, 1.0, 0.0), Vec3::new(-1.0, -1.0, 1.0), // far - Vec3::new( 1.0, -1.0, 1.0), - Vec3::new( 1.0, 1.0, 1.0), - Vec3::new(-1.0, 1.0, 1.0), + Vec3::new(1.0, -1.0, 1.0), + Vec3::new(1.0, 1.0, 1.0), + Vec3::new(-1.0, 1.0, 1.0), ]; let inv = view_proj.inverse(); - let corners: Vec<Vec3> = ndc_corners.iter().map(|&ndc| { - let clip = Vec4::new(ndc.x, ndc.y, ndc.z, 1.0); - let world = inv * clip; - world.truncate() / world.w - }).collect(); + let corners: Vec<Vec3> = ndc_corners + .iter() + .map(|&ndc| { + let clip = Vec4::new(ndc.x, ndc.y, ndc.z, 1.0); + let world = inv * clip; + world.truncate() / world.w + }) + .collect(); // near face self.draw_line(corners[0], corners[1], colour); @@ -340,7 +395,7 @@ impl DebugDraw { } for i in 0..points.len().saturating_sub(3) { - let (p0, p1, p2, p3) = (points[i], points[i+1], points[i+2], points[i+3]); + let (p0, p1, p2, p3) = (points[i], points[i + 1], points[i + 2], points[i + 3]); let mut prev = p1; // catmull-rom passes through p1..p_{n-2} for j in 1..=segments_per_span { let t = j as f32 / segments_per_span as f32; @@ -362,12 +417,10 @@ impl DebugDraw { fn catmull_rom(p0: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, t: f32) -> Vec3 { let t2 = t * t; let t3 = t2 * t; - 0.5 * ( - (2.0 * p1) - + (-p0 + p2) * t - + (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 - + (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3 - ) + 0.5 * ((2.0 * p1) + + (-p0 + p2) * t + + (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 + + (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3) } pub struct DebugDrawPipeline { @@ -378,91 +431,100 @@ pub struct DebugDrawPipeline { impl DebugDrawPipeline { pub fn new(graphics: Arc<SharedGraphicsContext>) -> Self { - let shader = Shader::new(graphics.clone(), include_str!("shaders/basic.wgsl"), Some("basic shader module")); - - let bind_group_layout = graphics.device.create_bind_group_layout(&BindGroupLayoutDescriptor { - label: Some("basic camera bind group layout"), - entries: &[ - BindGroupLayoutEntry { - binding: 0, - visibility: ShaderStages::VERTEX, - ty: BindingType::Buffer { - ty: BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - } - ], - }); + let shader = Shader::new( + graphics.clone(), + include_str!("shaders/basic.wgsl"), + Some("basic shader module"), + ); - let camera_uniform: UniformBuffer<Mat4> = UniformBuffer::new(&graphics.device, "basic camera uniform"); + let bind_group_layout = + graphics + .device + .create_bind_group_layout(&BindGroupLayoutDescriptor { + label: Some("basic camera bind group layout"), + entries: &[BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::VERTEX, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + }); + + let camera_uniform: UniformBuffer<Mat4> = + UniformBuffer::new(&graphics.device, "basic camera uniform"); let bind_group = graphics.device.create_bind_group(&BindGroupDescriptor { label: Some("basic camera bind group"), layout: &bind_group_layout, - entries: &[ - BindGroupEntry { - binding: 0, - resource: BindingResource::Buffer(camera_uniform.buffer().as_entire_buffer_binding()), - } - ], + entries: &[BindGroupEntry { + binding: 0, + resource: BindingResource::Buffer( + camera_uniform.buffer().as_entire_buffer_binding(), + ), + }], }); - let pipeline_layout = graphics.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("debug draw pipeline layout"), - bind_group_layouts: &[ - &bind_group_layout - ], - push_constant_ranges: &[], - }); + let pipeline_layout = + graphics + .device + .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("debug draw pipeline layout"), + bind_group_layouts: &[Some(&bind_group_layout)], + immediate_size: 0, + }); let hdr_format = graphics.hdr.read().format(); let sample_count: u32 = (*graphics.antialiasing.read()).into(); - let pipeline = graphics.device.create_render_pipeline(&RenderPipelineDescriptor { - label: Some("debug draw render pipeline"), - layout: Some(&pipeline_layout), - vertex: VertexState { - module: &shader.module, - entry_point: Some("vs_main"), - compilation_options: Default::default(), - buffers: &[DebugVertex::LAYOUT], - }, - fragment: Some(wgpu::FragmentState { - module: &shader.module, - entry_point: Some("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: PrimitiveState { - topology: PrimitiveTopology::LineList, - strip_index_format: None, - front_face: Default::default(), - cull_mode: None, - unclipped_depth: false, - polygon_mode: Default::default(), - conservative: false, - }, - depth_stencil: Some(DepthStencilState { - format: TextureFormat::Depth32Float, - depth_write_enabled: false, // don't write to depth, just read - depth_compare: CompareFunction::Less, - stencil: Default::default(), - bias: Default::default(), - }), - multisample: MultisampleState { - count: sample_count, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - cache: None, - }); + let pipeline = graphics + .device + .create_render_pipeline(&RenderPipelineDescriptor { + label: Some("debug draw render pipeline"), + layout: Some(&pipeline_layout), + vertex: VertexState { + module: &shader.module, + entry_point: Some("vs_main"), + compilation_options: Default::default(), + buffers: &[DebugVertex::LAYOUT], + }, + fragment: Some(wgpu::FragmentState { + module: &shader.module, + entry_point: Some("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: PrimitiveState { + topology: PrimitiveTopology::LineList, + strip_index_format: None, + front_face: Default::default(), + cull_mode: None, + unclipped_depth: false, + polygon_mode: Default::default(), + conservative: false, + }, + depth_stencil: Some(DepthStencilState { + format: TextureFormat::Depth32Float, + depth_write_enabled: Some(false), // don't write to depth, just read + depth_compare: Some(CompareFunction::Less), + stencil: Default::default(), + bias: Default::default(), + }), + multisample: MultisampleState { + count: sample_count, + mask: !0, + alpha_to_coverage_enabled: false, + }, + cache: None, + multiview_mask: None, + }); Self { pipeline, @@ -495,20 +557,21 @@ impl DebugDrawPipeline { depth_slice: None, resolve_target: hdr.resolve_target(), ops: Operations { - load: LoadOp::Load, // draw on top of existing frame + load: LoadOp::Load, // draw on top of existing frame store: StoreOp::Store, }, })], depth_stencil_attachment: Some(RenderPassDepthStencilAttachment { view: &graphics.depth_texture.view, depth_ops: Some(Operations { - load: LoadOp::Load, // read existing depth + load: LoadOp::Load, // read existing depth store: StoreOp::Store, }), stencil_ops: None, }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_pipeline(&self.pipeline); @@ -534,4 +597,4 @@ impl DebugVertex { 1 => Float32x4, ], }; -} +} @@ -112,6 +112,7 @@ impl EguiRenderer { timestamp_writes: None, label: Some("egui main render pass"), occlusion_query_set: None, + multiview_mask: None, }); self.renderer @@ -37,15 +37,12 @@ impl RotationEditorMode { } } -pub fn inspect_rotation_dquat( - ui: &mut Ui, - id_source: impl Hash, - rotation: &mut DQuat, -) -> bool { +pub fn inspect_rotation_dquat(ui: &mut Ui, id_source: impl Hash, rotation: &mut DQuat) -> bool { let mode_id = ui.make_persistent_id(("rotation_mode", id_source)); - let mut mode = ui - .ctx() - .data_mut(|d| d.get_temp::<RotationEditorMode>(mode_id).unwrap_or_default()); + let mut mode = ui.ctx().data_mut(|d| { + d.get_temp::<RotationEditorMode>(mode_id) + .unwrap_or_default() + }); ui.horizontal(|ui| { ui.label("Mode"); @@ -178,29 +175,17 @@ pub fn inspect_rotation_dquat( ui.horizontal(|ui| { ui.colored_label(egui::Color32::from_rgb(200, 80, 80), "X:"); - let rx = ui.add( - egui::DragValue::new(&mut x) - .speed(0.01) - .fixed_decimals(3), - ); + let rx = ui.add(egui::DragValue::new(&mut x).speed(0.01).fixed_decimals(3)); changed |= rx.changed(); any_dragging |= rx.dragged(); ui.colored_label(egui::Color32::from_rgb(80, 200, 80), "Y:"); - let ry = ui.add( - egui::DragValue::new(&mut y) - .speed(0.01) - .fixed_decimals(3), - ); + let ry = ui.add(egui::DragValue::new(&mut y).speed(0.01).fixed_decimals(3)); changed |= ry.changed(); any_dragging |= ry.dragged(); ui.colored_label(egui::Color32::from_rgb(80, 120, 220), "Z:"); - let rz = ui.add( - egui::DragValue::new(&mut z) - .speed(0.01) - .fixed_decimals(3), - ); + let rz = ui.add(egui::DragValue::new(&mut z).speed(0.01).fixed_decimals(3)); changed |= rz.changed(); any_dragging |= rz.dragged(); }); @@ -259,8 +244,9 @@ pub fn inspect_rotation_dquat( if any_dragging || changed { ui.ctx().data_mut(|d| d.insert_temp(raw_id, [x, y, z, w])); } else { - ui.ctx() - .data_mut(|d| d.insert_temp(raw_id, [rotation.x, rotation.y, rotation.z, rotation.w])); + ui.ctx().data_mut(|d| { + d.insert_temp(raw_id, [rotation.x, rotation.y, rotation.z, rotation.w]) + }); } if changed { @@ -86,7 +86,9 @@ impl Instance { pub fn to_raw(&self) -> InstanceRaw { let model_matrix = DMat4::from_scale_rotation_translation(self.scale, self.rotation, self.position); - let normal_matrix = Mat3::from_mat4(model_matrix.as_mat4()).inverse().transpose(); + let normal_matrix = Mat3::from_mat4(model_matrix.as_mat4()) + .inverse() + .transpose(); InstanceRaw { model: model_matrix.as_mat4().to_cols_array_2d(), normal: normal_matrix.to_cols_array_2d(), @@ -1,10 +1,12 @@ pub mod animation; pub mod asset; pub mod attenuation; +pub mod billboarding; pub mod bind_groups; pub mod buffer; pub mod camera; pub mod colour; +pub mod debug; pub mod egui_renderer; pub mod entity; pub mod features; @@ -13,6 +15,7 @@ pub mod input; pub mod lighting; pub mod mipmap; pub mod model; +pub mod multisampling; pub mod panic; pub mod pipelines; pub mod procedural; @@ -22,9 +25,6 @@ pub mod shader; pub mod sky; pub mod texture; pub mod utils; -pub mod multisampling; -pub mod billboarding; -pub mod debug; features! { pub mod feature_list { @@ -57,7 +57,11 @@ use std::{ sync::Arc, time::{Duration, Instant}, }; -use wgpu::{BindGroupLayoutEntry, BindingType, BufferBindingType, Device, ExperimentalFeatures, Instance, Queue, ShaderStages, Surface, SurfaceConfiguration, SurfaceError, TextureFormat, TextureFormatFeatureFlags}; +use wgpu::{ + BindGroupLayoutEntry, BindingType, BufferBindingType, CurrentSurfaceTexture, Device, + ExperimentalFeatures, Instance, Queue, ShaderStages, Surface, SurfaceConfiguration, + TextureFormat, TextureFormatFeatureFlags, +}; use winit::event::{DeviceEvent, DeviceId}; use winit::{ application::ApplicationHandler, @@ -67,12 +71,13 @@ use winit::{ window::Window, }; +use crate::debug::DebugDraw; use crate::egui_renderer::EguiRenderer; use crate::graphics::{CommandEncoder, SharedGraphicsContext}; use crate::mipmap::MipMapper; use crate::texture::{Texture, TextureBuilder}; -use crate::debug::DebugDraw; +use crate::multisampling::AntiAliasingMode; use crate::pipelines::hdr::HdrPipeline; use crate::scene::Scene; pub use dropbear_future_queue as future; @@ -80,7 +85,6 @@ pub use gilrs; pub use wgpu; pub use winit; use winit::window::{WindowAttributes, WindowId}; -use crate::multisampling::{AntiAliasingMode}; pub struct BindGroupLayouts { pub camera_bind_group_layout: wgpu::BindGroupLayout, @@ -138,9 +142,7 @@ impl BindGroupLayouts { binding: 2, visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, ty: BindingType::Buffer { - ty: BufferBindingType::Storage { - read_only: true - }, + ty: BufferBindingType::Storage { read_only: true }, has_dynamic_offset: false, min_binding_size: None, }, @@ -265,9 +267,7 @@ impl BindGroupLayouts { binding: 0, visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, ty: BindingType::Buffer { - ty: BufferBindingType::Storage { - read_only: true - }, + ty: BufferBindingType::Storage { read_only: true }, has_dynamic_offset: false, min_binding_size: None, }, @@ -278,9 +278,7 @@ impl BindGroupLayouts { binding: 1, visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, ty: BindingType::Buffer { - ty: BufferBindingType::Storage { - read_only: true - }, + ty: BufferBindingType::Storage { read_only: true }, has_dynamic_offset: false, min_binding_size: None, }, @@ -291,9 +289,7 @@ impl BindGroupLayouts { binding: 2, visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, ty: BindingType::Buffer { - ty: BufferBindingType::Storage { - read_only: true - }, + ty: BufferBindingType::Storage { read_only: true }, has_dynamic_offset: false, min_binding_size: None, }, @@ -520,7 +516,7 @@ Hardware: let flags = adapter.get_texture_format_features(config.format).flags; - let antialiasing = if flags.contains(TextureFormatFeatureFlags::MULTISAMPLE_X4) { + let antialiasing = if flags.contains(TextureFormatFeatureFlags::MULTISAMPLE_X4) { log::debug!("Rendering with MSAA4"); AntiAliasingMode::MSAA4 } else { @@ -532,15 +528,19 @@ Hardware: surface.configure(&device, &config); } - let depth_texture = Arc::new(TextureBuilder::new(&device) - .depth(&config, antialiasing) - .label("depth texture") - .build()); + let depth_texture = Arc::new( + TextureBuilder::new(&device) + .depth(&config, antialiasing) + .label("depth texture") + .build(), + ); - let viewport_texture = Arc::new(TextureBuilder::new(&device) - .viewport(&config) - .label("viewport texture") - .build()); + let viewport_texture = Arc::new( + TextureBuilder::new(&device) + .viewport(&config) + .label("viewport texture") + .build(), + ); let mipmapper = Arc::new(MipMapper::new(&device)); @@ -606,7 +606,9 @@ Hardware: } self.surface.configure(&self.device, &self.config.read()); self.is_surface_configured = true; - self.hdr.write().resize(&self.device, width, height, Some(*self.antialiasing.read())); + self.hdr + .write() + .resize(&self.device, width, height, Some(*self.antialiasing.read())); } let depth_texture = TextureBuilder::new(&self.device) @@ -618,7 +620,7 @@ Hardware: .viewport(&self.config.read()) .label("viewport texture") .build(); - + self.depth_texture = Arc::new(depth_texture); self.viewport_texture = Arc::new(viewport_texture); self.egui_renderer @@ -645,9 +647,12 @@ Hardware: .label("depth texture") .build(); self.depth_texture = Arc::new(depth_texture); - self.hdr - .write() - .resize(&self.device, config.width, config.height, Some(antialiasing)); + self.hdr.write().resize( + &self.device, + config.width, + config.height, + Some(antialiasing), + ); true } @@ -676,10 +681,12 @@ Hardware: .viewport(&config) .label("viewport texture") .build(); - + self.depth_texture = Arc::new(depth_texture); self.viewport_texture = Arc::new(viewport_texture); - self.hdr.write().resize(&self.device, width, height, Some(*self.antialiasing.read())); + self.hdr + .write() + .resize(&self.device, width, height, Some(*self.antialiasing.read())); self.egui_renderer .lock() .renderer() @@ -707,31 +714,25 @@ Hardware: let config = self.config.read().clone(); let output = match self.surface.get_current_texture() { - Ok(val) => val, - Err(e) => { - return match e { - SurfaceError::Lost => { - log_once::warn_once!("Surface lost, reconfiguring..."); - self.surface.configure(&self.device, &config); - Ok(Vec::new()) - } - SurfaceError::Outdated => { - log_once::warn_once!("Surface outdated, reconfiguring..."); - self.surface.configure(&self.device, &config); - Ok(Vec::new()) - } - SurfaceError::Timeout => { - log_once::warn_once!("Surface timeout, skipping frame"); - Ok(Vec::new()) - } - SurfaceError::OutOfMemory => { - Err(anyhow::anyhow!("Surface out of memory: {:?}", e)) - } - SurfaceError::Other => { - log_once::warn_once!("Surface error (Other): {:?}, skipping frame", e); - Ok(Vec::new()) - } - }; + wgpu::CurrentSurfaceTexture::Success(surface_texture) => surface_texture, + wgpu::CurrentSurfaceTexture::Suboptimal(surface_texture) => { + self.surface.configure(&self.device, &config); + surface_texture + } + wgpu::CurrentSurfaceTexture::Timeout + | wgpu::CurrentSurfaceTexture::Occluded + | wgpu::CurrentSurfaceTexture::Validation => { + // Skip this frame + return Ok(vec![]); + } + wgpu::CurrentSurfaceTexture::Outdated => { + self.surface.configure(&self.device, &config); + return Ok(vec![]); + } + wgpu::CurrentSurfaceTexture::Lost => { + // You could recreate the devices and all resources + // created with it here, but we'll just bail + anyhow::bail!("Lost device"); } }; @@ -769,6 +770,7 @@ Hardware: depth_stencil_attachment: None, occlusion_query_set: None, timestamp_writes: None, + multiview_mask: None, }); } @@ -1180,9 +1182,12 @@ impl App { } } - let instance = Arc::new(Instance::new(&wgpu::InstanceDescriptor { + let instance = Arc::new(Instance::new(wgpu::InstanceDescriptor { backends: wgpu::Backends::PRIMARY, - ..Default::default() + flags: Default::default(), + memory_budget_thresholds: Default::default(), + backend_options: Default::default(), + display: None, })); let result = Self { @@ -1470,7 +1475,8 @@ impl ApplicationHandler for App { scene::SceneCommand::SetAntialiasing(antialiasing) => { if let Some((state, graphics)) = self.windows.get_mut(&window_id) { if state.set_antialiasing(antialiasing) { - *graphics = Arc::new(graphics::SharedGraphicsContext::from_state(state)); + *graphics = + Arc::new(graphics::SharedGraphicsContext::from_state(state)); } } } @@ -5,10 +5,10 @@ use crate::graphics::SharedGraphicsContext; use crate::pipelines::light_cube::InstanceInput; use crate::procedural::ProcedurallyGeneratedObject; use crate::{entity::Transform, model::Model}; +use dropbear_utils::Dirty; use glam::{DMat4, DQuat, DVec3}; use std::fmt::{Display, Formatter}; use std::sync::Arc; -use dropbear_utils::Dirty; const LIGHT_FORWARD_AXIS: DVec3 = DVec3::new(0.0, -1.0, 0.0); @@ -312,14 +312,16 @@ impl Light { let label_str = label.unwrap_or("Light").to_string(); let buffer = UniformBuffer::new(&graphics.device, &label_str); - let bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some(&format!("{} light bind group", label_str)), - layout: &graphics.layouts.light_cube_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: buffer.buffer().as_entire_binding(), - }], - }); + let bind_group = graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some(&format!("{} light bind group", label_str)), + layout: &graphics.layouts.light_cube_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: buffer.buffer().as_entire_binding(), + }], + }); let transform = light.to_transform(); let instance: InstanceInput = DMat4::from_scale_rotation_translation( @@ -57,7 +57,7 @@ impl MipMapper { alpha_to_coverage_enabled: false, }, cache: None, - multiview: None, + multiview_mask: None, }); let storage_texture_layout = @@ -89,8 +89,8 @@ impl MipMapper { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: None, - bind_group_layouts: &[&storage_texture_layout], - push_constant_ranges: &[], + bind_group_layouts: &[Some(&storage_texture_layout)], + immediate_size: 0, }); let compute_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { @@ -232,6 +232,7 @@ impl MipMapper { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_pipeline(&self.blit_mipmap); pass.set_bind_group(0, &texture_bind_group, &[]); @@ -16,7 +16,10 @@ use serde::{Deserialize, Serialize}; use std::hash::{DefaultHasher, Hash, Hasher}; use std::sync::Arc; use std::{mem, ops::Range}; -use wgpu::{BufferAddress, VertexAttribute, VertexBufferLayout, util::DeviceExt}; +use wgpu::{ + BufferAddress, FilterMode, MipmapFilterMode, VertexAttribute, VertexBufferLayout, + util::DeviceExt, +}; // note to self: do not derive clone otherwise it wil take too much memory // #[derive(Clone)] @@ -70,7 +73,19 @@ pub struct Material { pub wrap_mode: TextureWrapMode, } -#[derive(Clone, Copy, Eq, PartialEq, Debug, Serialize, Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize, Default)] +#[derive( + Clone, + Copy, + Eq, + PartialEq, + Debug, + Serialize, + Deserialize, + Archive, + rkyv::Serialize, + rkyv::Deserialize, + Default, +)] pub enum AlphaMode { #[default] Opaque = 1, @@ -89,7 +104,9 @@ impl Into<AlphaMode> for gltf::material::AlphaMode { } /// Represents a node in the scene graph (can be a joint/bone or a mesh) -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Clone, Debug, serde::Serialize, serde::Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize, +)] pub struct Node { pub name: String, pub parent: Option<usize>, @@ -98,7 +115,9 @@ pub struct Node { } /// Local transform of a node relative to its parent -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Clone, Debug, serde::Serialize, serde::Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize, +)] pub struct NodeTransform { pub translation: glam::Vec3, pub rotation: glam::Quat, @@ -120,7 +139,9 @@ impl NodeTransform { } /// A skin defines how a mesh is bound to a skeleton -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Clone, Debug, serde::Serialize, serde::Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize, +)] pub struct Skin { pub name: String, /// Indices of joints (nodes) in the Model's nodes array @@ -132,7 +153,9 @@ pub struct Skin { } /// An animation that can be played on a skeleton -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize, +)] pub struct Animation { pub name: String, pub channels: Vec<AnimationChannel>, @@ -140,7 +163,9 @@ pub struct Animation { } /// Describes how an animation affects a specific node -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize, +)] pub struct AnimationChannel { /// Target node index in the Model's nodes array pub target_node: usize, @@ -152,7 +177,9 @@ pub struct AnimationChannel { pub interpolation: AnimationInterpolation, } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize, +)] pub enum ChannelValues { Translations(Vec<glam::Vec3>), Rotations(Vec<glam::Quat>), @@ -235,56 +262,58 @@ impl Material { .expect("occlusion fallback texture missing from registry") }; - graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("material bind group"), - layout: &graphics.layouts.material_bind_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: tint_buffer.buffer().as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::TextureView(&d.view), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::Sampler(&d.sampler), - }, - wgpu::BindGroupEntry { - binding: 3, - resource: wgpu::BindingResource::TextureView(&n.view), - }, - wgpu::BindGroupEntry { - binding: 4, - resource: wgpu::BindingResource::Sampler(&n.sampler), - }, - wgpu::BindGroupEntry { - binding: 5, - resource: wgpu::BindingResource::TextureView(&e.view), - }, - wgpu::BindGroupEntry { - binding: 6, - resource: wgpu::BindingResource::Sampler(&e.sampler), - }, - wgpu::BindGroupEntry { - binding: 7, - resource: wgpu::BindingResource::TextureView(&mr.view), - }, - wgpu::BindGroupEntry { - binding: 8, - resource: wgpu::BindingResource::Sampler(&mr.sampler), - }, - wgpu::BindGroupEntry { - binding: 9, - resource: wgpu::BindingResource::TextureView(&o.view), - }, - wgpu::BindGroupEntry { - binding: 10, - resource: wgpu::BindingResource::Sampler(&o.sampler), - }, - ], - }) + graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("material bind group"), + layout: &graphics.layouts.material_bind_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: tint_buffer.buffer().as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(&d.view), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::Sampler(&d.sampler), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::TextureView(&n.view), + }, + wgpu::BindGroupEntry { + binding: 4, + resource: wgpu::BindingResource::Sampler(&n.sampler), + }, + wgpu::BindGroupEntry { + binding: 5, + resource: wgpu::BindingResource::TextureView(&e.view), + }, + wgpu::BindGroupEntry { + binding: 6, + resource: wgpu::BindingResource::Sampler(&e.sampler), + }, + wgpu::BindGroupEntry { + binding: 7, + resource: wgpu::BindingResource::TextureView(&mr.view), + }, + wgpu::BindGroupEntry { + binding: 8, + resource: wgpu::BindingResource::Sampler(&mr.sampler), + }, + wgpu::BindGroupEntry { + binding: 9, + resource: wgpu::BindingResource::TextureView(&o.view), + }, + wgpu::BindGroupEntry { + binding: 10, + resource: wgpu::BindingResource::Sampler(&o.sampler), + }, + ], + }) } pub fn new( @@ -398,7 +427,18 @@ impl Material { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + serde::Serialize, + serde::Deserialize, + Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] pub enum AnimationInterpolation { /// The animated values are linearly interpolated between keyframes Linear, @@ -468,7 +508,10 @@ impl GLTFTextureInformation { address_mode_w: wgpu::AddressMode::Repeat, mag_filter, min_filter, - mipmap_filter, + mipmap_filter: match mipmap_filter { + FilterMode::Nearest => MipmapFilterMode::Nearest, + FilterMode::Linear => MipmapFilterMode::Linear, + }, lod_min_clamp: 0.0, lod_max_clamp: 32.0, compare: None, @@ -515,7 +558,9 @@ impl GLTFTextureInformation { } (rgba, wgpu::TextureFormat::Rgba32Float) } - Format::R32G32B32A32FLOAT => (image_data.pixels.clone(), wgpu::TextureFormat::Rgba32Float), + Format::R32G32B32A32FLOAT => { + (image_data.pixels.clone(), wgpu::TextureFormat::Rgba32Float) + } }; GLTFTextureInformation { @@ -751,7 +796,10 @@ impl Model { let mesh_name = mesh.name().unwrap_or("Unnamed Mesh").to_string(); puffin::profile_function!(&mesh_name); - let mesh_weights = mesh.weights().map(|weights| weights.to_vec()).unwrap_or_default(); + let mesh_weights = mesh + .weights() + .map(|weights| weights.to_vec()) + .unwrap_or_default(); for (primitive_index, primitive) in mesh.primitives().enumerate() { puffin::profile_scope!( @@ -829,7 +877,8 @@ impl Model { let mut morph_deltas: Vec<f32> = Vec::new(); let mut morph_target_count: usize = 0; - for (target_positions, _target_normals, _target_tangents) in reader.read_morph_targets() { + for (target_positions, _target_normals, _target_tangents) in reader.read_morph_targets() + { let target_positions: Vec<[f32; 3]> = target_positions .map(|iter| iter.collect()) .unwrap_or_else(|| vec![[0.0, 0.0, 0.0]; positions.len()]); @@ -851,7 +900,6 @@ impl Model { morph_target_count += 1; } - let expected_len = positions.len(); let check_len = |label: &str, len: usize| -> anyhow::Result<()> { @@ -1100,7 +1148,9 @@ impl Model { let interpolation = match channel.sampler().interpolation() { gltf::animation::Interpolation::Linear => AnimationInterpolation::Linear, gltf::animation::Interpolation::Step => AnimationInterpolation::Step, - gltf::animation::Interpolation::CubicSpline => AnimationInterpolation::CubicSpline, // note with morph target weights + gltf::animation::Interpolation::CubicSpline => { + AnimationInterpolation::CubicSpline + } // note with morph target weights }; channels.push(AnimationChannel { @@ -1194,16 +1244,15 @@ impl Model { puffin::profile_scope!("processing material textures"); let material_name = material_info.name; - let extract = - |info: Option<GLTFTextureInformation>| -> Option<ProcessedTexture> { - info.map(|info| ProcessedTexture { - pixels: info.pixels, - dimensions: (info.width, info.height), - format: info.format, - sampler: info.sampler, - mime_type: info.mime_type, - }) - }; + let extract = |info: Option<GLTFTextureInformation>| -> Option<ProcessedTexture> { + info.map(|info| ProcessedTexture { + pixels: info.pixels, + dimensions: (info.width, info.height), + format: info.format, + sampler: info.sampler, + mime_type: info.mime_type, + }) + }; let processed_diffuse = extract(material_info.diffuse_texture); let processed_normal = extract(material_info.normal_texture); @@ -1385,8 +1434,6 @@ impl Model { }); } - - log::debug!("Successfully loaded model [{:?}]", label); let model_path = optional_resref @@ -1396,11 +1443,15 @@ impl Model { let morph_deltas_buffer = if morph_deltas.is_empty() { None } else { - Some(graphics.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("model morph deltas buffer"), - contents: bytemuck::cast_slice(&morph_deltas), - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - })) + Some( + graphics + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("model morph deltas buffer"), + contents: bytemuck::cast_slice(&morph_deltas), + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + }), + ) }; let model = Model { @@ -1516,7 +1567,7 @@ where 0..1, globals_camera_bind_group, animation_bind_group, - environment_bind_group + environment_bind_group, ); } @@ -1646,7 +1697,18 @@ pub trait Vertex { /// }; /// ``` #[repr(C)] -#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable, Serialize, Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Copy, + Clone, + Debug, + bytemuck::Pod, + bytemuck::Zeroable, + Serialize, + Deserialize, + Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] pub struct ModelVertex { pub position: [f32; 3], pub normal: [f32; 3], @@ -1779,4 +1841,4 @@ pub struct MaterialUniform { pub has_metallic_texture: u32, pub has_occlusion_texture: u32, pub pad: u32, -} +} @@ -15,4 +15,4 @@ impl Into<u32> for AntiAliasingMode { AntiAliasingMode::MSAA4 => 4, } } -} +} @@ -1,7 +1,7 @@ -use std::sync::Arc; -use dropbear_utils::Dirty; use crate::buffer::UniformBuffer; use crate::graphics::SharedGraphicsContext; +use dropbear_utils::Dirty; +use std::sync::Arc; #[repr(C)] #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] @@ -36,17 +36,13 @@ impl GlobalsUniform { let data = Dirty::new(Globals::default()); buffer.write(&graphics.queue, &data); - Self { - data, - buffer, - } + Self { data, buffer } } pub fn write(&mut self, queue: &wgpu::Queue) { if self.data.is_dirty() { self.buffer.write(queue, &self.data); } - } pub fn set_num_lights(&mut self, num_lights: u32) { @@ -1,7 +1,7 @@ use crate::pipelines::create_render_pipeline; use crate::texture::{Texture, TextureBuilder}; -use wgpu::util::DeviceExt; use wgpu::Operations; +use wgpu::util::DeviceExt; #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] @@ -48,14 +48,15 @@ impl HdrPipeline { let msaa_texture = match antialiasing { crate::multisampling::AntiAliasingMode::None => None, - _ => Some(TextureBuilder::new(device) - .size(width, height) - .format(format) - .usage(wgpu::TextureUsages::RENDER_ATTACHMENT) - .mag_filter(wgpu::FilterMode::Nearest) - .label("Hdr::texture") - .antialiasing(antialiasing) - .build() + _ => Some( + TextureBuilder::new(device) + .size(width, height) + .format(format) + .usage(wgpu::TextureUsages::RENDER_ATTACHMENT) + .mag_filter(wgpu::FilterMode::Nearest) + .label("Hdr::texture") + .antialiasing(antialiasing) + .build(), ), }; @@ -124,8 +125,8 @@ impl HdrPipeline { let shader = wgpu::include_wgsl!("../shaders/hdr.wgsl"); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: None, - bind_group_layouts: &[&layout], - push_constant_ranges: &[], + bind_group_layouts: &[Some(&layout)], + immediate_size: 0, }); let pipeline = create_render_pipeline( @@ -180,7 +181,13 @@ impl HdrPipeline { } /// Resize the HDR texture - pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32, antialiasing: Option<crate::multisampling::AntiAliasingMode>) { + pub fn resize( + &mut self, + device: &wgpu::Device, + width: u32, + height: u32, + antialiasing: Option<crate::multisampling::AntiAliasingMode>, + ) { self.antialiasing = antialiasing.unwrap_or(self.antialiasing); self.texture = TextureBuilder::new(device) @@ -193,14 +200,15 @@ impl HdrPipeline { self.msaa_texture = match self.antialiasing { crate::multisampling::AntiAliasingMode::None => None, - _ => Some(TextureBuilder::new(device) - .size(width, height) - .format(self.format) - .usage(wgpu::TextureUsages::RENDER_ATTACHMENT) - .mag_filter(wgpu::FilterMode::Nearest) - .label("Hdr::texture") - .antialiasing(self.antialiasing) - .build() + _ => Some( + TextureBuilder::new(device) + .size(width, height) + .format(self.format) + .usage(wgpu::TextureUsages::RENDER_ATTACHMENT) + .mag_filter(wgpu::FilterMode::Nearest) + .label("Hdr::texture") + .antialiasing(self.antialiasing) + .build(), ), }; self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { @@ -269,9 +277,10 @@ impl HdrPipeline { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_pipeline(&self.pipeline); pass.set_bind_group(0, &self.bind_group, &[]); pass.draw(0..3, 0..1); } -} +} @@ -32,10 +32,10 @@ impl DropbearShaderPipeline for LightCubePipeline { .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("light cube pipeline layout"), bind_group_layouts: &[ - &graphics.layouts.camera_bind_group_layout, - &graphics.layouts.light_cube_layout, + Some(&graphics.layouts.camera_bind_group_layout), + Some(&graphics.layouts.light_cube_layout), ], - push_constant_ranges: &[], + immediate_size: 0, }); let hdr_format = graphics.hdr.read().format(); @@ -80,8 +80,8 @@ impl DropbearShaderPipeline for LightCubePipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: Texture::DEPTH_FORMAT, - depth_write_enabled: true, - depth_compare: CompareFunction::Greater, + depth_write_enabled: Some(true), + depth_compare: Some(CompareFunction::Greater), stencil: StencilState::default(), bias: DepthBiasState::default(), }), @@ -90,8 +90,8 @@ impl DropbearShaderPipeline for LightCubePipeline { mask: !0, alpha_to_coverage_enabled: false, }, - multiview: None, cache: None, + multiview_mask: None, }); let storage_buffer = @@ -152,7 +152,7 @@ impl LightCubePipeline { if light.component.enabled && light_index < MAX_LIGHTS { let uniform = light.uniform(); - + if uniform.is_dirty() { light.buffer.write(&graphics.queue, &uniform); } @@ -97,8 +97,8 @@ pub fn create_render_pipeline_ex( }, depth_stencil: depth_format.map(|format| wgpu::DepthStencilState { format, - depth_write_enabled, - depth_compare, + depth_write_enabled: Some(depth_write_enabled), + depth_compare: Some(depth_compare), stencil: wgpu::StencilState::default(), bias: wgpu::DepthBiasState::default(), }), @@ -108,6 +108,6 @@ pub fn create_render_pipeline_ex( alpha_to_coverage_enabled: false, }, cache: None, - multiview: None, + multiview_mask: None, }) } @@ -1,7 +1,7 @@ use crate::graphics::{InstanceRaw, SharedGraphicsContext}; use crate::model; use crate::model::Vertex; -use crate::pipelines::{DropbearShaderPipeline}; +use crate::pipelines::DropbearShaderPipeline; use crate::shader::Shader; use crate::texture::Texture; use std::sync::Arc; @@ -28,10 +28,10 @@ impl DropbearShaderPipeline for MainRenderPipeline { ); let bind_group_layouts = vec![ - &graphics.layouts.per_frame_layout, - &graphics.layouts.material_bind_layout, - &graphics.layouts.animation_layout, - &graphics.layouts.environment_layout, + Some(&graphics.layouts.per_frame_layout), + Some(&graphics.layouts.material_bind_layout), + Some(&graphics.layouts.animation_layout), + Some(&graphics.layouts.environment_layout), ]; let pipeline_layout = @@ -40,7 +40,7 @@ impl DropbearShaderPipeline for MainRenderPipeline { .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("main render pipeline layout"), bind_group_layouts: bind_group_layouts.as_slice(), - push_constant_ranges: &[], + immediate_size: 0, }); let hdr_format = graphics.hdr.read().format(); @@ -77,8 +77,8 @@ impl DropbearShaderPipeline for MainRenderPipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: Texture::DEPTH_FORMAT, - depth_write_enabled: true, - depth_compare: CompareFunction::Greater, + depth_write_enabled: Some(true), + depth_compare: Some(CompareFunction::Greater), stencil: StencilState::default(), bias: DepthBiasState::default(), }), @@ -87,8 +87,8 @@ impl DropbearShaderPipeline for MainRenderPipeline { mask: !0, alpha_to_coverage_enabled: false, }, - multiview: None, cache: None, + multiview_mask: None, }); log::debug!("Created main render pipeline"); @@ -111,7 +111,7 @@ impl DropbearShaderPipeline for MainRenderPipeline { fn pipeline(&self) -> &wgpu::RenderPipeline { &self.pipeline } - + fn shader(&self) -> &Shader { &self.shader } @@ -126,24 +126,26 @@ impl MainRenderPipeline { light_array_buffer: &wgpu::Buffer, ) -> &wgpu::BindGroup { if self.per_frame.is_none() { - let bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("per frame bind group"), - layout: &graphics.layouts.per_frame_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: globals_buffer.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: camera_buffer.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: light_array_buffer.as_entire_binding(), - }, - ], - }); + let bind_group = graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("per frame bind group"), + layout: &graphics.layouts.per_frame_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: globals_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: camera_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: light_array_buffer.as_entire_binding(), + }, + ], + }); self.per_frame = Some(bind_group); } @@ -162,56 +164,58 @@ impl MainRenderPipeline { occlusion_texture: &Texture, ) -> &wgpu::BindGroup { if self.per_material.is_none() { - let bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("per material bind group"), - layout: &graphics.layouts.material_bind_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: material_uniform_buffer.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::TextureView(&diffuse_texture.view), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), - }, - wgpu::BindGroupEntry { - binding: 3, - resource: wgpu::BindingResource::TextureView(&normal_texture.view), - }, - wgpu::BindGroupEntry { - binding: 4, - resource: wgpu::BindingResource::Sampler(&normal_texture.sampler), - }, - wgpu::BindGroupEntry { - binding: 5, - resource: wgpu::BindingResource::TextureView(&emissive_texture.view), - }, - wgpu::BindGroupEntry { - binding: 6, - resource: wgpu::BindingResource::Sampler(&emissive_texture.sampler), - }, - wgpu::BindGroupEntry { - binding: 7, - resource: wgpu::BindingResource::TextureView(&metallic_texture.view), - }, - wgpu::BindGroupEntry { - binding: 8, - resource: wgpu::BindingResource::Sampler(&metallic_texture.sampler), - }, - wgpu::BindGroupEntry { - binding: 9, - resource: wgpu::BindingResource::TextureView(&occlusion_texture.view), - }, - wgpu::BindGroupEntry { - binding: 10, - resource: wgpu::BindingResource::Sampler(&occlusion_texture.sampler), - }, - ], - }); + let bind_group = graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("per material bind group"), + layout: &graphics.layouts.material_bind_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: material_uniform_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(&diffuse_texture.view), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::TextureView(&normal_texture.view), + }, + wgpu::BindGroupEntry { + binding: 4, + resource: wgpu::BindingResource::Sampler(&normal_texture.sampler), + }, + wgpu::BindGroupEntry { + binding: 5, + resource: wgpu::BindingResource::TextureView(&emissive_texture.view), + }, + wgpu::BindGroupEntry { + binding: 6, + resource: wgpu::BindingResource::Sampler(&emissive_texture.sampler), + }, + wgpu::BindGroupEntry { + binding: 7, + resource: wgpu::BindingResource::TextureView(&metallic_texture.view), + }, + wgpu::BindGroupEntry { + binding: 8, + resource: wgpu::BindingResource::Sampler(&metallic_texture.sampler), + }, + wgpu::BindGroupEntry { + binding: 9, + resource: wgpu::BindingResource::TextureView(&occlusion_texture.view), + }, + wgpu::BindGroupEntry { + binding: 10, + resource: wgpu::BindingResource::Sampler(&occlusion_texture.sampler), + }, + ], + }); self.per_material = Some(bind_group); } @@ -227,28 +231,30 @@ impl MainRenderPipeline { morph_weights_buffer: &wgpu::Buffer, morph_info_buffer: &wgpu::Buffer, ) -> wgpu::BindGroup { - graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("animation bind group"), - layout: &graphics.layouts.animation_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: skinning_buffer.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: morph_deltas_buffer.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: morph_weights_buffer.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 3, - resource: morph_info_buffer.as_entire_binding(), - }, - ], - }) + graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("animation bind group"), + layout: &graphics.layouts.animation_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: skinning_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: morph_deltas_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: morph_weights_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: morph_info_buffer.as_entire_binding(), + }, + ], + }) } pub fn environment_bind_group( @@ -258,20 +264,22 @@ impl MainRenderPipeline { environment_sampler: &wgpu::Sampler, ) -> &wgpu::BindGroup { if self.environment.is_none() { - let bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("environment bind group"), - layout: &graphics.layouts.environment_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(environment_view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(environment_sampler), - }, - ], - }); + let bind_group = graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("environment bind group"), + layout: &graphics.layouts.environment_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(environment_view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(environment_sampler), + }, + ], + }); self.environment = Some(bind_group); } @@ -15,13 +15,35 @@ use wgpu::util::DeviceExt; pub mod cube; -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Clone, + Debug, + PartialEq, + Eq, + Hash, + Serialize, + Deserialize, + Archive, + rkyv::Serialize, + rkyv::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, Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Clone, + Debug, + PartialEq, + Eq, + Hash, + Serialize, + Deserialize, + Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] pub struct ProcedurallyGeneratedObject { pub vertices: Vec<ModelVertex>, pub indices: Vec<u32>, @@ -85,11 +107,8 @@ impl ProcedurallyGeneratedObject { let material = material.unwrap_or_else(|| { let mut _rguard = registry.write(); - let white_srgb_texture = _rguard.solid_texture_rgba8( - graphics.clone(), - [255, 255, 255, 255], - None, - ); + let white_srgb_texture = + _rguard.solid_texture_rgba8(graphics.clone(), [255, 255, 255, 255], None); Material::new( &mut _rguard, @@ -6,8 +6,8 @@ use winit::event::WindowEvent; use winit::event_loop::ActiveEventLoop; use winit::window::WindowId; -use crate::{WindowData, graphics::SharedGraphicsContext, input}; use crate::multisampling::AntiAliasingMode; +use crate::{WindowData, graphics::SharedGraphicsContext, input}; use parking_lot::RwLock; use std::{collections::HashMap, rc::Rc, sync::Arc}; @@ -1,5 +1,5 @@ use crate::graphics::SharedGraphicsContext; -use crate::pipelines::{create_render_pipeline_ex}; +use crate::pipelines::create_render_pipeline_ex; use crate::texture::{Texture, TextureBuilder}; use image::codecs::hdr::HdrDecoder; use std::io::Cursor; @@ -56,7 +56,7 @@ impl CubeTexture { address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter, min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::MipmapFilterMode::Linear, ..Default::default() }); @@ -124,8 +124,8 @@ impl HdrLoader { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: None, - bind_group_layouts: &[&equirect_layout], - push_constant_ranges: &[], + bind_group_layouts: &[Some(&equirect_layout)], + immediate_size: 0, }); let equirect_to_cubemap = @@ -170,22 +170,21 @@ impl HdrLoader { let mip_gen_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: None, - bind_group_layouts: &[&mip_gen_layout], - push_constant_ranges: &[], + bind_group_layouts: &[Some(&mip_gen_layout)], + immediate_size: 0, }); let mip_gen_module = device.create_shader_module(wgpu::include_wgsl!("shaders/mip_generator.wgsl")); - let mip_gen_pipeline = - device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { - label: Some("env cubemap mip generator"), - layout: Some(&mip_gen_pipeline_layout), - module: &mip_gen_module, - entry_point: Some("generate_mip"), - compilation_options: Default::default(), - cache: None, - }); + let mip_gen_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: Some("env cubemap mip generator"), + layout: Some(&mip_gen_pipeline_layout), + module: &mip_gen_module, + entry_point: Some("generate_mip"), + compilation_options: Default::default(), + cache: None, + }); Self { equirect_to_cubemap, @@ -213,11 +212,7 @@ impl HdrLoader { #[cfg(not(target_arch = "wasm32"))] let pixels = { let dec = image::DynamicImage::from_decoder(hdr_decoder)?; - let pixels: Vec<[f32; 4]> = dec - .into_rgba32f() - .pixels() - .map(|p| p.0) - .collect(); + let pixels: Vec<[f32; 4]> = dec.into_rgba32f().pixels().map(|p| p.0).collect(); pixels }; #[cfg(target_arch = "wasm32")] @@ -340,10 +335,9 @@ impl HdrLoader { ], }); - let mut mip_encoder = - device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("mip gen encoder"), - }); + let mut mip_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("mip gen encoder"), + }); { let mut pass = mip_encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { label: Some("mip gen pass"), @@ -378,76 +372,84 @@ impl SkyPipeline { camera_buffer: &wgpu::Buffer, ) -> Self { puffin::profile_function!(); - let camera_layout = graphics.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("sky camera bind group layout"), - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - }); - - let environment_layout = graphics.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("sky environment bind group layout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::Cube, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }); + let camera_layout = + graphics + .device + .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("sky camera bind group layout"), + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + }); - let camera_bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("sky camera bind group"), - layout: &camera_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: camera_buffer.as_entire_binding(), - }], - }); + let environment_layout = + graphics + .device + .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("sky environment bind group layout"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::Cube, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + }); - let environment_bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("sky environment bind group"), - layout: &environment_layout, - entries: &[ - wgpu::BindGroupEntry { + let camera_bind_group = graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("sky camera bind group"), + layout: &camera_layout, + entries: &[wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::TextureView(sky_texture.view()), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(sky_texture.sampler()), - }, - ], - }); + resource: camera_buffer.as_entire_binding(), + }], + }); + + let environment_bind_group = + graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("sky environment bind group"), + layout: &environment_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: &[ - &camera_layout, - &environment_layout, - ], - push_constant_ranges: &[], + bind_group_layouts: &[Some(&camera_layout), Some(&environment_layout)], + immediate_size: 0, }); let shader = wgpu::include_wgsl!("shaders/sky.wgsl"); create_render_pipeline_ex( @@ -2,13 +2,16 @@ use std::sync::Arc; use crate::asset::AssetRegistry; use crate::graphics::SharedGraphicsContext; -use crate::utils::{ResourceReference}; +use crate::multisampling::AntiAliasingMode; +use crate::utils::ResourceReference; use image::{DynamicImage, GenericImageView, RgbaImage}; -use uuid::Uuid; use rkyv::Archive; use serde::{Deserialize, Serialize}; -use wgpu::{SamplerDescriptor, TextureAspect, TextureFormat, TextureUsages, TextureViewDescriptor, TextureViewDimension}; -use crate::multisampling::{AntiAliasingMode}; +use uuid::Uuid; +use wgpu::{ + SamplerDescriptor, TextureAspect, TextureFormat, TextureUsages, TextureViewDescriptor, + TextureViewDimension, +}; /// Describes a texture, like an image of some sort. Can be a normal texture on a model or a viewport or depth texture. pub struct Texture { @@ -41,7 +44,7 @@ pub struct TextureBuilder<'a> { mag_filter: wgpu::FilterMode, min_filter: wgpu::FilterMode, - mipmap_filter: wgpu::FilterMode, + mipmap_filter: wgpu::MipmapFilterMode, wrap_mode: TextureWrapMode, compare: Option<wgpu::CompareFunction>, lod_min_clamp: f32, @@ -133,11 +136,7 @@ impl Image { if let Some(rgba) = RgbaImage::from_raw(self.width, self.height, self.pixel_data.to_vec()) { DynamicImage::ImageRgba8(rgba) } else { - DynamicImage::ImageRgba8(RgbaImage::from_pixel( - 1, - 1, - image::Rgba([255, 0, 255, 255]), - )) + DynamicImage::ImageRgba8(RgbaImage::from_pixel(1, 1, image::Rgba([255, 0, 255, 255]))) } } } @@ -166,7 +165,7 @@ impl<'a> TextureBuilder<'a> { auto_mip: false, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::MipmapFilterMode::Linear, wrap_mode: TextureWrapMode::Repeat, compare: None, lod_min_clamp: 0.0, @@ -201,7 +200,11 @@ impl<'a> TextureBuilder<'a> { } /// preset: depth_texture() - pub fn depth(mut self, config: &'a wgpu::SurfaceConfiguration, antialiasing: AntiAliasingMode) -> Self { + pub fn depth( + mut self, + config: &'a wgpu::SurfaceConfiguration, + antialiasing: AntiAliasingMode, + ) -> Self { self.source = TextureSource::Empty; self.width = config.width.max(1); self.height = config.height.max(1); @@ -210,7 +213,7 @@ impl<'a> TextureBuilder<'a> { self.sample_count = antialiasing.into(); self.mag_filter = wgpu::FilterMode::Linear; self.min_filter = wgpu::FilterMode::Linear; - self.mipmap_filter = wgpu::FilterMode::Nearest; + self.mipmap_filter = wgpu::MipmapFilterMode::Nearest; self.compare = Some(wgpu::CompareFunction::LessEqual); self.lod_min_clamp = 0.0; self.lod_max_clamp = 100.0; @@ -226,7 +229,7 @@ impl<'a> TextureBuilder<'a> { self.usage = wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING; self.mag_filter = wgpu::FilterMode::Linear; self.min_filter = wgpu::FilterMode::Linear; - self.mipmap_filter = wgpu::FilterMode::Nearest; + self.mipmap_filter = wgpu::MipmapFilterMode::Nearest; self } @@ -326,7 +329,7 @@ impl<'a> TextureBuilder<'a> { reference: ResourceReference::from_bytes(bytes), }; self.auto_mip = true; - self.mipmap_filter = wgpu::FilterMode::Linear; + self.mipmap_filter = wgpu::MipmapFilterMode::Linear; self.usage = wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST @@ -334,7 +337,11 @@ impl<'a> TextureBuilder<'a> { self } - pub fn with_raw_pixels(mut self, graphics: Arc<SharedGraphicsContext>, pixels: &'a [u8]) -> Self { + pub fn with_raw_pixels( + mut self, + graphics: Arc<SharedGraphicsContext>, + pixels: &'a [u8], + ) -> Self { self.graphics = Some(graphics); let hash = AssetRegistry::hash_bytes(pixels); @@ -368,11 +375,7 @@ impl<'a> TextureBuilder<'a> { dimensions.0, dimensions.1 ); - DynamicImage::ImageRgba8(RgbaImage::from_pixel( - 1, - 1, - image::Rgba([255, 0, 255, 255]), - )) + DynamicImage::ImageRgba8(RgbaImage::from_pixel(1, 1, image::Rgba([255, 0, 255, 255]))) }; self.source = TextureSource::Image { @@ -381,7 +384,7 @@ impl<'a> TextureBuilder<'a> { reference: ResourceReference::from_bytes(pixels), }; self.auto_mip = true; - self.mipmap_filter = wgpu::FilterMode::Linear; + self.mipmap_filter = wgpu::MipmapFilterMode::Linear; self.usage = wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST @@ -412,13 +415,20 @@ impl<'a> TextureBuilder<'a> { pub fn build(self) -> Texture { puffin::profile_function!(self.label.unwrap_or("TextureBuilder::build")); - let view_desc: Option<wgpu::TextureViewDescriptor<'_>> = self.view_descriptor.clone().and_then(|v| Some(v.into())); + let view_desc: Option<wgpu::TextureViewDescriptor<'_>> = + self.view_descriptor.clone().and_then(|v| Some(v.into())); let Some(device) = self.device else { - panic!("TextureBuilder::build() requires a device, and it should be provided to have this to exist. weird...") + panic!( + "TextureBuilder::build() requires a device, and it should be provided to have this to exist. weird..." + ) }; match &self.source { - TextureSource::Image { image, hash, reference } => { + TextureSource::Image { + image, + hash, + reference, + } => { let graphics = self .graphics .as_ref() @@ -428,7 +438,11 @@ impl<'a> TextureBuilder<'a> { let mut image = image.to_dynamic(); if let Some((width, height)) = requested_dimensions { if image.width() != width || image.height() != height { - image = image.resize_exact(width, height, image::imageops::FilterType::Triangle); + image = image.resize_exact( + width, + height, + image::imageops::FilterType::Triangle, + ); } } @@ -442,15 +456,10 @@ impl<'a> TextureBuilder<'a> { }; let mip_level_count = self.compute_mip_level_count(size); - let texture = self.create_texture(&graphics.device, size, self.format, mip_level_count); + let texture = + self.create_texture(&graphics.device, size, self.format, mip_level_count); Self::upload_level0(&graphics.queue, &texture, size, &rgba, 4); - self.finish_uploaded_texture( - &graphics, - texture, - size, - *hash, - reference.clone(), - ) + self.finish_uploaded_texture(&graphics, texture, size, *hash, reference.clone()) } _ => { let size = wgpu::Extent3d { @@ -470,9 +479,7 @@ impl<'a> TextureBuilder<'a> { view_formats: &[], }); - let view = texture.create_view( - &view_desc.unwrap_or_default() - ); + let view = texture.create_view(&view_desc.unwrap_or_default()); let sampler = device.create_sampler(&self.build_sampler_desc()); Texture { @@ -581,7 +588,11 @@ impl<'a> TextureBuilder<'a> { reference: ResourceReference, ) -> Texture { let sampler_desc = self.build_sampler_desc(); - let view_descriptor: wgpu::TextureViewDescriptor<'_> = self.view_descriptor.clone().and_then(|v| Some(v.into())).unwrap_or_default(); + let view_descriptor: wgpu::TextureViewDescriptor<'_> = self + .view_descriptor + .clone() + .and_then(|v| Some(v.into())) + .unwrap_or_default(); let view = texture.create_view(&view_descriptor); let sampler = graphics.device.create_sampler(&sampler_desc); @@ -596,9 +607,10 @@ impl<'a> TextureBuilder<'a> { }; if self.auto_mip { - if let Err(err) = graphics - .mipmapper - .compute_mipmaps(&graphics.device, &graphics.queue, &built) + if let Err(err) = + graphics + .mipmapper + .compute_mipmaps(&graphics.device, &graphics.queue, &built) { log_once::warn_once!("Failed to generate mipmaps: {}", err); } @@ -630,7 +642,19 @@ impl Texture { pub const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb; } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + Serialize, + Deserialize, + Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] pub enum TextureWrapMode { Repeat, Clamp, @@ -1,8 +1,8 @@ //! Utilities and helper functions for the dropbear renderer. use crate::procedural::ProcedurallyGeneratedObject; -use serde::{Deserialize, Serialize}; use rkyv::Archive; +use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; use std::path::Path; use std::sync::Arc; @@ -85,7 +85,18 @@ pub fn relative_path_from_euca(uri: &str) -> anyhow::Result<&str> { /// /// An empty `File("")` acts as a "no asset" sentinel wherever an /// `Option<ResourceReference>` is not available (e.g. rkyv-archived structs). -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Clone, + Debug, + PartialEq, + Eq, + Hash, + Serialize, + Deserialize, + Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] pub enum ResourceReference { /// A resource-root-relative file path, e.g. `"models/cube.glb"`. File(String), @@ -237,4 +248,3 @@ where } } } - @@ -729,8 +729,8 @@ fn build_kotlin_wrapper( let jni_path: syn::Path = kotlin_args.jni_path.unwrap_or_else(|| parse_quote!(::jni)); let mut wrapper_inputs = vec![ - quote! { mut env: #jni_path::JNIEnv }, - quote! { _: #jni_path::objects::JClass }, + quote! { mut unowned_env: #jni_path::EnvUnowned<'local> }, + quote! { _: #jni_path::objects::JClass<'local> }, ]; let mut conversions = Vec::new(); let mut call_args = Vec::new(); @@ -743,8 +743,8 @@ fn build_kotlin_wrapper( let #name = match ::hecs::Entity::from_bits(#name as u64) { Some(v) => v, None => { - let _ = env.throw_new("java/lang/RuntimeException", "Invalid entity id"); - return crate::ffi_error_return!(); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from("Invalid entity id")); + return Ok(crate::ffi_error_return!()); } }; }); @@ -775,25 +775,16 @@ fn build_kotlin_wrapper( } if is_string_type(&arg.ty) { - wrapper_inputs.push(quote! { #name: #jni_path::objects::JString }); + wrapper_inputs.push(quote! { #name: #jni_path::objects::JString<'local> }); conversions.push(quote! { - let #name = match env.get_string(&#name) { - Ok(v) => match v.to_str() { - Ok(v) => v.to_string(), - Err(e) => { - let _ = env.throw_new( - "java/lang/RuntimeException", - format!("Failed to convert string to utf8: {:?}", e) - ); - return crate::ffi_error_return!(); - } - }, + let #name = match #name.mutf8_chars(&mut env) { + Ok(chars) => chars.to_str().into_owned(), Err(e) => { let _ = env.throw_new( - "java/lang/RuntimeException", - format!("Failed to get string from jni: {:?}", e) + #jni_path::strings::JNIString::from("java/lang/RuntimeException"), + #jni_path::strings::JNIString::from(format!("Failed to get string from jni: {:?}", e)) ); - return crate::ffi_error_return!(); + return Ok(crate::ffi_error_return!()); } }; }); @@ -807,7 +798,7 @@ fn build_kotlin_wrapper( } if !is_primitive_type(&arg.ty) { - wrapper_inputs.push(quote! { #name: #jni_path::objects::JObject }); + wrapper_inputs.push(quote! { #name: #jni_path::objects::JObject<'local> }); let (target_ty, is_mut_ref) = match &arg.ty { Type::Reference(reference) => (&*reference.elem, reference.mutability.is_some()), _ => (&arg.ty, false), @@ -818,8 +809,8 @@ fn build_kotlin_wrapper( let #value_name = match crate::scripting::jni::utils::FromJObject::from_jobject(&mut env, &#name) { Ok(v) => v, Err(e) => { - let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to convert object: {:?}", e)); - return crate::ffi_error_return!(); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from(format!("Failed to convert object: {:?}", e))); + return Ok(crate::ffi_error_return!()); } }; }); @@ -856,9 +847,11 @@ fn build_kotlin_wrapper( quote! { #[unsafe(no_mangle)] #[allow(non_snake_case)] - pub extern "system" fn #jni_ident(#(#wrapper_inputs),*) -> #jni_return_ty { - #(#conversions)* - #result_match + pub extern "system" fn #jni_ident<'local>(#(#wrapper_inputs),*) -> #jni_return_ty { + unowned_env.with_env(|mut env| -> ::std::result::Result<#jni_return_ty, #jni_path::errors::Error> { + #(#conversions)* + Ok(#result_match) + }).resolve::<#jni_path::errors::ThrowRuntimeExAndDefault>() } } } @@ -877,7 +870,7 @@ fn build_jni_return( crate::scripting::result::DropbearNativeResult::Ok(()) => (), crate::scripting::result::DropbearNativeResult::Err(e) => { eprintln!("JNI call failed in {}: {:?}", stringify!(#inner_name), e); - let _ = env.throw_new("java/lang/RuntimeException", format!("JNI call failed: {:?}", e)); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from(format!("JNI call failed: {:?}", e))); } } }; @@ -892,22 +885,22 @@ fn build_jni_return( match #inner_name(#(#call_args),*) { crate::scripting::result::DropbearNativeResult::Ok(val) => match val { Some(v) => { - let cls = match env.find_class(#wrapper) { + let cls = match env.load_class(#jni_path::strings::JNIString::from(#wrapper)) { Ok(cls) => cls, Err(e) => { eprintln!("return_boxed failed for {}: {:?}", #wrapper, e); - let _ = env.throw_new("java/lang/RuntimeException", format!("Boxing failed: {:?}", e)); - return std::ptr::null_mut(); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from(format!("Boxing failed: {:?}", e))); + return Ok(std::ptr::null_mut()); } }; let param: #jni_path::objects::JValue = #jvalue_expr; - let ret = match env.call_static_method(cls, "valueOf", #sig, &[param]) { + let ret = match env.call_static_method(cls, #jni_path::strings::JNIString::from("valueOf"), #sig, &[param]) { Ok(ret) => ret, Err(e) => { eprintln!("return_boxed failed for {}: {:?}", #wrapper, e); - let _ = env.throw_new("java/lang/RuntimeException", format!("Boxing failed: {:?}", e)); - return std::ptr::null_mut(); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from(format!("Boxing failed: {:?}", e))); + return Ok(std::ptr::null_mut()); } }; @@ -915,7 +908,7 @@ fn build_jni_return( Ok(obj) => obj.into_raw(), Err(e) => { eprintln!("return_boxed failed for {}: {:?}", #wrapper, e); - let _ = env.throw_new("java/lang/RuntimeException", format!("Boxing failed: {:?}", e)); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from(format!("Boxing failed: {:?}", e))); std::ptr::null_mut() } } @@ -924,7 +917,7 @@ fn build_jni_return( }, crate::scripting::result::DropbearNativeResult::Err(e) => { eprintln!("JNI call failed in {}: {:?}", stringify!(#inner_name), e); - let _ = env.throw_new("java/lang/RuntimeException", format!("JNI call failed: {:?}", e)); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from(format!("JNI call failed: {:?}", e))); std::ptr::null_mut() } } @@ -939,7 +932,7 @@ fn build_jni_return( Some(v) => match env.new_string(v) { Ok(s) => s.into_raw(), Err(e) => { - let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create jstring: {:?}", e)); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from(format!("Failed to create jstring: {:?}", e))); std::ptr::null_mut() } }, @@ -947,7 +940,7 @@ fn build_jni_return( }, crate::scripting::result::DropbearNativeResult::Err(e) => { eprintln!("JNI call failed in {}: {:?}", stringify!(#inner_name), e); - let _ = env.throw_new("java/lang/RuntimeException", format!("JNI call failed: {:?}", e)); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from(format!("JNI call failed: {:?}", e))); std::ptr::null_mut() } } @@ -961,7 +954,7 @@ fn build_jni_return( Some(v) => match crate::scripting::jni::utils::ToJObject::to_jobject(&v, &mut env) { Ok(obj) => obj.into_raw(), Err(e) => { - let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to convert object: {:?}", e)); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from(format!("Failed to convert object: {:?}", e))); std::ptr::null_mut() } }, @@ -969,7 +962,7 @@ fn build_jni_return( }, crate::scripting::result::DropbearNativeResult::Err(e) => { eprintln!("JNI call failed in {}: {:?}", stringify!(#inner_name), e); - let _ = env.throw_new("java/lang/RuntimeException", format!("JNI call failed: {:?}", e)); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from(format!("JNI call failed: {:?}", e))); std::ptr::null_mut() } } @@ -983,13 +976,13 @@ fn build_jni_return( crate::scripting::result::DropbearNativeResult::Ok(val) => match env.new_string(val) { Ok(s) => s.into_raw(), Err(e) => { - let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create jstring: {:?}", e)); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from(format!("Failed to create jstring: {:?}", e))); crate::ffi_error_return!() } }, crate::scripting::result::DropbearNativeResult::Err(e) => { eprintln!("JNI call failed in {}: {:?}", stringify!(#inner_name), e); - let _ = env.throw_new("java/lang/RuntimeException", format!("JNI call failed: {:?}", e)); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from(format!("JNI call failed: {:?}", e))); crate::ffi_error_return!() } } @@ -1005,7 +998,7 @@ fn build_jni_return( crate::scripting::result::DropbearNativeResult::Ok(val) => #cast, crate::scripting::result::DropbearNativeResult::Err(e) => { eprintln!("JNI call failed in {}: {:?}", stringify!(#inner_name), e); - let _ = env.throw_new("java/lang/RuntimeException", format!("JNI call failed: {:?}", e)); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from(format!("JNI call failed: {:?}", e))); crate::ffi_error_return!() } } @@ -1018,13 +1011,13 @@ fn build_jni_return( crate::scripting::result::DropbearNativeResult::Ok(val) => match crate::scripting::jni::utils::ToJObject::to_jobject(&val, &mut env) { Ok(obj) => obj.into_raw(), Err(e) => { - let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to convert object: {:?}", e)); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from(format!("Failed to convert object: {:?}", e))); std::ptr::null_mut() } }, crate::scripting::result::DropbearNativeResult::Err(e) => { eprintln!("JNI call failed in {}: {:?}", stringify!(#inner_name), e); - let _ = env.throw_new("java/lang/RuntimeException", format!("JNI call failed: {:?}", e)); + let _ = env.throw_new(#jni_path::strings::JNIString::from("java/lang/RuntimeException"), #jni_path::strings::JNIString::from(format!("JNI call failed: {:?}", e))); std::ptr::null_mut() } } @@ -1060,7 +1053,7 @@ fn jni_param_type(ty: &Type, jni_path: &syn::Path) -> proc_macro2::TokenStream { fn jni_arg_cast(ty: &Type, name: &proc_macro2::TokenStream) -> proc_macro2::TokenStream { if is_bool_type(ty) { - return quote! { #name != 0 }; + return quote! { #name }; } if is_i8_type(ty) { return quote! { #name as i8 }; @@ -1108,7 +1101,7 @@ fn jni_value_cast( jni_path: &syn::Path, ) -> proc_macro2::TokenStream { if is_bool_type(ty) { - return quote! { if #name { 1 } else { 0 } }; + return quote! { #name }; } if is_i8_type(ty) || is_u8_type(ty) { return quote! { #name as #jni_path::sys::jbyte }; @@ -1142,56 +1135,56 @@ fn jni_boxing_info( ) { if is_i32_type(ty) || is_u32_type(ty) { return ( - quote! { "(I)Ljava/lang/Integer;" }, + quote! { #jni_path::jni_sig!((int) -> java.lang.Integer) }, quote! { "java/lang/Integer" }, quote! { #jni_path::objects::JValue::Int(v as #jni_path::sys::jint) }, ); } if is_i64_type(ty) || is_u64_type(ty) || is_isize_type(ty) || is_usize_type(ty) { return ( - quote! { "(J)Ljava/lang/Long;" }, + quote! { #jni_path::jni_sig!((long) -> java.lang.Long) }, quote! { "java/lang/Long" }, quote! { #jni_path::objects::JValue::Long(v as #jni_path::sys::jlong) }, ); } if is_i16_type(ty) || is_u16_type(ty) { return ( - quote! { "(S)Ljava/lang/Short;" }, + quote! { #jni_path::jni_sig!((short) -> java.lang.Short) }, quote! { "java/lang/Short" }, quote! { #jni_path::objects::JValue::Short(v as #jni_path::sys::jshort) }, ); } if is_i8_type(ty) || is_u8_type(ty) { return ( - quote! { "(B)Ljava/lang/Byte;" }, + quote! { #jni_path::jni_sig!((byte) -> java.lang.Byte) }, quote! { "java/lang/Byte" }, quote! { #jni_path::objects::JValue::Byte(v as #jni_path::sys::jbyte) }, ); } if is_bool_type(ty) { return ( - quote! { "(Z)Ljava/lang/Boolean;" }, + quote! { #jni_path::jni_sig!((boolean) -> java.lang.Boolean) }, quote! { "java/lang/Boolean" }, - quote! { #jni_path::objects::JValue::Bool(if v { 1 } else { 0 }) }, + quote! { #jni_path::objects::JValue::Bool(v) }, ); } if is_float_type(ty) { return ( - quote! { "(F)Ljava/lang/Float;" }, + quote! { #jni_path::jni_sig!((float) -> java.lang.Float) }, quote! { "java/lang/Float" }, quote! { #jni_path::objects::JValue::Float(v as #jni_path::sys::jfloat) }, ); } if is_double_type(ty) { return ( - quote! { "(D)Ljava/lang/Double;" }, + quote! { #jni_path::jni_sig!((double) -> java.lang.Double) }, quote! { "java/lang/Double" }, quote! { #jni_path::objects::JValue::Double(v as #jni_path::sys::jdouble) }, ); } ( - quote! { "(J)Ljava/lang/Long;" }, + quote! { #jni_path::jni_sig!((long) -> java.lang.Long) }, quote! { "java/lang/Long" }, quote! { #jni_path::objects::JValue::Long(v as #jni_path::sys::jlong) }, ) @@ -7,28 +7,31 @@ pub struct Dirty<T> { impl<T> Dirty<T> { /// Creates a new [`Dirty`] of type [`T`]. Marks clean on initial creation. pub fn new(value: T) -> Self { - Self { value, dirty: false } + Self { + value, + dirty: false, + } } - /// Creates a new [`Dirty`] of type [`T`]. Marks dirty on initial creation. + /// Creates a new [`Dirty`] of type [`T`]. Marks dirty on initial creation. pub fn new_dirty(value: T) -> Self { Self { value, dirty: true } } /// Fetches a reference to the value. - /// + /// /// Does not change the state of the cleanliness pub fn get(&self) -> &T { &self.value } - /// Sets this to a new value, marking dirty in the process. + /// Sets this to a new value, marking dirty in the process. pub fn set(&mut self, value: T) { self.value = value; self.dirty = true; } - /// Mutates the inner value and marks dirty. + /// Mutates the inner value and marks dirty. pub fn mutate(&mut self, f: impl FnOnce(&mut T)) { f(&mut self.value); self.dirty = true; @@ -44,7 +47,7 @@ impl<T> Dirty<T> { self.dirty = false; } - /// Marks the value as dirty. + /// Marks the value as dirty. pub fn mark_dirty(&mut self) { self.dirty = true; } @@ -79,4 +82,4 @@ impl<T> std::ops::DerefMut for Dirty<T> { self.dirty = true; &mut self.value } -} +} @@ -260,7 +260,9 @@ impl<K: Eq + std::hash::Hash, V> StaleTracker<K, V> { /// Iterates over key/value pairs without updating access generation. pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> { - self.map.iter().map(|(key, (value, _generation))| (key, value)) + self.map + .iter() + .map(|(key, (value, _generation))| (key, value)) } } @@ -1,5 +1,5 @@ -mod hashmap; pub mod either; +mod hashmap; +pub use either::*; pub use hashmap::*; -pub use either::*; @@ -1,4 +1,6 @@ -use crate::component::{Component, ComponentDescriptor, DisabilityFlags, InspectableComponent, SerializedComponent}; +use crate::component::{ + Component, ComponentDescriptor, DisabilityFlags, InspectableComponent, SerializedComponent, +}; use crate::ptr::WorldPtr; use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; @@ -66,7 +68,10 @@ impl Component for AnimationComponent { .and_then(|index| model.animations.get(index)) .and_then(|animation| { if self.local_pose.is_empty() { - return animation.channels.first().map(|channel| channel.target_node); + return animation + .channels + .first() + .map(|channel| channel.target_node); } let mut pose_nodes: Vec<usize> = self.local_pose.keys().cloned().collect(); @@ -1,12 +1,12 @@ pub mod model; pub mod texture; +use jni::{jni_sig, jni_str, Env}; use crate::ptr::{AssetRegistryPtr, AssetRegistryUnwrapped}; use crate::scripting::jni::utils::FromJObject; use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; use dropbear_engine::asset::AssetKind; -use jni::JNIEnv; use jni::objects::JObject; #[dropbear_macro::export( @@ -40,11 +40,11 @@ fn dropbear_asset_get_asset( } impl FromJObject for AssetKind { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> where Self: Sized, { - let ordinal = env.call_method(obj, "ordinal", "()I", &[])?.i()?; + let ordinal = env.call_method(obj, jni_str!("ordinal"), jni_sig!(() -> i32), &[])?.i()?; match ordinal { 0 => Ok(AssetKind::Texture), @@ -8,8 +8,9 @@ use dropbear_engine::model::{ Animation, AnimationChannel, AnimationInterpolation, ChannelValues, Material, Mesh, ModelVertex, Node, NodeTransform, Skin, }; -use jni::JNIEnv; +use jni::{jni_sig, jni_str, Env}; use jni::objects::{JObject, JValue}; +use jni::sys::jboolean; #[repr(C)] #[derive(Clone, Debug)] @@ -25,9 +26,9 @@ pub struct NModelVertex { } impl ToJObject for NModelVertex { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/asset/model/ModelVertex") + .load_class(jni_str!("com/dropbear/asset/model/ModelVertex")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let position = self.position.to_jobject(env)?; @@ -53,7 +54,7 @@ impl ToJObject for NModelVertex { let obj = env .new_object( &class, - "(Lcom/dropbear/math/Vector3f;Lcom/dropbear/math/Vector3f;Lcom/dropbear/math/Vector4f;Lcom/dropbear/math/Vector2f;Lcom/dropbear/math/Vector2f;Lcom/dropbear/math/Vector4f;[ILcom/dropbear/math/Vector4f;)V", + jni_sig!((com.dropbear.math.Vector3f, com.dropbear.math.Vector3f, com.dropbear.math.Vector4f, com.dropbear.math.Vector2f, com.dropbear.math.Vector2f, com.dropbear.math.Vector4f, [int], com.dropbear.math.Vector4f) -> ()), &args, ) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; @@ -72,9 +73,9 @@ pub struct NMesh { } impl ToJObject for NMesh { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/asset/model/Mesh") + .load_class(jni_str!("com.dropbear.asset.model.Mesh")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let name = env @@ -90,7 +91,7 @@ impl ToJObject for NMesh { ]; let obj = env - .new_object(&class, "(Ljava/lang/String;IILjava/util/List;)V", &args) + .new_object(&class, jni_sig!((java.lang.String, int, int, java.util.List) -> ()), &args) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) @@ -118,9 +119,9 @@ pub struct NMaterial { } impl ToJObject for NMaterial { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/asset/model/Material") + .load_class(jni_str!("com.dropbear.asset.model.Material")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let name = env @@ -157,7 +158,7 @@ impl ToJObject for NMaterial { JValue::Double(self.metallic_factor as f64), JValue::Double(self.roughness_factor as f64), JValue::Object(&alpha_cutoff), - JValue::Bool(if self.double_sided { 1 } else { 0 }), + JValue::Bool(self.double_sided), JValue::Double(self.occlusion_strength as f64), JValue::Double(self.normal_scale as f64), JValue::Object(&uv_tiling), @@ -169,7 +170,21 @@ impl ToJObject for NMaterial { let obj = env .new_object( &class, - "(Ljava/lang/String;Lcom/dropbear/asset/Texture;Lcom/dropbear/asset/Texture;Lcom/dropbear/math/Vector4d;Lcom/dropbear/math/Vector3d;DDLjava/lang/Double;ZDDLcom/dropbear/math/Vector2d;Lcom/dropbear/asset/Texture;Lcom/dropbear/asset/Texture;Lcom/dropbear/asset/Texture;)V", + jni_sig!(( + java.lang.String, + com.dropbear.asset.Texture, + com.dropbear.asset.Texture, + com.dropbear.math.Vector4d, + com.dropbear.math.Vector3d, + double, double, + java.lang.Double, + boolean, + double, double, + com.dropbear.math.Vector2d, + com.dropbear.asset.Texture, + com.dropbear.asset.Texture, + com.dropbear.asset.Texture + ) -> ()), &args, ) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; @@ -187,9 +202,9 @@ pub struct NNodeTransform { } impl ToJObject for NNodeTransform { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/asset/model/NodeTransform") + .load_class(jni_str!("com.dropbear.asset.model.NodeTransform")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let translation = self.translation.to_jobject(env)?; @@ -205,10 +220,10 @@ impl ToJObject for NNodeTransform { let obj = env .new_object( &class, - "(Lcom/dropbear/math/Vector3d;Lcom/dropbear/math/Quaterniond;Lcom/dropbear/math/Vector3d;)V", + jni_sig!("(Lcom/dropbear/math/Vector3d;Lcom/dropbear/math/Quaterniond;Lcom/dropbear/math/Vector3d;)V"), &args, ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;; Ok(obj) } @@ -224,9 +239,9 @@ pub struct NNode { } impl ToJObject for NNode { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/asset/model/Node") + .load_class(jni_str!("com.dropbear.asset.model.Node")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let name = env @@ -246,10 +261,10 @@ impl ToJObject for NNode { let obj = env .new_object( &class, - "(Ljava/lang/String;Ljava/lang/Integer;Ljava/util/List;Lcom/dropbear/asset/model/NodeTransform;)V", + jni_sig!("(Ljava/lang/String;Ljava/lang/Integer;Ljava/util/List;Lcom/dropbear/asset/model/NodeTransform;)V"), &args, ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;; Ok(obj) } @@ -265,9 +280,9 @@ pub struct NSkin { } impl ToJObject for NSkin { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/asset/model/Skin") + .load_class(jni_str!("com.dropbear.asset.model.Skin")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let name = env @@ -287,10 +302,10 @@ impl ToJObject for NSkin { let obj = env .new_object( &class, - "(Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/lang/Integer;)V", + jni_sig!("(Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/lang/Integer;)V"), &args, ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;; Ok(obj) } @@ -305,9 +320,9 @@ pub struct NAnimation { } impl ToJObject for NAnimation { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/asset/model/Animation") + .load_class(jni_str!("com.dropbear.asset.model.Animation")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let name = env @@ -322,8 +337,8 @@ impl ToJObject for NAnimation { ]; let obj = env - .new_object(&class, "(Ljava/lang/String;Ljava/util/List;D)V", &args) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + .new_object(&class, jni_sig!("(Ljava/lang/String;Ljava/util/List;D)V"), &args) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;; Ok(obj) } @@ -339,9 +354,9 @@ pub struct NAnimationChannel { } impl ToJObject for NAnimationChannel { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/asset/model/AnimationChannel") + .load_class(jni_str!("com.dropbear.asset.model.AnimationChannel")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let times = self.times.as_slice().to_jobject(env)?; @@ -358,10 +373,10 @@ impl ToJObject for NAnimationChannel { let obj = env .new_object( &class, - "(I[DLcom/dropbear/asset/model/ChannelValues;Lcom/dropbear/asset/model/AnimationInterpolation;)V", + jni_sig!("(I[DLcom/dropbear/asset/model/ChannelValues;Lcom/dropbear/asset/model/AnimationInterpolation;)V"), &args, ) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;; Ok(obj) } @@ -377,9 +392,9 @@ pub enum NAnimationInterpolation { } impl ToJObject for NAnimationInterpolation { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/asset/model/AnimationInterpolation") + .load_class(jni_str!("com.dropbear.asset.model.AnimationInterpolation")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let field_name = match self { @@ -391,12 +406,12 @@ impl ToJObject for NAnimationInterpolation { let value = env .get_static_field( class, - field_name, - "Lcom/dropbear/asset/model/AnimationInterpolation;", + jni::strings::JNIString::from(field_name), + jni_sig!("Lcom/dropbear/asset/model/AnimationInterpolation;"), ) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .l() - .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; + .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;; Ok(value) } @@ -413,45 +428,45 @@ pub enum NChannelValues { } impl ToJObject for NChannelValues { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { match self { NChannelValues::Translations { values } => { let class = env - .find_class("com/dropbear/asset/model/ChannelValues$Translations") + .load_class(jni_str!("com.dropbear.asset.model.ChannelValues$Translations")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let list = values.to_jobject(env)?; let obj = env - .new_object(&class, "(Ljava/util/List;)V", &[JValue::Object(&list)]) + .new_object(&class, jni_sig!("(Ljava/util/List;)V"), &[JValue::Object(&list)]) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) } NChannelValues::Rotations { values } => { let class = env - .find_class("com/dropbear/asset/model/ChannelValues$Rotations") + .load_class(jni_str!("com.dropbear.asset.model.ChannelValues$Rotations")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let list = values.to_jobject(env)?; let obj = env - .new_object(&class, "(Ljava/util/List;)V", &[JValue::Object(&list)]) + .new_object(&class, jni_sig!("(Ljava/util/List;)V"), &[JValue::Object(&list)]) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) } NChannelValues::Scales { values } => { let class = env - .find_class("com/dropbear/asset/model/ChannelValues$Scales") + .load_class(jni_str!("com.dropbear.asset.model.ChannelValues$Scales")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let list = values.to_jobject(env)?; let obj = env - .new_object(&class, "(Ljava/util/List;)V", &[JValue::Object(&list)]) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + .new_object(&class, jni_sig!("(Ljava/util/List;)V"), &[JValue::Object(&list)]) + .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;; Ok(obj) } NChannelValues::MorphWeights { values } => { let class = env - .find_class("com/dropbear/asset/model/ChannelValues$MorphWeights") + .load_class(jni_str!("com.dropbear.asset.model.ChannelValues$MorphWeights")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let list = values.to_jobject(env)?; let obj = env - .new_object(&class, "(Ljava/util/List;)V", &[JValue::Object(&list)]) + .new_object(&class, jni_sig!("(Ljava/util/List;)V"), &[JValue::Object(&list)]) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) } @@ -459,12 +474,12 @@ impl ToJObject for NChannelValues { } } -fn new_texture<'a>(env: &mut JNIEnv<'a>, texture_id: u64) -> DropbearNativeResult<JObject<'a>> { +fn new_texture<'a>(env: &mut Env<'a>, texture_id: u64) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/asset/Texture") + .load_class(jni_str!("com.dropbear.asset.Texture")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - env.new_object(&class, "(J)V", &[JValue::Long(texture_id as i64)]) + env.new_object(&class, jni_sig!("(J)V"), &[JValue::Long(texture_id as i64)]) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) } @@ -508,12 +523,8 @@ fn map_material( normal_scale: material.normal_scale, uv_tiling: NVector2::from(material.uv_tiling), emissive_texture: material.emissive_texture.and_then(|v| Some(v.id)), - metallic_roughness_texture: material - .metallic_roughness_texture - .and_then(|v| Some(v.id)), - occlusion_texture: material - .occlusion_texture - .and_then(|v| Some(v.id)), + metallic_roughness_texture: material.metallic_roughness_texture.and_then(|v| Some(v.id)), + occlusion_texture: material.occlusion_texture.and_then(|v| Some(v.id)), } } @@ -1,10 +1,13 @@ -use std::sync::Arc; -use hecs::{Entity, World}; +use crate::component::{ + Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, + SerializedComponent, +}; +use crate::physics::PhysicsState; use dropbear_engine::entity::inspect_rotation_quat; use dropbear_engine::graphics::SharedGraphicsContext; -use crate::component::{Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, SerializedComponent}; -use crate::physics::PhysicsState; use egui::{CollapsingHeader, Ui}; +use hecs::{Entity, World}; +use std::sync::Arc; fn default_world_size() -> glam::Vec2 { glam::Vec2::ONE @@ -45,7 +48,7 @@ impl SerializedComponent for BillboardComponent {} impl Component for BillboardComponent { type SerializedForm = Self; - type RequiredComponentTypes = (Self, ); + type RequiredComponentTypes = (Self,); fn descriptor() -> ComponentDescriptor { ComponentDescriptor { @@ -56,15 +59,23 @@ impl Component for BillboardComponent { category: Some("UI".to_string()), description: Some("Renders a camera-facing textured quad".to_string()), } - } + } - fn init(ser: &'_ Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> ComponentInitFuture<'_, Self> { - Box::pin(async move { - Ok((ser.clone(),)) - }) + fn init( + ser: &'_ Self::SerializedForm, + _graphics: Arc<SharedGraphicsContext>, + ) -> ComponentInitFuture<'_, 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 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> { @@ -86,7 +97,10 @@ impl InspectableComponent for BillboardComponent { .show(ui, |ui| { if ui.button("Edit in UI Editor").clicked() { ui.ctx().data_mut(|d| { - d.insert_temp::<Option<Entity>>(egui::Id::new("open_ui_editor"), Some(entity)); + d.insert_temp::<Option<Entity>>( + egui::Id::new("open_ui_editor"), + Some(entity), + ); }); } @@ -124,7 +138,6 @@ impl InspectableComponent for BillboardComponent { rotation, ); } - }); } -} +} @@ -1,6 +1,7 @@ //! Additional information and context for cameras from the [`dropbear_engine::camera`] use crate::component::{ - Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, SerializedComponent, + Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, + SerializedComponent, }; use crate::ptr::WorldPtr; use crate::scripting::result::DropbearNativeResult; @@ -87,105 +88,105 @@ impl InspectableComponent for Camera { .default_open(true) .id_salt(format!("Camera3D {}", entity.to_bits())) .show(ui, |ui| { - let mut changed = false; - - 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(); + let mut changed = false; + + 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(); + }); + + 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(); + }); + + 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(); + }); + + ui.horizontal(|ui| { + ui.label("Aspect"); + changed |= ui + .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), + ) + .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), + ) + .changed(); + }); + + ui.horizontal(|ui| { + ui.label("FOV"); + changed |= ui + .add(egui::Slider::new(&mut self.settings.fov_y, 1.0..=179.0).suffix("°")) + .changed(); + }); + + ui.horizontal(|ui| { + ui.label("Speed"); + changed |= ui + .add(egui::DragValue::new(&mut self.settings.speed).speed(0.1)) + .changed(); + }); + + ui.horizontal(|ui| { + ui.label("Sensitivity"); + changed |= ui + .add(egui::DragValue::new(&mut self.settings.sensitivity).speed(0.001)) + .changed(); + }); + + if changed { + self.update_view_proj(); + } }); - - 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(); - }); - - 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(); - }); - - ui.horizontal(|ui| { - ui.label("Aspect"); - changed |= ui - .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), - ) - .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), - ) - .changed(); - }); - - ui.horizontal(|ui| { - ui.label("FOV"); - changed |= ui - .add(egui::Slider::new(&mut self.settings.fov_y, 1.0..=179.0).suffix("°")) - .changed(); - }); - - ui.horizontal(|ui| { - ui.label("Speed"); - changed |= ui - .add(egui::DragValue::new(&mut self.settings.speed).speed(0.1)) - .changed(); - }); - - ui.horizontal(|ui| { - ui.label("Sensitivity"); - changed |= ui - .add(egui::DragValue::new(&mut self.settings.sensitivity).speed(0.001)) - .changed(); - }); - - if changed { - self.update_view_proj(); - } - }); } } @@ -3,7 +3,7 @@ use crate::physics::PhysicsState; use crate::scripting::types::KotlinComponents; use crate::ser::model::EucalyptusModel; use crate::states::{SerializedMaterialCustomisation, SerializedMeshRenderer}; -use crate::utils::{ResolveReference}; +use crate::utils::ResolveReference; use downcast_rs::{Downcast, impl_downcast}; use dropbear_engine::asset::{ASSET_REGISTRY, Handle}; use dropbear_engine::entity::{EntityTransform, MeshRenderer, Transform}; @@ -201,14 +201,19 @@ impl ComponentRegistry { Box::pin(async move { let bundle = T::init(serialized, graphics).await?; let applier: Box<dyn ComponentApply + Send + Sync> = - Box::new(TwoWayApplier::<T> { bundle, _phantom: std::marker::PhantomData }); + Box::new(TwoWayApplier::<T> { + bundle, + _phantom: std::marker::PhantomData, + }); Ok(applier) }) }), ); - self.defaults - .insert(type_id.clone(), Box::new(|| Box::new(T::SerializedForm::default()))); + self.defaults.insert( + type_id.clone(), + Box::new(|| Box::new(T::SerializedForm::default())), + ); self.removers.insert( type_id.clone(), @@ -238,7 +243,9 @@ impl ComponentRegistry { let world_ref = unsafe { &*world_ptr }; // skip update on DisabledFlags::Hidden if !matches!(disabled_flags, DisabilityFlags::Never) { - if let Ok(status) = world_ref.get::<&crate::entity_status::EntityStatus>(entity) { + if let Ok(status) = + world_ref.get::<&crate::entity_status::EntityStatus>(entity) + { if status.disabled { continue; } @@ -269,7 +276,8 @@ impl ComponentRegistry { /// Get descriptor for a specific component type pub fn get_descriptor<T: Component + 'static>(&self) -> Option<&ComponentDescriptor> { - self.descriptors.get(&LanguageTypeId::Rust(TypeId::of::<T>())) + self.descriptors + .get(&LanguageTypeId::Rust(TypeId::of::<T>())) } /// Get descriptor by fully qualified type name @@ -304,7 +312,8 @@ impl ComponentRegistry { /// Check if a component type is registered pub fn is_registered<T: Component + 'static>(&self) -> bool { - self.descriptors.contains_key(&LanguageTypeId::Rust(TypeId::of::<T>())) + self.descriptors + .contains_key(&LanguageTypeId::Rust(TypeId::of::<T>())) } /// Get the LanguageTypeId for a component by its fully qualified type name @@ -451,7 +460,11 @@ impl ComponentRegistry { let type_ids = world .entity(entity) - .map(|e| e.component_types().map(LanguageTypeId::Rust).collect::<Vec<_>>()) + .map(|e| { + e.component_types() + .map(LanguageTypeId::Rust) + .collect::<Vec<_>>() + }) .unwrap_or_default(); for type_id in type_ids { @@ -503,8 +516,7 @@ impl ComponentRegistry { /// and before the first scene load. Declarations pushed by the JNI bridge during JVM startup /// will then be visible in the editor Add-Component picker, finders, and removers. pub fn drain_kotlin_queue(&mut self) { - let decls: Vec<KotlinComponentDecl> = - std::mem::take(&mut *KOTLIN_COMPONENT_QUEUE.lock()); + let decls: Vec<KotlinComponentDecl> = std::mem::take(&mut *KOTLIN_COMPONENT_QUEUE.lock()); for decl in decls { self.register_kotlin_descriptor(decl); } @@ -557,7 +569,9 @@ impl ComponentRegistry { self.defaults.insert( id.clone(), Box::new(move || { - Box::new(KotlinComponents { fqcns: vec![fqcn.clone()] }) + Box::new(KotlinComponents { + fqcns: vec![fqcn.clone()], + }) }), ); @@ -570,7 +584,11 @@ impl ComponentRegistry { .query::<(hecs::Entity, &KotlinComponents)>() .iter() .filter_map(|(entity, kc)| { - if kc.has(&fqcn_for_update) { Some(entity.to_bits().get()) } else { None } + if kc.has(&fqcn_for_update) { + Some(entity.to_bits().get()) + } else { + None + } }) .collect(); @@ -810,11 +828,15 @@ impl Component for MeshRenderer { match extension.as_deref() { Some("eucmdl") => { let bytes = std::fs::read(&path)?; - let model = rkyv::from_bytes::<EucalyptusModel, rkyv::rancor::Error>(&bytes) - .map_err(|e| anyhow::anyhow!( + let model = rkyv::from_bytes::<EucalyptusModel, rkyv::rancor::Error>( + &bytes, + ) + .map_err(|e| { + anyhow::anyhow!( "Failed to deserialize .eucmdl '{}' into EucalyptusModel: {e}", path.display() - ))?; + ) + })?; let runtime_model = model.load(model_ref.clone(), graphics); let mut registry = ASSET_REGISTRY.write(); @@ -996,19 +1018,20 @@ impl Component for MeshRenderer { } fn save(&self, _world: &World, _entity: Entity) -> Box<dyn SerializedComponent> { - let save_optional_texture_reference = |tex_reference: Option<ResourceReference>| -> Option<TextureReference> { - let resource = tex_reference?; - if let ResourceReference::File(rel) = &resource { - if !rel.is_empty() { - let project_root = crate::states::PROJECT.read().project_path.clone(); - let abs = project_root.join("resources").join(rel); - if let Ok(entry) = crate::metadata::generate_eucmeta(&abs, &project_root) { - return Some(TextureReference::AssetUuid(entry.uuid)); + let save_optional_texture_reference = + |tex_reference: Option<ResourceReference>| -> Option<TextureReference> { + let resource = tex_reference?; + if let ResourceReference::File(rel) = &resource { + if !rel.is_empty() { + let project_root = crate::states::PROJECT.read().project_path.clone(); + let abs = project_root.join("resources").join(rel); + if let Ok(entry) = crate::metadata::generate_eucmeta(&abs, &project_root) { + return Some(TextureReference::AssetUuid(entry.uuid)); + } } } - } - None - }; + None + }; let asset = ASSET_REGISTRY.read(); let model = asset.get_model(self.model()); @@ -1043,8 +1066,7 @@ impl Component for MeshRenderer { .as_ref() .and_then(|m| m.materials.iter().find(|default| default.name == *label)); - let diffuse_texture = if default_material - .map(|default| default.diffuse_texture) + let diffuse_texture = if default_material.map(|default| default.diffuse_texture) == Some(mat.diffuse_texture) { None @@ -1094,7 +1116,8 @@ impl Component for MeshRenderer { mat.metallic_roughness_texture .and_then(|h| asset.get_texture(h).and_then(|t| t.reference.clone())) }; - let metallic_roughness_texture = save_optional_texture_reference(metallic_roughness_texture); + let metallic_roughness_texture = + save_optional_texture_reference(metallic_roughness_texture); texture_override.insert( label.to_string(), @@ -1175,8 +1198,9 @@ 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) { @@ -221,7 +221,9 @@ impl ProjectConfig { } fn collect_eucs_files(dir: &Path, out: &mut Vec<std::path::PathBuf>) { - let Ok(entries) = fs::read_dir(dir) else { return; }; + let Ok(entries) = fs::read_dir(dir) else { + return; + }; for entry in entries.flatten() { let path = entry.path(); if path.is_dir() { @@ -237,30 +239,17 @@ impl ProjectConfig { for path in eucs_files { match SceneConfig::read_from(&path) { - Ok(scene) => { - log::debug!("Loaded scene config from file, added to SCENES entry: {}", scene.scene_name); - scene_configs.push(scene); - } - Err(e) => { - if let Some(io_err) = e.downcast_ref::<std::io::Error>() { - if io_err.kind() == std::io::ErrorKind::NotFound { - 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 - ); - } else { - if let Some(scene) = - deal_with_bad_scene(&path, &e, &project_root) - { - scene_configs.push(scene); - } - } - } + Ok(scene) => { + log::debug!( + "Loaded scene config from file, added to SCENES entry: {}", + scene.scene_name + ); + scene_configs.push(scene); + } + Err(e) => { + if let Some(io_err) = e.downcast_ref::<std::io::Error>() { + if io_err.kind() == std::io::ErrorKind::NotFound { + log::warn!("Scene file {:?} not found", path); } else { if let Some(first) = scene_configs.first() { log::warn!( @@ -275,8 +264,22 @@ impl ProjectConfig { } } } + } 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 + ); + } else { + if let Some(scene) = deal_with_bad_scene(&path, &e, &project_root) { + scene_configs.push(scene); + } + } } } + } } if scene_configs.is_empty() { @@ -88,7 +88,12 @@ fn draw_circle( ) -> DropbearNativeResult<()> { let c = Vec3::new(center.x as f32, center.y as f32, center.z as f32); let n = Vec3::new(normal.x as f32, normal.y as f32, normal.z as f32); - with_debug!(graphics, |dd| dd.draw_circle(c, radius, n, colour.to_f32_array())); + with_debug!(graphics, |dd| dd.draw_circle( + c, + radius, + n, + colour.to_f32_array() + )); Ok(()) } @@ -103,7 +108,11 @@ fn draw_sphere( colour: &NColour, ) -> DropbearNativeResult<()> { let c = Vec3::new(center.x as f32, center.y as f32, center.z as f32); - with_debug!(graphics, |dd| dd.draw_sphere(c, radius, colour.to_f32_array())); + with_debug!(graphics, |dd| dd.draw_sphere( + c, + radius, + colour.to_f32_array() + )); Ok(()) } @@ -120,7 +129,13 @@ fn draw_globe( colour: &NColour, ) -> DropbearNativeResult<()> { let c = Vec3::new(center.x as f32, center.y as f32, center.z as f32); - with_debug!(graphics, |dd| dd.draw_globe(c, radius, lat_lines, lon_lines, colour.to_f32_array())); + with_debug!(graphics, |dd| dd.draw_globe( + c, + radius, + lat_lines, + lon_lines, + colour.to_f32_array() + )); Ok(()) } @@ -152,8 +167,17 @@ fn draw_obb( colour: &NColour, ) -> DropbearNativeResult<()> { let c = Vec3::new(center.x as f32, center.y as f32, center.z as f32); - let he = Vec3::new(half_extents.x as f32, half_extents.y as f32, half_extents.z as f32); - let r = Quat::from_xyzw(rotation.x as f32, rotation.y as f32, rotation.z as f32, rotation.w as f32); + let he = Vec3::new( + half_extents.x as f32, + half_extents.y as f32, + half_extents.z as f32, + ); + let r = Quat::from_xyzw( + rotation.x as f32, + rotation.y as f32, + rotation.z as f32, + rotation.w as f32, + ); with_debug!(graphics, |dd| dd.draw_obb(c, he, r, colour.to_f32_array())); Ok(()) } @@ -171,12 +195,20 @@ fn draw_capsule( ) -> DropbearNativeResult<()> { let va = Vec3::new(a.x as f32, a.y as f32, a.z as f32); let vb = Vec3::new(b.x as f32, b.y as f32, b.z as f32); - with_debug!(graphics, |dd| dd.draw_capsule(va, vb, radius, colour.to_f32_array())); + with_debug!(graphics, |dd| dd.draw_capsule( + va, + vb, + radius, + colour.to_f32_array() + )); Ok(()) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.rendering.DebugDrawNative", func = "drawCylinder"), + kotlin( + class = "com.dropbear.rendering.DebugDrawNative", + func = "drawCylinder" + ), c )] fn draw_cylinder( @@ -189,7 +221,13 @@ fn draw_cylinder( ) -> DropbearNativeResult<()> { let c = Vec3::new(center.x as f32, center.y as f32, center.z as f32); let ax = Vec3::new(axis.x as f32, axis.y as f32, axis.z as f32); - with_debug!(graphics, |dd| dd.draw_cylinder(c, half_height, radius, ax, colour.to_f32_array())); + with_debug!(graphics, |dd| dd.draw_cylinder( + c, + half_height, + radius, + ax, + colour.to_f32_array() + )); Ok(()) } @@ -207,6 +245,12 @@ fn draw_cone( ) -> DropbearNativeResult<()> { let a = Vec3::new(apex.x as f32, apex.y as f32, apex.z as f32); let d = Vec3::new(dir.x as f32, dir.y as f32, dir.z as f32); - with_debug!(graphics, |dd| dd.draw_cone(a, d, angle, length, colour.to_f32_array())); + with_debug!(graphics, |dd| dd.draw_cone( + a, + d, + angle, + length, + colour.to_f32_array() + )); Ok(()) } @@ -18,10 +18,7 @@ fn label_exists_for_entity( } #[dropbear_macro::export( - kotlin( - class = "com.dropbear.EntityRefNative", - func = "getEntityLabel" - ), + kotlin(class = "com.dropbear.EntityRefNative", func = "getEntityLabel"), c )] fn get_label( @@ -33,10 +30,7 @@ fn get_label( } #[dropbear_macro::export( - kotlin( - class = "com.dropbear.EntityRefNative", - func = "getChildren" - ), + kotlin(class = "com.dropbear.EntityRefNative", func = "getChildren"), c )] fn get_children( @@ -57,10 +51,7 @@ fn get_children( } #[dropbear_macro::export( - kotlin( - class = "com.dropbear.EntityRefNative", - func = "getChildByLabel" - ), + kotlin(class = "com.dropbear.EntityRefNative", func = "getChildByLabel"), c )] fn get_child_by_label( @@ -86,10 +77,7 @@ fn get_child_by_label( } } -#[dropbear_macro::export( - kotlin(class = "com.dropbear.EntityRefNative", func = "getParent"), - c -)] +#[dropbear_macro::export(kotlin(class = "com.dropbear.EntityRefNative", func = "getParent"), c)] fn get_parent( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, @@ -67,7 +67,8 @@ impl Component for EntityStatus { _entity: hecs::Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>, - ) { } + ) { + } fn save(&self, _world: &hecs::World, _entity: hecs::Entity) -> Box<dyn SerializedComponent> { Box::new(SerializedEntityStatus { @@ -84,7 +85,7 @@ impl InspectableComponent for EntityStatus { _entity: hecs::Entity, _ui: &mut egui::Ui, _graphics: Arc<SharedGraphicsContext>, - ) { - // nothing... + ) { + // nothing... } } @@ -8,7 +8,7 @@ use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; use dropbear_engine::gilrs::{Button, GamepadId}; use glam::Vec2; -use jni::JNIEnv; +use jni::Env; use jni::objects::JObject; use jni::sys::jlong; use std::sync::Arc; @@ -216,13 +216,14 @@ struct ConnectedGamepadIds { } impl ToJObject for ConnectedGamepadIds { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let output = env - .new_long_array(self.ids.len() as i32) + .new_long_array(self.ids.len()) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; let long_ids: Vec<jlong> = self.ids.iter().map(|&id| id as jlong).collect(); - env.set_long_array_region(&output, 0, &long_ids) + output + .set_region(env, 0, &long_ids) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(JObject::from(output)) } @@ -9,7 +9,7 @@ pub mod shared { use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; use crate::types::NVector2; - use jni::JNIEnv; + use jni::{Env, jni_str, jni_sig}; use jni::objects::{JObject, JValue}; fn map_int_to_gamepad_button(ordinal: i32) -> Option<dropbear_engine::gilrs::Button> { @@ -88,8 +88,8 @@ pub mod shared { } 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").map_err(|e| { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let cls = env.load_class(jni_str!("com/dropbear/math/Vector2d")).map_err(|e| { eprintln!("Could not find Vector2d class: {:?}", e); DropbearNativeError::GenericError })?; @@ -97,31 +97,31 @@ pub mod shared { let obj = env .new_object( cls, - "(DD)V", + jni_sig!((double, double) -> void), &[JValue::Double(self.x), JValue::Double(self.y)], ) .map_err(|e| { eprintln!("Failed to create Vector2d object: {:?}", e); DropbearNativeError::GenericError - })?; + })?;; Ok(obj) } } impl FromJObject for NVector2 { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> where Self: Sized, { let x_val = env - .get_field(obj, "x", "D") + .get_field(obj, jni_str!("x"), jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let y_val = env - .get_field(obj, "y", "D") + .get_field(obj, jni_str!("y"), jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -1,4 +1,4 @@ -use glam::{Vec3, Mat4, Vec4}; +use glam::{Mat4, Vec3, Vec4}; /// Helpers for converting from a 2D display to a 3D object, such as billboard ui and some /// other applications. @@ -15,7 +15,7 @@ impl NormalisedDeviceCoordinates { let [touch_x, touch_y] = touch_pos.into(); let [screen_width, screen_height] = screen_dims.into(); - let ndc_x = (touch_x / screen_width) * 2.0 - 1.0; + let ndc_x = (touch_x / screen_width) * 2.0 - 1.0; let ndc_y = -(touch_y / screen_height) * 2.0 + 1.0; // flip Y, screen Y is down // unproject to view space @@ -28,35 +28,33 @@ impl NormalisedDeviceCoordinates { // ray origin = camera position, direction = toward unprojected point let ray_origin = (inv_view * Vec4::new(0.0, 0.0, 0.0, 1.0)).truncate(); - let ray_dir = (world_near.truncate() - ray_origin).normalize(); + let ray_dir = (world_near.truncate() - ray_origin).normalize(); (ray_origin, ray_dir) } - pub fn ray_aabb( - origin: Vec3, - dir: Vec3, - aabb_min: Vec3, - aabb_max: Vec3, - ) -> Option<f32> { + pub fn ray_aabb(origin: Vec3, dir: Vec3, aabb_min: Vec3, aabb_max: Vec3) -> Option<f32> { let inv_dir = 1.0 / dir; let t1 = (aabb_min - origin) * inv_dir; let t2 = (aabb_max - origin) * inv_dir; let t_min = t1.min(t2).max_element(); let t_max = t1.max(t2).min_element(); - if t_max >= t_min && t_max > 0.0 { Some(t_min) } else { None } + if t_max >= t_min && t_max > 0.0 { + Some(t_min) + } else { + None + } } - pub fn ray_plane( - origin: Vec3, - dir: Vec3, - plane_normal: Vec3, - plane_d: f32, - ) -> Option<Vec3> { + pub fn ray_plane(origin: Vec3, dir: Vec3, plane_normal: Vec3, plane_d: f32) -> Option<Vec3> { let denom = plane_normal.dot(dir); - if denom.abs() < 1e-10 { return None; } // ray parallel to plane + if denom.abs() < 1e-10 { + return None; + } // ray parallel to plane let t = (plane_d - plane_normal.dot(origin)) / denom; - if t < 0.0 { return None; } // intersection behind ray + if t < 0.0 { + return None; + } // intersection behind ray Some(origin + dir * t) } -} +} @@ -14,33 +14,34 @@ pub mod input; pub mod lighting; pub mod logging; pub mod mesh; +pub mod metadata; pub mod physics; +pub mod plugin; pub mod properties; pub mod ptr; +pub mod resource; pub mod runtime; pub mod scene; pub mod scripting; +pub mod ser; pub mod states; pub mod transform; pub mod types; -pub mod utils; pub mod ui; -pub mod ser; -pub mod metadata; -pub mod resource; +pub mod utils; pub mod uuid; -pub mod plugin; pub use dropbear_macro as macros; +use crate::billboard::BillboardComponent; use crate::component::ComponentRegistry; +use crate::entity_status::EntityStatus; use crate::physics::collider::ColliderGroup; use crate::physics::kcc::KCC; use crate::physics::rigidbody::RigidBody; -use crate::billboard::BillboardComponent; -use crate::entity_status::EntityStatus; use crate::scripting::types::KotlinComponents; use crate::states::Script; +use crate::transform::OnRails; use crate::ui::HUDComponent; use dropbear_engine::animation::AnimationComponent; use dropbear_engine::camera::Camera; @@ -49,7 +50,6 @@ use dropbear_engine::lighting::Light; pub use egui; use properties::CustomProperties; pub use rapier3d; -use crate::transform::OnRails; /// The appdata directory for storing any information. /// @@ -1,5 +1,6 @@ use crate::component::{ - Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, SerializedComponent, + Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, + SerializedComponent, }; use crate::ptr::WorldPtr; use crate::scripting::jni::utils::{FromJObject, ToJObject}; @@ -14,7 +15,7 @@ use dropbear_engine::lighting::{Light, LightType}; use egui::{CollapsingHeader, ComboBox, DragValue, Ui}; use glam::{DQuat, DVec3, Vec3}; use hecs::{Entity, World}; -use jni::JNIEnv; +use jni::{Env, jni_str, jni_sig}; use jni::objects::{JObject, JValue}; use std::sync::Arc; @@ -124,9 +125,7 @@ impl InspectableComponent for Light { let mut display_pos = |yueye: &mut Ui| { let pos_id = yueye.make_persistent_id(("light_pos", entity.to_bits())); - let stored = yueye - .ctx() - .data(|d| d.get_temp::<[f64; 3]>(pos_id)); + let stored = yueye.ctx().data(|d| d.get_temp::<[f64; 3]>(pos_id)); let [mut px, mut py, mut pz] = stored.unwrap_or([ self.component.position.x, self.component.position.y, @@ -161,14 +160,21 @@ impl InspectableComponent for Light { }); if any_dragging || changed || reset { - yueye.ctx().data_mut(|d| d.insert_temp(pos_id, [px, py, pz])); + yueye + .ctx() + .data_mut(|d| d.insert_temp(pos_id, [px, py, pz])); self.component.position = DVec3::new(px, py, pz); } else { - yueye.ctx().data_mut(|d| d.insert_temp(pos_id, [ - self.component.position.x, - self.component.position.y, - self.component.position.z, - ])); + yueye.ctx().data_mut(|d| { + d.insert_temp( + pos_id, + [ + self.component.position.x, + self.component.position.y, + self.component.position.z, + ], + ) + }); } changed @@ -181,14 +187,13 @@ impl InspectableComponent for Light { direction = LIGHT_FORWARD_AXIS; } - let light_rot_cache_id = - egui::Id::new(("light_quat_cache", entity.to_bits())); + let light_rot_cache_id = egui::Id::new(("light_quat_cache", entity.to_bits())); let mut rotation = { - let cached = - yueye.ctx().data(|d| d.get_temp::<DQuat>(light_rot_cache_id)); + let cached = yueye + .ctx() + .data(|d| d.get_temp::<DQuat>(light_rot_cache_id)); if let Some(cached_quat) = cached { - let cached_fwd = - (cached_quat * LIGHT_FORWARD_AXIS).normalize_or_zero(); + let cached_fwd = (cached_quat * LIGHT_FORWARD_AXIS).normalize_or_zero(); if cached_fwd.dot(direction) > 0.999_999 { // Direction unchanged externally; keep the accumulated quat. cached_quat @@ -217,9 +222,9 @@ impl InspectableComponent for Light { if yueye.button("Reset Rotation").clicked() { self.component.direction = LIGHT_FORWARD_AXIS; - yueye.ctx().data_mut(|d| { - d.insert_temp(light_rot_cache_id, DQuat::IDENTITY) - }); + yueye + .ctx() + .data_mut(|d| d.insert_temp(light_rot_cache_id, DQuat::IDENTITY)); changed = true; } @@ -243,7 +248,9 @@ impl InspectableComponent for Light { } if position_changed { - if let Ok(entity_transform) = world.query_one::<&mut EntityTransform>(entity).get() { + if let Ok(entity_transform) = + world.query_one::<&mut EntityTransform>(entity).get() + { entity_transform.local_mut().position = self.component.position; } else if let Ok(transform) = world.query_one::<&mut Transform>(entity).get() { transform.position = self.component.position; @@ -256,9 +263,12 @@ impl InspectableComponent for Light { self.component.direction = desired; let rotation = DQuat::from_rotation_arc(LIGHT_FORWARD_AXIS, desired); - if let Ok(entity_transform) = world.query_one::<&mut EntityTransform>(entity).get() { + if let Ok(entity_transform) = + world.query_one::<&mut EntityTransform>(entity).get() + { entity_transform.local_mut().rotation = rotation; - } else if let Ok(transform) = world.query_one::<&mut Transform>(entity).get() + } else if let Ok(transform) = + world.query_one::<&mut Transform>(entity).get() { transform.rotation = rotation; } @@ -275,7 +285,11 @@ impl InspectableComponent for Light { ui.horizontal(|ui| { ui.label("Intensity"); - ui.add(DragValue::new(&mut self.component.intensity).speed(0.05).range(0.0..=f64::MAX)); + ui.add( + DragValue::new(&mut self.component.intensity) + .speed(0.05) + .range(0.0..=f64::MAX), + ); }); if matches!( @@ -305,11 +319,19 @@ impl InspectableComponent for Light { 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).range(0.0..=180.0)); + ui.add( + DragValue::new(&mut self.component.cutoff_angle) + .speed(0.1) + .range(0.0..=180.0), + ); }); ui.horizontal(|ui| { ui.label("Outer Cutoff"); - ui.add(DragValue::new(&mut self.component.outer_cutoff_angle).speed(0.1).range(0.0..=180.0)); + ui.add( + DragValue::new(&mut self.component.outer_cutoff_angle) + .speed(0.1) + .range(0.0..=180.0), + ); }); if self.component.outer_cutoff_angle <= self.component.cutoff_angle { @@ -322,9 +344,17 @@ impl InspectableComponent for Light { 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).range(0.0..=f64::MAX)); + ui.add( + DragValue::new(&mut self.component.depth.start) + .speed(0.1) + .range(0.0..=f64::MAX), + ); ui.label(".."); - ui.add(DragValue::new(&mut self.component.depth.end).speed(0.1).range(0.0..=f64::MAX)); + ui.add( + DragValue::new(&mut self.component.depth.end) + .speed(0.1) + .range(0.0..=f64::MAX), + ); }); if self.component.depth.end < self.component.depth.start { @@ -359,9 +389,9 @@ impl NColour { } impl FromJObject for NColour { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { let class = env - .find_class("com/dropbear/utils/Colour") + .load_class(jni_str!("com/dropbear/utils/Colour")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; if !env @@ -371,9 +401,9 @@ impl FromJObject for NColour { return Err(DropbearNativeError::InvalidArgument); } - let mut get_byte = |field: &str| -> DropbearNativeResult<u8> { + let mut get_byte = |field| -> DropbearNativeResult<u8> { let v = env - .get_field(obj, field, "B") + .get_field(obj, field, jni_sig!(byte)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .b() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -381,18 +411,18 @@ impl FromJObject for NColour { }; Ok(Self { - r: get_byte("r")?, - g: get_byte("g")?, - b: get_byte("b")?, - a: get_byte("a")?, + r: get_byte(jni_str!("r"))?, + g: get_byte(jni_str!("g"))?, + b: get_byte(jni_str!("b"))?, + a: get_byte(jni_str!("a"))?, }) } } impl ToJObject for NColour { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/utils/Colour") + .load_class(jni_str!("com/dropbear/utils/Colour")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let args = [ @@ -402,7 +432,7 @@ impl ToJObject for NColour { JValue::Byte(self.a as i8), ]; - env.new_object(&class, "(BBBB)V", &args) + env.new_object(&class, jni_sig!((byte, byte, byte, byte) -> void), &args) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) } } @@ -415,9 +445,9 @@ struct NRange { } impl FromJObject for NRange { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { let class = env - .find_class("com/dropbear/utils/Range") + .load_class(jni_str!("com/dropbear/utils/Range")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; if !env @@ -428,13 +458,13 @@ impl FromJObject for NRange { } let start = env - .get_field(obj, "start", "D") + .get_field(obj, jni_str!("start"), jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32; let end = env - .get_field(obj, "end", "D") + .get_field(obj, jni_str!("end"), jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32; @@ -444,9 +474,9 @@ impl FromJObject for NRange { } impl ToJObject for NRange { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/utils/Range") + .load_class(jni_str!("com/dropbear/utils/Range")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let args = [ @@ -454,7 +484,7 @@ impl ToJObject for NRange { JValue::Double(self.end as f64), ]; - env.new_object(&class, "(DD)V", &args) + env.new_object(&class, jni_sig!((double, double) -> void), &args) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) } } @@ -468,9 +498,9 @@ struct NAttenuation { } impl FromJObject for NAttenuation { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { let class = env - .find_class("com/dropbear/lighting/Attenuation") + .load_class(jni_str!("com/dropbear/lighting/Attenuation")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; if !env @@ -481,19 +511,19 @@ impl FromJObject for NAttenuation { } let constant = env - .call_method(obj, "getConstant", "()F", &[]) + .call_method(obj, jni_str!("getConstant"), jni_sig!(() -> float), &[]) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .f() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let linear = env - .call_method(obj, "getLinear", "()F", &[]) + .call_method(obj, jni_str!("getLinear"), jni_sig!(() -> float), &[]) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .f() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let quadratic = env - .call_method(obj, "getQuadratic", "()F", &[]) + .call_method(obj, jni_str!("getQuadratic"), jni_sig!(() -> float), &[]) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .f() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -507,9 +537,9 @@ impl FromJObject for NAttenuation { } impl ToJObject for NAttenuation { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/lighting/Attenuation") + .load_class(jni_str!("com/dropbear/lighting/Attenuation")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let args = [ @@ -518,7 +548,7 @@ impl ToJObject for NAttenuation { JValue::Float(self.quadratic), ]; - env.new_object(&class, "(FFF)V", &args) + env.new_object(&class, jni_sig!((float, float, float) -> void), &args) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) } } @@ -8,7 +8,7 @@ #[cfg(feature = "editor")] use egui::Context; use std::fmt::{Display, Formatter}; - +use egui::Ui; #[cfg(feature = "editor")] use egui_toast::Toasts; @@ -53,7 +53,7 @@ pub static GLOBAL_TOASTS: Lazy<Mutex<Toasts>> = Lazy::new(|| { /// /// Useful when paired with a function that contains [`crate`] #[cfg(feature = "editor")] -pub fn render(context: &Context) { +pub fn render(context: &mut Ui) { let mut toasts = GLOBAL_TOASTS.lock(); toasts.show(context); } @@ -163,9 +163,7 @@ fn get_texture( .get(idx) .ok_or(DropbearNativeError::InvalidArgument)?; - Ok(Some(material - .diffuse_texture - .id)) + Ok(Some(material.diffuse_texture.id)) } #[dropbear_macro::export( @@ -1,14 +1,14 @@ -use std::fs; -use std::time::SystemTime; -use uuid::Uuid; -use std::path::{Path, PathBuf}; -use std::sync::Arc; +use crate::resource::ResourceReference; +use crate::uuid::UuidV4; use rkyv::Archive; use ron::ser::PrettyConfig; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; -use crate::resource::ResourceReference; -use crate::uuid::UuidV4; +use std::fs; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use std::time::SystemTime; +use uuid::Uuid; /// The type of asset, used for filtering in the asset browser /// and determining which importer to invoke. @@ -82,9 +82,7 @@ impl AssetEntry { ResourceReference::File(relative) => { Some(ResourceReference::File(project_root.join(relative))) } - ResourceReference::Procedural => { - Some(ResourceReference::Procedural) - } + ResourceReference::Procedural => Some(ResourceReference::Procedural), ResourceReference::Embedded(_) | ResourceReference::Packed { .. } => None, } } @@ -226,7 +224,9 @@ pub fn scan_and_generate_eucmeta(project_root: &Path) -> usize { let resources_dir = project_root.join("resources"); let mut created = 0usize; fn walk(dir: &Path, project_root: &Path, created: &mut usize) { - let Ok(read) = std::fs::read_dir(dir) else { return }; + let Ok(read) = std::fs::read_dir(dir) else { + return; + }; for entry in read.flatten() { let path = entry.path(); if path.is_dir() { @@ -247,13 +247,20 @@ pub fn scan_and_generate_eucmeta(project_root: &Path) -> usize { } match generate_eucmeta(&path, project_root) { Ok(_) => *created += 1, - Err(e) => log::warn!("Failed to generate .eucmeta for '{}': {}", path.display(), e), + Err(e) => log::warn!( + "Failed to generate .eucmeta for '{}': {}", + path.display(), + e + ), } } } walk(&resources_dir, project_root, &mut created); if created > 0 { - log::info!("Generated {} .eucmeta sidecar(s) during project scan", created); + log::info!( + "Generated {} .eucmeta sidecar(s) during project scan", + created + ); } created } @@ -287,4 +294,4 @@ pub fn find_asset_by_uuid(project_root: &Path, uuid: Uuid) -> anyhow::Result<Ass let resources_dir = project_root.join("resources"); scan_dir(&resources_dir, uuid) .ok_or_else(|| anyhow::anyhow!("No .eucmeta file found for UUID {}", uuid)) -} +} @@ -16,7 +16,9 @@ pub mod collider_group; pub mod shader; -use crate::component::{Component, ComponentDescriptor, DisabilityFlags, InspectableComponent, SerializedComponent}; +use crate::component::{ + Component, ComponentDescriptor, DisabilityFlags, InspectableComponent, SerializedComponent, +}; use crate::physics::PhysicsState; use crate::physics::collider::shared::{get_collider, get_collider_mut}; use crate::ptr::PhysicsStatePtr; @@ -25,10 +27,10 @@ use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; use crate::states::Label; use crate::types::{NCollider, NVector3}; -use ::jni::JNIEnv; +use ::jni::{Env, jni_str, jni_sig}; use ::jni::objects::{JObject, JValue}; use dropbear_engine::asset::ASSET_REGISTRY; -use dropbear_engine::entity::{inspect_rotation_dquat, MeshRenderer}; +use dropbear_engine::entity::{MeshRenderer, inspect_rotation_dquat}; use dropbear_engine::graphics::SharedGraphicsContext; use dropbear_engine::wgpu::util::{BufferInitDescriptor, DeviceExt}; use dropbear_engine::wgpu::{Buffer, BufferUsages}; @@ -146,10 +148,7 @@ impl InspectableComponent for ColliderGroup { } if ui.button("Derive from Mesh").clicked() { - let model_handle = world - .get::<&MeshRenderer>(entity) - .ok() - .map(|r| r.model()); + let model_handle = world.get::<&MeshRenderer>(entity).ok().map(|r| r.model()); if let Some(handle) = model_handle { let registry = ASSET_REGISTRY.read(); @@ -167,7 +166,9 @@ impl InspectableComponent for ColliderGroup { } } - if min.iter().all(|v| v.is_finite()) && max.iter().all(|v| v.is_finite()) { + if min.iter().all(|v| v.is_finite()) + && max.iter().all(|v| v.is_finite()) + { let half_extents = [ (max[0] - min[0]) * 0.5, (max[1] - min[1]) * 0.5, @@ -504,10 +505,10 @@ impl Default for ColliderShape { } impl ToJObject for ColliderShape { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { match self { ColliderShape::Box { half_extents } => { - let vec_cls = env.find_class("com/dropbear/math/Vector3d").map_err(|e| { + let vec_cls = env.load_class(jni_str!("com/dropbear/math/Vector3d")).map_err(|e| { eprintln!("[JNI Error] Vector3d class not found: {:?}", e); DropbearNativeError::JNIClassNotFound })?; @@ -515,7 +516,7 @@ impl ToJObject for ColliderShape { let vec_obj = env .new_object( &vec_cls, - "(DDD)V", + jni_sig!("(DDD)V"), &[ JValue::Double(half_extents.x), JValue::Double(half_extents.y), @@ -525,7 +526,7 @@ impl ToJObject for ColliderShape { .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; let cls = env - .find_class("com/dropbear/physics/ColliderShape$Box") + .load_class(jni_str!("com/dropbear/physics/ColliderShape$Box")) .map_err(|e| { eprintln!("[JNI Error] ColliderShape$Box class not found: {:?}", e); DropbearNativeError::JNIClassNotFound @@ -534,7 +535,7 @@ impl ToJObject for ColliderShape { let obj = env .new_object( &cls, - "(Lcom/dropbear/math/Vector3d;)V", + jni_sig!("(Lcom/dropbear/math/Vector3d;)V"), &[JValue::Object(&vec_obj)], ) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; @@ -543,11 +544,11 @@ impl ToJObject for ColliderShape { } ColliderShape::Sphere { radius } => { let cls = env - .find_class("com/dropbear/physics/ColliderShape$Sphere") + .load_class(jni_str!("com/dropbear/physics/ColliderShape$Sphere")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let obj = env - .new_object(&cls, "(F)V", &[JValue::Float(*radius)]) + .new_object(&cls, jni_sig!("(F)V"), &[JValue::Float(*radius)]) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) @@ -557,13 +558,13 @@ impl ToJObject for ColliderShape { radius, } => { let cls = env - .find_class("com/dropbear/physics/ColliderShape$Capsule") + .load_class(jni_str!("com/dropbear/physics/ColliderShape$Capsule")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let obj = env .new_object( &cls, - "(FF)V", + jni_sig!("(FF)V"), &[JValue::Float(*half_height), JValue::Float(*radius)], ) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; @@ -575,7 +576,7 @@ impl ToJObject for ColliderShape { radius, } => { let cls = env - .find_class("com/dropbear/physics/ColliderShape$Cylinder") + .load_class(jni_str!("com/dropbear/physics/ColliderShape$Cylinder")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; // let ctor = env.get_method_id(&cls, "<init>", "(FF)V") @@ -584,7 +585,7 @@ impl ToJObject for ColliderShape { let obj = env .new_object( &cls, - "(FF)V", + jni_sig!("(FF)V"), &[JValue::Float(*half_height), JValue::Float(*radius)], ) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; @@ -596,13 +597,13 @@ impl ToJObject for ColliderShape { radius, } => { let cls = env - .find_class("com/dropbear/physics/ColliderShape$Cone") + .load_class(jni_str!("com/dropbear/physics/ColliderShape$Cone")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let obj = env .new_object( &cls, - "(FF)V", + jni_sig!("(FF)V"), &[JValue::Float(*half_height), JValue::Float(*radius)], ) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; @@ -614,34 +615,34 @@ impl ToJObject for ColliderShape { } impl FromJObject for ColliderShape { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> where Self: Sized, { - let is_instance = |env: &mut JNIEnv, obj: &JObject, class_name: &str| -> bool { + let is_instance = |env: &mut Env, obj: &JObject, class_name: &jni::strings::JNIStr| -> bool { env.is_instance_of(obj, class_name).unwrap_or(false) }; - if is_instance(env, obj, "com/dropbear/physics/ColliderShape$Box") { + if is_instance(env, obj, jni_str!("com/dropbear/physics/ColliderShape$Box")) { let vec_obj_val = env - .get_field(obj, "halfExtents", "Lcom/dropbear/math/Vector3d;") - .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; + .get_field(obj, jni_str!("halfExtents"), jni_sig!("Lcom/dropbear/math/Vector3d;")) + .map_err(|_| DropbearNativeError::JNIFailedToGetField)?;; let vec_obj = vec_obj_val .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let x = env - .get_field(&vec_obj, "x", "D") + .get_field(&vec_obj, jni_str!("x"), jni_sig!("D")) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .unwrap_or(0.0); let y = env - .get_field(&vec_obj, "y", "D") + .get_field(&vec_obj, jni_str!("y"), jni_sig!("D")) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .unwrap_or(0.0); let z = env - .get_field(&vec_obj, "z", "D") + .get_field(&vec_obj, jni_str!("z"), jni_sig!("D")) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .unwrap_or(0.0); @@ -651,9 +652,9 @@ impl FromJObject for ColliderShape { }); } - if is_instance(env, obj, "com/dropbear/physics/ColliderShape$Sphere") { + if is_instance(env, obj, jni_str!("com/dropbear/physics/ColliderShape$Sphere")) { let radius = env - .get_field(obj, "radius", "F") + .get_field(obj, jni_str!("radius"), jni_sig!("F")) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .f() .unwrap_or(0.0); @@ -661,14 +662,14 @@ impl FromJObject for ColliderShape { return Ok(ColliderShape::Sphere { radius }); } - if is_instance(env, obj, "com/dropbear/physics/ColliderShape$Capsule") { + if is_instance(env, obj, jni_str!("com/dropbear/physics/ColliderShape$Capsule")) { let hh = env - .get_field(obj, "halfHeight", "F") + .get_field(obj, jni_str!("halfHeight"), jni_sig!("F")) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .f() .unwrap_or(0.0); let r = env - .get_field(obj, "radius", "F") + .get_field(obj, jni_str!("radius"), jni_sig!("F")) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .f() .unwrap_or(0.0); @@ -679,14 +680,14 @@ impl FromJObject for ColliderShape { }); } - if is_instance(env, obj, "com/dropbear/physics/ColliderShape$Cylinder") { + if is_instance(env, obj, jni_str!("com/dropbear/physics/ColliderShape$Cylinder")) { let hh = env - .get_field(obj, "halfHeight", "F") + .get_field(obj, jni_str!("halfHeight"), jni_sig!("F")) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .f() .unwrap_or(0.0); let r = env - .get_field(obj, "radius", "F") + .get_field(obj, jni_str!("radius"), jni_sig!("F")) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .f() .unwrap_or(0.0); @@ -697,14 +698,14 @@ impl FromJObject for ColliderShape { }); } - if is_instance(env, obj, "com/dropbear/physics/ColliderShape$Cone") { + if is_instance(env, obj, jni_str!("com/dropbear/physics/ColliderShape$Cone")) { let hh = env - .get_field(obj, "halfHeight", "F") + .get_field(obj, jni_str!("halfHeight"), jni_sig!("F")) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .f() .unwrap_or(0.0); let r = env - .get_field(obj, "radius", "F") + .get_field(obj, jni_str!("radius"), jni_sig!("F")) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .f() .unwrap_or(0.0); @@ -36,9 +36,9 @@ impl DropbearShaderPipeline for ColliderWireframePipeline { .create_pipeline_layout(&PipelineLayoutDescriptor { label: Some("collider wireframe pipeline layout descriptor"), bind_group_layouts: &[ - &graphics.layouts.camera_bind_group_layout, // @group(0) + Some(&graphics.layouts.camera_bind_group_layout), // @group(0) ], - push_constant_ranges: &[], + immediate_size: 0, }); let hdr_format = graphics.hdr.read().format(); @@ -86,8 +86,8 @@ impl DropbearShaderPipeline for ColliderWireframePipeline { }, depth_stencil: Some(DepthStencilState { format: Texture::DEPTH_FORMAT, - depth_write_enabled: false, - depth_compare: CompareFunction::Always, + depth_write_enabled: Some(false), + depth_compare: Some(CompareFunction::Always), stencil: StencilState::default(), bias: DepthBiasState::default(), }), @@ -96,8 +96,8 @@ impl DropbearShaderPipeline for ColliderWireframePipeline { mask: !0, alpha_to_coverage_enabled: false, }, - multiview: None, cache: None, + multiview_mask: None, }); Self { @@ -3,9 +3,11 @@ pub mod character_collision; -use crate::component::{Component, ComponentDescriptor, DisabilityFlags, InspectableComponent, SerializedComponent}; +use crate::component::{ + Component, ComponentDescriptor, DisabilityFlags, InspectableComponent, SerializedComponent, +}; use crate::physics::PhysicsState; -use crate::ptr::{WorldPtr}; +use crate::ptr::WorldPtr; use crate::scripting::jni::utils::ToJObject; use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; @@ -14,9 +16,11 @@ use crate::types::{IndexNative, NQuaternion, NVector3}; use dropbear_engine::graphics::SharedGraphicsContext; use egui::{ComboBox, DragValue, Ui}; use hecs::{Entity, World}; -use jni::JNIEnv; +use jni::{Env, jni_str, jni_sig}; use jni::objects::{JObject, JValue}; -use rapier3d::control::{CharacterAutostep, CharacterCollision, CharacterLength, KinematicCharacterController}; +use rapier3d::control::{ + CharacterAutostep, CharacterCollision, CharacterLength, KinematicCharacterController, +}; use rapier3d::dynamics::RigidBodyType; use rapier3d::math::Rotation; use rapier3d::na::{UnitVector3, Vector3}; @@ -44,25 +48,21 @@ pub struct CharacterMovementResult { } impl ToJObject for CharacterMovementResult { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/physics/CharacterMovementResult") + .load_class(jni_str!("com/dropbear/physics/CharacterMovementResult")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let translation_obj = self.translation.to_jobject(env)?; let args = [ JValue::Object(&translation_obj), - JValue::Bool(self.grounded as u8), - JValue::Bool(self.is_sliding_down_slope as u8), + JValue::Bool(self.grounded), + JValue::Bool(self.is_sliding_down_slope), ]; let obj = env - .new_object( - &class, - "(Lcom/dropbear/math/Vector3d;ZZ)V", - &args, - ) + .new_object(&class, jni_sig!((com.dropbear.math.Vector3d, boolean, boolean) -> void), &args) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) @@ -119,7 +119,10 @@ impl InspectableComponent for KCC { ) { egui::CollapsingHeader::new("Kinematic Character Controller") .default_open(true) - .id_salt(format!("Kinematic Character Controller {}", entity.to_bits())) + .id_salt(format!( + "Kinematic Character Controller {}", + entity.to_bits() + )) .show(ui, |ui| { fn edit_character_length( ui: &mut Ui, @@ -278,17 +281,17 @@ struct CharacterCollisionArray { } impl ToJObject for CharacterCollisionArray { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let collision_cls = env - .find_class("com/dropbear/physics/CharacterCollision") + .load_class(jni_str!("com/dropbear/physics/CharacterCollision")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let entity_cls = env - .find_class("com/dropbear/EntityId") + .load_class(jni_str!("com/dropbear/EntityId")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let entity_obj = env - .new_object(&entity_cls, "(J)V", &[JValue::Long(self.entity_id as i64)]) + .new_object(&entity_cls, jni_sig!((long) -> void), &[JValue::Long(self.entity_id as i64)]) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; let out = env @@ -304,12 +307,12 @@ impl ToJObject for CharacterCollisionArray { let collision_obj = env .new_object( &collision_cls, - "(Lcom/dropbear/EntityId;Lcom/dropbear/physics/Index;)V", + jni_sig!((com.dropbear.EntityId, com.dropbear.physics.Index) -> void), &[JValue::Object(&entity_obj), JValue::Object(&index_obj)], ) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; - env.set_object_array_element(&out, i as i32, collision_obj) + out.set_element(env, i, collision_obj) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; } @@ -529,5 +532,9 @@ fn get_movement_result( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<Option<CharacterMovementResult>> { - world.get::<&KCC>(entity).map(|kcc| kcc.movement.clone()).map(Ok).unwrap_or(Ok(None)) -} + world + .get::<&KCC>(entity) + .map(|kcc| kcc.movement.clone()) + .map(Ok) + .unwrap_or(Ok(None)) +} @@ -2,7 +2,7 @@ use crate::scripting::jni::utils::ToJObject; use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; use crate::types::{IndexNative, NCollider, NShapeCastStatus, NTransform, NVector3}; -use ::jni::JNIEnv; +use ::jni::{Env, jni_str, jni_sig}; use ::jni::objects::{JObject, JValue}; use hecs::{Entity, World}; use rapier3d::control::CharacterCollision; @@ -53,9 +53,9 @@ fn collider_ffi_from_handle( } impl ToJObject for NShapeCastStatus { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/physics/ShapeCastStatus") + .load_class(jni_str!("com/dropbear/physics/ShapeCastStatus")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let name = match self { @@ -72,9 +72,9 @@ impl ToJObject for NShapeCastStatus { let value = env .call_static_method( class, - "valueOf", - "(Ljava/lang/String;)Lcom/dropbear/physics/ShapeCastStatus;", - &[JValue::Object(&name_jstring)], + jni_str!("valueOf"), + jni_sig!((java.lang.String) -> com.dropbear.physics.ShapeCastStatus), + &[JValue::from(&name_jstring)], ) .map_err(|_| DropbearNativeError::JNIMethodNotFound)? .l() @@ -1,6 +1,8 @@ //! [rapier3d] RigidBodies -use crate::component::{Component, ComponentDescriptor, DisabilityFlags, InspectableComponent, SerializedComponent}; +use crate::component::{ + Component, ComponentDescriptor, DisabilityFlags, InspectableComponent, SerializedComponent, +}; use crate::physics::PhysicsState; use crate::ptr::{PhysicsStatePtr, WorldPtr}; use crate::scripting::jni::utils::{FromJObject, ToJObject}; @@ -8,7 +10,7 @@ use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; use crate::states::Label; use crate::types::{IndexNative, NCollider, NVector3, RigidBodyContext}; -use ::jni::JNIEnv; +use ::jni::{Env, jni_str, jni_sig}; use ::jni::objects::{JObject, JValue}; use dropbear_engine::graphics::SharedGraphicsContext; use egui::{CollapsingHeader, ComboBox, DragValue, Ui}; @@ -59,12 +61,12 @@ pub struct AxisLock { } impl FromJObject for AxisLock { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> where Self: Sized, { let class = env - .find_class("com/dropbear/physics/AxisLock") + .load_class(jni_str!("com/dropbear/physics/AxisLock")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; if !env @@ -75,19 +77,19 @@ impl FromJObject for AxisLock { } let x = env - .get_field(obj, "x", "Z") + .get_field(obj, jni_str!("x"), jni_sig!(boolean)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .z() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let y = env - .get_field(obj, "y", "Z") + .get_field(obj, jni_str!("y"), jni_sig!(boolean)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .z() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let z = env - .get_field(obj, "z", "Z") + .get_field(obj, jni_str!("z"), jni_sig!(boolean)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .z() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -97,21 +99,19 @@ impl FromJObject for AxisLock { } impl ToJObject for AxisLock { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/physics/AxisLock") + .load_class(jni_str!("com/dropbear/physics/AxisLock")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - 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), + JValue::Bool(self.x), + JValue::Bool(self.y), + JValue::Bool(self.z), ]; let obj = env - .new_object(&class, constructor_sig, &args) + .new_object(&class, jni_sig!((boolean, boolean, boolean) -> void), &args) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) @@ -0,0 +1 @@ + @@ -1,5 +1,6 @@ use crate::component::{ - Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, SerializedComponent, + Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, + SerializedComponent, }; use crate::ptr::WorldPtr; use crate::scripting::native::DropbearNativeError; @@ -1,6 +1,6 @@ +use serde::{Deserialize, Serialize}; use std::path::PathBuf; use std::sync::Arc; -use serde::{Serialize, Deserialize}; /// A resolved pointer to asset data, handed to the loader. /// @@ -24,10 +24,7 @@ pub enum ResourceReference { /// A byte range within a loaded `.eucpak` file. /// The loader holds the pak in memory and slices it directly — /// zero additional allocation. - Packed { - offset: u64, - length: u64, - }, + Packed { offset: u64, length: u64 }, /// No backing data — generated entirely at runtime. Procedural, @@ -50,6 +47,9 @@ impl ResourceReference { /// Returns true if this reference can be loaded without any I/O. pub fn is_in_memory(&self) -> bool { - matches!(self, Self::Embedded(_) | Self::Packed { .. } | Self::Procedural) + matches!( + self, + Self::Embedded(_) | Self::Packed { .. } | Self::Procedural + ) } -} +} @@ -2,12 +2,13 @@ //! //! 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 components; pub mod error; pub mod jni; pub mod native; pub mod result; -pub mod utils; pub mod types; +pub mod utils; pub static JVM_ARGS: OnceLock<String> = OnceLock::new(); pub static AWAIT_JDB: OnceLock<bool> = OnceLock::new(); @@ -563,6 +564,12 @@ impl ScriptManager { } } + /// Locates all components available within the libraries provided. + /// + /// # ScriptTarget behaviours + /// - [`ScriptTarget::JVM`] - Located the `RunnableRegistry.kt` manifest file, locates ComponentRegistry, then sets all the related function pointers + pub fn discover_components(&mut self) -> anyhow::Result<()> { Ok(()) } + /// Reloads the .jar file by unloading the previous classes and reloading them back in, /// allowing for hot reloading. /// @@ -0,0 +1,51 @@ +use crate::scripting::ScriptManager; + +/// As the name suggests, it's just callbacks defined from another source for +/// Component based functions. +/// +/// I need to get a better name +pub enum ComponentDefinedSomewhereElseCallbacks { + /// Defines a JVM based interface, which uses the [jni] crate for its backend implementation. + JVM {}, + + /// Defines a C-based interface, such as Kotlin/Native or another c-based language. + Native {}, +} + +mod jvm { + use crate::scripting::jni::JavaContext; + + impl JavaContext { + pub fn register_components() -> anyhow::Result<()> { + Ok(()) + } + + pub fn register_callbacks() -> anyhow::Result<()> { + Ok(()) + } + } +} + +mod native { + use crate::scripting::native::NativeLibrary; + + impl NativeLibrary { + pub fn register_components() -> anyhow::Result<()> { + Ok(()) + } + + pub fn register_callbacks() -> anyhow::Result<()> { + Ok(()) + } + } +} + +impl ScriptManager { + pub fn register_components() -> anyhow::Result<()> { + Ok(()) + } + + pub fn register_callbacks() -> anyhow::Result<()> { + Ok(()) + } +} @@ -12,14 +12,15 @@ 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::objects::{Global, JClass, JLongArray, JObject, JString, JValue}; +use jni::strings::JNIString; use jni::sys::jlong; -use jni::{InitArgsBuilder, JNIEnv, JNIVersion, JavaVM}; +use jni::{InitArgsBuilder, JNIVersion, JavaVM, jni_sig, jni_str}; +use jni::signature::RuntimeMethodSignature; use once_cell::sync::OnceCell; use sha2::{Digest, Sha256}; use std::fs; use std::path::PathBuf; -use std::sync::Arc; #[cfg(feature = "jvm_debug")] use crate::scripting::AWAIT_JDB; @@ -35,9 +36,6 @@ pub enum RuntimeMode { const LIBRARY_PATH: &[u8] = include_bytes!("../../../../build/libs/dropbear-1.0-SNAPSHOT-all.jar"); pub static RUNTIME_MODE: OnceCell<RuntimeMode> = OnceCell::new(); -/// Shared handle to the active [`JavaVM`], set once during [`JavaContext::new`]. -/// Callers may clone the `Arc` to attach new threads without any unsafe code. -pub static GLOBAL_JVM: OnceCell<Arc<JavaVM>> = OnceCell::new(); #[cfg(feature = "jvm_debug")] fn is_port_available(port: u16) -> bool { @@ -51,11 +49,11 @@ fn is_port_available(port: u16) -> bool { } /// Provides a context for any eucalyptus-core JNI calls and JVM hosting. + pub struct JavaContext { - pub(crate) jvm: Arc<JavaVM>, - native_engine_instance: Option<GlobalRef>, - dropbear_engine_class: Option<GlobalRef>, - system_manager_instance: Option<GlobalRef>, + native_engine_instance: Option<Global<JObject<'static>>>, + dropbear_engine_class: Option<Global<JObject<'static>>>, + system_manager_instance: Option<Global<JObject<'static>>>, pub(crate) jar_path: PathBuf, } @@ -77,14 +75,14 @@ impl JavaContext { let embedded_jar_hash = { let mut hasher = Sha256::new(); hasher.update(LIBRARY_PATH); - format!("{:x}", hasher.finalize()) + hasher.finalize() }; let stored_hash = fs::read_to_string(&hash_file_path).ok(); let should_update = match stored_hash { Some(stored) => { - if stored.trim() == embedded_jar_hash { + if stored.trim().as_bytes() == embedded_jar_hash.as_slice() { log::debug!("Host library JAR hash matches stored hash. No update needed."); false } else { @@ -144,7 +142,7 @@ 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::V21); let mut args_log = Vec::new(); @@ -277,15 +275,11 @@ impl JavaContext { let _ = JVM_ARGS.set(args); let jvm_init_args = jvm_args.build()?; - let jvm = JavaVM::new(jvm_init_args)?; + JavaVM::new(jvm_init_args)?; log::info!("Created JVM instance"); - let jvm = Arc::new(jvm); - let _ = GLOBAL_JVM.set(jvm.clone()); - Ok(Self { - jvm, native_engine_instance: None, dropbear_engine_class: None, system_manager_instance: None, @@ -294,9 +288,8 @@ impl JavaContext { } pub fn init(&mut self, context: &DropbearContext) -> anyhow::Result<()> { - let mut env = self.jvm.attach_current_thread()?; - - let result = (|| -> anyhow::Result<()> { + let jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| { let world_handle = context.world as jlong; let input_handle = context.input as jlong; let graphics_handle = context.command_buffer as jlong; @@ -325,18 +318,20 @@ impl JavaContext { 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)?; + env.load_class(jni_str!("com.dropbear.ffi.DropbearContext"))?; + let runtime_sig = RuntimeMethodSignature::from_str(&sig) + .map_err(|e| jni::errors::Error::MethodNotFound { name: sig.clone(), sig: e.to_string() })?; + let dropbear_context_obj = env.new_object(dropbear_context_class, runtime_sig.method_signature(), &args)?;; log::trace!("Locating \"com/dropbear/ffi/NativeEngine\" class"); - let native_engine_class: JClass = env.find_class("com/dropbear/ffi/NativeEngine")?; + let native_engine_class: JClass = env.load_class(jni_str!("com.dropbear.ffi.NativeEngine"))?; 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"); - let native_engine_obj = env.new_object(native_engine_class, "()V", &[])?; + let native_engine_obj = env.new_object(native_engine_class, jni_sig!(()), &[])?; let native_engine_global_ref = env.new_global_ref(native_engine_obj)?; self.native_engine_instance = Some(native_engine_global_ref); self.native_engine_instance @@ -346,21 +341,22 @@ impl JavaContext { }; log::trace!( - "Calling NativeEngine.init() with arg [\"com/dropbear/ffi/DropbearContext\"]" + "Calling NativeEngine.init() with arg [\"com.dropbear.ffi.DropbearContext\"]" ); + env.call_method( native_engine_obj, - "init", - "(Lcom/dropbear/ffi/DropbearContext;)V", + jni_str!("init"), + jni_sig!((context: com.dropbear.ffi.DropbearContext) -> ()), &[JValue::Object(&dropbear_context_obj)], )?; if self.dropbear_engine_class.is_none() { - let dropbear_class: JClass = env.find_class("com/dropbear/DropbearEngine")?; + let dropbear_class: JClass = env.load_class(jni_str!("com.dropbear.DropbearEngine"))?; log::trace!("Creating DropbearEngine constructor with arg (NativeEngine_object)"); let dropbear_obj = env.new_object( dropbear_class, - "(Lcom/dropbear/ffi/NativeEngine;)V", + jni_sig!((native: com.dropbear.ffi.NativeEngine) -> ()), &[JValue::Object(native_engine_obj)], )?; @@ -371,12 +367,12 @@ impl JavaContext { let jar_path_jstring = env.new_string(self.jar_path.to_string_lossy())?; let log_level_str = { LOG_LEVEL.lock().to_string() }; - let log_level_enum_class = env.find_class("com/dropbear/logging/LogLevel")?; + let log_level_enum_class = env.load_class(jni_str!("com.dropbear.logging.LogLevel"))?; let log_level_enum_instance = env .get_static_field( log_level_enum_class, - log_level_str, - "Lcom/dropbear/logging/LogLevel;", + JNIString::from(log_level_str), + jni_sig!(com.dropbear.logging.LogLevel), )? .l()?; @@ -385,22 +381,22 @@ impl JavaContext { 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")?; - env.new_object(socket_writer_class, "()V", &[])? + env.load_class(jni_str!("com.dropbear.logging.SocketWriter"))?; + env.new_object(socket_writer_class, jni_sig!(()), &[])? } 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")?; - env.new_object(std_out_writer_class, "()V", &[])? + env.load_class(jni_str!("com.dropbear.logging.StdoutWriter"))?; + env.new_object(std_out_writer_class, jni_sig!(()), &[])? } } _ => { let std_out_writer_class = - env.find_class("com/dropbear/logging/StdoutWriter")?; - env.new_object(std_out_writer_class, "()V", &[])? + env.load_class(jni_str!("com.dropbear.logging.StdoutWriter"))?; + env.new_object(std_out_writer_class, jni_sig!(()), &[])? } }; @@ -413,7 +409,7 @@ impl JavaContext { log::trace!("Locating \"com/dropbear/host/SystemManager\" class"); let system_manager_class: JClass = - env.find_class("com/dropbear/host/SystemManager")?; + env.load_class(jni_str!("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)" ); @@ -422,7 +418,7 @@ impl JavaContext { let system_manager_obj = env.new_object( system_manager_class, - "(Ljava/lang/String;Lcom/dropbear/DropbearEngine;Lcom/dropbear/logging/LogWriter;Lcom/dropbear/logging/LogLevel;Ljava/lang/String;)V", + jni_sig!((java.lang.String, com.dropbear.DropbearEngine, com.dropbear.logging.LogWriter, com.dropbear.logging.LogLevel, java.lang.String) -> ()), &[ JValue::Object(&jar_path_jstring), JValue::Object(engine_ref), @@ -437,90 +433,10 @@ impl JavaContext { self.system_manager_instance = Some(manager_global_ref); } - Self::call_component_manager_register_all(&mut env); + Self::register_components()?; Ok(()) - })(); - - Self::get_exception(&mut env)?; - - result - } - /// Registers the components within the .jar file as created in the ComponentManager. - fn call_component_manager_register_all(env: &mut JNIEnv) { - match env.find_class("com/dropbear/decl/ComponentManager") { - Err(_) => { - let _ = env.exception_clear(); - log::debug!( - "ComponentManager class not found in user JAR, no @EcsComponent types will be registered" - ); - } - Ok(class) => { - if let Err(e) = - env.call_static_method(&class, "registerAll", "()V", &[]) - { - let _ = env.exception_clear(); - log::warn!("ComponentManager.registerAll() failed: {:?}", e); - } else { - log::debug!("ComponentManager.registerAll() called successfully"); - } - } - } - } - - pub fn get_exception(env: &mut JNIEnv) -> anyhow::Result<()> { - if let Ok(ex) = env.exception_occurred() { - if ex.is_null() { - return Ok(()); - } - - env.exception_clear()?; - - 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(); - - let stack_trace_result = env.call_method( - &ex, - "getStackTrace", - "()[Ljava/lang/StackTraceElement;", - &[], - )?; - let stack_trace_obj = stack_trace_result.l()?; - let stack_trace_array = JObjectArray::from(stack_trace_obj); - let stack_len = env.get_array_length(&stack_trace_array)?; - - let mut error_msg = format!("{}\n", message_str); - - 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_obj = element_str_result.l()?; - let element_jstring = JString::from(element_str_obj); - let element_string: String = env.get_string(&element_jstring)?.into(); - - error_msg.push_str(&format!(" at {}\n", element_string)); - } - - 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_obj = cause_str_result.l()?; - let cause_jstring = JString::from(cause_str_obj); - let cause_string: String = env.get_string(&cause_jstring)?.into(); - error_msg.push_str(&format!("Caused by: {}\n", cause_string)); - } - - return Err(anyhow::anyhow!("Java exception: {}", error_msg)); - } - - Ok(()) + }) } pub fn reload(&mut self, _world: WorldPtr) -> anyhow::Result<()> { @@ -530,36 +446,32 @@ impl JavaContext { ); if let Some(ref manager_ref) = self.system_manager_instance { - let mut env = self.jvm.attach_current_thread()?; - - let result = (|| -> anyhow::Result<()> { + let jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<()> { log::trace!("Calling SystemManager.reloadJar()"); let jar_path_jstring = env.new_string(self.jar_path.to_string_lossy())?; env.call_method( manager_ref, - "reloadJar", - "(Ljava/lang/String;)V", + jni_str!("reloadJar"), + jni_sig!((java.lang.String) -> ()), &[JValue::Object(&jar_path_jstring)], )?; - Self::call_component_manager_register_all(&mut env); + Self::register_components()?; Ok(()) - })(); - - Self::get_exception(&mut env)?; - - Ok(result?) + })?; + Ok(()) } else { log::warn!("SystemManager instance not found during reload."); // self.init(world)?; - return Err(anyhow::anyhow!("SystemManager not initialised for reload.")); + Err(anyhow::anyhow!("SystemManager not initialised for reload.")) } } pub fn load_systems_for_tag(&mut self, tag: &str) -> anyhow::Result<()> { if let Some(ref manager_ref) = self.system_manager_instance { - let mut env = self.jvm.attach_current_thread()?; + let jvm = JavaVM::singleton()?; - let result = (|| -> anyhow::Result<()> { + jvm.attach_current_thread(|env| -> anyhow::Result<()> { log::trace!( "Calling SystemManager.loadSystemsForTag() with tag: {}", tag @@ -567,23 +479,20 @@ impl JavaContext { let tag_jstring = env.new_string(tag)?; env.call_method( manager_ref, - "loadSystemsForTag", - "(Ljava/lang/String;)V", + jni_str!("loadSystemsForTag"), + jni_sig!((java.lang.String) -> ()), &[JValue::Object(&tag_jstring)], )?; log::debug!("Loaded systems for tag: {}", tag); Ok(()) - })(); - - Self::get_exception(&mut env)?; - - Ok(result?) + })?; + Ok(()) } else { - return Err(anyhow::anyhow!( + Err(anyhow::anyhow!( "SystemManager not initialised when loading systems for tag: {}", tag - )); + )) } } @@ -593,9 +502,8 @@ impl JavaContext { entity_ids: &[u64], ) -> 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 jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<()> { log::trace!( "Calling SystemManager.loadSystemsForEntities() with tag: {}, count: {}", tag, @@ -603,11 +511,11 @@ impl JavaContext { ); let tag_jstring = env.new_string(tag)?; - let entity_array: JLongArray = env.new_long_array(entity_ids.len() as i32)?; + let entity_array: JLongArray = env.new_long_array(entity_ids.len())?; if !entity_ids.is_empty() { - env.set_long_array_region( - &entity_array, + entity_array.set_region( + env, 0, &entity_ids.iter().map(|e| *e as i64).collect::<Vec<_>>(), )?; @@ -617,8 +525,8 @@ impl JavaContext { env.call_method( manager_ref, - "loadSystemsForEntities", - "(Ljava/lang/String;[J)V", + jni_str!("loadSystemsForEntities"), + jni_sig!((java.lang.String, [long]) -> ()), &[ JValue::Object(&tag_jstring), JValue::Object(&entity_array_obj), @@ -626,10 +534,9 @@ impl JavaContext { )?; Ok(()) - })(); + })?; - Self::get_exception(&mut env)?; - Ok(result?) + Ok(()) } else { Err(anyhow::anyhow!( "SystemManager not initialised when loading systems for tag: {}", @@ -645,18 +552,17 @@ impl JavaContext { event: &CollisionEvent, ) -> 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 jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<()> { let tag_jstring = env.new_string(tag)?; let event_obj = event - .to_jobject(&mut env) + .to_jobject(env) .map_err(|e| anyhow::anyhow!("Failed to marshal CollisionEvent to JVM: {e}"))?; env.call_method( manager_ref, - "collisionEvent", - "(Ljava/lang/String;JLcom/dropbear/physics/CollisionEvent;)V", + jni_str!("collisionEvent"), + jni_sig!((java.lang.String, long, com.dropbear.physics.CollisionEvent) -> ()), &[ JValue::Object(&tag_jstring), JValue::Long(entity_id as i64), @@ -665,10 +571,7 @@ impl JavaContext { )?; Ok(()) - })(); - - Self::get_exception(&mut env)?; - Ok(result?) + }) } else { Err(anyhow::anyhow!( "SystemManager not initialised when delivering collision events." @@ -683,18 +586,17 @@ impl JavaContext { 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 jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<()> { let tag_jstring = env.new_string(tag)?; - let event_obj = event.to_jobject(&mut env).map_err(|e| { + let event_obj = event.to_jobject(env).map_err(|e| { anyhow::anyhow!("Failed to marshal ContactForceEvent to JVM: {e}") })?; env.call_method( manager_ref, - "collisionForceEvent", - "(Ljava/lang/String;JLcom/dropbear/physics/ContactForceEvent;)V", + jni_str!("collisionForceEvent"), + jni_sig!((java.lang.String, long, com.dropbear.physics.ContactForceEvent) -> ()), &[ JValue::Object(&tag_jstring), JValue::Long(entity_id as i64), @@ -703,10 +605,7 @@ impl JavaContext { )?; Ok(()) - })(); - - Self::get_exception(&mut env)?; - Ok(result?) + }) } else { Err(anyhow::anyhow!( "SystemManager not initialised when delivering contact force events." @@ -716,54 +615,44 @@ impl JavaContext { pub fn update_all_systems(&self, dt: f64) -> 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 jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<()> { log_once::trace_once!("Calling SystemManager.updateAllSystems() with dt: {}", dt); env.call_method( manager_ref, - "updateAllSystems", - "(D)V", + jni_str!("updateAllSystems"), + jni_sig!((f64) -> ()), &[JValue::Double(dt)], )?; log_once::trace_once!("Updated all systems with dt: {}", dt); Ok(()) - })(); - - Self::get_exception(&mut env)?; - - Ok(result?) + }) } else { - return Err(anyhow::anyhow!( + Err(anyhow::anyhow!( "SystemManager not initialised when updating systems." - )); + )) } } pub fn physics_update_all_systems(&self, dt: f64) -> 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 jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<()> { log_once::trace_once!( "Calling SystemManager.physicsUpdateAllSystems() with dt: {}", dt ); env.call_method( manager_ref, - "physicsUpdateAllSystems", - "(D)V", + jni_str!("physicsUpdateAllSystems"), + jni_sig!((f64) -> ()), &[JValue::Double(dt)], )?; Ok(()) - })(); - - Self::get_exception(&mut env)?; - - Ok(result?) + }) } else { Err(anyhow::anyhow!( "SystemManager not initialised when physics updating systems." @@ -773,61 +662,49 @@ impl JavaContext { pub fn update_systems_for_tag(&self, tag: &str, dt: f64) -> 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 jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<()> { log::trace!( - "Calling SystemManager.updateSystemsByTag() with tag: {}, dt: {}", + "Calling SystemManager.updateSystemsForTag() with tag: {}, dt: {}", tag, dt ); let tag_jstring = env.new_string(tag)?; env.call_method( manager_ref, - "updateSystemsByTag", - "(Ljava/lang/String;D)V", + jni_str!("updateSystemsForTag"), + jni_sig!((java.lang.String, f64) -> ()), &[JValue::Object(&tag_jstring), JValue::Double(dt)], )?; - - log::debug!("Updated systems for tag: {} with dt: {}", tag, dt); Ok(()) - })(); - - Self::get_exception(&mut env)?; - - Ok(result?) + }) } else { - return Err(anyhow::anyhow!( + Err(anyhow::anyhow!( "SystemManager not initialised when updating systems for tag: {}", tag - )); + )) } } pub fn physics_update_systems_for_tag(&self, tag: &str, dt: f64) -> 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 jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<()> { log::trace!( - "Calling SystemManager.physicsUpdateSystemsByTag() with tag: {}, dt: {}", + "Calling SystemManager.physicsUpdateSystemsForTag() with tag: {}, dt: {}", tag, dt ); let tag_jstring = env.new_string(tag)?; env.call_method( manager_ref, - "physicsUpdateSystemsByTag", - "(Ljava/lang/String;D)V", + jni_str!("physicsUpdateSystemsForTag"), + jni_sig!((java.lang.String, f64) -> ()), &[JValue::Object(&tag_jstring), JValue::Double(dt)], )?; Ok(()) - })(); - - Self::get_exception(&mut env)?; - - Ok(result?) + }) } else { Err(anyhow::anyhow!( "SystemManager not initialised when physics updating systems for tag: {}", @@ -843,9 +720,8 @@ impl JavaContext { dt: f64, ) -> 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 jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<()> { log::trace!( "Calling SystemManager.updateSystemsForEntities() with tag: {}, count: {}, dt: {}", tag, @@ -854,15 +730,15 @@ impl JavaContext { ); let tag_jstring = env.new_string(tag)?; - let entity_array: JLongArray = env.new_long_array(entity_ids.len() as i32)?; + let entity_array: JLongArray = env.new_long_array(entity_ids.len())?; log::trace!("u64 entity: {:?}", entity_ids); log::trace!( "i64 entity: {:?}", entity_ids.iter().map(|e| *e as i64).collect::<Vec<_>>() ); if !entity_ids.is_empty() { - env.set_long_array_region( - &entity_array, + entity_array.set_region( + env, 0, &entity_ids.iter().map(|e| *e as i64).collect::<Vec<_>>(), )?; @@ -871,8 +747,8 @@ impl JavaContext { env.call_method( manager_ref, - "updateSystemsForEntities", - "(Ljava/lang/String;[JD)V", + jni_str!("updateSystemsForEntities"), + jni_sig!((java.lang.String, [long], f64) -> ()), &[ JValue::Object(&tag_jstring), JValue::Object(&entity_array_obj), @@ -887,11 +763,7 @@ impl JavaContext { dt ); Ok(()) - })(); - - Self::get_exception(&mut env)?; - - Ok(result?) + }) } else { Err(anyhow::anyhow!( "SystemManager not initialised when updating systems for tag: {}", @@ -907,9 +779,8 @@ impl JavaContext { dt: f64, ) -> 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 jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<()> { log::trace!( "Calling SystemManager.physicsUpdateSystemsForEntities() with tag: {}, count: {}, dt: {}", tag, @@ -918,10 +789,10 @@ impl JavaContext { ); let tag_jstring = env.new_string(tag)?; - let entity_array: JLongArray = env.new_long_array(entity_ids.len() as i32)?; + let entity_array: JLongArray = env.new_long_array(entity_ids.len())?; if !entity_ids.is_empty() { - env.set_long_array_region( - &entity_array, + entity_array.set_region( + env, 0, &entity_ids.iter().map(|e| *e as i64).collect::<Vec<_>>(), )?; @@ -930,8 +801,8 @@ impl JavaContext { env.call_method( manager_ref, - "physicsUpdateSystemsForEntities", - "(Ljava/lang/String;[JD)V", + jni_str!("physicsUpdateSystemsForEntities"), + jni_sig!((java.lang.String, [long], f64) -> ()), &[ JValue::Object(&tag_jstring), JValue::Object(&entity_array_obj), @@ -940,11 +811,7 @@ impl JavaContext { )?; Ok(()) - })(); - - Self::get_exception(&mut env)?; - - Ok(result?) + }) } else { Err(anyhow::anyhow!( "SystemManager not initialised when physics updating systems for tag: {}", @@ -955,9 +822,8 @@ impl JavaContext { pub fn unload_systems_for_tag(&self, tag: &str) -> 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 jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<()> { log::trace!( "Calling SystemManager.unloadSystemsByTag() with tag: {}", tag @@ -965,17 +831,13 @@ impl JavaContext { let tag_jstring = env.new_string(tag)?; env.call_method( manager_ref, - "unloadSystemsByTag", - "(Ljava/lang/String;)V", + jni_str!("unloadSystemsByTag"), + jni_sig!((java.lang.String) -> ()), &[JValue::Object(&tag_jstring)], )?; Ok(()) - })(); - - Self::get_exception(&mut env)?; - - Ok(result?) + }) } else { Err(anyhow::anyhow!( "SystemManager not initialised when unloading systems for tag: {}", @@ -986,9 +848,8 @@ impl JavaContext { pub fn destroy_systems_for_tag(&self, tag: &str) -> 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 jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<()> { log::trace!( "Calling SystemManager.destroySystemsByTag() with tag: {}", tag @@ -996,17 +857,13 @@ impl JavaContext { let tag_jstring = env.new_string(tag)?; env.call_method( manager_ref, - "destroySystemsByTag", - "(Ljava/lang/String;)V", + jni_str!("destroySystemsByTag"), + jni_sig!((java.lang.String) -> ()), &[JValue::Object(&tag_jstring)], )?; Ok(()) - })(); - - Self::get_exception(&mut env)?; - - Ok(result?) + }) } else { Err(anyhow::anyhow!( "SystemManager not initialised when destroying systems for tag: {}", @@ -1017,17 +874,12 @@ impl JavaContext { pub fn unload_all_systems(&self) -> 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 jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<()> { log::trace!("Calling SystemManager.unloadAllSystems()"); - env.call_method(manager_ref, "unloadAllSystems", "()V", &[])?; + env.call_method(manager_ref, jni_str!("unloadAllSystems"), jni_sig!(()), &[])?; Ok(()) - })(); - - Self::get_exception(&mut env)?; - - Ok(result?) + }) } else { Err(anyhow::anyhow!( "SystemManager not initialised when unloading all systems." @@ -1037,24 +889,19 @@ impl JavaContext { pub fn get_system_count_for_tag(&self, tag: &str) -> anyhow::Result<i32> { if let Some(ref manager_ref) = self.system_manager_instance { - let mut env = self.jvm.attach_current_thread()?; - - let result = (|| -> anyhow::Result<i32> { + let jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<i32> { log::trace!("Calling SystemManager.getSystemCount() for tag: {}", tag); let tag_jstring = env.new_string(tag)?; let result = env.call_method( manager_ref, - "getSystemCount", - "(Ljava/lang/String;)I", + jni_str!("getSystemCount"), + jni_sig!((java.lang.String) -> i32), &[JValue::Object(&tag_jstring)], )?; Ok(result.i()?) - })(); - - Self::get_exception(&mut env)?; - - Ok(result?) + }) } else { Err(anyhow::anyhow!( "SystemManager not initialised when getting system count for tag: {}", @@ -1065,24 +912,19 @@ impl JavaContext { pub fn has_systems_for_tag(&self, tag: &str) -> anyhow::Result<bool> { if let Some(ref manager_ref) = self.system_manager_instance { - let mut env = self.jvm.attach_current_thread()?; - - let result = (|| -> anyhow::Result<bool> { + let jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<bool> { log::trace!("Calling SystemManager.hasSystemsForTag() for tag: {}", tag); let tag_jstring = env.new_string(tag)?; let result = env.call_method( manager_ref, - "hasSystemsForTag", - "(Ljava/lang/String;)Z", + jni_str!("hasSystemsForTag"), + jni_sig!((java.lang.String) -> boolean), &[JValue::Object(&tag_jstring)], )?; Ok(result.z()?) - })(); - - Self::get_exception(&mut env)?; - - Ok(result?) + }) } else { Err(anyhow::anyhow!( "SystemManager not initialised when checking for systems for tag: {}", @@ -1093,18 +935,18 @@ impl JavaContext { pub fn get_total_system_count(&self) -> anyhow::Result<i32> { if let Some(ref manager_ref) = self.system_manager_instance { - let mut env = self.jvm.attach_current_thread()?; - - let result = (|| -> anyhow::Result<i32> { + let jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<i32> { log::trace!("Calling SystemManager.getTotalSystemCount()"); - let result = env.call_method(manager_ref, "getTotalSystemCount", "()I", &[])?; + let result = env.call_method( + manager_ref, + jni_str!("getTotalSystemCount"), + jni_sig!(() -> i32), + &[], + )?; Ok(result.i()?) - })(); - - Self::get_exception(&mut env)?; - - Ok(result?) + }) } else { Err(anyhow::anyhow!( "SystemManager not initialised when getting total system count." @@ -1139,39 +981,41 @@ impl Drop for JavaContext { impl LastErrorMessage for JavaContext { fn get_last_error(&self) -> Option<String> { - let mut env = self.jvm.attach_current_thread().ok()?; - - let dropbear_kt_class = env.find_class("com/dropbear/DropbearEngineKt").ok()?; + let jvm = JavaVM::singleton().ok()?; + jvm.attach_current_thread(|env| -> anyhow::Result<Option<String>> { + let dropbear_kt_class = env.load_class(jni_str!("com.dropbear.DropbearEngineKt"))?; - let field_value = env - .get_static_field(dropbear_kt_class, "lastErrorMessage", "Ljava/lang/String;") - .ok()?; + let field_value = env + .get_static_field(dropbear_kt_class, jni_str!("lastErrorMessage"), jni_sig!(java.lang.String))?; - let jobj = field_value.l().ok()?; - - if jobj.is_null() { - return None; - } + let jobj = field_value.l()?; - let jstring = jni::objects::JString::from(jobj); - let rust_string = env.get_string(&jstring).ok()?; - Some(rust_string.to_string_lossy().into_owned()) + if jobj.is_null() { + return Ok(None); + } + let rust_string = JString::cast_local(env, jobj).map_err(|_| anyhow::anyhow!("Failed to cast JString"))?; + Ok(Some(rust_string.to_string())) + }) + .ok() + .flatten() } fn set_last_error(&self, err_msg: impl Into<String>) -> anyhow::Result<()> { let msg = err_msg.into(); + let jvm = JavaVM::singleton()?; + jvm.attach_current_thread(|env| -> anyhow::Result<()> { + let dropbear_kt_class = env.load_class(jni_str!("com.dropbear.DropbearEngineKt"))?; - let mut env = self.jvm.attach_current_thread()?; - - let dropbear_kt_class = env.find_class("com/dropbear/DropbearEngineKt")?; + let jstring = env.new_string(&msg)?; - let jstring = env.new_string(&msg)?; - - let static_field = - env.get_static_field_id(&dropbear_kt_class, "lastErrorMessage", "Ljava/lang/String;")?; - - env.set_static_field(dropbear_kt_class, static_field, JValue::Object(&jstring))?; + env.set_static_field( + dropbear_kt_class, + jni_str!("lastErrorMessage"), + jni_sig!(java.lang.String), + JValue::Object(&jstring), + )?; - Ok(()) + Ok(()) + }) } } @@ -1,19 +1,19 @@ use crate::scripting::jni::utils::{FromJObject, ToJObject}; use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; -use jni::JNIEnv; +use jni::{jni_sig, jni_str, Env}; 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>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { match self { Some(value) => { let class = env - .find_class("java/lang/Integer") + .load_class(jni_str!("java.lang.Integer")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - env.new_object(&class, "(I)V", &[JValue::Int(*value)]) + env.new_object(&class, jni_sig!((int) -> void), &[JValue::Int(*value)]) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) } None => Ok(JObject::null()), @@ -22,13 +22,13 @@ impl ToJObject for Option<i32> { } impl FromJObject for Option<i32> { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { if obj.is_null() { return Ok(None); } let class = env - .find_class("java/lang/Integer") + .load_class(jni_str!("java.lang.Integer")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; if !env @@ -39,7 +39,7 @@ impl FromJObject for Option<i32> { } let value = env - .call_method(obj, "intValue", "()I", &[]) + .call_method(obj, jni_str!("intValue"), jni_sig!(() -> i32), &[]) .map_err(|_| DropbearNativeError::JNIMethodNotFound)? .i() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -49,25 +49,24 @@ impl FromJObject for Option<i32> { } impl ToJObject for Vec<i32> { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { self.as_slice().to_jobject(env) } } impl ToJObject for &[i32] { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let array = env - .new_int_array(self.len() as i32) + .new_int_array(self.len()) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; let buf: Vec<jint> = self.iter().map(|v| *v as jint).collect(); - env.set_int_array_region(&array, 0, &buf) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + array.set_region(env, 0, &buf).map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(JObject::from(array)) } } impl ToJObject for &[Vec<i32>] { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let list = new_array_list(env)?; for value in self.iter() { let boxed = value.as_slice().to_jobject(env)?; @@ -78,14 +77,14 @@ impl ToJObject for &[Vec<i32>] { } impl ToJObject for Option<f32> { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { match self { Some(value) => { let class = env - .find_class("java/lang/Float") + .load_class(jni_str!("java.lang.Float")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - env.new_object(&class, "(F)V", &[JValue::Float(*value)]) + env.new_object(&class, jni_sig!((f32) -> ()), &[JValue::Float(*value)]) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) } None => Ok(JObject::null()), @@ -94,14 +93,14 @@ impl ToJObject for Option<f32> { } impl ToJObject for Option<f64> { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { match self { Some(value) => { let class = env - .find_class("java/lang/Double") + .load_class(jni_str!("java.lang.Double")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - env.new_object(&class, "(D)V", &[JValue::Double(*value)]) + env.new_object(&class, jni_sig!((f64) -> ()), &[JValue::Double(*value)]) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) } None => Ok(JObject::null()), @@ -110,25 +109,24 @@ impl ToJObject for Option<f64> { } impl ToJObject for Vec<f64> { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { self.as_slice().to_jobject(env) } } impl ToJObject for &[f64] { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let array = env - .new_double_array(self.len() as i32) + .new_double_array(self.len()) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; let buf: Vec<jdouble> = self.iter().map(|v| *v as jdouble).collect(); - env.set_double_array_region(&array, 0, &buf) - .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; + array.set_region(env, 0, &buf).map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(JObject::from(array)) } } impl ToJObject for &[Vec<f64>] { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let list = new_array_list(env)?; for value in self.iter() { let array = value.as_slice().to_jobject(env)?; @@ -138,19 +136,19 @@ impl ToJObject for &[Vec<f64>] { } } -fn new_array_list<'a>(env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { +fn new_array_list<'a>(env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("java/util/ArrayList") + .load_class(jni_str!("java.util.ArrayList")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - env.new_object(&class, "()V", &[]) + env.new_object(&class, jni_sig!(() -> ()), &[]) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) } -fn array_list_add(env: &mut JNIEnv, list: &JObject, item: &JObject) -> DropbearNativeResult<()> { +fn array_list_add(env: &mut Env, list: &JObject, item: &JObject) -> DropbearNativeResult<()> { env.call_method( list, - "add", - "(Ljava/lang/Object;)Z", + jni_str!("add"), + jni_sig!((java.lang.Object) -> boolean), &[JValue::Object(item)], ) .map_err(|_| DropbearNativeError::JNIMethodNotFound)?; @@ -158,22 +156,22 @@ fn array_list_add(env: &mut JNIEnv, list: &JObject, item: &JObject) -> DropbearN } impl ToJObject for Vec<u64> { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { self.as_slice().to_jobject(env) } } impl ToJObject for &[u64] { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let array = env.new_long_array(self.len() as i32)?; + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let array = env.new_long_array(self.len())?; let buf: Vec<jlong> = self.iter().map(|v| *v as jlong).collect(); - env.set_long_array_region(&array, 0, &buf)?; + array.set_region(env, 0, &buf)?; Ok(JObject::from(array)) } } impl ToJObject for String { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { 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::{Env, jni_sig, jni_str}; const JAVA_MOUSE_BUTTON_LEFT: jint = 0; const JAVA_MOUSE_BUTTON_RIGHT: jint = 1; @@ -26,30 +26,30 @@ pub fn Java_button_to_rust(button_code: jint) -> Option<winit::event::MouseButto /// Trait that defines conversion from a Java object to a Rust struct. pub trait FromJObject { /// Converts a Java object to a Rust struct. - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> where Self: Sized; } /// Converts a Rust object (struct or enum) into a java [JObject] pub trait ToJObject { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>>; + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>>; } impl<T> ToJObject for Vec<T> where T: ToJObject, { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { - let list_class = env.find_class("java/util/ArrayList")?; - let list_obj = env.new_object(&list_class, "()V", &[])?; + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let list_class = env.load_class(jni_str!("java.util.ArrayList"))?; + let list_obj = env.new_object(&list_class, jni_sig!(()), &[])?; for item in self { let obj = item.to_jobject(env)?; let _ = env.call_method( &list_obj, - "add", - "(Ljava/lang/Object;)Z", + jni_str!("add"), + jni_sig!((java.lang.Object) -> boolean), &[JValue::Object(&obj)], )?; } @@ -62,16 +62,16 @@ impl<T> FromJObject for Vec<T> where T: FromJObject, { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> where Self: Sized, { - let size = env.call_method(obj, "size", "()I", &[])?.i()? as jint; + let size = env.call_method(obj, jni_str!("size"), jni_sig!(() -> int), &[])?.i()? as jint; let mut out = Vec::with_capacity(size as usize); for i in 0..size { let item = env - .call_method(obj, "get", "(I)Ljava/lang/Object;", &[JValue::Int(i)])? + .call_method(obj, jni_str!("get"), jni_sig!((int) -> java.lang.Object), &[JValue::Int(i)])? .l()?; let value = T::from_jobject(env, &item)?; out.push(value); @@ -21,7 +21,7 @@ use crate::types::{ }; use hecs::ComponentError; use jni::errors::JniError; -use jni::signature::TypeSignature; +use jni::signature::RuntimeMethodSignature; use std::path::Path; use thiserror::Error; @@ -52,7 +52,6 @@ pub struct NativeLibrary { pub(crate) set_last_err_msg_fn: Symbol<'static, sig::SetLastErrorMessage>, update_kotlin_component_fn: Option<Symbol<'static, sig::UpdateKotlinComponent>>, - #[allow(dead_code)] inspect_kotlin_component_fn: Option<Symbol<'static, sig::InspectKotlinComponent>>, } @@ -157,17 +156,21 @@ impl NativeLibrary { let update_kotlin_component_fn = library .get::<sig::UpdateKotlinComponent>(b"dropbear_update_kotlin_component\0") .ok() - .map(|s| std::mem::transmute::< - Symbol<sig::UpdateKotlinComponent>, - Symbol<'static, sig::UpdateKotlinComponent>, - >(s)); + .map(|s| { + std::mem::transmute::< + Symbol<sig::UpdateKotlinComponent>, + Symbol<'static, sig::UpdateKotlinComponent>, + >(s) + }); let inspect_kotlin_component_fn = library .get::<sig::InspectKotlinComponent>(b"dropbear_inspect_kotlin_component\0") .ok() - .map(|s| std::mem::transmute::< - Symbol<sig::InspectKotlinComponent>, - Symbol<'static, sig::InspectKotlinComponent>, - >(s)); + .map(|s| { + std::mem::transmute::< + Symbol<sig::InspectKotlinComponent>, + Symbol<'static, sig::InspectKotlinComponent>, + >(s) + }); Ok(Self { library, @@ -586,7 +589,7 @@ pub enum DropbearNativeError { #[error("Invalid constructor return type (must be void)")] InvalidCtorReturn, #[error("Invalid number or type of arguments passed to java method: {0}")] - InvalidArgList(TypeSignature), + InvalidArgList(RuntimeMethodSignature), #[error("Method not found: {name} {sig}")] MethodNotFound { name: String, sig: String }, #[error("Field not found: {name} {sig}")] @@ -607,8 +610,8 @@ pub enum DropbearNativeError { FieldAlreadySet(String), #[error("Throw failed with error code {0}")] ThrowFailed(i32), - #[error("Parse failed for input: {1}")] - ParseFailed(#[source] combine::error::StringStreamError, String), + #[error("Parse failed for input: {0}")] + ParseFailed(String), #[error("JNI call failed")] JniCall(#[source] JniError), } @@ -661,7 +664,7 @@ impl DropbearNativeError { DropbearNativeError::JavaVMMethodNotFound(_) => -210, DropbearNativeError::FieldAlreadySet(_) => -211, DropbearNativeError::ThrowFailed(_) => -212, - DropbearNativeError::ParseFailed(_, _) => -213, + DropbearNativeError::ParseFailed(_) => -213, DropbearNativeError::JniCall(_) => -214, } } @@ -680,19 +683,16 @@ impl From<jni::errors::Error> for DropbearNativeError { DropbearNativeError::FieldNotFound { name, sig } } jni::errors::Error::JavaException => DropbearNativeError::JavaException, - jni::errors::Error::JNIEnvMethodNotFound(s) => { + jni::errors::Error::EnvMethodNotFound(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::FieldAlreadySet(s) => DropbearNativeError::FieldAlreadySet(s), jni::errors::Error::ThrowFailed(i) => DropbearNativeError::ThrowFailed(i), - jni::errors::Error::ParseFailed(e, s) => DropbearNativeError::ParseFailed(e, s), + jni::errors::Error::ParseFailed(s) => DropbearNativeError::ParseFailed(s), jni::errors::Error::JniCall(e) => DropbearNativeError::JniCall(e), + _ => DropbearNativeError::UnknownError, } } } @@ -94,7 +94,9 @@ impl InspectableComponent for KotlinComponents { ui.separator(); - ui.label("This should not be visible in the editor. this is considered `internal=true`"); + ui.label( + "This should not be visible in the editor. this is considered `internal=true`", + ); }); } } @@ -134,12 +136,20 @@ fn register_kotlin_component_jni( category: String, description: String, ) -> DropbearNativeResult<()> { - use crate::component::{KotlinComponentDecl, KOTLIN_COMPONENT_QUEUE}; + use crate::component::{KOTLIN_COMPONENT_QUEUE, KotlinComponentDecl}; KOTLIN_COMPONENT_QUEUE.lock().push(KotlinComponentDecl { fqcn, type_name, - category: if category.is_empty() { None } else { Some(category) }, - description: if description.is_empty() { None } else { Some(description) }, + category: if category.is_empty() { + None + } else { + Some(category) + }, + description: if description.is_empty() { + None + } else { + Some(description) + }, }); Ok(()) -} +} @@ -24,9 +24,6 @@ impl Display for SerializedType { impl SerializedType { pub fn iter_extensions() -> impl Iterator<Item = String> { - [ - Self::GenericBinary.to_string(), - Self::Model.to_string() - ].into_iter() + [Self::GenericBinary.to_string(), Self::Model.to_string()].into_iter() } -} +} @@ -1,13 +1,15 @@ -use std::sync::Arc; -use std::collections::hash_map::DefaultHasher; -use std::hash::{Hash, Hasher}; -use dropbear_engine::asset::{Handle, ASSET_REGISTRY}; +use dropbear_engine::asset::{ASSET_REGISTRY, Handle}; use dropbear_engine::graphics::SharedGraphicsContext; -use dropbear_engine::model::{AlphaMode, Animation, Material, Mesh, Model, ModelVertex, Node, Skin}; +use dropbear_engine::model::{ + AlphaMode, Animation, Material, Mesh, Model, ModelVertex, Node, Skin, +}; use dropbear_engine::texture::{Texture, TextureWrapMode}; use dropbear_engine::utils::ResourceReference; -use dropbear_engine::wgpu::util::DeviceExt; use dropbear_engine::wgpu; +use dropbear_engine::wgpu::util::DeviceExt; +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; +use std::sync::Arc; use uuid::Uuid; use crate::uuid::UuidV4; @@ -18,7 +20,15 @@ use crate::uuid::UuidV4; /// a `.eucmeta` sidecar. `Embedded` is used for textures that were packed /// directly inside a source file (e.g. GLTF-embedded data) and have not yet /// been extracted as standalone assets. -#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, Clone, serde::Serialize, serde::Deserialize)] +#[derive( + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, + Debug, + Clone, + serde::Serialize, + serde::Deserialize, +)] pub enum EucalyptusTextureRef { /// UUID of a file-backed texture tracked by a `.eucmeta` sidecar. AssetUuid(UuidV4), @@ -36,7 +46,9 @@ impl EucalyptusTextureRef { /// The serialized format for a Model without all the buffers and stuff. /// /// This is stored in the file system as `*.eucmdl`. -#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, serde::Serialize, serde::Deserialize)] +#[derive( + rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, serde::Serialize, serde::Deserialize, +)] pub struct EucalyptusModel { pub label: String, pub meshes: Vec<EucalyptusMesh>, // this needs to be custom type because of wgpu buffers @@ -65,11 +77,15 @@ impl EucalyptusModel { let morph_deltas_buffer = if self.morph_deltas.is_empty() { None } else { - Some(graphics.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("model morph deltas buffer"), - contents: bytemuck::cast_slice(&self.morph_deltas), - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - })) + Some( + graphics + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("model morph deltas buffer"), + contents: bytemuck::cast_slice(&self.morph_deltas), + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + }), + ) }; Model { @@ -109,7 +125,11 @@ impl From<Model> for EucalyptusModel { Self { label: value.label.clone(), meshes: value.meshes.into_iter().map(EucalyptusMesh::from).collect(), - materials: value.materials.into_iter().map(EucalyptusMaterial::from).collect(), + materials: value + .materials + .into_iter() + .map(EucalyptusMaterial::from) + .collect(), skins: value.skins, animations: value.animations, nodes: value.nodes, @@ -118,7 +138,9 @@ impl From<Model> for EucalyptusModel { } } -#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, serde::Serialize, serde::Deserialize)] +#[derive( + rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, serde::Serialize, serde::Deserialize, +)] pub struct EucalyptusMesh { pub name: String, pub num_elements: u32, @@ -147,25 +169,23 @@ impl From<Mesh> for EucalyptusMesh { impl EucalyptusMesh { fn load(&self, graphics: Arc<SharedGraphicsContext>) -> Mesh { - let vertex_buffer = - graphics - .device - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some(&format!("{} Vertex Buffer", self.name)), - contents: bytemuck::cast_slice(&self.vertices), - usage: wgpu::BufferUsages::VERTEX, - }); + let vertex_buffer = graphics + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(&format!("{} Vertex Buffer", self.name)), + contents: bytemuck::cast_slice(&self.vertices), + usage: wgpu::BufferUsages::VERTEX, + }); let index_count = self.num_elements.min(self.vertices.len() as u32); let indices = (0..index_count).collect::<Vec<u32>>(); - let index_buffer = - graphics - .device - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some(&format!("{} Index Buffer", self.name)), - contents: bytemuck::cast_slice(&indices), - usage: wgpu::BufferUsages::INDEX, - }); + let index_buffer = graphics + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(&format!("{} Index Buffer", self.name)), + contents: bytemuck::cast_slice(&indices), + usage: wgpu::BufferUsages::INDEX, + }); Mesh { name: self.name.clone(), @@ -182,7 +202,9 @@ impl EucalyptusMesh { } } -#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, serde::Serialize, serde::Deserialize)] +#[derive( + rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, serde::Serialize, serde::Deserialize, +)] pub struct EucalyptusMaterial { pub name: String, pub diffuse_texture: Option<EucalyptusTextureRef>, @@ -219,9 +241,7 @@ impl From<Material> for EucalyptusMaterial { .ok() .map(|entry| EucalyptusTextureRef::from_uuid(entry.uuid)) } - ResourceReference::Embedded(bytes) => { - Some(EucalyptusTextureRef::Embedded(bytes)) - } + ResourceReference::Embedded(bytes) => Some(EucalyptusTextureRef::Embedded(bytes)), _ => None, } }; @@ -273,14 +293,17 @@ impl EucalyptusMaterial { } } let bytes = std::fs::read(&abs) - .map_err(|e| log::warn!("load_texture: failed to read '{}': {}", abs.display(), e)) + .map_err(|e| { + log::warn!("load_texture: failed to read '{}': {}", abs.display(), e) + }) .ok()?; let label = format!("{}_{}", self.name, suffix); let engine_ref = ResourceReference::from_path(&abs).ok(); - let mut texture = dropbear_engine::texture::TextureBuilder::new(&graphics.device) - .with_bytes(graphics.clone(), bytes.as_slice()) - .label(label.as_str()) - .build(); + let mut texture = + dropbear_engine::texture::TextureBuilder::new(&graphics.device) + .with_bytes(graphics.clone(), bytes.as_slice()) + .label(label.as_str()) + .build(); texture.reference = engine_ref; let mut registry = ASSET_REGISTRY.write(); Some(registry.add_texture_with_label(entry.name, texture)) @@ -303,7 +326,9 @@ impl EucalyptusMaterial { fn load(&self, graphics: Arc<SharedGraphicsContext>) -> Material { let diffuse_texture = { - let maybe = self.diffuse_texture.as_ref() + let maybe = self + .diffuse_texture + .as_ref() .and_then(|r| self.load_texture(graphics.clone(), r, "diffuse")); if let Some(handle) = maybe { handle @@ -324,10 +349,12 @@ impl EucalyptusMaterial { .emissive_texture .as_ref() .and_then(|reference| self.load_texture(graphics.clone(), reference, "emissive")); - let metallic_roughness_texture = self - .metallic_roughness_texture - .as_ref() - .and_then(|reference| self.load_texture(graphics.clone(), reference, "metallic_roughness")); + let metallic_roughness_texture = + self.metallic_roughness_texture + .as_ref() + .and_then(|reference| { + self.load_texture(graphics.clone(), reference, "metallic_roughness") + }); let occlusion_texture = self .occlusion_texture .as_ref() @@ -361,4 +388,4 @@ impl EucalyptusMaterial { material.sync_uniform(&graphics); material } -} +} @@ -1,7 +1,9 @@ /// A template that can be used to display entities and their children. /// -/// Contains all the assets required. On final compilation, it will be resolved, -#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, serde::Serialize, serde::Deserialize)] +/// Contains all the assets required. On final compilation, it will be resolved, +#[derive( + rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, serde::Serialize, serde::Deserialize, +)] pub struct Template { pub label: String, } @@ -11,7 +13,5 @@ impl Template { Self { label } } - pub fn update(&mut self, ) { - - } -} + pub fn update(&mut self) {} +} @@ -4,7 +4,8 @@ use crate::camera::{CameraComponent, CameraType}; use crate::component::{ - Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, SerializedComponent, + Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, + SerializedComponent, }; use crate::config::{ProjectConfig, SourceConfig}; use crate::properties::Value; @@ -14,8 +15,8 @@ use dropbear_engine::entity::Transform; use dropbear_engine::graphics::SharedGraphicsContext; use dropbear_engine::lighting::LightComponent; use dropbear_engine::model::AlphaMode; -use dropbear_engine::texture::{TextureReference, TextureWrapMode}; use dropbear_engine::procedural::ProcedurallyGeneratedObject; +use dropbear_engine::texture::{TextureReference, TextureWrapMode}; use egui::{CollapsingHeader, TextEdit, Ui}; use hecs::{Entity, World}; use once_cell::sync::Lazy; @@ -1,12 +1,15 @@ -use std::fmt::{Display, Formatter}; -use crate::component::{Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, SerializedComponent}; +use crate::component::{ + Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, + SerializedComponent, +}; use crate::hierarchy::EntityTransformExt; +use crate::physics::PhysicsState; use crate::ptr::WorldPtr; use crate::scripting::jni::utils::{FromJObject, ToJObject}; use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; use crate::types::{NTransform, NVector3}; -use ::jni::JNIEnv; +use ::jni::{Env, jni_str, jni_sig}; use ::jni::objects::{JObject, JValue}; use dropbear_engine::camera::Camera; use dropbear_engine::entity::{EntityTransform, Transform}; @@ -15,8 +18,8 @@ use egui::{CollapsingHeader, ComboBox, Ui}; use glam::{DMat3, DQuat, DVec3, Vec3}; use hecs::{Entity, World}; use splines::{Interpolation, Key, Spline}; +use std::fmt::{Display, Formatter}; use std::sync::Arc; -use crate::physics::PhysicsState; /// Allows for the entity to have constricted movement, such as a Camera that follows a player /// on a set path. @@ -74,7 +77,11 @@ fn path_tangent_rotation(path: &[DVec3], t: f32) -> DQuat { if forward.length_squared() < 1e-10 { return DQuat::IDENTITY; } - let world_up = if forward.dot(DVec3::Y).abs() > 0.99 { DVec3::Z } else { DVec3::Y }; + let world_up = if forward.dot(DVec3::Y).abs() > 0.99 { + DVec3::Z + } else { + DVec3::Y + }; let right = world_up.cross(forward).normalize(); let up = forward.cross(right).normalize(); DQuat::from_mat3(&DMat3::from_cols(right, up, -forward)) @@ -101,10 +108,7 @@ pub enum RailDrive { /// Progress advances automatically at a fixed speed. /// /// Use for cutscenes, intros, or automated camera pans. - Automatic { - speed: f32, - looping: bool, - }, + Automatic { speed: f32, looping: bool }, /// Progress is tied to the closest point on the rail to a target entity. /// @@ -112,7 +116,7 @@ pub enum RailDrive { FollowEntity { target: Entity, /// If true, progress can never decrease and the camera won't scroll backwards. - monotonic: bool + monotonic: bool, }, /// Progress is driven by a specific axis of the target entity's world position. @@ -136,12 +140,16 @@ pub enum RailDrive { impl Display for RailDrive { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", match self { - RailDrive::Automatic { .. } => "Automatic", - RailDrive::FollowEntity { .. } => "FollowEntity", - RailDrive::AxisDriven { .. } => "AxisDriven", - RailDrive::Manual => "Manual", - }) + write!( + f, + "{}", + match self { + RailDrive::Automatic { .. } => "Automatic", + RailDrive::FollowEntity { .. } => "FollowEntity", + RailDrive::AxisDriven { .. } => "AxisDriven", + RailDrive::Manual => "Manual", + } + ) } } @@ -163,11 +171,21 @@ impl Component for OnRails { } } - fn init(ser: &'_ Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> ComponentInitFuture<'_, Self> { + fn init( + ser: &'_ Self::SerializedForm, + _graphics: Arc<SharedGraphicsContext>, + ) -> ComponentInitFuture<'_, Self> { Box::pin(async move { Ok((ser.clone(), EntityTransform::default())) }) } - 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 n = self.path.len(); if n < 2 { return; @@ -178,7 +196,9 @@ impl Component for OnRails { self.path .iter() .enumerate() - .map(|(i, p)| Key::new(i as f64 / (n - 1) as f64, get(p), Interpolation::Linear)) + .map(|(i, p)| { + Key::new(i as f64 / (n - 1) as f64, get(p), Interpolation::Linear) + }) .collect(), ) }; @@ -231,7 +251,11 @@ impl Component for OnRails { best_t }; } - RailDrive::AxisDriven { target, axis, range } => { + RailDrive::AxisDriven { + target, + axis, + range, + } => { let (target, axis, range) = (*target, *axis, *range); let Ok(target_et) = world.get::<&EntityTransform>(target) else { return; @@ -350,8 +374,14 @@ impl InspectableComponent for OnRails { }); if drive_tag != prev_tag { self.drive = match drive_tag { - 0 => RailDrive::Automatic { speed: 0.1, looping: false }, - 1 => RailDrive::FollowEntity { target: entity, monotonic: false }, + 0 => RailDrive::Automatic { + speed: 0.1, + looping: false, + }, + 1 => RailDrive::FollowEntity { + target: entity, + monotonic: false, + }, 2 => RailDrive::AxisDriven { target: entity, axis: Vec3::X, @@ -367,7 +397,11 @@ impl InspectableComponent for OnRails { RailDrive::Automatic { speed, looping } => { ui.horizontal(|ui| { ui.label("Speed:"); - ui.add(egui::DragValue::new(speed).speed(0.001).range(0.0_f32..=f32::MAX)); + ui.add( + egui::DragValue::new(speed) + .speed(0.001) + .range(0.0_f32..=f32::MAX), + ); }); ui.checkbox(looping, "Looping"); } @@ -375,7 +409,11 @@ impl InspectableComponent for OnRails { ui.label(format!("Target entity: {}", target.to_bits())); ui.checkbox(monotonic, "Monotonic"); } - RailDrive::AxisDriven { target, axis, range } => { + RailDrive::AxisDriven { + target, + axis, + range, + } => { ui.label(format!("Target entity: {}", target.to_bits())); ui.horizontal(|ui| { ui.label("Axis:"); @@ -492,21 +530,27 @@ impl InspectableComponent for EntityTransform { .default_open(true) .id_salt(format!("Entity Transform {}", entity.to_bits())) .show(ui, |ui| { - CollapsingHeader::new("Local").default_open(true).id_salt(format!("Local {}", entity.to_bits())).show(ui, |ui| { - self.local_mut().inspect(ui); - }); - ui.add_space(4.0); - CollapsingHeader::new("World").default_open(true).id_salt(format!("World {}", entity.to_bits())).show(ui, |ui| { - self.world_mut().inspect(ui); + CollapsingHeader::new("Local") + .default_open(true) + .id_salt(format!("Local {}", entity.to_bits())) + .show(ui, |ui| { + self.local_mut().inspect(ui); + }); + ui.add_space(4.0); + CollapsingHeader::new("World") + .default_open(true) + .id_salt(format!("World {}", entity.to_bits())) + .show(ui, |ui| { + self.world_mut().inspect(ui); + }); }); - }); } } impl FromJObject for Transform { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { let pos_val = env - .get_field(obj, "position", "Lcom/dropbear/math/Vector3d;") + .get_field(obj, jni_str!("position"), jni_sig!(com.dropbear.math.Vector3d)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; let pos_obj = pos_val @@ -514,7 +558,7 @@ impl FromJObject for Transform { .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let rot_val = env - .get_field(obj, "rotation", "Lcom/dropbear/math/Quaterniond;") + .get_field(obj, jni_str!("rotation"), jni_sig!(com.dropbear.math.Quaterniond)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; let rot_obj = rot_val @@ -522,7 +566,7 @@ impl FromJObject for Transform { .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let scale_val = env - .get_field(obj, "scale", "Lcom/dropbear/math/Vector3d;") + .get_field(obj, jni_str!("scale"), jni_sig!(com.dropbear.math.Vector3d)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; let scale_obj = scale_val @@ -532,17 +576,17 @@ impl FromJObject for Transform { let position: DVec3 = NVector3::from_jobject(env, &pos_obj)?.into(); let scale: DVec3 = NVector3::from_jobject(env, &scale_obj)?.into(); - let mut get_double = |field: &str| -> DropbearNativeResult<f64> { - env.get_field(&rot_obj, field, "D") + let mut get_double = |field| -> DropbearNativeResult<f64> { + env.get_field(&rot_obj, field, jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed) }; - let rx = get_double("x")?; - let ry = get_double("y")?; - let rz = get_double("z")?; - let rw = get_double("w")?; + let rx = get_double(jni_str!("x"))?; + let ry = get_double(jni_str!("y"))?; + let rz = get_double(jni_str!("z"))?; + let rw = get_double(jni_str!("w"))?; let rotation = DQuat::from_xyzw(rx, ry, rz, rw); @@ -555,8 +599,8 @@ 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| { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let cls = env.load_class(jni_str!("com/dropbear/math/Transform")).map_err(|e| { eprintln!("Could not find Transform class: {:?}", e); DropbearNativeError::JNIClassNotFound })?; @@ -578,19 +622,19 @@ impl ToJObject for Transform { JValue::Double(s.z), ]; - let obj = env.new_object(cls, "(DDDDDDDDDD)V", &args).map_err(|e| { + let obj = env.new_object(cls, jni_sig!((double, double, double, double, double, double, double, double, double, double) -> void), &args).map_err(|e| { eprintln!("Failed to create Transform object: {:?}", e); DropbearNativeError::JNIFailedToCreateObject - })?; + })?;; Ok(obj) } } impl FromJObject for EntityTransform { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { let local_val = env - .get_field(obj, "local", "Lcom/dropbear/math/Transform;") + .get_field(obj, jni_str!("local"), jni_sig!(com.dropbear.math.Transform)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; let local_obj = local_val @@ -598,7 +642,7 @@ impl FromJObject for EntityTransform { .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let world_val = env - .get_field(obj, "world", "Lcom/dropbear/math/Transform;") + .get_field(obj, jni_str!("world"), jni_sig!(com.dropbear.math.Transform)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; let world_obj = world_val @@ -613,9 +657,9 @@ impl FromJObject for EntityTransform { } impl ToJObject for EntityTransform { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let cls = env - .find_class("com/dropbear/components/EntityTransform") + .load_class(jni_str!("com/dropbear/components/EntityTransform")) .map_err(|e| { eprintln!("Could not find EntityTransform class: {:?}", e); DropbearNativeError::JNIClassNotFound @@ -629,7 +673,7 @@ impl ToJObject for EntityTransform { let obj = env .new_object( cls, - "(Lcom/dropbear/math/Transform;Lcom/dropbear/math/Transform;)V", + jni_sig!((com.dropbear.math.Transform, com.dropbear.math.Transform) -> void), &args, ) .map_err(|e| { @@ -753,19 +797,27 @@ fn propagate_transform( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getEnabled"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getEnabled" + ), c )] fn on_rails_get_enabled( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<bool> { - let rails = world.get::<&OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; Ok(rails.enabled) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "setEnabled"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "setEnabled" + ), c )] fn on_rails_set_enabled( @@ -773,13 +825,18 @@ fn on_rails_set_enabled( #[dropbear_macro::entity] entity: hecs::Entity, enabled: bool, ) -> DropbearNativeResult<()> { - let mut rails = world.get::<&mut OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; rails.enabled = enabled; Ok(()) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "existsForEntity"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "existsForEntity" + ), c )] fn on_rails_exists_for_entity( @@ -790,19 +847,27 @@ fn on_rails_exists_for_entity( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getProgress"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getProgress" + ), c )] fn on_rails_get_progress( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<f32> { - let rails = world.get::<&OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; Ok(rails.progress) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "setProgress"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "setProgress" + ), c )] fn on_rails_set_progress( @@ -810,25 +875,35 @@ fn on_rails_set_progress( #[dropbear_macro::entity] entity: hecs::Entity, progress: f32, ) -> DropbearNativeResult<()> { - let mut rails = world.get::<&mut OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; rails.progress = progress.clamp(0.0, 1.0); Ok(()) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getPathLen"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getPathLen" + ), c )] fn on_rails_get_path_len( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<i32> { - let rails = world.get::<&OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; Ok(rails.path.len() as i32) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getPathPoint"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getPathPoint" + ), c )] fn on_rails_get_path_point( @@ -836,26 +911,39 @@ fn on_rails_get_path_point( #[dropbear_macro::entity] entity: hecs::Entity, index: i32, ) -> DropbearNativeResult<NVector3> { - let rails = world.get::<&OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; - let point = rails.path.get(index as usize).ok_or(DropbearNativeError::InvalidArgument)?; + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + let point = rails + .path + .get(index as usize) + .ok_or(DropbearNativeError::InvalidArgument)?; Ok(NVector3::from(point)) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "clearPath"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "clearPath" + ), c )] fn on_rails_clear_path( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<()> { - let mut rails = world.get::<&mut OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; rails.path.clear(); Ok(()) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "pushPathPoint"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "pushPathPoint" + ), c )] fn on_rails_push_path_point( @@ -863,32 +951,42 @@ fn on_rails_push_path_point( #[dropbear_macro::entity] entity: hecs::Entity, point: &NVector3, ) -> DropbearNativeResult<()> { - let mut rails = world.get::<&mut OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; rails.path.push(DVec3::from(point)); Ok(()) } // `0` = Automatic, `1` = FollowEntity, `2` = AxisDriven, `3` = Manual. #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getDriveType"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveType" + ), c )] fn on_rails_get_drive_type( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<i32> { - let rails = world.get::<&OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; let tag = match &rails.drive { - RailDrive::Automatic { .. } => 0, + RailDrive::Automatic { .. } => 0, RailDrive::FollowEntity { .. } => 1, - RailDrive::AxisDriven { .. } => 2, - RailDrive::Manual => 3, + RailDrive::AxisDriven { .. } => 2, + RailDrive::Manual => 3, }; Ok(tag) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "setDriveAutomatic"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "setDriveAutomatic" + ), c )] fn on_rails_set_drive_automatic( @@ -897,13 +995,18 @@ fn on_rails_set_drive_automatic( speed: f32, looping: bool, ) -> DropbearNativeResult<()> { - let mut rails = world.get::<&mut OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; rails.drive = RailDrive::Automatic { speed, looping }; Ok(()) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "setDriveFollowEntity"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "setDriveFollowEntity" + ), c )] fn on_rails_set_drive_follow_entity( @@ -912,14 +1015,23 @@ fn on_rails_set_drive_follow_entity( target: u64, monotonic: bool, ) -> DropbearNativeResult<()> { - let target_entity = hecs::Entity::from_bits(target).ok_or(DropbearNativeError::InvalidEntity)?; - let mut rails = world.get::<&mut OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; - rails.drive = RailDrive::FollowEntity { target: target_entity, monotonic }; + let target_entity = + hecs::Entity::from_bits(target).ok_or(DropbearNativeError::InvalidEntity)?; + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + rails.drive = RailDrive::FollowEntity { + target: target_entity, + monotonic, + }; Ok(()) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "setDriveAxisDriven"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "setDriveAxisDriven" + ), c )] fn on_rails_set_drive_axis_driven( @@ -930,8 +1042,11 @@ fn on_rails_set_drive_axis_driven( range_min: f32, range_max: f32, ) -> DropbearNativeResult<()> { - let target_entity = hecs::Entity::from_bits(target).ok_or(DropbearNativeError::InvalidEntity)?; - let mut rails = world.get::<&mut OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; + let target_entity = + hecs::Entity::from_bits(target).ok_or(DropbearNativeError::InvalidEntity)?; + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; rails.drive = RailDrive::AxisDriven { target: target_entity, axis: Vec3::new(axis.x as f32, axis.y as f32, axis.z as f32), @@ -941,110 +1056,187 @@ fn on_rails_set_drive_axis_driven( } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "setDriveManual"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "setDriveManual" + ), c )] fn on_rails_set_drive_manual( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<()> { - let mut rails = world.get::<&mut OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; + let mut rails = world + .get::<&mut OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; rails.drive = RailDrive::Manual; Ok(()) } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getDriveAutomaticSpeed"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveAutomaticSpeed" + ), c )] fn on_rails_get_drive_automatic_speed( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<f32> { - let rails = world.get::<&OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::Automatic { speed, .. } = &rails.drive { Ok(*speed) } else { Err(DropbearNativeError::InvalidArgument) } + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::Automatic { speed, .. } = &rails.drive { + Ok(*speed) + } else { + Err(DropbearNativeError::InvalidArgument) + } } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getDriveAutomaticLooping"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveAutomaticLooping" + ), c )] fn on_rails_get_drive_automatic_looping( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<bool> { - let rails = world.get::<&OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::Automatic { looping, .. } = &rails.drive { Ok(*looping) } else { Err(DropbearNativeError::InvalidArgument) } + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::Automatic { looping, .. } = &rails.drive { + Ok(*looping) + } else { + Err(DropbearNativeError::InvalidArgument) + } } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getDriveFollowEntityTarget"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveFollowEntityTarget" + ), c )] fn on_rails_get_drive_follow_entity_target( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<u64> { - let rails = world.get::<&OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::FollowEntity { target, .. } = &rails.drive { Ok(target.to_bits().get()) } else { Err(DropbearNativeError::InvalidArgument) } + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::FollowEntity { target, .. } = &rails.drive { + Ok(target.to_bits().get()) + } else { + Err(DropbearNativeError::InvalidArgument) + } } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getDriveFollowEntityMonotonic"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveFollowEntityMonotonic" + ), c )] fn on_rails_get_drive_follow_entity_monotonic( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<bool> { - let rails = world.get::<&OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::FollowEntity { monotonic, .. } = &rails.drive { Ok(*monotonic) } else { Err(DropbearNativeError::InvalidArgument) } + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::FollowEntity { monotonic, .. } = &rails.drive { + Ok(*monotonic) + } else { + Err(DropbearNativeError::InvalidArgument) + } } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getDriveAxisDrivenTarget"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveAxisDrivenTarget" + ), c )] fn on_rails_get_drive_axis_driven_target( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<u64> { - let rails = world.get::<&OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::AxisDriven { target, .. } = &rails.drive { Ok(target.to_bits().get()) } else { Err(DropbearNativeError::InvalidArgument) } + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::AxisDriven { target, .. } = &rails.drive { + Ok(target.to_bits().get()) + } else { + Err(DropbearNativeError::InvalidArgument) + } } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getDriveAxisDrivenAxis"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveAxisDrivenAxis" + ), c )] fn on_rails_get_drive_axis_driven_axis( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<NVector3> { - let rails = world.get::<&OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::AxisDriven { axis, .. } = &rails.drive { Ok(NVector3::from(*axis)) } else { Err(DropbearNativeError::InvalidArgument) } + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::AxisDriven { axis, .. } = &rails.drive { + Ok(NVector3::from(*axis)) + } else { + Err(DropbearNativeError::InvalidArgument) + } } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getDriveAxisDrivenRangeMin"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveAxisDrivenRangeMin" + ), c )] fn on_rails_get_drive_axis_driven_range_min( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<f32> { - let rails = world.get::<&OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::AxisDriven { range, .. } = &rails.drive { Ok(range.0) } else { Err(DropbearNativeError::InvalidArgument) } + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::AxisDriven { range, .. } = &rails.drive { + Ok(range.0) + } else { + Err(DropbearNativeError::InvalidArgument) + } } #[dropbear_macro::export( - kotlin(class = "com.dropbear.components.camera.OnRailsNative", func = "getDriveAxisDrivenRangeMax"), + kotlin( + class = "com.dropbear.components.camera.OnRailsNative", + func = "getDriveAxisDrivenRangeMax" + ), c )] fn on_rails_get_drive_axis_driven_range_max( #[dropbear_macro::define(WorldPtr)] world: &hecs::World, #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<f32> { - let rails = world.get::<&OnRails>(entity).map_err(|_| DropbearNativeError::MissingComponent)?; - if let RailDrive::AxisDriven { range, .. } = &rails.drive { Ok(range.1) } else { Err(DropbearNativeError::InvalidArgument) } + let rails = world + .get::<&OnRails>(entity) + .map_err(|_| DropbearNativeError::MissingComponent)?; + if let RailDrive::AxisDriven { range, .. } = &rails.drive { + Ok(range.1) + } else { + Err(DropbearNativeError::InvalidArgument) + } } @@ -6,7 +6,7 @@ use crate::scripting::result::DropbearNativeResult; use dropbear_engine::entity::Transform; use glam::{DQuat, DVec3, Vec3}; use hecs::Entity; -use jni::JNIEnv; +use jni::{Env, jni_str, jni_sig}; use jni::objects::{JObject, JValue}; use jni::sys::jdouble; use rapier3d::data::Index; @@ -173,12 +173,12 @@ impl From<NVector3> for glam::DVec3 { } impl FromJObject for NVector3 { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> where Self: Sized, { let class = env - .find_class("com/dropbear/math/Vector3d") + .load_class(jni_str!("com/dropbear/math/Vector3d")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; if !env @@ -189,19 +189,19 @@ impl FromJObject for NVector3 { } let x = env - .get_field(obj, "x", "D") + .get_field(obj, jni_str!("x"), jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let y = env - .get_field(obj, "y", "D") + .get_field(obj, jni_str!("y"), jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let z = env - .get_field(obj, "z", "D") + .get_field(obj, jni_str!("z"), jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -211,13 +211,11 @@ impl FromJObject for NVector3 { } impl ToJObject for NVector3 { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/math/Vector3d") + .load_class(jni_str!("com/dropbear/math/Vector3d")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let constructor_sig = "(DDD)V"; - let args = [ jni::objects::JValue::Double(self.x), jni::objects::JValue::Double(self.y), @@ -225,7 +223,7 @@ impl ToJObject for NVector3 { ]; let obj = env - .new_object(&class, constructor_sig, &args) + .new_object(&class, jni_sig!((double, double, double) -> void), &args) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) @@ -294,12 +292,12 @@ impl From<NVector4> for glam::DVec4 { } impl FromJObject for NVector4 { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> where Self: Sized, { let class = env - .find_class("com/dropbear/math/Vector4d") + .load_class(jni_str!("com/dropbear/math/Vector4d")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; if !env @@ -310,25 +308,25 @@ impl FromJObject for NVector4 { } let x = env - .get_field(obj, "x", "D") + .get_field(obj, jni_str!("x"), jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let y = env - .get_field(obj, "y", "D") + .get_field(obj, jni_str!("y"), jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let z = env - .get_field(obj, "z", "D") + .get_field(obj, jni_str!("z"), jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let w = env - .get_field(obj, "w", "D") + .get_field(obj, jni_str!("w"), jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -338,13 +336,11 @@ impl FromJObject for NVector4 { } impl ToJObject for NVector4 { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/math/Vector3d") + .load_class(jni_str!("com/dropbear/math/Vector3d")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; - let constructor_sig = "(DDD)V"; - let args = [ jni::objects::JValue::Double(self.x), jni::objects::JValue::Double(self.y), @@ -353,7 +349,7 @@ impl ToJObject for NVector4 { ]; let obj = env - .new_object(&class, constructor_sig, &args) + .new_object(&class, jni_sig!((double, double, double, double) -> void), &args) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; Ok(obj) @@ -436,9 +432,9 @@ impl From<NVector4> for NQuaternion { } impl ToJObject for NQuaternion { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/math/Quaterniond") + .load_class(jni_str!("com/dropbear/math/Quaterniond")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let args = [ @@ -448,18 +444,18 @@ impl ToJObject for NQuaternion { JValue::Double(self.w), ]; - env.new_object(&class, "(DDDD)V", &args) + env.new_object(&class, jni_sig!((double, double, double, double) -> void), &args) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) } } impl FromJObject for NQuaternion { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> where Self: Sized, { let class = env - .find_class("com/dropbear/math/Quaterniond") + .load_class(jni_str!("com/dropbear/math/Quaterniond")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; if !env @@ -470,25 +466,25 @@ impl FromJObject for NQuaternion { } let x = env - .get_field(obj, "x", "D") + .get_field(obj, jni_str!("x"), jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let y = env - .get_field(obj, "y", "D") + .get_field(obj, jni_str!("y"), jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let z = env - .get_field(obj, "z", "D") + .get_field(obj, jni_str!("z"), jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let w = env - .get_field(obj, "w", "D") + .get_field(obj, jni_str!("w"), jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -526,9 +522,9 @@ impl From<NTransform> for Transform { } impl FromJObject for NTransform { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> { let pos_val = env - .get_field(obj, "position", "Lcom/dropbear/math/Vector3d;") + .get_field(obj, jni_str!("position"), jni_sig!(com.dropbear.math.Vector3d)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; let pos_obj = pos_val @@ -536,7 +532,7 @@ impl FromJObject for NTransform { .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let rot_val = env - .get_field(obj, "rotation", "Lcom/dropbear/math/Quaterniond;") + .get_field(obj, jni_str!("rotation"), jni_sig!(com.dropbear.math.Quaterniond)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; let rot_obj = rot_val @@ -544,7 +540,7 @@ impl FromJObject for NTransform { .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let scale_val = env - .get_field(obj, "scale", "Lcom/dropbear/math/Vector3d;") + .get_field(obj, jni_str!("scale"), jni_sig!(com.dropbear.math.Vector3d)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)?; let scale_obj = scale_val @@ -554,17 +550,17 @@ impl FromJObject for NTransform { let position: DVec3 = NVector3::from_jobject(env, &pos_obj)?.into(); let scale: DVec3 = NVector3::from_jobject(env, &scale_obj)?.into(); - let mut get_double = |field: &str| -> DropbearNativeResult<f64> { - env.get_field(&rot_obj, field, "D") + let mut get_double = |field| -> DropbearNativeResult<f64> { + env.get_field(&rot_obj, field, jni_sig!(double)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .d() .map_err(|_| DropbearNativeError::JNIUnwrapFailed) }; - let rx = get_double("x")?; - let ry = get_double("y")?; - let rz = get_double("z")?; - let rw = get_double("w")?; + let rx = get_double(jni_str!("x"))?; + let ry = get_double(jni_str!("y"))?; + let rz = get_double(jni_str!("z"))?; + let rw = get_double(jni_str!("w"))?; let rotation = DQuat::from_xyzw(rx, ry, rz, rw); @@ -577,9 +573,9 @@ impl FromJObject for NTransform { } impl ToJObject for NTransform { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/math/Transform") + .load_class(jni_str!("com/dropbear/math/Transform")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let args = [ @@ -595,7 +591,7 @@ impl ToJObject for NTransform { JValue::Double(self.scale.z), ]; - env.new_object(&class, "(DDDDDDDDDD)V", &args) + env.new_object(&class, jni_sig!((double, double, double, double, double, double, double, double, double, double) -> void), &args) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject) } } @@ -609,27 +605,27 @@ pub struct NCollider { } impl ToJObject for NCollider { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let collider_cls = env - .find_class("com/dropbear/physics/Collider") + .load_class(jni_str!("com/dropbear/physics/Collider")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let index_cls = env - .find_class("com/dropbear/physics/Index") + .load_class(jni_str!("com/dropbear/physics/Index")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let entity_cls = env - .find_class("com/dropbear/EntityId") + .load_class(jni_str!("com/dropbear/EntityId")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let entity_obj = env - .new_object(&entity_cls, "(J)V", &[JValue::Long(self.entity_id as i64)]) + .new_object(&entity_cls, jni_sig!((long) -> void), &[JValue::Long(self.entity_id as i64)]) .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?; let index_obj = env .new_object( &index_cls, - "(II)V", + jni_sig!((int, int) -> void), &[ JValue::Int(self.index.index as i32), JValue::Int(self.index.generation as i32), @@ -640,7 +636,7 @@ impl ToJObject for NCollider { let collider_obj = env .new_object( collider_cls, - "(Lcom/dropbear/physics/Index;Lcom/dropbear/EntityId;I)V", + jni_sig!((com.dropbear.physics.Index, com.dropbear.EntityId, int) -> void), &[ JValue::Object(&index_obj), JValue::Object(&entity_obj), @@ -654,42 +650,42 @@ impl ToJObject for NCollider { } impl FromJObject for NCollider { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> where Self: Sized, { let index_obj = env - .get_field(obj, "index", "Lcom/dropbear/physics/Index;") + .get_field(obj, jni_str!("index"), jni_sig!(com.dropbear.physics.Index)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let entity_obj = env - .get_field(obj, "entity", "Lcom/dropbear/EntityId;") + .get_field(obj, jni_str!("entity"), jni_sig!(com.dropbear.EntityId)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let id_val = env - .get_field(obj, "id", "I") + .get_field(obj, jni_str!("id"), jni_sig!(int)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .i() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let entity_raw = env - .get_field(&entity_obj, "raw", "J") + .get_field(&entity_obj, jni_str!("raw"), jni_sig!(long)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .j() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let idx_val = env - .get_field(&index_obj, "index", "I") + .get_field(&index_obj, jni_str!("index"), jni_sig!(int)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .i() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let gen_val = env - .get_field(&index_obj, "generation", "I") + .get_field(&index_obj, jni_str!("generation"), jni_sig!(int)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .i() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -729,8 +725,8 @@ 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").map_err(|e| { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let cls = env.load_class(jni_str!("com/dropbear/physics/Index")).map_err(|e| { eprintln!("[JNI Error] Could not find Index class: {:?}", e); DropbearNativeError::GenericError })?; @@ -738,7 +734,7 @@ impl ToJObject for IndexNative { let obj = env .new_object( cls, - "(II)V", + jni_sig!((int, int) -> void), &[ JValue::Int(self.index as i32), JValue::Int(self.generation as i32), @@ -754,7 +750,7 @@ impl ToJObject for IndexNative { } impl ToJObject for Option<IndexNative> { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { match self { Some(value) => value.to_jobject(env), None => Ok(JObject::null()), @@ -763,18 +759,18 @@ impl ToJObject for Option<IndexNative> { } impl FromJObject for IndexNative { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> where Self: Sized, { let idx_val = env - .get_field(obj, "index", "I") + .get_field(obj, jni_str!("index"), jni_sig!(int)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .i() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let gen_val = env - .get_field(obj, "generation", "I") + .get_field(obj, jni_str!("generation"), jni_sig!(int)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .i() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -794,36 +790,36 @@ pub struct RigidBodyContext { } impl FromJObject for RigidBodyContext { - fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> + fn from_jobject(env: &mut Env, obj: &JObject) -> DropbearNativeResult<Self> where Self: Sized, { let index_obj = env - .get_field(obj, "index", "Lcom/dropbear/physics/Index;") + .get_field(obj, jni_str!("index"), jni_sig!(com.dropbear.physics.Index)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let idx_val = env - .get_field(&index_obj, "index", "I") + .get_field(&index_obj, jni_str!("index"), jni_sig!(int)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .i() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let gen_val = env - .get_field(&index_obj, "generation", "I") + .get_field(&index_obj, jni_str!("generation"), jni_sig!(int)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .i() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let entity_obj = env - .get_field(obj, "entity", "Lcom/dropbear/EntityId;") + .get_field(obj, jni_str!("entity"), jni_sig!(com.dropbear.EntityId)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .l() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; let entity_raw = env - .get_field(&entity_obj, "raw", "J") + .get_field(&entity_obj, jni_str!("raw"), jni_sig!(long)) .map_err(|_| DropbearNativeError::JNIFailedToGetField)? .j() .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?; @@ -839,8 +835,8 @@ 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").map_err(|e| { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { + let index_cls = env.load_class(jni_str!("com/dropbear/physics/Index")).map_err(|e| { eprintln!( "[JNI Error] Class 'com/dropbear/physics/Index' not found: {:?}", e @@ -848,7 +844,7 @@ impl ToJObject for RigidBodyContext { DropbearNativeError::JNIClassNotFound })?; - let entity_cls = env.find_class("com/dropbear/EntityId").map_err(|e| { + let entity_cls = env.load_class(jni_str!("com/dropbear/EntityId")).map_err(|e| { eprintln!( "[JNI Error] Class 'com/dropbear/EntityId' not found: {:?}", e @@ -857,7 +853,7 @@ impl ToJObject for RigidBodyContext { })?; let rb_cls = env - .find_class("com/dropbear/physics/RigidBody") + .load_class(jni_str!("com/dropbear/physics/RigidBody")) .map_err(|e| { eprintln!( "[JNI Error] Class 'com/dropbear/physics/RigidBody' not found: {:?}", @@ -869,7 +865,7 @@ impl ToJObject for RigidBodyContext { let index_obj = env .new_object( &index_cls, - "(II)V", + jni_sig!((int, int) -> void), &[ JValue::Int(self.index.index as i32), JValue::Int(self.index.generation as i32), @@ -881,7 +877,7 @@ impl ToJObject for RigidBodyContext { })?; let entity_obj = env - .new_object(&entity_cls, "(J)V", &[JValue::Long(self.entity_id as i64)]) + .new_object(&entity_cls, jni_sig!((long) -> void), &[JValue::Long(self.entity_id as i64)]) .map_err(|e| { eprintln!("[JNI Error] Failed to create EntityId object: {:?}", e); DropbearNativeError::JNIFailedToCreateObject @@ -890,7 +886,7 @@ impl ToJObject for RigidBodyContext { let rb_obj = env .new_object( rb_cls, - "(Lcom/dropbear/physics/Index;Lcom/dropbear/EntityId;)V", + jni_sig!((com.dropbear.physics.Index, com.dropbear.EntityId) -> void), &[JValue::Object(&index_obj), JValue::Object(&entity_obj)], ) .map_err(|e| { @@ -910,11 +906,11 @@ pub struct RayHit { } impl ToJObject for RayHit { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let collider = self.collider.to_jobject(env)?; let distance = self.distance as jdouble; - let class = env.find_class("com/dropbear/physics/RayHit").map_err(|e| { + let class = env.load_class(jni_str!("com/dropbear/physics/RayHit")).map_err(|e| { eprintln!("[JNI Error] Failed to create RayHit object: {:?}", e); DropbearNativeError::JNIClassNotFound })?; @@ -922,7 +918,7 @@ impl ToJObject for RayHit { let object = env .new_object( class, - "(Lcom/dropbear/physics/Collider;D)V", + jni_sig!((com.dropbear.physics.Collider, double) -> void), &[JValue::Object(&collider), JValue::Double(distance)], ) .map_err(|e| { @@ -983,7 +979,7 @@ pub struct NShapeCastHit { } impl ToJObject for NShapeCastHit { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { use jni::sys::jdouble; let collider = self.collider.to_jobject(env)?; @@ -996,7 +992,7 @@ impl ToJObject for NShapeCastHit { let distance = self.distance as jdouble; let class = env - .find_class("com/dropbear/physics/ShapeCastHit") + .load_class(jni_str!("com/dropbear/physics/ShapeCastHit")) .map_err(|e| { eprintln!("[JNI Error] Failed to find ShapeCastHit class: {:?}", e); DropbearNativeError::JNIClassNotFound @@ -1005,7 +1001,7 @@ impl ToJObject for NShapeCastHit { let object = env .new_object( class, - "(Lcom/dropbear/physics/Collider;DLcom/dropbear/math/Vector3d;Lcom/dropbear/math/Vector3d;Lcom/dropbear/math/Vector3d;Lcom/dropbear/math/Vector3d;Lcom/dropbear/physics/ShapeCastStatus;)V", + jni_sig!("(Lcom/dropbear/physics/Collider;DLcom/dropbear/math/Vector3d;Lcom/dropbear/math/Vector3d;Lcom/dropbear/math/Vector3d;Lcom/dropbear/math/Vector3d;Lcom/dropbear/physics/ShapeCastStatus;)V"), &[ JValue::Object(&collider), JValue::Double(distance), @@ -1034,9 +1030,9 @@ pub enum CollisionEventType { } impl ToJObject for CollisionEventType { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/physics/CollisionEventType") + .load_class(jni_str!("com/dropbear/physics/CollisionEventType")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let name = match self { @@ -1050,9 +1046,9 @@ impl ToJObject for CollisionEventType { let value = env .call_static_method( class, - "valueOf", - "(Ljava/lang/String;)Lcom/dropbear/physics/CollisionEventType;", - &[JValue::Object(&name_jstring)], + jni_str!("valueOf"), + jni_sig!((java.lang.String) -> com.dropbear.physics.CollisionEventType), + &[JValue::from(&name_jstring)], ) .map_err(|_| DropbearNativeError::JNIMethodNotFound)? .l() @@ -1196,9 +1192,9 @@ impl CollisionEvent { } impl ToJObject for CollisionEvent { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/physics/CollisionEvent") + .load_class(jni_str!("com/dropbear/physics/CollisionEvent")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let event_type = self.event_type.to_jobject(env)?; @@ -1209,7 +1205,7 @@ impl ToJObject for CollisionEvent { let obj = env .new_object( class, - "(Lcom/dropbear/physics/CollisionEventType;Lcom/dropbear/physics/Collider;Lcom/dropbear/physics/Collider;I)V", + jni_sig!("(Lcom/dropbear/physics/CollisionEventType;Lcom/dropbear/physics/Collider;Lcom/dropbear/physics/Collider;I)V"), &[ JValue::Object(&event_type), JValue::Object(&collider1), @@ -1299,9 +1295,9 @@ impl ContactForceEvent { } impl ToJObject for ContactForceEvent { - fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/physics/ContactForceEvent") + .load_class(jni_str!("com/dropbear/physics/ContactForceEvent")) .map_err(|_| DropbearNativeError::JNIClassNotFound)?; let collider1 = self.collider1.to_jobject(env)?; @@ -1312,7 +1308,7 @@ impl ToJObject for ContactForceEvent { let obj = env .new_object( class, - "(Lcom/dropbear/physics/Collider;Lcom/dropbear/physics/Collider;Lcom/dropbear/math/Vector3d;DLcom/dropbear/math/Vector3d;D)V", + jni_sig!("(Lcom/dropbear/physics/Collider;Lcom/dropbear/physics/Collider;Lcom/dropbear/math/Vector3d;DLcom/dropbear/math/Vector3d;D)V"), &[ JValue::Object(&collider1), JValue::Object(&collider2), @@ -1,9 +1,12 @@ -use std::sync::Arc; +use crate::component::{ + Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, + SerializedComponent, +}; +use crate::physics::PhysicsState; +use dropbear_engine::graphics::SharedGraphicsContext; use egui::{CollapsingHeader, Ui}; use hecs::{Entity, World}; -use dropbear_engine::graphics::SharedGraphicsContext; -use crate::component::{Component, ComponentDescriptor, ComponentInitFuture, DisabilityFlags, InspectableComponent, SerializedComponent}; -use crate::physics::PhysicsState; +use std::sync::Arc; #[derive(Default, Clone, serde::Serialize, serde::Deserialize)] pub struct HUDComponent { @@ -25,7 +28,7 @@ impl SerializedComponent for HUDComponent {} impl Component for HUDComponent { type SerializedForm = Self; - type RequiredComponentTypes = (Self, ); + type RequiredComponentTypes = (Self,); fn descriptor() -> ComponentDescriptor { ComponentDescriptor { @@ -34,16 +37,28 @@ impl Component for HUDComponent { fqtn: "eucalyptus_core::ui::HUDComponent".to_string(), type_name: "HUD".to_string(), category: Some("UI".to_string()), - description: Some("Renders a camera-facing textured quad, typically used for a HUD or 2D context".to_string()), + description: Some( + "Renders a camera-facing textured quad, typically used for a HUD or 2D context" + .to_string(), + ), } } - fn init(ser: &'_ Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> ComponentInitFuture<'_, Self> { + fn init( + ser: &'_ Self::SerializedForm, + _graphics: Arc<SharedGraphicsContext>, + ) -> ComponentInitFuture<'_, 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 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> { @@ -52,16 +67,25 @@ impl Component for HUDComponent { } impl InspectableComponent for HUDComponent { - fn inspect(&mut self, _world: &World, entity: Entity, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { + fn inspect( + &mut self, + _world: &World, + entity: Entity, + ui: &mut Ui, + _graphics: Arc<SharedGraphicsContext>, + ) { CollapsingHeader::new("HUD") .default_open(true) .id_salt(format!("HUD {}", entity.to_bits())) .show(ui, |ui| { - if ui.button("Edit in UI Editor").clicked() { - ui.ctx().data_mut(|d| { - d.insert_temp::<Option<Entity>>(egui::Id::new("open_ui_editor"), Some(entity)); - }); - } - }); + if ui.button("Edit in UI Editor").clicked() { + ui.ctx().data_mut(|d| { + d.insert_temp::<Option<Entity>>( + egui::Id::new("open_ui_editor"), + Some(entity), + ); + }); + } + }); } -} +} @@ -3,10 +3,10 @@ pub mod option; use crate::scripting::result::DropbearNativeResult; -use crate::states::Node; use crate::ser::model::{EucalyptusMaterial, EucalyptusMesh, EucalyptusModel}; +use crate::states::Node; use dropbear_engine::utils::ResourceReference; -use jni::JNIEnv; +use jni::{Env, jni_str, jni_sig}; use jni::objects::{JObject, JValue}; use std::collections::hash_map::DefaultHasher; use std::fs; @@ -304,15 +304,15 @@ impl ResolveReference for ResourceReference { } } -/// Converts a [`ResourceReference`] into a file. +/// Converts a [`ResourceReference`] into a file. pub trait AsFile { - /// Converts a [`ResourceReference`] into a file. - /// + /// Converts a [`ResourceReference`] into a file. + /// /// # Different type's behaviours /// - `ResourceReference::File("")` => Returns an error (empty path). /// - `ResourceReference::File(path)` => Returns the resolved file path. /// - `ResourceReference::Embedded` => Saves bytes to a `*.eucbin` in the gen/ folder. - /// - `ResourceReference::Procedural` => Serializes geometry to a `*.eucmdl` in the gen/ folder. + /// - `ResourceReference::Procedural` => Serializes geometry to a `*.eucmdl` in the gen/ folder. fn as_file(&self, new_ref: Option<String>) -> anyhow::Result<PathBuf>; } @@ -338,9 +338,7 @@ impl AsFile for ResourceReference { ResourceReference::Embedded(bytes) => { let hash = hash_value(bytes); let root = resources_root_for_write()?; - let out_path = root - .join("gen") - .join(format!("{hash:016x}.eucbin")); + let out_path = root.join("gen").join(format!("{hash:016x}.eucbin")); if let Some(parent) = out_path.parent() { fs::create_dir_all(parent)?; @@ -356,8 +354,7 @@ impl AsFile for ResourceReference { root.join(relative.trim_start_matches('/')) } else { let root = resources_root_for_write()?; - root.join("gen") - .join(format!("{hash:016x}.eucmdl")) + root.join("gen").join(format!("{hash:016x}.eucmdl")) }; let label_stem = out_path @@ -371,11 +368,13 @@ impl AsFile for ResourceReference { let vertex = obj .vertices .get(index as usize) - .ok_or_else(|| anyhow::anyhow!( - "Procedural object index {} is out of bounds for {} vertices", - index, - obj.vertices.len() - ))? + .ok_or_else(|| { + anyhow::anyhow!( + "Procedural object index {} is out of bounds for {} vertices", + index, + obj.vertices.len() + ) + })? .clone(); expanded_vertices.push(vertex); } @@ -422,8 +421,9 @@ impl AsFile for ResourceReference { fs::create_dir_all(parent)?; } - let serialized = rkyv::to_bytes::<rkyv::rancor::Error>(&model) - .map_err(|e| anyhow::anyhow!("Failed to serialize proc object model as rkyv bytes: {e}"))?; + let serialized = rkyv::to_bytes::<rkyv::rancor::Error>(&model).map_err(|e| { + anyhow::anyhow!("Failed to serialize proc object model as rkyv bytes: {e}") + })?; fs::write(&out_path, serialized.as_ref())?; Ok(out_path) @@ -628,19 +628,8 @@ macro_rules! convert_ptr { #[macro_export] macro_rules! convert_jstring { ($env:expr, $jstring:expr) => {{ - match $env.get_string(&$jstring) { - Ok(java_string) => match java_string.to_str() { - Ok(rust_str) => rust_str.to_string(), - Err(e) => { - let message = format!( - "[{}] [ERROR] Failed to convert Java string to Rust string: {}", - stringify!($jstring), - e - ); - println!("{}", message); - return $crate::ffi_error_return!(); - } - }, + match $jstring.mutf8_chars(&mut $env) { + Ok(chars) => chars.to_str().into_owned(), Err(e) => { let message = format!( "[{}] [ERROR] Failed to get string from JNI: {}", @@ -772,12 +761,17 @@ macro_rules! ffi_error_return { } } - impl<T> ErrorValue for $crate::scripting::result::DropbearNativeResult<T> { + impl ErrorValue for bool { fn error_value() -> Self { - Err($crate::scripting::native::DropbearNativeError::NullPointer) + false } } + impl<T: ErrorValue, E> ErrorValue for ::std::result::Result<T, E> { + fn error_value() -> Self { + Ok(T::error_value()) + } + } ErrorValue::error_value() }}; @@ -873,9 +867,9 @@ 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>> { + fn to_jobject<'a>(&self, env: &mut Env<'a>) -> DropbearNativeResult<JObject<'a>> { let class = env - .find_class("com/dropbear/utils/Progress") + .load_class(jni_str!("com/dropbear/utils/Progress")) .map_err(|_| crate::scripting::native::DropbearNativeError::JNIClassNotFound)?; let message_jstring = env @@ -885,11 +879,11 @@ impl crate::scripting::jni::utils::ToJObject for crate::utils::Progress { let obj = env .new_object( &class, - "(DDLjava/lang/String;)V", + jni_sig!((double, double, java.lang.String) -> void), &[ JValue::Double(self.current as f64), JValue::Double(self.total as f64), - JValue::Object(&JObject::from(message_jstring)), + JValue::from(&message_jstring), ], ) .map_err(|_| crate::scripting::native::DropbearNativeError::JNIFailedToCreateObject)?; @@ -7,9 +7,16 @@ use uuid::Uuid; /// The inner `[u8; 16]` is trivially archivable, giving us zero-copy access at /// runtime. Use `From`/`Into` to convert to/from the standard `uuid::Uuid` type. #[derive( - Clone, Debug, PartialEq, Eq, Hash, - Archive, RkyvSerialize, RkyvDeserialize, - Serialize, Deserialize, + Clone, + Debug, + PartialEq, + Eq, + Hash, + Archive, + RkyvSerialize, + RkyvDeserialize, + Serialize, + Deserialize, )] #[repr(transparent)] pub struct UuidV4([u8; 16]); @@ -63,9 +63,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); }); @@ -18,7 +18,6 @@ pub fn build(_project_config: PathBuf) -> anyhow::Result<PathBuf> { // todo: remake this entire function return Err(anyhow::anyhow!("Not implemented yet")); - // log::info!("Started project building"); // // create a build directory // let project_root = project_config @@ -2,12 +2,12 @@ mod window; -use std::collections::VecDeque; use crate::debug::window::DebugWindow; use crate::editor::Signal; use dropbear_engine::DropbearWindowBuilder; use egui::Ui; use parking_lot::RwLock; +use std::collections::VecDeque; use std::rc::Rc; use winit::window::WindowAttributes; @@ -2,7 +2,7 @@ //! //! Can also be technically used as a template for other windowed scenes (as its essentially the basics) -use egui::CentralPanel; +use egui::{CentralPanel, UiBuilder}; use gilrs::{Button, GamepadId}; use winit::dpi::PhysicalPosition; use winit::event::MouseButton; @@ -47,7 +47,8 @@ impl Scene for DebugWindow { _dt: f32, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, ) { - CentralPanel::default().show(&graphics.get_egui_context(), |ui| { + let mut ui = egui::Ui::new(graphics.get_egui_context(), egui::Id::from("egui ui for debug window"), UiBuilder::default()); + CentralPanel::default().show_inside(&mut ui, |ui| { ui.label("Hello Debug Window!"); }); @@ -1,8 +1,7 @@ use super::*; use crate::editor::ViewportMode; -use std::hash::Hasher; -use std::{collections::HashMap, hash::Hash, path::PathBuf, sync::LazyLock}; use crate::editor::docks::console::EucalyptusConsole; +use crate::editor::page::EditorTabVisibility; use crate::plugin::PluginRegistry; use dropbear_engine::entity::{EntityTransform, Transform}; use dropbear_engine::utils::ResourceReference; @@ -11,8 +10,9 @@ use egui_dock::TabViewer; use glam::Vec3; use hecs::{Entity, World}; use parking_lot::Mutex; +use std::hash::Hasher; +use std::{collections::HashMap, hash::Hash, path::PathBuf, sync::LazyLock}; use transform_gizmo_egui::{EnumSet, Gizmo, GizmoMode, GizmoOrientation}; -use crate::editor::page::EditorTabVisibility; /// State for an active click-and-drag operation on a 3D entity in the viewport. pub struct DragState { @@ -171,9 +171,8 @@ pub struct EditorTabRegistry { pub displayers: HashMap<EditorTabId, EditorTabDisplayer>, } -pub type EditorTabDisplayer = Box< - dyn for<'a> Fn(&mut EditorTabViewer<'a>, &mut egui::Ui) + Send + Sync + 'static, ->; +pub type EditorTabDisplayer = + Box<dyn for<'a> Fn(&mut EditorTabViewer<'a>, &mut egui::Ui) + Send + Sync + 'static>; impl EditorTabRegistry { pub fn new() -> Self { @@ -227,11 +226,7 @@ impl EditorTabRegistry { } fn normalize_id(id: u64) -> u64 { - if id == 0 { - 1 - } else { - id - } + if id == 0 { 1 } else { id } } } @@ -241,7 +236,6 @@ impl Default for EditorTabRegistry { } } - pub struct EditorTabDockDescriptor { pub id: &'static str, pub title: String, @@ -9,8 +9,15 @@ 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, PathBuf}}; +use std::{ + cmp::Ordering, + fs, + hash::DefaultHasher, + io, + path::{Path, PathBuf}, +}; +use crate::editor::page::EditorTabVisibility; use crate::editor::{ AssetDivision, AssetNodeInfo, AssetNodeKind, ComponentNodeSelection, DraggedAsset, EditorTabDock, EditorTabDockDescriptor, EditorTabViewer, FsEntry, ResourceDivision, @@ -18,7 +25,6 @@ use crate::editor::{ }; use eucalyptus_core::component::DRAGGED_ASSET_ID; use eucalyptus_core::hierarchy::Hierarchy; -use crate::editor::page::EditorTabVisibility; impl<'a> EditorTabViewer<'a> { pub(crate) fn show_asset_viewer(&mut self, ui: &mut egui::Ui) { @@ -913,10 +919,7 @@ impl<'a> EditorTabViewer<'a> { ) } - fn with_icon_kind( - builder: NodeBuilder<u64>, - kind: AssetNodeKind, - ) -> NodeBuilder<u64> { + fn with_icon_kind(builder: NodeBuilder<u64>, kind: AssetNodeKind) -> NodeBuilder<u64> { builder.icon(move |ui| { egui_extras::install_image_loaders(ui.ctx()); Self::draw_asset_icon(ui, kind) @@ -1092,7 +1095,11 @@ impl<'a> EditorTabViewer<'a> { if old_meta.exists() { let new_meta = PathBuf::from(format!("{}.eucmeta", target_path.display())); if let Err(e) = fs::rename(&old_meta, &new_meta) { - warn!("Failed to move .eucmeta sidecar '{}': {}", old_meta.display(), e); + warn!( + "Failed to move .eucmeta sidecar '{}': {}", + old_meta.display(), + e + ); } } } @@ -1118,7 +1125,11 @@ impl<'a> EditorTabViewer<'a> { let meta = PathBuf::from(format!("{}.eucmeta", info.path.display())); if meta.exists() { if let Err(e) = fs::remove_file(&meta) { - warn!("Failed to remove .eucmeta sidecar '{}': {}", meta.display(), e); + warn!( + "Failed to remove .eucmeta sidecar '{}': {}", + meta.display(), + e + ); } } } @@ -1226,7 +1237,11 @@ impl<'a> EditorTabViewer<'a> { if old_meta.exists() { let new_meta = PathBuf::from(format!("{}.eucmeta", target_path.display())); if let Err(e) = fs::rename(&old_meta, &new_meta) { - warn!("Failed to move .eucmeta sidecar '{}': {}", old_meta.display(), e); + warn!( + "Failed to move .eucmeta sidecar '{}': {}", + old_meta.display(), + e + ); } } } @@ -1271,11 +1286,13 @@ impl<'a> EditorTabViewer<'a> { let handle = match extension.as_deref() { Some("eucmdl") => { let model = rkyv::from_bytes::<EucalyptusModel, rkyv::rancor::Error>(&buffer) - .map_err(|e| anyhow::anyhow!( + .map_err(|e| { + anyhow::anyhow!( "Unable to deserialize .eucmdl model '{}': {}", path.display(), e - ))?; + ) + })?; let runtime_model = model.load(reference.clone(), graphics.clone()); let mut registry = ASSET_REGISTRY.write(); @@ -1306,7 +1323,12 @@ impl<'a> EditorTabViewer<'a> { }); } - fn queue_texture_load(&self, reference: ResourceReference, label: String, strict_image_decode: bool) { + fn queue_texture_load( + &self, + reference: ResourceReference, + label: String, + strict_image_decode: bool, + ) { if ASSET_REGISTRY .read() .get_texture_handle_by_reference(&reference) @@ -1322,9 +1344,7 @@ impl<'a> EditorTabViewer<'a> { let path = reference.resolve()?; let bytes = fs::read(&path)?; - if strict_image_decode - && let Err(err) = image::load_from_memory(bytes.as_slice()) - { + if strict_image_decode && let Err(err) = image::load_from_memory(bytes.as_slice()) { let error = anyhow::anyhow!( "'{}' is not a texture-compatible eucbin payload: {}", path.display(), @@ -1391,7 +1411,9 @@ impl<'a> EditorTabViewer<'a> { let target_entity = Self::entity_from_node_id(drag.target); for &source_id in &drag.source { - let Some(source_entity) = Self::entity_from_node_id(source_id) else { continue }; + let Some(source_entity) = Self::entity_from_node_id(source_id) else { + continue; + }; if cfg.component_selection(source_id).is_some() { continue; @@ -2,12 +2,9 @@ use std::path::PathBuf; use egui::{Margin, RichText}; -use crate::editor::{ - EditorTabDock, EditorTabDockDescriptor, EditorTabViewer - , -}; use crate::editor::docks::console::{ConsoleItem, ErrorLevel}; use crate::editor::page::EditorTabVisibility; +use crate::editor::{EditorTabDock, EditorTabDockDescriptor, EditorTabViewer}; impl<'a> EditorTabViewer<'a> { pub fn build_console(&mut self, ui: &mut egui::Ui) { @@ -149,8 +146,8 @@ pub struct BuildConsoleDock; impl EditorTabDock for BuildConsoleDock { fn desc() -> EditorTabDockDescriptor { - EditorTabDockDescriptor { - id: "build_console", + EditorTabDockDescriptor { + id: "build_console", title: "Build Output".to_string(), visibility: EditorTabVisibility::all(), // idk about this one } @@ -101,4 +101,4 @@ pub struct ConsoleItem { pub msg: String, pub file_location: Option<PathBuf>, pub line_ref: Option<String>, -} +} @@ -8,8 +8,11 @@ use eucalyptus_core::{ use hecs::{Entity, World}; use std::collections::{BTreeMap, HashMap, VecDeque}; -use crate::editor::{Editor, EditorTabDock, EditorTabDockDescriptor, EditorTabViewer, Signal, StaticallyKept, TABS_GLOBAL}; use crate::editor::page::EditorTabVisibility; +use crate::editor::{ + Editor, EditorTabDock, EditorTabDockDescriptor, EditorTabViewer, Signal, StaticallyKept, + TABS_GLOBAL, +}; impl<'a> EditorTabViewer<'a> { pub(crate) fn entity_list(&mut self, ui: &mut egui::Ui) { @@ -299,8 +302,8 @@ pub struct EntityListDock; impl EditorTabDock for EntityListDock { fn desc() -> EditorTabDockDescriptor { - EditorTabDockDescriptor { - id: "entity_list", + EditorTabDockDescriptor { + id: "entity_list", title: "Model/Entity List".to_string(), visibility: EditorTabVisibility::GameEditor, } @@ -3,4 +3,4 @@ pub mod build_console; pub mod console; pub mod entity_list; pub mod resource; -pub mod viewport; +pub mod viewport; @@ -1,9 +1,9 @@ -use hecs::Entity; -use eucalyptus_core::entity_status::EntityStatus; +use crate::editor::page::EditorTabVisibility; use crate::editor::{EditorTabDock, EditorTabDockDescriptor, EditorTabViewer, TABS_GLOBAL}; use dropbear_engine::camera::Camera; use eucalyptus_core::camera::{CameraComponent, CameraType}; -use crate::editor::page::EditorTabVisibility; +use eucalyptus_core::entity_status::EntityStatus; +use hecs::Entity; impl<'a> EditorTabViewer<'a> { pub(crate) fn resource_inspector(&mut self, ui: &mut egui::Ui) { @@ -44,17 +44,20 @@ impl<'a> EditorTabViewer<'a> { s.disabled = disabled; } } else { - let _ = self.world.insert_one( - inspect_entity, - EntityStatus { hidden, disabled }, - ); + let _ = self + .world + .insert_one(inspect_entity, EntityStatus { hidden, disabled }); } } } ui.separator(); let mut local_unset_comp = false; - if let Ok((_, comp)) = self.world.query_one::<(&Camera, &CameraComponent)>(inspect_entity).get() { + if let Ok((_, comp)) = self + .world + .query_one::<(&Camera, &CameraComponent)>(inspect_entity) + .get() + { let is_active = self .active_camera .lock() @@ -126,8 +129,8 @@ pub struct ResourceInspectorDock; impl EditorTabDock for ResourceInspectorDock { fn desc() -> EditorTabDockDescriptor { - EditorTabDockDescriptor { - id: "inspector", + EditorTabDockDescriptor { + id: "inspector", title: "Resource Inspector".to_string(), visibility: EditorTabVisibility::GameEditor, } @@ -1,16 +1,19 @@ -use crate::editor::{DragState, EditorTabDock, EditorTabDockDescriptor, EditorTabViewer, Signal, TABS_GLOBAL, UndoableAction}; +use crate::editor::page::EditorTabVisibility; +use crate::editor::{ + DragState, EditorTabDock, EditorTabDockDescriptor, EditorTabViewer, Signal, TABS_GLOBAL, + UndoableAction, +}; use dropbear_engine::asset::ASSET_REGISTRY; use dropbear_engine::camera::Camera; use dropbear_engine::entity::{EntityTransform, MeshRenderer, Transform}; use dropbear_engine::lighting::Light; use eucalyptus_core::camera::CameraComponent; use eucalyptus_core::hierarchy::EntityTransformExt; +use eucalyptus_core::input::ndc::NormalisedDeviceCoordinates; use eucalyptus_core::utils::ViewportMode; use glam::DVec3; use hecs::Entity; use transform_gizmo_egui::{GizmoConfig, GizmoExt, GizmoOrientation}; -use eucalyptus_core::input::ndc::NormalisedDeviceCoordinates; -use crate::editor::page::EditorTabVisibility; impl<'a> EditorTabViewer<'a> { fn on_pointer_down(&mut self, touch_pos: [f32; 2], screen_size: [f32; 2], camera: &Camera) { @@ -25,8 +28,10 @@ impl<'a> EditorTabViewer<'a> { let mut closest: Option<(Entity, f32)> = None; - for (entity, transform, mesh) in - self.world.query::<(Entity, &EntityTransform, &MeshRenderer)>().iter() + for (entity, transform, mesh) in self + .world + .query::<(Entity, &EntityTransform, &MeshRenderer)>() + .iter() { let (aabb_min, aabb_max) = compute_world_aabb(transform, mesh); if let Some(t) = @@ -38,8 +43,10 @@ impl<'a> EditorTabViewer<'a> { } } - for (entity, transform, mesh) in - self.world.query::<(Entity, &Transform, &MeshRenderer)>().iter() + for (entity, transform, mesh) in self + .world + .query::<(Entity, &Transform, &MeshRenderer)>() + .iter() { let (aabb_min, aabb_max) = compute_transform_aabb(transform, mesh); if let Some(t) = @@ -69,8 +76,11 @@ impl<'a> EditorTabViewer<'a> { let plane_d = plane_normal.dot(hit_point); let pick_offset = hit_point - entity_origin; - let initial_entity_transform = - self.world.get::<&EntityTransform>(entity).ok().map(|et| *et); + let initial_entity_transform = self + .world + .get::<&EntityTransform>(entity) + .ok() + .map(|et| *et); let initial_transform = if initial_entity_transform.is_none() { self.world.get::<&Transform>(entity).ok().map(|tr| *tr) } else { @@ -167,7 +177,10 @@ impl<'a> EditorTabViewer<'a> { let desired_height = (available_size.y * pixels_per_point).max(1.0).round() as u32; if self.tex_size.width != desired_width || self.tex_size.height != desired_height { if self.signal.is_empty() { - self.signal.push_back(Signal::UpdateViewportSize((desired_width as f32, desired_height as f32))); + self.signal.push_back(Signal::UpdateViewportSize(( + desired_width as f32, + desired_height as f32, + ))); } } @@ -195,7 +208,6 @@ impl<'a> EditorTabViewer<'a> { // let (_rect, _response) = // ui.allocate_exact_size(available_size, egui::Sense::click_and_drag()); - let (_full, _) = ui.allocate_exact_size(available_size, egui::Sense::hover()); ui.painter().image( @@ -270,10 +282,7 @@ impl<'a> EditorTabViewer<'a> { let mut handled = false; let mut updated_light_transform: Option<Transform> = None; - if let Ok(mut entity_transform) = self - .world - .get::<&mut EntityTransform>(*entity_id) - { + if let Ok(mut entity_transform) = self.world.get::<&mut EntityTransform>(*entity_id) { let was_focused = cfg.is_focused; cfg.is_focused = self.gizmo.is_focused(); @@ -302,8 +311,8 @@ impl<'a> EditorTabViewer<'a> { let safe = |v: f64| if v.abs() < 1e-9 { 1.0 } else { v }; - let delta_pos = new_synced_pos - prev_world_pos; - let delta_rot = new_synced_rot * prev_world_rot.inverse(); + let delta_pos = new_synced_pos - prev_world_pos; + let delta_rot = new_synced_rot * prev_world_rot.inverse(); let delta_scale = glam::DVec3::new( new_synced_scale.x / safe(prev_world_scale.x), new_synced_scale.y / safe(prev_world_scale.y), @@ -314,11 +323,11 @@ impl<'a> EditorTabViewer<'a> { GizmoOrientation::Global => { let world = entity_transform.world_mut(); world.position += delta_pos; - world.rotation = delta_rot * world.rotation; - world.scale *= delta_scale; + world.rotation = delta_rot * world.rotation; + world.scale *= delta_scale; } GizmoOrientation::Local => { - let world_rot = entity_transform.world().rotation; + let world_rot = entity_transform.world().rotation; let world_scale = entity_transform.world().scale; let safe_ws = glam::DVec3::new( @@ -331,9 +340,10 @@ impl<'a> EditorTabViewer<'a> { local.position += world_rot.inverse() * delta_pos / safe_ws; - local.rotation = world_rot.inverse() * delta_rot * world_rot * local.rotation; + local.rotation = + world_rot.inverse() * delta_rot * world_rot * local.rotation; - local.scale *= delta_scale; + local.scale *= delta_scale; } } @@ -343,7 +353,8 @@ impl<'a> EditorTabViewer<'a> { if was_focused && !cfg.is_focused { if let Some(original) = cfg.entity_transform_original { if original != *entity_transform { - self.undo_stack.push(UndoableAction::EntityTransform(*entity_id, original)); + self.undo_stack + .push(UndoableAction::EntityTransform(*entity_id, original)); log::debug!("Pushed entity transform action to stack"); } } @@ -374,19 +385,18 @@ impl<'a> EditorTabViewer<'a> { { transform.position = new_transform.translation.into(); transform.rotation = new_transform.rotation.into(); - transform.scale = new_transform.scale.into(); + transform.scale = new_transform.scale.into(); updated_light_transform = Some(*transform); } if was_focused && !cfg.is_focused { let transform_changed = cfg.old_pos.position != transform.position || cfg.old_pos.rotation != transform.rotation - || cfg.old_pos.scale != transform.scale; + || cfg.old_pos.scale != transform.scale; if transform_changed { - self.undo_stack.push( - UndoableAction::Transform(*entity_id, cfg.old_pos) - ); + self.undo_stack + .push(UndoableAction::Transform(*entity_id, cfg.old_pos)); log::debug!("Pushed transform action to stack"); } } @@ -396,7 +406,7 @@ impl<'a> EditorTabViewer<'a> { if let Some(updated_transform) = updated_light_transform { if let Ok(mut light) = self.world.get::<&mut Light>(*entity_id) { let forward = DVec3::new(0.0, -1.0, 0.0); - light.component.position = updated_transform.position; + light.component.position = updated_transform.position; light.component.direction = (updated_transform.rotation * forward).normalize_or_zero(); } @@ -406,7 +416,10 @@ impl<'a> EditorTabViewer<'a> { } /// Compute a world-space AABB for an entity that has an [`EntityTransform`]. -fn compute_world_aabb(transform: &EntityTransform, mesh: &MeshRenderer) -> (glam::Vec3, glam::Vec3) { +fn compute_world_aabb( + transform: &EntityTransform, + mesh: &MeshRenderer, +) -> (glam::Vec3, glam::Vec3) { aabb_for_world_matrix(transform.world().matrix().as_mat4(), mesh) } @@ -418,10 +431,7 @@ fn compute_transform_aabb(transform: &Transform, mesh: &MeshRenderer) -> (glam:: /// Transform the model's local AABB by `world_mat` and return the resulting world AABB. /// /// If the model hasn't loaded yet a unit box centred on the origin is used as a fallback. -fn aabb_for_world_matrix( - world_mat: glam::Mat4, - mesh: &MeshRenderer, -) -> (glam::Vec3, glam::Vec3) { +fn aabb_for_world_matrix(world_mat: glam::Mat4, mesh: &MeshRenderer) -> (glam::Vec3, glam::Vec3) { use glam::Vec3; let (local_min, local_max) = { @@ -466,7 +476,6 @@ fn aabb_for_world_matrix( (world_min, world_max) } - pub struct ViewportDock; impl EditorTabDock for ViewportDock { @@ -481,4 +490,4 @@ impl EditorTabDock for ViewportDock { fn display(viewer: &mut EditorTabViewer<'_>, ui: &mut egui::Ui) { viewer.viewport_tab(ui); } -} +} @@ -199,7 +199,9 @@ impl Keyboard for Editor { } KeyCode::KeyV => { if ctrl_pressed && !is_playing { - if let Some(Signal::Copy(entities, parent_map)) = self.signal.iter().find(|s| matches!(s, Signal::Copy(_, _))) { + if let Some(Signal::Copy(entities, parent_map)) = + self.signal.iter().find(|s| matches!(s, Signal::Copy(_, _))) + { let entities = entities.clone(); let parent_map = parent_map.clone(); self.signal.push_back(Signal::Paste(entities, parent_map)); @@ -1,37 +1,41 @@ pub mod dock; +pub mod docks; pub mod input; +pub mod page; pub mod scene; pub mod settings; -pub mod page; pub mod ui; -pub mod docks; pub(crate) use crate::editor::dock::*; use crate::about::AboutWindow; use crate::build::build; use crate::debug; -use docks::console::EucalyptusConsole; -use crate::editor::settings::editor::{EditorSettingsWindow, EDITOR_SETTINGS}; +use crate::editor::page::EditorTabVisibility; +use crate::editor::settings::editor::{EDITOR_SETTINGS, EditorSettingsWindow}; use crate::editor::settings::project::ProjectSettingsWindow; +use crate::editor::ui::UiEditor; use crate::plugin::PluginRegistry; use crate::stats::NerdStats; -use crossbeam_channel::{unbounded, Receiver, Sender}; +use crossbeam_channel::{Receiver, Sender, unbounded}; +use docks::console::EucalyptusConsole; +use dropbear_engine::animation::{MAX_MORPH_WEIGHTS, MorphTargetInfo}; use dropbear_engine::billboarding::BillboardPipeline; use dropbear_engine::buffer::ResizableBuffer; use dropbear_engine::entity::EntityTransform; use dropbear_engine::graphics::InstanceRaw; use dropbear_engine::mipmap::MipMapper; +use dropbear_engine::multisampling::AntiAliasingMode; 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::sky::{HdrLoader, SkyPipeline, DEFAULT_SKY_TEXTURE}; +use dropbear_engine::sky::{DEFAULT_SKY_TEXTURE, HdrLoader, SkyPipeline}; use dropbear_engine::{ - camera::Camera, entity::Transform, future::FutureHandle, graphics::SharedGraphicsContext, scene::SceneCommand, - DropbearWindowBuilder, WindowData, + DropbearWindowBuilder, WindowData, camera::Camera, entity::Transform, future::FutureHandle, + graphics::SharedGraphicsContext, scene::SceneCommand, }; -use egui::{self, Context}; +use egui::{self, Context, Ui}; use egui_dock::{DockArea, DockState, NodeIndex, Style}; use eucalyptus_core::component::{ComponentRegistry, SerializedComponent}; use eucalyptus_core::hierarchy::{Children, SceneHierarchy}; @@ -41,14 +45,14 @@ 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 eucalyptus_core::{register_components, APP_INFO}; +use eucalyptus_core::{APP_INFO, register_components}; use eucalyptus_core::{ camera::{CameraComponent, CameraType, DebugCamera}, fatal, info, input::InputState, scripting::BuildStatus, states, - states::{WorldLoadingStatus, PROJECT, SCENES}, + states::{PROJECT, SCENES, WorldLoadingStatus}, success, utils::ViewportMode, warn, @@ -61,21 +65,17 @@ use kino_ui::windowing::KinoWinitWindowing; use log::{debug, error}; use parking_lot::{Mutex, RwLock}; use rfd::FileDialog; +use std::cmp::PartialEq; use std::collections::{HashSet, VecDeque}; use std::rc::Rc; use std::{collections::HashMap, path::PathBuf, sync::Arc, time::Instant}; -use std::cmp::PartialEq; use tokio::sync::oneshot; use transform_gizmo_egui::{EnumSet, Gizmo, GizmoMode, GizmoOrientation}; -use wgpu::{Color, Extent3d}; use wgpu::util::DeviceExt; +use wgpu::{Color, Extent3d}; use winit::dpi::PhysicalSize; -use dropbear_engine::multisampling::AntiAliasingMode; use winit::window::{CursorGrabMode, WindowAttributes}; use winit::{keyboard::KeyCode, window::Window}; -use dropbear_engine::animation::{MorphTargetInfo, MAX_MORPH_WEIGHTS}; -use crate::editor::page::EditorTabVisibility; -use crate::editor::ui::UiEditor; pub struct Editor { pub dt: f32, @@ -111,9 +111,17 @@ pub struct Editor { pub(crate) default_morph_info_buffer: Option<wgpu::Buffer>, pub(crate) default_animation_bind_group: Option<wgpu::BindGroup>, pub(crate) static_batches: HashMap<u64, Vec<(Entity, InstanceRaw)>>, - pub(crate) animated_instances: Vec<(Entity, u64, InstanceRaw, wgpu::Buffer, wgpu::Buffer, wgpu::Buffer, u32)>, + pub(crate) animated_instances: Vec<( + Entity, + u64, + InstanceRaw, + wgpu::Buffer, + wgpu::Buffer, + wgpu::Buffer, + u32, + )>, pub(crate) animated_bind_group_cache: HashMap<Entity, (u64, wgpu::BindGroup)>, - pub(crate) last_morph_info_per_mesh: HashMap<u32, MorphTargetInfo>, // key = morph_deltas_offset + pub(crate) last_morph_info_per_mesh: HashMap<u32, MorphTargetInfo>, // key = morph_deltas_offset pub active_camera: Arc<Mutex<Option<Entity>>>, @@ -224,8 +232,7 @@ impl Editor { let mut dock_state = DockState::new(tabs); let surface = dock_state.main_surface_mut(); - let [_old, right] = - surface.split_right(NodeIndex::root(), 0.25, vec![resource_tab]); + let [_old, right] = surface.split_right(NodeIndex::root(), 0.25, vec![resource_tab]); let [_old, _] = surface.split_left(NodeIndex::root(), 0.20, vec![entity_list_tab]); let [_old, _] = surface.split_below(right, 0.5, vec![asset_tab]); @@ -428,11 +435,25 @@ impl Editor { .unwrap_or_default(); for child in children { - visit(world, child, registry, Some(&own_label), entities, parent_map); + visit( + world, + child, + registry, + Some(&own_label), + entities, + parent_map, + ); } } - visit(world, entity, registry, None, &mut entities, &mut parent_map); + visit( + world, + entity, + registry, + None, + &mut entities, + &mut parent_map, + ); (entities, parent_map) } @@ -540,7 +561,7 @@ impl Editor { pub fn save_project_config(&mut self) -> anyhow::Result<()> { log::debug!("starting save of project config"); self.save_current_scene()?; - + { log::debug!("Writing to editor settings"); let mut config = EDITOR_SETTINGS.write(); @@ -919,7 +940,7 @@ impl Editor { self.queue_scene_load_from_path(&path) } - pub fn show_ui(&mut self, ctx: &Context, graphics: Arc<SharedGraphicsContext>) { + pub fn show_ui(&mut self, ui: &mut Ui, graphics: Arc<SharedGraphicsContext>) { puffin::profile_function!(); { @@ -933,7 +954,7 @@ impl Editor { } } - egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| { + egui::Panel::top("menu_bar").show_inside(ui, |ui| { puffin::profile_scope!("show_ui.top_bar"); let top_bar_rect = ui.max_rect(); @@ -1292,10 +1313,10 @@ impl Editor { }); }); - egui::TopBottomPanel::bottom("status bar") + egui::Panel::bottom("status bar") .resizable(false) - .show(ctx, |ui| { - puffin::profile_scope!("show_ui.status_bar"); + .show_inside(ui, |ui| { + puffin::profile_scope!("show_ui.status_bar"); let active_camera = *self.active_camera.lock(); let (label, is_debug, active_entity) = if let Some(entity) = active_camera { let camera_label = self @@ -1355,7 +1376,10 @@ impl Editor { if let Ok(l) = label { ui.colored_label(text_color, format!("Editing {l}")); } else { - ui.colored_label(egui::Color32::from_rgb(255, 0, 0), format!("error: unable to fetch label for entity {:?}", e)); + ui.colored_label( + egui::Color32::from_rgb(255, 0, 0), + format!("error: unable to fetch label for entity {:?}", e), + ); } } else { ui.label("Not editing anything"); @@ -1365,7 +1389,7 @@ impl Editor { }); let Some(view) = self.texture_id else { - egui::CentralPanel::default().show(ctx, |ui| { + egui::CentralPanel::default().show_inside(ui, |ui| { puffin::profile_scope!("show_ui.viewport_initialising"); ui.centered_and_justified(|ui| { ui.label("Viewport is still initialising..."); @@ -1374,49 +1398,52 @@ impl Editor { return; }; - egui::CentralPanel::default().show(ctx, |ui| { + egui::CentralPanel::default().show_inside(ui, |ui| { puffin::profile_scope!("show_ui.dock_area"); - DockArea::new(if self.current_page.contains(EditorTabVisibility::GameEditor) { - &mut self.game_editor_dock_state - } else if self.current_page.contains(EditorTabVisibility::UIEditor) { - &mut self.ui_editor_dock_state - } else { - panic!("Unable to locate create dock area: current page not set, likely editor bug"); - }) - .style(Style::from_egui(ui.style().as_ref())) - .show_inside( - ui, - &mut EditorTabViewer { - view, - graphics: graphics.clone(), - gizmo: &mut self.gizmo, - tex_size: self.size, - world: &mut self.world, - selected_entity: &mut self.selected_entity, - viewport_mode: &mut self.viewport_mode, - undo_stack: &mut self.undo_stack, - signal: &mut self.signal, - active_camera: &mut self.active_camera, - gizmo_mode: &mut self.gizmo_mode, - gizmo_orientation: &mut self.gizmo_orientation, - editor_mode: &mut self.editor_state, - plugin_registry: &mut self.plugin_registry, - build_logs: &mut self.build_logs, - component_registry: &self.component_registry, - tab_registry: &self.tab_registry, - eucalyptus_console: &mut self.console, - current_scene_name: &mut self.current_scene_name, - ui_editor: &mut self.ui_editor, - viewport_drag: &mut self.viewport_drag, - }, - ); - + DockArea::new( + if self.current_page.contains(EditorTabVisibility::GameEditor) { + &mut self.game_editor_dock_state + } else if self.current_page.contains(EditorTabVisibility::UIEditor) { + &mut self.ui_editor_dock_state + } else { + panic!( + "Unable to locate create dock area: current page not set, likely editor bug" + ); + }, + ) + .style(Style::from_egui(ui.style().as_ref())) + .show_inside( + ui, + &mut EditorTabViewer { + view, + graphics: graphics.clone(), + gizmo: &mut self.gizmo, + tex_size: self.size, + world: &mut self.world, + selected_entity: &mut self.selected_entity, + viewport_mode: &mut self.viewport_mode, + undo_stack: &mut self.undo_stack, + signal: &mut self.signal, + active_camera: &mut self.active_camera, + gizmo_mode: &mut self.gizmo_mode, + gizmo_orientation: &mut self.gizmo_orientation, + editor_mode: &mut self.editor_state, + plugin_registry: &mut self.plugin_registry, + build_logs: &mut self.build_logs, + component_registry: &self.component_registry, + tab_registry: &self.tab_registry, + eucalyptus_console: &mut self.console, + current_scene_name: &mut self.current_scene_name, + ui_editor: &mut self.ui_editor, + viewport_drag: &mut self.viewport_drag, + }, + ); }); { let mut project_path = self.project_path.lock(); crate::utils::show_new_project_window( - ctx, + &graphics.get_egui_context(), &mut self.show_new_project, &mut self.project_name, &mut project_path, @@ -1432,14 +1459,14 @@ impl Editor { self.pending_scene_switch = false; } - self.show_nerd_stats_window(ctx); + self.show_nerd_stats_window(&graphics.get_egui_context()); let mut open_flag = self.open_new_scene_window; let mut close_requested = false; if open_flag { egui::Window::new("New Scene") .open(&mut open_flag) - .show(ctx, |ui| { + .show(&graphics.get_egui_context(), |ui| { ui.vertical(|ui| { ui.label("Name: "); ui.text_edit_singleline(&mut self.new_scene_name); @@ -1450,14 +1477,10 @@ impl Editor { let project = PROJECT.read(); project.project_path.join("resources").join("scenes") }; - let target_dir = self - .new_scene_target_dir - .as_deref() - .unwrap_or(&scenes_root); + let target_dir = + self.new_scene_target_dir.as_deref().unwrap_or(&scenes_root); let display_path = target_dir - .strip_prefix( - PROJECT.read().project_path.as_path(), - ) + .strip_prefix(PROJECT.read().project_path.as_path()) .unwrap_or(target_dir) .display() .to_string(); @@ -1474,9 +1497,7 @@ impl Editor { self.new_scene_target_dir = Some(dir); } } - if self.new_scene_target_dir.is_some() - && ui.button("Reset").clicked() - { + if self.new_scene_target_dir.is_some() && ui.button("Reset").clicked() { self.new_scene_target_dir = None; } }); @@ -1484,12 +1505,8 @@ impl Editor { ui.add_space(4.0); if ui.button("Create").clicked() { - let dir = self - .new_scene_target_dir - .clone() - .unwrap_or(scenes_root); - self.pending_scene_creation = - Some((self.new_scene_name.clone(), dir)); + let dir = self.new_scene_target_dir.clone().unwrap_or(scenes_root); + self.pending_scene_creation = Some((self.new_scene_name.clone(), dir)); close_requested = true; } }); @@ -1593,7 +1610,8 @@ impl Editor { self.collider_wireframe_pipeline = Some(ColliderWireframePipeline::new(graphics.clone())); self.mipmapper = None; self.billboard_pipeline = Some(BillboardPipeline::new(graphics.clone())); - *graphics.debug_draw.lock() = Some(dropbear_engine::debug::DebugDraw::new(graphics.clone())); + *graphics.debug_draw.lock() = + Some(dropbear_engine::debug::DebugDraw::new(graphics.clone())); self.kino = Some(KinoState::new( KinoWGPURenderer::new( &graphics.device, @@ -1624,31 +1642,43 @@ impl Editor { if let Ok(camera) = self.world.query_one::<&Camera>(camera_entity).get() { let max_skinning_matrices = 256usize; let identity = vec![Mat4::IDENTITY; max_skinning_matrices]; - let skinning_buffer = graphics.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("editor default skinning buffer"), - contents: bytemuck::cast_slice(&identity), - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - }); + let skinning_buffer = + graphics + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("editor default skinning buffer"), + contents: bytemuck::cast_slice(&identity), + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + }); - let morph_deltas_buffer = graphics.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("editor default morph deltas buffer"), - contents: bytemuck::cast_slice(&[0.0f32]), - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - }); + let morph_deltas_buffer = + graphics + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("editor default morph deltas buffer"), + contents: bytemuck::cast_slice(&[0.0f32]), + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + }); let morph_weights = vec![0.0f32; MAX_MORPH_WEIGHTS]; - let morph_weights_buffer = graphics.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("editor default morph weights buffer"), - contents: bytemuck::cast_slice(&morph_weights), - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - }); + let morph_weights_buffer = + graphics + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("editor default morph weights buffer"), + contents: bytemuck::cast_slice(&morph_weights), + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + }); let morph_info = dropbear_engine::animation::MorphTargetInfo::default(); - let morph_info_buffer = graphics.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("editor default morph info buffer"), - contents: bytemuck::bytes_of(&morph_info), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - }); + let morph_info_buffer = + graphics + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("editor default morph info buffer"), + contents: bytemuck::bytes_of(&morph_info), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); self.default_skinning_buffer = Some(skinning_buffer); self.default_morph_deltas_buffer = Some(morph_deltas_buffer); @@ -1672,28 +1702,30 @@ impl Editor { .as_ref() .expect("Default morph info buffer missing"); - self.default_animation_bind_group = Some(graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("editor default animation bind group"), - layout: &graphics.layouts.animation_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: skinning_buffer.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: morph_deltas_buffer.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: morph_weights_buffer.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 3, - resource: morph_info_buffer.as_entire_binding(), - }, - ], - })); + self.default_animation_bind_group = Some(graphics.device.create_bind_group( + &wgpu::BindGroupDescriptor { + label: Some("editor default animation bind group"), + layout: &graphics.layouts.animation_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: skinning_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: morph_deltas_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: morph_weights_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: morph_info_buffer.as_entire_binding(), + }, + ], + }, + )); if let Some(main_pipeline) = self.main_render_pipeline.as_mut() { if let (Some(globals), Some(light_pipeline)) = ( @@ -1897,8 +1929,8 @@ pub enum Signal { /// Adds a new component instance using the async init pipeline. AddComponent(hecs::Entity, Box<dyn SerializedComponent>), RequestNewWindow(WindowData), - ReloadWGPUData{ - skybox_texture: Option<Vec<u8>> + ReloadWGPUData { + skybox_texture: Option<Vec<u8>>, }, UpdateViewportSize((f32, f32)), } @@ -8,4 +8,4 @@ bitflags! { /// The editor for UI const UIEditor = 1 << 1; } -} +} @@ -19,22 +19,23 @@ 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, SCENES}; +use eucalyptus_core::states::{Label, SCENES, WorldLoadingStatus}; use eucalyptus_core::ui::HUDComponent; use hecs::Entity; +use kino_ui::rendering::KinoRenderTargetId; use log; use magna_carta::ScriptManifest; use parking_lot::Mutex; use std::collections::HashMap; +use std::hash::{DefaultHasher, Hash, Hasher}; use std::sync::Arc; use std::{ fs, path::{Path, PathBuf}, }; -use std::hash::{DefaultHasher, Hash, Hasher}; -use winit::{event::WindowEvent, event_loop::ActiveEventLoop, keyboard::KeyCode}; +use egui::UiBuilder; use winit::event::{MouseScrollDelta, TouchPhase}; -use kino_ui::rendering::KinoRenderTargetId; +use winit::{event::WindowEvent, event_loop::ActiveEventLoop, keyboard::KeyCode}; impl Scene for Editor { fn load(&mut self, graphics: Arc<SharedGraphicsContext>) { @@ -48,12 +49,17 @@ impl Scene for Editor { let mut processor = match magna_carta::KotlinProcessor::new() { Ok(p) => p, Err(e) => { - log::warn!("Failed to create KotlinProcessor for component scan: {}", e); + log::warn!( + "Failed to create KotlinProcessor for component scan: {}", + e + ); return; } }; let mut manifest = ScriptManifest::new(); - if let Err(e) = magna_carta::visit_kotlin_files(&src_path, &mut processor, &mut manifest) { + if let Err(e) = + magna_carta::visit_kotlin_files(&src_path, &mut processor, &mut manifest) + { log::warn!("Kotlin component scan failed: {}", e); } else { let count = manifest.components().len(); @@ -65,10 +71,15 @@ impl Scene for Editor { description: None, }); } - log::info!("Registered {} Kotlin component descriptor(s) from project sources", count); + log::info!( + "Registered {} Kotlin component descriptor(s) from project sources", + count + ); } } else { - log::warn!("Could not obtain exclusive access to component_registry for Kotlin component scan"); + log::warn!( + "Could not obtain exclusive access to component_registry for Kotlin component scan" + ); } } } @@ -489,6 +500,7 @@ impl Scene for Editor { }), occlusion_query_set: None, timestamp_writes: None, + multiview_mask: None, }); } @@ -547,8 +559,8 @@ impl Scene for Editor { let instance = renderer.instance.to_raw(); if let Some(animation) = animation { - let has_skinning = !animation.skinning_matrices.is_empty(); - let has_morph = !animation.morph_weights.is_empty(); + let has_skinning = !animation.skinning_matrices.is_empty(); + let has_morph = !animation.morph_weights.is_empty(); if !has_skinning && !has_morph { self.static_batches @@ -639,19 +651,20 @@ impl Scene for Editor { }; let entity = batched_instances.first().map(|(e, _)| *e); - let instances: Vec<InstanceRaw> = batched_instances - .iter() - .map(|(_, inst)| *inst) - .collect(); - - 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 instances: Vec<InstanceRaw> = + batched_instances.iter().map(|(_, inst)| *inst).collect(); + + 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); model_cache.insert(*handle, model.clone()); @@ -694,6 +707,7 @@ impl Scene for Editor { }), occlusion_query_set: None, timestamp_writes: None, + multiview_mask: None, }); render_pass.set_pipeline(light_pipeline.pipeline()); @@ -747,7 +761,9 @@ impl Scene for Editor { for (model, handle, instance_count, entity) in prepared_models { let Some(entity) = entity else { continue }; - let Ok(renderer) = self.world.get::<&MeshRenderer>(entity) else { continue }; + let Ok(renderer) = self.world.get::<&MeshRenderer>(entity) else { + continue; + }; let morph_deltas_buffer = model .morph_deltas_buffer @@ -787,10 +803,13 @@ impl Scene for Editor { }), occlusion_query_set: None, timestamp_writes: None, + multiview_mask: None, }); render_pass.set_pipeline(pipeline.pipeline()); - let Some(instance_buffer) = self.instance_buffer_cache.get(&handle) else { continue }; + let Some(instance_buffer) = self.instance_buffer_cache.get(&handle) else { + continue; + }; render_pass.set_vertex_buffer(1, instance_buffer.slice(instance_count as usize)); for mesh in &model.meshes { @@ -814,20 +833,24 @@ impl Scene for Editor { num_targets: mesh.morph_target_count, base_offset: mesh.morph_deltas_offset, weight_offset: 0, - uses_morph: if mesh.morph_target_count > 0 && !weights.is_empty() { 1 } else { 0 }, + uses_morph: if mesh.morph_target_count > 0 && !weights.is_empty() { + 1 + } else { + 0 + }, _padding: Default::default(), }; let cache_key = mesh.morph_deltas_offset; - let needs_write = self - .last_morph_info_per_mesh - .get(&cache_key) - .map_or(true, |prev| { - prev.num_vertices != info.num_vertices - || prev.num_targets != info.num_targets - || prev.base_offset != info.base_offset - || prev.uses_morph != info.uses_morph - }); + let needs_write = + self.last_morph_info_per_mesh + .get(&cache_key) + .map_or(true, |prev| { + prev.num_vertices != info.num_vertices + || prev.num_targets != info.num_targets + || prev.base_offset != info.base_offset + || prev.uses_morph != info.uses_morph + }); if needs_write { graphics.queue.write_buffer( @@ -839,12 +862,13 @@ impl Scene for Editor { } let material = &model.materials[mesh.material]; - let material = if let Some(mat) = renderer.material_snapshot.get(&material.name) { + let material = if let Some(mat) = renderer.material_snapshot.get(&material.name) + { mat } else { log_once::warn_once!( - "Unable to locate MeshRenderer's material_snapshot for that specific material" - ); + "Unable to locate MeshRenderer's material_snapshot for that specific material" + ); material }; @@ -870,9 +894,7 @@ impl Scene for Editor { .expect("Per-frame bind group not initialised") .clone(); - for (entity, _, instance, _, _, _, _) - in &self.animated_instances - { + for (entity, _, instance, _, _, _, _) in &self.animated_instances { let instance_buffer = self .animated_instance_buffers .entry(*entity) @@ -887,10 +909,19 @@ impl Scene for Editor { instance_buffer.write(&graphics.device, &graphics.queue, &[*instance]); } - for (entity, handle, _, skinning_buffer, morph_weights_buffer, morph_info_buffer, morph_weight_count) - in &self.animated_instances + for ( + entity, + handle, + _, + skinning_buffer, + morph_weights_buffer, + morph_info_buffer, + morph_weight_count, + ) in &self.animated_instances { - let Ok(renderer) = self.world.get::<&MeshRenderer>(*entity) else { continue }; + let Ok(renderer) = self.world.get::<&MeshRenderer>(*entity) else { + continue; + }; puffin::profile_scope!("rendering animated model", format!("{:?}", entity)); { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { @@ -914,6 +945,7 @@ impl Scene for Editor { }), occlusion_query_set: None, timestamp_writes: None, + multiview_mask: None, }); render_pass.set_pipeline(pipeline.pipeline()); @@ -945,12 +977,15 @@ impl Scene for Editor { morph_weights_buffer, morph_info_buffer, ); - self.animated_bind_group_cache.insert(*entity, (bind_group_stamp, bg)); + self.animated_bind_group_cache + .insert(*entity, (bind_group_stamp, bg)); } &self.animated_bind_group_cache[entity].1 }; - let Some(instance_buffer) = self.animated_instance_buffers.get(entity) else { continue }; + let Some(instance_buffer) = self.animated_instance_buffers.get(entity) else { + continue; + }; render_pass.set_vertex_buffer(1, instance_buffer.slice(1)); for mesh in &model.meshes { @@ -965,20 +1000,23 @@ impl Scene for Editor { _padding: Default::default(), }; - graphics - .queue - .write_buffer(morph_info_buffer, 0, bytemuck::bytes_of(&info)); + graphics.queue.write_buffer( + morph_info_buffer, + 0, + bytemuck::bytes_of(&info), + ); let material = &model.materials[mesh.material]; - let material = - if let Some(mat) = renderer.material_snapshot.get(&material.name) { - mat - } else { - log_once::warn_once!( - "Unable to locate MeshRenderer's material_snapshot for that specific material" - ); - material - }; + let material = if let Some(mat) = + renderer.material_snapshot.get(&material.name) + { + mat + } else { + log_once::warn_once!( + "Unable to locate MeshRenderer's material_snapshot for that specific material" + ); + material + }; render_pass.draw_mesh_instanced( mesh, @@ -1017,6 +1055,7 @@ impl Scene for Editor { }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); render_pass.set_pipeline(&sky.pipeline); @@ -1064,6 +1103,7 @@ impl Scene for Editor { }), occlusion_query_set: None, timestamp_writes: None, + multiview_mask: None, }); render_pass.set_pipeline(&collider_pipeline.pipeline); @@ -1159,12 +1199,9 @@ impl Scene for Editor { { puffin::profile_scope!("rendering billboard targets"); if let Some(kino) = &mut self.kino { - let mut kino_encoder = CommandEncoder::new(graphics.clone(), Some("kino billboard encoder")); - kino.render_billboard_targets( - &graphics.device, - &graphics.queue, - &mut kino_encoder, - ); + let mut kino_encoder = + CommandEncoder::new(graphics.clone(), Some("kino billboard encoder")); + kino.render_billboard_targets(&graphics.device, &graphics.queue, &mut kino_encoder); if let Err(e) = kino_encoder.submit() { log_once::error_once!("Unable to submit billboard kino pass: {}", e); @@ -1233,7 +1270,8 @@ impl Scene for Editor { } }; - let transform = Mat4::from_scale_rotation_translation(scale, rotation, position); + let transform = + Mat4::from_scale_rotation_translation(scale, rotation, position); billboards.push((transform, texture_view)); } @@ -1260,6 +1298,7 @@ impl Scene for Editor { }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); for (transform, texture_view) in billboards { @@ -1398,8 +1437,14 @@ impl Editor { Some("kt") | Some("eucp") | Some("eucs") ); if is_resource { - if let Err(e) = eucalyptus_core::metadata::generate_eucmeta(&target_path, &project_root) { - log::warn!("Failed to generate .eucmeta for '{}': {}", target_path.display(), e); + if let Err(e) = + eucalyptus_core::metadata::generate_eucmeta(&target_path, &project_root) + { + log::warn!( + "Failed to generate .eucmeta for '{}': {}", + target_path.display(), + e + ); } } } @@ -1450,7 +1495,9 @@ impl Editor { 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()); + let mut ui = Ui::new(graphics.get_egui_context(), egui::Id::new("ui"), UiBuilder::default()); + + self.show_ui(&mut ui, graphics.clone()); + eucalyptus_core::logging::render(&mut ui); } } @@ -29,7 +29,10 @@ impl<'a> EditorTabViewer<'a> { ui.label("Renders collider wireframes for debugging"); let mut overlay_billboard = scene.settings.overlay_billboard; - if ui.checkbox(&mut overlay_billboard, "Overlay Billboard UI").changed() { + if ui + .checkbox(&mut overlay_billboard, "Overlay Billboard UI") + .changed() + { scene.settings.overlay_billboard = overlay_billboard; } ui.label("Renders billboard UI widgets for all entities"); @@ -43,7 +46,10 @@ impl<'a> EditorTabViewer<'a> { ui.separator(); ui.label("Ambient Strength"); let mut ambient = scene.settings.ambient_strength; - if ui.add(egui::Slider::new(&mut ambient, 0.0..=2.0).step_by(0.01)).changed() { + if ui + .add(egui::Slider::new(&mut ambient, 0.0..=2.0).step_by(0.01)) + .changed() + { scene.settings.ambient_strength = ambient; } ui.label("Controls the intensity of ambient/IBL lighting"); @@ -1,13 +1,14 @@ //! The scene for a window that opens up settings related to the eucalyptus-editor. +use crate::editor::EditorTabId; use app_dirs2::AppDataType; use dropbear_engine::input::{Controller, Keyboard, Mouse}; +use dropbear_engine::multisampling::AntiAliasingMode; use dropbear_engine::scene::{Scene, SceneCommand}; use egui::{CentralPanel, ComboBox, Id, Slider, SliderClamping}; use egui_dock::DockState; use egui_ltreeview::{Action, NodeBuilder}; use eucalyptus_core::input::InputState; -use crate::editor::EditorTabId; use eucalyptus_core::utils::option::HistoricalOption; use eucalyptus_core::{APP_INFO, warn}; use gilrs::{Button, GamepadId}; @@ -19,7 +20,6 @@ use winit::event::MouseButton; use winit::event_loop::ActiveEventLoop; use winit::keyboard::KeyCode; use winit::window::WindowId; -use dropbear_engine::multisampling::AntiAliasingMode; pub static EDITOR_SETTINGS: Lazy<RwLock<EditorSettings>> = Lazy::new(|| RwLock::new(EditorSettings::new())); @@ -29,10 +29,10 @@ pub static EDITOR_SETTINGS: Lazy<RwLock<EditorSettings>> = /// This is not related to a project, and is for each user who uses the editor. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct EditorSettings { - /// The layout of the dock within the game editor page. + /// The layout of the dock within the game editor page. #[serde(default)] pub game_editor_dock_state: Option<DockState<EditorTabId>>, - + /// The layout of the dock within the UI editor #[serde(default)] pub ui_editor_dock_state: Option<DockState<EditorTabId>>, @@ -236,14 +236,21 @@ impl Scene for EditorSettingsWindow { ComboBox::from_id_salt("anti-aliasing-mode-combobox") .selected_text(match editor.anti_aliasing_mode { AntiAliasingMode::None => "None", - AntiAliasingMode::MSAA4 => "MSAA4" + AntiAliasingMode::MSAA4 => "MSAA4", }) .show_ui(ui, |ui| { - ui.selectable_value(&mut editor.anti_aliasing_mode, AntiAliasingMode::None, "None"); - ui.selectable_value(&mut editor.anti_aliasing_mode, AntiAliasingMode::MSAA4, "MSAA4"); + ui.selectable_value( + &mut editor.anti_aliasing_mode, + AntiAliasingMode::None, + "None", + ); + ui.selectable_value( + &mut editor.anti_aliasing_mode, + AntiAliasingMode::MSAA4, + "MSAA4", + ); }); } - } _ => {} }); @@ -1,10 +1,8 @@ -use egui::Ui; -use crate::editor::{EditorTabDock, EditorTabDockDescriptor, EditorTabViewer}; use crate::editor::page::EditorTabVisibility; +use crate::editor::{EditorTabDock, EditorTabDockDescriptor, EditorTabViewer}; +use egui::Ui; -pub struct UIInspector { - -} +pub struct UIInspector {} impl EditorTabDock for UIInspector { fn desc() -> EditorTabDockDescriptor { @@ -18,4 +16,4 @@ impl EditorTabDock for UIInspector { fn display(_viewer: &mut EditorTabViewer<'_>, ui: &mut Ui) { ui.label("Not implemented yet."); } -} +} @@ -1,13 +1,13 @@ -use std::sync::Arc; use bytemuck::{Pod, Zeroable}; use dropbear_engine::graphics::SharedGraphicsContext; use egui::TextureId; use glam::{Mat4, Vec2, Vec4}; use kino_ui::camera::Camera2D; +use std::sync::Arc; use wgpu::TextureFormat; -pub mod viewport; pub mod inspector; +pub mod viewport; pub mod widget_tree; pub struct UiEditor { @@ -34,9 +34,7 @@ impl UiEditor { } } - pub fn update(&mut self) { - - } + pub fn update(&mut self) {} pub fn render(&mut self, graphics: Arc<SharedGraphicsContext>, width: u32, height: u32) { if self.grid_pipeline.is_none() { @@ -74,14 +72,7 @@ impl UiEditor { let half_h = height / (2.0 * self.zoom.max(0.1)); let view = Mat4::from_translation((-self.camera_position).extend(0.0)); - let proj = Mat4::orthographic_rh( - -half_w, - half_w, - half_h, - -half_h, - -1.0, - 1.0, - ); + let proj = Mat4::orthographic_rh(-half_w, half_w, half_h, -half_h, -1.0, 1.0); proj * view } @@ -102,7 +93,9 @@ impl UiEditor { } pub fn texture_id(&self) -> Option<TextureId> { - self.grid_pipeline.as_ref().and_then(|pipeline| pipeline.texture_id) + self.grid_pipeline + .as_ref() + .and_then(|pipeline| pipeline.texture_id) } fn setup(&mut self, graphics: Arc<SharedGraphicsContext>) { @@ -133,21 +126,22 @@ impl UIGridPipeline { .device .create_shader_module(wgpu::include_wgsl!("shader/grid.wgsl")); - let camera_bind_group_layout = graphics - .device - .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("ui grid camera bind group layout"), - entries: &[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, - }], - }); + let camera_bind_group_layout = + graphics + .device + .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("ui grid camera bind group layout"), + entries: &[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, + }], + }); let camera_buffer = graphics.device.create_buffer(&wgpu::BufferDescriptor { label: Some("ui grid camera uniform buffer"), @@ -156,61 +150,67 @@ impl UIGridPipeline { mapped_at_creation: false, }); - let camera_bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("ui grid camera bind group"), - layout: &camera_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: camera_buffer.as_entire_binding(), - }], - }); + let camera_bind_group = graphics + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("ui grid camera bind group"), + layout: &camera_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: camera_buffer.as_entire_binding(), + }], + }); - let layout = graphics.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("ui grid pipeline layout"), - bind_group_layouts: &[&camera_bind_group_layout], - push_constant_ranges: &[], - }); + let layout = graphics + .device + .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("ui grid pipeline layout"), + bind_group_layouts: &[Some(&camera_bind_group_layout)], + immediate_size: 0, + }); let sample_count: u32 = (*graphics.antialiasing.read()).into(); let format = graphics.surface_format.add_srgb_suffix(); - let pipeline = graphics.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("ui grid pipeline"), - layout: Some(&layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: Some("vs_main"), - compilation_options: Default::default(), - buffers: &[], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - polygon_mode: wgpu::PolygonMode::Fill, - unclipped_depth: false, - conservative: false, - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: sample_count, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: Some("fs_main"), - compilation_options: Default::default(), - targets: &[Some(wgpu::ColorTargetState { - format, - blend: Some(wgpu::BlendState::ALPHA_BLENDING), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - multiview: None, - cache: None, - }); + let pipeline = graphics + .device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("ui grid pipeline"), + layout: Some(&layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: Some("vs_main"), + compilation_options: Default::default(), + buffers: &[], + }, + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: sample_count, + mask: !0, + alpha_to_coverage_enabled: false, + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: Some("fs_main"), + compilation_options: Default::default(), + targets: &[Some(wgpu::ColorTargetState { + format, + blend: Some(wgpu::BlendState::ALPHA_BLENDING), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + cache: None, + multiview_mask: None, + }); Self { size: wgpu::Extent3d { @@ -324,11 +324,7 @@ impl UIGridPipeline { .egui_renderer .lock() .renderer() - .register_native_texture( - &graphics.device, - &resolve_view, - wgpu::FilterMode::Linear, - ); + .register_native_texture(&graphics.device, &resolve_view, wgpu::FilterMode::Linear); self.texture_id = Some(texture_id); } @@ -396,6 +392,7 @@ impl UIGridPipeline { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); self.render(&mut render_pass); @@ -418,4 +415,4 @@ struct UIGridCameraUniform { inv_view_proj: [[f32; 4]; 4], viewport_size: [f32; 2], _padding: [f32; 2], -} +} @@ -1,7 +1,7 @@ +use crate::editor::page::EditorTabVisibility; +use crate::editor::{EditorTabDock, EditorTabDockDescriptor, EditorTabViewer}; use egui::Ui; use glam::Vec2; -use crate::editor::{EditorTabDock, EditorTabDockDescriptor, EditorTabViewer}; -use crate::editor::page::EditorTabVisibility; pub struct UICanvas; @@ -44,7 +44,7 @@ impl EditorTabDock for UICanvas { } // scroll wheel - let scroll_y = i.raw_scroll_delta.y; + let scroll_y = i.smooth_scroll_delta.y; let is_trackpad_panning = i.smooth_scroll_delta != egui::Vec2::ZERO; if scroll_y.abs() > 0.0 && !is_trackpad_panning { viewer.ui_editor.zoom_by(scroll_y * 0.0025); @@ -67,10 +67,8 @@ impl EditorTabDock for UICanvas { if response.hovered() && is_middle_down { let pointer_delta_points = ui.ctx().input(|i| i.pointer.delta()); if pointer_delta_points != egui::Vec2::ZERO { - let delta_pixels = Vec2::new( - pointer_delta_points.x * ppp, - pointer_delta_points.y * ppp, - ); + let delta_pixels = + Vec2::new(pointer_delta_points.x * ppp, pointer_delta_points.y * ppp); viewer.ui_editor.pan_by_pixels(delta_pixels); } } @@ -78,22 +76,19 @@ impl EditorTabDock for UICanvas { let cursor_info = response.hover_pos().map(|hover_pos| { let local = hover_pos - response.rect.min; let pixel = Vec2::new(local.x * ppp, local.y * ppp); - viewer.ui_editor.world_from_screen_pixels(pixel, viewport_pixels) + viewer + .ui_editor + .world_from_screen_pixels(pixel, viewport_pixels) }); let zoom_percent = viewer.ui_editor.zoom() * 100.0; let hud = if let Some(world) = cursor_info { format!( "Coords: ({:.1}, {:.1}) u | zoom: {:.0}%", - world.x, - world.y, - zoom_percent + world.x, world.y, zoom_percent ) } else { - format!( - "Coords: (-, -) u | zoom: {:.0}%", - zoom_percent - ) + format!("Coords: (-, -) u | zoom: {:.0}%", zoom_percent) }; let text_pos = response.rect.left_top() + egui::vec2(8.0, 8.0); @@ -110,4 +105,4 @@ impl EditorTabDock for UICanvas { }); } } -} +} @@ -1,10 +1,8 @@ -use egui::Ui; -use crate::editor::{EditorTabDock, EditorTabDockDescriptor, EditorTabViewer}; use crate::editor::page::EditorTabVisibility; +use crate::editor::{EditorTabDock, EditorTabDockDescriptor, EditorTabViewer}; +use egui::Ui; -pub struct UIWidgetTree { - -} +pub struct UIWidgetTree {} impl EditorTabDock for UIWidgetTree { fn desc() -> EditorTabDockDescriptor { @@ -18,4 +16,4 @@ impl EditorTabDock for UIWidgetTree { fn display(_viewer: &mut EditorTabViewer<'_>, ui: &mut Ui) { ui.label("Not implemented yet."); } -} +} @@ -18,11 +18,9 @@ use editor::docks::resource::ResourceInspectorDock; use editor::docks::viewport::ViewportDock; pub use redback_runtime as runtime; -use crate::editor::{ - dock::ConsoleDock, ui::viewport::UICanvas, EditorTabRegistry -}; use crate::editor::ui::inspector::UIInspector; use crate::editor::ui::widget_tree::UIWidgetTree; +use crate::editor::{EditorTabRegistry, dock::ConsoleDock, ui::viewport::UICanvas}; dropbear_engine::features! { pub mod features { @@ -210,8 +210,9 @@ async fn main() -> anyhow::Result<()> { if let Err(e) = EditorSettings::read() { panic!( "Unable to launch eucalyptus-editor: {} - \nTry deleting your editor.eucc file located at {:?}", - e, app_dirs2::app_root(app_dirs2::AppDataType::UserData, &APP_INFO)? + \nTry deleting your editor.eucc file located at {:?}", + e, + app_dirs2::app_root(app_dirs2::AppDataType::UserData, &APP_INFO)? ); } @@ -6,7 +6,7 @@ use dropbear_engine::{ input::{Controller, Keyboard, Mouse}, scene::{Scene, SceneCommand}, }; -use egui::{self, FontId, Frame, RichText}; +use egui::{self, FontId, Frame, RichText, UiBuilder}; use egui_toast::{ToastOptions, Toasts}; use eucalyptus_core::config::ProjectConfig; use eucalyptus_core::states::PROJECT; @@ -302,6 +302,8 @@ impl Scene for MainMenu { ProjectProgress::Done => {} } } + + let mut ui = egui::Ui::new(graphics.get_egui_context(), egui::Id::new("main menu ui"), UiBuilder::default()); let screen_size: (f32, f32) = ( graphics.window.inner_size().width as f32 - 100.0, @@ -313,7 +315,7 @@ impl Scene for MainMenu { egui::CentralPanel::default() .frame(Frame::new()) - .show(&egui_ctx, |ui| { + .show_inside(&mut ui, |ui| { ui.vertical_centered(|ui| { ui.add_space(64.0); ui.label(RichText::new("Eucalyptus").font(FontId::proportional(32.0))); @@ -506,7 +508,7 @@ impl Scene for MainMenu { }); } - self.toast.show(&egui_ctx); + self.toast.show(&mut ui); } fn exit(&mut self, _event_loop: &ActiveEventLoop) { @@ -6,8 +6,8 @@ use dropbear_engine::graphics::SharedGraphicsContext; use egui::Align2; use eucalyptus_core::camera::{CameraComponent, CameraType}; use eucalyptus_core::scene::SceneEntity; -use eucalyptus_core::scripting::{BuildStatus, build_jvm}; use eucalyptus_core::scripting::types::KotlinComponents; +use eucalyptus_core::scripting::{BuildStatus, build_jvm}; use eucalyptus_core::states::{Label, PROJECT}; use eucalyptus_core::{fatal, info, success, success_without_console, warn, warn_without_console}; use std::collections::{HashMap, HashSet}; @@ -35,13 +35,13 @@ impl SignalController for Editor { Signal::Copy(entities, parent_map) => { requeue.push(Signal::Copy(entities, parent_map)); Ok(()) - }, + } Signal::AssetCopy { source, division } => { self.asset_clipboard = Some(AssetClipboard { source: source.clone(), division: division, }); - + Ok(()) } Signal::AssetPaste { @@ -51,50 +51,50 @@ impl SignalController for Editor { let clipboard = self.asset_clipboard.clone(); if clipboard.is_none() { warn!("Nothing copied to paste"); - + return Ok(()); } let clipboard = clipboard.unwrap(); if clipboard.division != division { warn!("Cannot paste across different asset divisions"); - + return Ok(()); } if !clipboard.source.is_file() { warn!("Copied asset is not a file"); - + return Ok(()); } if !target_dir.exists() { warn!("Target directory does not exist"); - + return Ok(()); } let Some(file_name) = clipboard.source.file_name() else { warn!("Unable to paste: invalid file name"); - + return Ok(()); }; let target_path = target_dir.join(file_name); if target_path.exists() { warn!("Target already exists: {}", target_path.display()); - + return Ok(()); } if let Err(err) = fs::copy(&clipboard.source, &target_path) { warn!("Unable to paste file: {}", err); - + return Ok(()); } info!("Pasted asset to {}", target_path.display()); - + Ok(()) } Signal::Paste(entities, parent_map) => { @@ -124,7 +124,8 @@ impl SignalController for Editor { .collect(); // Keep clipboard alive so the user can paste again. - self.signal.push_back(Signal::Copy(renamed.clone(), renamed_parent_map.clone())); + self.signal + .push_back(Signal::Copy(renamed.clone(), renamed_parent_map.clone())); // Queue a PendingSpawn for each entity, with parent_label set as needed. for scene_entity in renamed { @@ -147,17 +148,16 @@ impl SignalController for Editor { }; if is_viewport_cam { warn!("You can't delete the viewport camera"); - + Ok(()) } else { match self.world.despawn(*sel_e) { Ok(_) => { info!("Decimated entity"); - + Ok(()) } Err(e) => { - fatal!("Failed to delete entity: {}", e); Err(anyhow::anyhow!(e)) } @@ -182,13 +182,13 @@ impl SignalController for Editor { warn_without_console!("Nothing to undo"); log::debug!("No undoable actions in stack"); } - + Ok(()) } Signal::Play => { if matches!(self.editor_state, EditorState::Playing) { log::warn!("Unable to play: already in playing mode"); - + return Err(anyhow::anyhow!("Unable to play: already in playing mode")); } @@ -240,12 +240,13 @@ impl SignalController for Editor { .pressed_keys .contains(&KeyCode::ControlLeft) || self - .input_state - .pressed_keys - .contains(&KeyCode::ControlRight); + .input_state + .pressed_keys + .contains(&KeyCode::ControlRight); #[cfg(target_os = "macos")] - let ctrl_pressed = self.input_state.pressed_keys.contains(&KeyCode::SuperLeft) - || self.input_state.pressed_keys.contains(&KeyCode::SuperRight); + let ctrl_pressed = + self.input_state.pressed_keys.contains(&KeyCode::SuperLeft) + || self.input_state.pressed_keys.contains(&KeyCode::SuperRight); let alt_pressed = self.input_state.pressed_keys.contains(&KeyCode::AltLeft) || self.input_state.pressed_keys.contains(&KeyCode::AltRight); @@ -268,8 +269,8 @@ impl SignalController for Editor { }; let libs_dir = project_root.join("build").join("libs"); if !libs_dir.exists() { - let err = - "Build succeeded but 'build/libs' directory is missing".to_string(); + let err = "Build succeeded but 'build/libs' directory is missing" + .to_string(); return Err(anyhow::anyhow!(err)); } @@ -279,15 +280,15 @@ impl SignalController for Editor { path.extension() .map_or(false, |ext| ext.eq_ignore_ascii_case("jar")) && !path - .file_name() - .unwrap_or_default() - .to_string_lossy() - .contains("-sources") + .file_name() + .unwrap_or_default() + .to_string_lossy() + .contains("-sources") && !path - .file_name() - .unwrap_or_default() - .to_string_lossy() - .contains("-javadoc") + .file_name() + .unwrap_or_default() + .to_string_lossy() + .contains("-javadoc") }) .collect(); @@ -316,7 +317,6 @@ impl SignalController for Editor { info!("Using cached JAR: {}", jar_path.display()); self.show_build_window = false; - self.load_play_mode()?; return Ok(()); @@ -346,12 +346,12 @@ impl SignalController for Editor { if let Some(handle) = self.handle_created { if let Some(result) = graphics .future_queue - .exchange_owned_as::<anyhow::Result<PathBuf>>(&handle) - { + .exchange_owned_as::<anyhow::Result<PathBuf>>( + &handle, + ) { local_handle_exchanged = Some(result); } } else { - self.show_build_window = false; self.editor_state = EditorState::Editing; } @@ -363,7 +363,6 @@ impl SignalController for Editor { self.build_progress = 0.0; fatal!("Failed to build gradle, check logs"); - self.show_build_window = false; self.editor_state = EditorState::Editing; // self.dock_state @@ -441,7 +440,6 @@ impl SignalController for Editor { self.handle_created = None; self.progress_rx = None; self.editor_state = EditorState::Editing; - } } @@ -452,10 +450,12 @@ impl SignalController for Editor { match result { Ok(path) => { - log::debug!("Path is valid, JAR location as {}", path.display()); + log::debug!( + "Path is valid, JAR location as {}", + path.display() + ); success!("Build completed successfully!"); self.show_build_window = false; - self.load_play_mode()?; } @@ -464,7 +464,10 @@ impl SignalController for Editor { self.build_logs.push(error_msg.clone()); self.last_build_error = Some(self.build_logs.join("\n")); - fatal!("Failed to ready script manager interface because {}", e); + fatal!( + "Failed to ready script manager interface because {}", + e + ); self.show_build_window = false; self.show_build_error_window = true; self.editor_state = EditorState::Editing; @@ -488,7 +491,9 @@ impl SignalController for Editor { ui.vertical(|ui| { ui.heading("Build Failed"); ui.add_space(5.0); - ui.label("The Gradle build failed. See the error log below:"); + ui.label( + "The Gradle build failed. See the error log below:", + ); ui.add_space(10.0); ui.separator(); ui.add_space(10.0); @@ -498,10 +503,12 @@ impl SignalController for Editor { .max_height(300.0) .show(ui, |ui| { ui.add( - egui::TextEdit::multiline(&mut error_log.as_str()) - .font(egui::TextStyle::Monospace) - .desired_width(f32::INFINITY) - .desired_rows(20), + egui::TextEdit::multiline( + &mut error_log.as_str(), + ) + .font(egui::TextStyle::Monospace) + .desired_width(f32::INFINITY) + .desired_rows(20), ); }); @@ -521,7 +528,9 @@ impl SignalController for Editor { } } - if matches!(self.editor_state, EditorState::Building) || self.show_build_error_window { + if matches!(self.editor_state, EditorState::Building) + || self.show_build_error_window + { requeue.push(Signal::Play); } Ok(()) @@ -543,26 +552,41 @@ impl SignalController for Editor { success!("Exited play mode"); log::info!("Back to the editor you go..."); - Ok(()) } Signal::AddComponent(entity, component) => { if let Some(kc_box) = component.as_any().downcast_ref::<KotlinComponents>() { for fqcn in kc_box.fqcns.clone() { - if let Ok(mut existing) = self.world.get::<&mut KotlinComponents>(entity) { + if let Ok(mut existing) = + self.world.get::<&mut KotlinComponents>(entity) + { if existing.has(&fqcn) { - warn!("Entity {:?} already has Kotlin component '{}'", entity, fqcn); + warn!( + "Entity {:?} already has Kotlin component '{}'", + entity, fqcn + ); } else { existing.attach(&fqcn); - success!("Added Kotlin component '{}' to entity {:?}", fqcn, entity); + success!( + "Added Kotlin component '{}' to entity {:?}", + fqcn, + entity + ); } } else { let mut new_kc = KotlinComponents::default(); new_kc.attach(&fqcn); if let Err(e) = self.world.insert_one(entity, new_kc) { - warn!("Failed to insert KotlinComponents for '{}': {}", fqcn, e); + warn!( + "Failed to insert KotlinComponents for '{}': {}", + fqcn, e + ); } else { - success!("Added Kotlin component '{}' to entity {:?}", fqcn, entity); + success!( + "Added Kotlin component '{}' to entity {:?}", + fqcn, + entity + ); } } } @@ -576,7 +600,7 @@ impl SignalController for Editor { "Failed to resolve component type for add request on entity {:?}", entity ); - + return Ok(()); }; @@ -591,10 +615,9 @@ impl SignalController for Editor { warn!( "Entity {:?} already has component '{}'", - entity, - component_name + entity, component_name ); - + return Ok(()); } @@ -614,13 +637,13 @@ impl SignalController for Editor { self.pending_components.push((entity, handle)); success!("Queued component addition for entity {:?}", entity); - + Ok(()) } Signal::RequestNewWindow(window_data) => { use dropbear_engine::scene::SceneCommand; self.scene_command = SceneCommand::RequestWindow(window_data.clone()); - + Ok(()) } Signal::UpdateViewportSize((x, y)) => { @@ -632,7 +655,7 @@ impl SignalController for Editor { self.scene_command = dropbear_engine::scene::SceneCommand::ResizeViewport((width, height)); } - + Ok(()) } Signal::FlushUnusedAssets => { @@ -663,9 +686,7 @@ impl SignalController for Editor { Ok(()) } }?; - if !show { - - } + if !show {} if let Some(signal) = local_signal { self.signal.push_back(signal); } @@ -76,13 +76,9 @@ impl PendingSpawnController for Editor { appliers.push(applier); } - Ok::< - ( - Label, - Vec<Box<dyn ComponentApply + Send + Sync>>, - ), - anyhow::Error, - >((label, appliers)) + Ok::<(Label, Vec<Box<dyn ComponentApply + Send + Sync>>), anyhow::Error>(( + label, appliers, + )) }; let handle = queue.push(Box::pin(future)); @@ -161,10 +157,14 @@ impl PendingSpawnController for Editor { let mut completed_components = Vec::new(); for (index, (entity, handle)) in self.pending_components.iter().enumerate() { if let Some(result) = queue.exchange_owned(handle) { - if let Ok(r) = result.downcast::<anyhow::Result<Box<dyn ComponentApply + Send + Sync>>>() { + if let Ok(r) = + result.downcast::<anyhow::Result<Box<dyn ComponentApply + Send + Sync>>>() + { match Arc::try_unwrap(r) { Ok(Ok(applier)) => { - if let Err(e) = applier.apply_to_existing_entity(&mut self.world, *entity) { + if let Err(e) = + applier.apply_to_existing_entity(&mut self.world, *entity) + { fatal!("Failed to add component bundle: {}", e); } else { success!("Added component to entity {:?}", entity); @@ -1,4 +1,3 @@ - use crate::math::Rect; use bytemuck::{Pod, Zeroable}; use glam::{Mat4, Vec2, Vec3}; @@ -5,9 +5,9 @@ pub mod camera; pub mod math; pub mod rendering; pub mod resp; +mod tree; pub mod widgets; pub mod windowing; -mod tree; pub mod crates { pub use glyphon; @@ -15,19 +15,20 @@ pub mod crates { pub use winit; } -pub use widgets::shorthand::*; pub use tree::{WidgetDescriptor, WidgetNode, WidgetTree}; +pub use widgets::shorthand::*; use crate::asset::{AssetServer, Handle}; use crate::camera::Camera2D; use crate::math::Rect; -use crate::rendering::{KinoRenderContext, KinoRenderTargetId, KinoWGPURenderer}; use crate::rendering::text::{KinoTextRenderer, TextEntry}; use crate::rendering::texture::Texture; use crate::rendering::vertex::Vertex; +use crate::rendering::{KinoRenderContext, KinoRenderTargetId, KinoWGPURenderer}; use crate::resp::WidgetResponse; use crate::widgets::{ContaineredWidget, NativeWidget}; use crate::windowing::KinoWinitWindowing; +use dropbear_utils::StaleTracker; use glam::Vec2; use rendering::batching::VertexBatch; use std::borrow::Cow; @@ -35,7 +36,6 @@ use std::collections::{HashMap, VecDeque}; use std::fmt::Debug; use std::hash::{DefaultHasher, Hash, Hasher}; use wgpu::{LoadOp, StoreOp}; -use dropbear_utils::StaleTracker; /// Holds the state of all the instructions, and the vertices+indices for rendering as well /// as the responses. @@ -67,7 +67,7 @@ impl KinoState { 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. pub fn windowing(&mut self) -> &mut KinoWinitWindowing { @@ -164,9 +164,10 @@ impl KinoState { self.render_target_cache .iter() .filter_map(|(target, context)| match (target, context) { - (KinoRenderTargetId::Billboard(entity_id), KinoRenderContext::Billboard { view, .. }) => { - Some((*entity_id, view.clone())) - } + ( + KinoRenderTargetId::Billboard(entity_id), + KinoRenderContext::Billboard { view, .. }, + ) => Some((*entity_id, view.clone())), _ => None, }) .collect() @@ -228,10 +229,7 @@ impl KinoState { } } - fn used_extent( - geometry: &[TexturedGeometry], - text_entries: &[TextEntry], - ) -> (u32, u32) { + fn used_extent(geometry: &[TexturedGeometry], text_entries: &[TextEntry]) -> (u32, u32) { let mut max_extent = Vec2::ZERO; for textured in geometry { @@ -309,6 +307,7 @@ impl KinoState { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); for mut tg in geometry.drain(..) { @@ -431,6 +430,7 @@ impl KinoState { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); for mut tg in hud_geometry.drain(..) { @@ -749,7 +749,7 @@ pub struct TexturedGeometry { } #[derive(Default)] -pub struct PrimitiveBatch{ +pub struct PrimitiveBatch { geometry: Vec<TexturedGeometry>, } @@ -125,7 +125,7 @@ pub enum KinoRenderContext { Billboard { texture: texture::Texture, view: wgpu::TextureView, - } + }, } #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] @@ -16,8 +16,11 @@ 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], - push_constant_ranges: &[], + bind_group_layouts: &[ + Some(&texture_bind_group_layout), + Some(&camera_bind_group_layout), + ], + immediate_size: 0, }); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -46,8 +49,8 @@ impl KinoRendererPipeline { }, depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, cache: None, + multiview_mask: None, }); log::debug!("Created pipeline"); @@ -76,4 +76,4 @@ impl WidgetTree { pub(crate) fn submit_node(node: WidgetNode, kino: &mut KinoState) { node.widget.submit(node.children, kino); -} +} @@ -2,13 +2,13 @@ use crate::math::Rect; use crate::rendering::text::TextEntry; use crate::resp::WidgetResponse; use crate::widgets::NativeWidget; +#[cfg(feature = "ser")] +use crate::widgets::text::ser::{SerializedAttrs, SerializedMetrics}; use crate::{KinoState, WidgetId}; use glam::Vec2; use glyphon::{Attrs, AttrsOwned, Buffer, Color, Metrics, Shaping}; use std::any::Any; use winit::event::{ElementState, MouseButton}; -#[cfg(feature = "ser")] -use crate::widgets::text::ser::{SerializedAttrs, SerializedMetrics}; /// Creates a label with the specified text and properties. /// @@ -94,11 +94,13 @@ impl NativeWidget for Text { Some(self.size.y), ); } + buffer.set_text( &mut state.renderer.text.font_system, &self.text, &attributes.as_attrs(), Shaping::Basic, + None, // todo: figure out what to put in here ); let mut max_x = 0.0f32; @@ -175,7 +177,7 @@ impl crate::WidgetDescriptor for Text { #[cfg(feature = "ser")] pub mod ser { - use glyphon::{AttrsOwned, Color, FamilyOwned, Metrics, Style, Stretch, Weight}; + use glyphon::{AttrsOwned, Color, FamilyOwned, Metrics, Stretch, Style, Weight}; #[derive(Clone, serde::Serialize, serde::Deserialize)] pub struct SerializedMetrics { @@ -222,17 +224,17 @@ pub mod ser { Self { color: a.color_opt.map(|c| c.0), family: match a.family_owned { - FamilyOwned::Name(s) => SerializedFamily::Name(s.to_string()), - FamilyOwned::Serif => SerializedFamily::Serif, + FamilyOwned::Name(s) => SerializedFamily::Name(s.to_string()), + FamilyOwned::Serif => SerializedFamily::Serif, FamilyOwned::SansSerif => SerializedFamily::SansSerif, - FamilyOwned::Cursive => SerializedFamily::Cursive, - FamilyOwned::Fantasy => SerializedFamily::Fantasy, + FamilyOwned::Cursive => SerializedFamily::Cursive, + FamilyOwned::Fantasy => SerializedFamily::Fantasy, FamilyOwned::Monospace => SerializedFamily::Monospace, }, weight: a.weight.0, style: match a.style { - Style::Normal => 0, - Style::Italic => 1, + Style::Normal => 0, + Style::Italic => 1, Style::Oblique => 2, }, stretch: a.stretch.to_number() as u8, @@ -244,11 +246,11 @@ pub mod ser { fn from(s: SerializedAttrs) -> Self { use glyphon::Attrs; let family_owned = match s.family { - SerializedFamily::Name(n) => FamilyOwned::Name(n.into()), - SerializedFamily::Serif => FamilyOwned::Serif, + SerializedFamily::Name(n) => FamilyOwned::Name(n.into()), + SerializedFamily::Serif => FamilyOwned::Serif, SerializedFamily::SansSerif => FamilyOwned::SansSerif, - SerializedFamily::Cursive => FamilyOwned::Cursive, - SerializedFamily::Fantasy => FamilyOwned::Fantasy, + SerializedFamily::Cursive => FamilyOwned::Cursive, + SerializedFamily::Fantasy => FamilyOwned::Fantasy, SerializedFamily::Monospace => FamilyOwned::Monospace, }; let style = match s.style { @@ -112,11 +112,20 @@ impl Generator for KotlinJVMGenerator { writeln!(output)?; writeln!(output, "object ComponentManager {{")?; - writeln!(output, " private val instances: MutableMap<String, com.dropbear.ecs.NativeComponent> = mutableMapOf()")?; + writeln!( + output, + " private val instances: MutableMap<String, com.dropbear.ecs.NativeComponent> = mutableMapOf()" + )?; writeln!(output)?; - writeln!(output, " private fun registerOne(component: com.dropbear.ecs.NativeComponent) {{")?; + writeln!( + output, + " private fun registerOne(component: com.dropbear.ecs.NativeComponent) {{" + )?; writeln!(output, " component.register()")?; - writeln!(output, " instances[component.fullyQualifiedTypeName] = component")?; + writeln!( + output, + " instances[component.fullyQualifiedTypeName] = component" + )?; writeln!(output, " }}")?; writeln!(output)?; writeln!(output, " @Synchronized")?; @@ -130,9 +139,15 @@ impl Generator for KotlinJVMGenerator { } writeln!(output, " }}")?; writeln!(output)?; - writeln!(output, " fun updateKotlinComponent(fqcn: String, entityId: Long, dt: Double) {{")?; + writeln!( + output, + " fun updateKotlinComponent(fqcn: String, entityId: Long, dt: Double) {{" + )?; writeln!(output, " val instance = instances[fqcn] ?: return")?; - writeln!(output, " val engine = com.dropbear.DropbearEngine(com.dropbear.DropbearEngine.native)")?; + writeln!( + output, + " val engine = com.dropbear.DropbearEngine(com.dropbear.DropbearEngine.native)" + )?; writeln!(output, " instance.setCurrentEntity(entityId)")?; writeln!(output, " instance.updateComponent(engine, dt)")?; writeln!(output, " instance.clearCurrentEntity()")?; @@ -140,7 +155,10 @@ impl Generator for KotlinJVMGenerator { writeln!(output)?; writeln!(output, " fun inspectKotlinComponent(fqcn: String) {{")?; writeln!(output, " val instance = instances[fqcn] ?: return")?; - writeln!(output, " val engine = com.dropbear.DropbearEngine(com.dropbear.DropbearEngine.native)")?; + writeln!( + output, + " val engine = com.dropbear.DropbearEngine(com.dropbear.DropbearEngine.native)" + )?; writeln!(output, " instance.inspect(engine)")?; writeln!(output, " }}")?; writeln!(output, "}}")?; @@ -556,11 +556,20 @@ object ScriptManager {{ writeln!(output)?; writeln!(output, "object ComponentManager {{")?; - writeln!(output, " private val instances: MutableMap<String, com.dropbear.ecs.NativeComponent> = mutableMapOf()")?; + writeln!( + output, + " private val instances: MutableMap<String, com.dropbear.ecs.NativeComponent> = mutableMapOf()" + )?; writeln!(output)?; - writeln!(output, " private fun registerOne(component: com.dropbear.ecs.NativeComponent) {{")?; + writeln!( + output, + " private fun registerOne(component: com.dropbear.ecs.NativeComponent) {{" + )?; writeln!(output, " component.register()")?; - writeln!(output, " instances[component.fullyQualifiedTypeName] = component")?; + writeln!( + output, + " instances[component.fullyQualifiedTypeName] = component" + )?; writeln!(output, " }}")?; writeln!(output)?; writeln!(output, " fun registerAll() {{")?; @@ -573,9 +582,15 @@ object ScriptManager {{ } writeln!(output, " }}")?; writeln!(output)?; - writeln!(output, " fun updateKotlinComponent(fqcn: String, entityId: Long, dt: Double) {{")?; + writeln!( + output, + " fun updateKotlinComponent(fqcn: String, entityId: Long, dt: Double) {{" + )?; writeln!(output, " val instance = instances[fqcn] ?: return")?; - writeln!(output, " val engine = com.dropbear.DropbearEngine(com.dropbear.DropbearEngine.native)")?; + writeln!( + output, + " val engine = com.dropbear.DropbearEngine(com.dropbear.DropbearEngine.native)" + )?; writeln!(output, " instance.setCurrentEntity(entityId)")?; writeln!(output, " instance.updateComponent(engine, dt)")?; writeln!(output, " instance.clearCurrentEntity()")?; @@ -583,7 +598,10 @@ object ScriptManager {{ writeln!(output)?; writeln!(output, " fun inspectKotlinComponent(fqcn: String) {{")?; writeln!(output, " val instance = instances[fqcn] ?: return")?; - writeln!(output, " val engine = com.dropbear.DropbearEngine(com.dropbear.DropbearEngine.native)")?; + writeln!( + output, + " val engine = com.dropbear.DropbearEngine(com.dropbear.DropbearEngine.native)" + )?; writeln!(output, " instance.inspect(engine)")?; writeln!(output, " }}")?; writeln!(output, "}}")?; @@ -29,7 +29,10 @@ impl Default for ScriptManifest { impl ScriptManifest { pub fn new() -> Self { - Self { items: Vec::new(), components: Vec::new() } + Self { + items: Vec::new(), + components: Vec::new(), + } } pub fn add_item(&mut self, item: ManifestItem) { @@ -84,12 +87,22 @@ pub struct ComponentManifestItem { impl ComponentManifestItem { pub fn new(fqcn: String, simple_name: String, file_path: PathBuf) -> Self { - Self { fqcn, simple_name, file_path } + Self { + fqcn, + simple_name, + file_path, + } } - pub fn fqcn(&self) -> &str { &self.fqcn } - pub fn simple_name(&self) -> &str { &self.simple_name } - pub fn file_path(&self) -> &PathBuf { &self.file_path } + pub fn fqcn(&self) -> &str { + &self.fqcn + } + pub fn simple_name(&self) -> &str { + &self.simple_name + } + pub fn file_path(&self) -> &PathBuf { + &self.file_path + } } impl ManifestItem { @@ -494,7 +507,9 @@ impl KotlinProcessor { let Some((class_node, _)) = classes .iter() .filter(|(node, _)| node.start_byte() > annotation.node.end_byte()) - .min_by_key(|(node, _)| node.start_byte().saturating_sub(annotation.node.end_byte())) + .min_by_key(|(node, _)| { + node.start_byte().saturating_sub(annotation.node.end_byte()) + }) else { continue; }; @@ -821,7 +836,9 @@ pub fn visit_kotlin_files( manifest.add_item(item); } - for component in processor.process_file_for_components(&source_code, path.clone())? { + for component in + processor.process_file_for_components(&source_code, path.clone())? + { manifest.add_component(component); } } @@ -1,6 +1,7 @@ //! Allows you to a launch play mode as another window. use crossbeam_channel::{Receiver, unbounded}; +use dropbear_engine::animation::MorphTargetInfo; use dropbear_engine::billboarding::BillboardPipeline; use dropbear_engine::buffer::ResizableBuffer; use dropbear_engine::camera::Camera; @@ -28,6 +29,7 @@ use eucalyptus_core::states::{SCENES, Script, WorldLoadingStatus}; use futures::executor; use glam::Mat4; use hecs::{Entity, World}; +use jni::objects::JValue; use kino_ui::KinoState; use kino_ui::rendering::KinoWGPURenderer; use kino_ui::windowing::KinoWinitWindowing; @@ -35,11 +37,9 @@ use log::error; use std::collections::HashMap; use std::path::PathBuf; use std::sync::Arc; -use jni::objects::JValue; use wgpu::SurfaceConfiguration; use wgpu::util::DeviceExt; use winit::window::Fullscreen; -use dropbear_engine::animation::MorphTargetInfo; mod command; mod input; @@ -145,7 +145,15 @@ pub struct PlayMode { default_animation_bind_group: Option<wgpu::BindGroup>, billboard_pipeline: Option<BillboardPipeline>, pub(crate) static_batches: HashMap<u64, Vec<(Entity, InstanceRaw)>>, - pub(crate) animated_instances: Vec<(Entity, u64, InstanceRaw, wgpu::Buffer, wgpu::Buffer, wgpu::Buffer, u32)>, + pub(crate) animated_instances: Vec<( + Entity, + u64, + InstanceRaw, + wgpu::Buffer, + wgpu::Buffer, + wgpu::Buffer, + u32, + )>, pub(crate) animated_bind_group_cache: HashMap<Entity, (u64, wgpu::BindGroup)>, pub(crate) last_morph_info_per_mesh: HashMap<u32, MorphTargetInfo>, @@ -384,7 +392,8 @@ impl PlayMode { )); self.billboard_pipeline = Some(BillboardPipeline::new(graphics.clone())); - *graphics.debug_draw.lock() = Some(dropbear_engine::debug::DebugDraw::new(graphics.clone())); + *graphics.debug_draw.lock() = + Some(dropbear_engine::debug::DebugDraw::new(graphics.clone())); let sky_texture_result = HdrLoader::from_equirectangular_bytes( &graphics.device, @@ -483,45 +492,47 @@ impl PlayMode { log::debug!("Loaded scripts successfully!"); } - // todo: this wont work for native contexts. - if let Some(registry) = Arc::get_mut(&mut self.component_registry) { - registry.drain_kotlin_queue(); - - if let Some(jvm) = eucalyptus_core::scripting::jni::GLOBAL_JVM.get() { - let jvm = jvm.clone(); - registry.set_kotlin_update_fn(move |fqcn: &str, entity_id: u64, dt: f32| { - let Ok(mut env) = jvm.attach_current_thread() else { - log::warn!("kotlin_update_fn: failed to attach JVM thread"); - return; - }; - let Ok(fqcn_jstr) = env.new_string(fqcn) else { return }; - let Ok(class) = env.find_class("com/dropbear/decl/ComponentManager") else { - log::warn!( - "kotlin_update_fn: ComponentManager class not found - has the project JAR been loaded?" - ); - return; - }; - if let Err(e) = env.call_static_method( - class, - "updateKotlinComponent", - "(Ljava/lang/String;JD)V", - &[ - JValue::Object(&fqcn_jstr), - JValue::Long(entity_id as i64), - JValue::Double(dt as f64), - ], - ) { - log::warn!( - "kotlin_update_fn: updateKotlinComponent('{}', {}) failed: {:?}", - fqcn, entity_id, e - ); - let _ = env.exception_clear(); - } - }); - } - } else { - log::warn!("drain_kotlin_queue: could not get exclusive access to component_registry; Kotlin component descriptors were not registered"); - } + if let Err(_e) = self.script_manager.discover_components() {} + + // // todo: this wont work for native contexts. + // if let Some(registry) = Arc::get_mut(&mut self.component_registry) { + // registry.drain_kotlin_queue(); + // + // if let Some(jvm) = eucalyptus_core::scripting::jni::GLOBAL_JVM.get() { + // let jvm = jvm.clone(); + // registry.set_kotlin_update_fn(move |fqcn: &str, entity_id: u64, dt: f32| { + // let Ok(mut env) = jvm.attach_current_thread() else { + // log::warn!("kotlin_update_fn: failed to attach JVM thread"); + // return; + // }; + // let Ok(fqcn_jstr) = env.new_string(fqcn) else { return }; + // let Ok(class) = env.load_class("com/dropbear/decl/ComponentManager") else { + // log::warn!( + // "kotlin_update_fn: ComponentManager class not found - has the project JAR been loaded?" + // ); + // return; + // }; + // if let Err(e) = env.call_static_method( + // class, + // "updateKotlinComponent", + // "(Ljava/lang/String;JD)V", + // &[ + // JValue::Object(&fqcn_jstr), + // JValue::Long(entity_id as i64), + // JValue::Double(dt as f64), + // ], + // ) { + // log::warn!( + // "kotlin_update_fn: updateKotlinComponent('{}', {}) failed: {:?}", + // fqcn, entity_id, e + // ); + // let _ = env.exception_clear(); + // } + // }); + // } + // } else { + // log::warn!("drain_kotlin_queue: could not get exclusive access to component_registry; Kotlin component descriptors were not registered"); + // } self.scripts_ready = true; log::debug!("Scripts reloaded successfully!"); @@ -13,9 +13,9 @@ use dropbear_engine::model::{DrawLight, DrawModel}; use dropbear_engine::pipelines::DropbearShaderPipeline; use dropbear_engine::scene::{Scene, SceneCommand}; use eucalyptus_core::billboard::BillboardComponent; -use eucalyptus_core::entity_status::EntityStatus; use eucalyptus_core::command::CommandBufferPoller; use eucalyptus_core::egui::CentralPanel; +use eucalyptus_core::entity_status::EntityStatus; use eucalyptus_core::hierarchy::{EntityTransformExt, Parent}; use eucalyptus_core::physics::collider::{ColliderGroup, ColliderShape}; use eucalyptus_core::physics::kcc::KCC; @@ -659,6 +659,7 @@ impl Scene for PlayMode { }), occlusion_query_set: None, timestamp_writes: None, + multiview_mask: None, }); } @@ -718,8 +719,8 @@ impl Scene for PlayMode { let instance = renderer.instance.to_raw(); if let Some(animation) = animation { - let has_skinning = !animation.skinning_matrices.is_empty(); - let has_morph = !animation.morph_weights.is_empty(); + let has_skinning = !animation.skinning_matrices.is_empty(); + let has_morph = !animation.morph_weights.is_empty(); if !has_skinning && !has_morph { self.static_batches @@ -810,19 +811,20 @@ impl Scene for PlayMode { }; let entity = batched_instances.first().map(|(e, _)| *e); - let instances: Vec<InstanceRaw> = batched_instances - .iter() - .map(|(_, inst)| *inst) - .collect(); - - 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 instances: Vec<InstanceRaw> = + batched_instances.iter().map(|(_, inst)| *inst).collect(); + + 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); model_cache.insert(*handle, model.clone()); @@ -865,6 +867,7 @@ impl Scene for PlayMode { }), occlusion_query_set: None, timestamp_writes: None, + multiview_mask: None, }); render_pass.set_pipeline(light_pipeline.pipeline()); @@ -918,7 +921,9 @@ impl Scene for PlayMode { for (model, handle, instance_count, entity) in prepared_models { let Some(entity) = entity else { continue }; - let Ok(renderer) = self.world.get::<&MeshRenderer>(entity) else { continue }; + let Ok(renderer) = self.world.get::<&MeshRenderer>(entity) else { + continue; + }; let morph_deltas_buffer = model .morph_deltas_buffer @@ -958,10 +963,13 @@ impl Scene for PlayMode { }), occlusion_query_set: None, timestamp_writes: None, + multiview_mask: None, }); render_pass.set_pipeline(pipeline.pipeline()); - let Some(instance_buffer) = self.instance_buffer_cache.get(&handle) else { continue }; + let Some(instance_buffer) = self.instance_buffer_cache.get(&handle) else { + continue; + }; render_pass.set_vertex_buffer(1, instance_buffer.slice(instance_count as usize)); for mesh in &model.meshes { @@ -985,20 +993,24 @@ impl Scene for PlayMode { num_targets: mesh.morph_target_count, base_offset: mesh.morph_deltas_offset, weight_offset: 0, - uses_morph: if mesh.morph_target_count > 0 && !weights.is_empty() { 1 } else { 0 }, + uses_morph: if mesh.morph_target_count > 0 && !weights.is_empty() { + 1 + } else { + 0 + }, _padding: Default::default(), }; let cache_key = mesh.morph_deltas_offset; - let needs_write = self - .last_morph_info_per_mesh - .get(&cache_key) - .map_or(true, |prev| { - prev.num_vertices != info.num_vertices - || prev.num_targets != info.num_targets - || prev.base_offset != info.base_offset - || prev.uses_morph != info.uses_morph - }); + let needs_write = + self.last_morph_info_per_mesh + .get(&cache_key) + .map_or(true, |prev| { + prev.num_vertices != info.num_vertices + || prev.num_targets != info.num_targets + || prev.base_offset != info.base_offset + || prev.uses_morph != info.uses_morph + }); if needs_write { graphics.queue.write_buffer( @@ -1010,12 +1022,13 @@ impl Scene for PlayMode { } let material = &model.materials[mesh.material]; - let material = if let Some(mat) = renderer.material_snapshot.get(&material.name) { + let material = if let Some(mat) = renderer.material_snapshot.get(&material.name) + { mat } else { log_once::warn_once!( - "Unable to locate MeshRenderer's material_snapshot for that specific material" - ); + "Unable to locate MeshRenderer's material_snapshot for that specific material" + ); material }; @@ -1041,9 +1054,7 @@ impl Scene for PlayMode { .expect("Per-frame bind group not initialised") .clone(); - for (entity, _, instance, _, _, _, _) - in &self.animated_instances - { + for (entity, _, instance, _, _, _, _) in &self.animated_instances { let instance_buffer = self .animated_instance_buffers .entry(*entity) @@ -1058,10 +1069,19 @@ impl Scene for PlayMode { instance_buffer.write(&graphics.device, &graphics.queue, &[*instance]); } - for (entity, handle, _, skinning_buffer, morph_weights_buffer, morph_info_buffer, morph_weight_count) - in &self.animated_instances + for ( + entity, + handle, + _, + skinning_buffer, + morph_weights_buffer, + morph_info_buffer, + morph_weight_count, + ) in &self.animated_instances { - let Ok(renderer) = self.world.get::<&MeshRenderer>(*entity) else { continue }; + let Ok(renderer) = self.world.get::<&MeshRenderer>(*entity) else { + continue; + }; puffin::profile_scope!("rendering animated model", format!("{:?}", entity)); { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { @@ -1085,6 +1105,7 @@ impl Scene for PlayMode { }), occlusion_query_set: None, timestamp_writes: None, + multiview_mask: None, }); render_pass.set_pipeline(pipeline.pipeline()); @@ -1116,12 +1137,15 @@ impl Scene for PlayMode { morph_weights_buffer, morph_info_buffer, ); - self.animated_bind_group_cache.insert(*entity, (bind_group_stamp, bg)); + self.animated_bind_group_cache + .insert(*entity, (bind_group_stamp, bg)); } &self.animated_bind_group_cache[entity].1 }; - let Some(instance_buffer) = self.animated_instance_buffers.get(entity) else { continue }; + let Some(instance_buffer) = self.animated_instance_buffers.get(entity) else { + continue; + }; render_pass.set_vertex_buffer(1, instance_buffer.slice(1)); for mesh in &model.meshes { @@ -1136,20 +1160,23 @@ impl Scene for PlayMode { _padding: Default::default(), }; - graphics - .queue - .write_buffer(morph_info_buffer, 0, bytemuck::bytes_of(&info)); + graphics.queue.write_buffer( + morph_info_buffer, + 0, + bytemuck::bytes_of(&info), + ); let material = &model.materials[mesh.material]; - let material = - if let Some(mat) = renderer.material_snapshot.get(&material.name) { - mat - } else { - log_once::warn_once!( - "Unable to locate MeshRenderer's material_snapshot for that specific material" - ); - material - }; + let material = if let Some(mat) = + renderer.material_snapshot.get(&material.name) + { + mat + } else { + log_once::warn_once!( + "Unable to locate MeshRenderer's material_snapshot for that specific material" + ); + material + }; render_pass.draw_mesh_instanced( mesh, @@ -1188,6 +1215,7 @@ impl Scene for PlayMode { }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); render_pass.set_pipeline(&sky.pipeline); @@ -1239,7 +1267,10 @@ impl Scene for PlayMode { let r = radius * scale.x.max(scale.y).max(scale.z); debug_draw.draw_sphere(translation, r, colour); } - ColliderShape::Capsule { half_height, radius } => { + ColliderShape::Capsule { + half_height, + radius, + } => { let axis = rotation * Vec3::Y; let top = translation + axis * (half_height * scale.y); let bottom = translation - axis * (half_height * scale.y); @@ -1250,7 +1281,10 @@ impl Scene for PlayMode { colour, ); } - ColliderShape::Cylinder { half_height, radius } => { + ColliderShape::Cylinder { + half_height, + radius, + } => { let axis = rotation * Vec3::Y; debug_draw.draw_cylinder( translation, @@ -1260,7 +1294,10 @@ impl Scene for PlayMode { colour, ); } - ColliderShape::Cone { half_height, radius } => { + ColliderShape::Cone { + half_height, + radius, + } => { let axis = rotation * Vec3::Y; let apex = translation + axis * (half_height * scale.y); let height = 2.0 * half_height * scale.y; @@ -1284,12 +1321,9 @@ impl Scene for PlayMode { { puffin::profile_scope!("rendering billboard targets"); if let Some(kino) = &mut self.kino { - let mut kino_encoder = CommandEncoder::new(graphics.clone(), Some("kino billboard encoder")); - kino.render_billboard_targets( - &graphics.device, - &graphics.queue, - &mut kino_encoder, - ); + let mut kino_encoder = + CommandEncoder::new(graphics.clone(), Some("kino billboard encoder")); + kino.render_billboard_targets(&graphics.device, &graphics.queue, &mut kino_encoder); if let Err(e) = kino_encoder.submit() { log_once::error_once!("Unable to submit billboard kino pass: {}", e); @@ -1358,7 +1392,8 @@ impl Scene for PlayMode { } }; - let transform = Mat4::from_scale_rotation_translation(scale, rotation, position); + let transform = + Mat4::from_scale_rotation_translation(scale, rotation, position); billboards.push((transform, texture_view)); } @@ -1385,6 +1420,7 @@ impl Scene for PlayMode { }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); for (transform, texture_view) in billboards { Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME @@ -114,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -213,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. @@ -70,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell