kitgit

tirbofish/dropbear · commit

5903bc07e761860186928b0b9ce885c40caea276

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

Unverified

Thribhu K <4tkbytes@pm.me> · 2026-03-28 09:33

view full diff

diff --git a/.sdkmanrc b/.sdkmanrc
index 5a6900d..5e93e02 100644
--- a/.sdkmanrc
+++ b/.sdkmanrc
@@ -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
diff --git a/Cargo.toml b/Cargo.toml
index e665142..776ed78 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/crates/dropbear-engine/build.rs b/crates/dropbear-engine/build.rs
index 383ef4b..0c9ae06 100644
--- a/crates/dropbear-engine/build.rs
+++ b/crates/dropbear-engine/build.rs
@@ -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(
diff --git a/crates/dropbear-engine/src/animation.rs b/crates/dropbear-engine/src/animation.rs
index c1b1841..d78bbef 100644
--- a/crates/dropbear-engine/src/animation.rs
+++ b/crates/dropbear-engine/src/animation.rs
@@ -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;
\ No newline at end of file
+pub const MAX_MORPH_WEIGHTS: usize = 4096;
diff --git a/crates/dropbear-engine/src/asset.rs b/crates/dropbear-engine/src/asset.rs
index 7d4b0e2..65e10e3 100644
--- a/crates/dropbear-engine/src/asset.rs
+++ b/crates/dropbear-engine/src/asset.rs
@@ -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
     }
diff --git a/crates/dropbear-engine/src/billboarding.rs b/crates/dropbear-engine/src/billboarding.rs
index 95c4fd3..ad5e596 100644
--- a/crates/dropbear-engine/src/billboarding.rs
+++ b/crates/dropbear-engine/src/billboarding.rs
@@ -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);
     }
-}
\ No newline at end of file
+}
diff --git a/crates/dropbear-engine/src/bind_groups.rs b/crates/dropbear-engine/src/bind_groups.rs
index 72407f2..e1ec4dc 100644
--- a/crates/dropbear-engine/src/bind_groups.rs
+++ b/crates/dropbear-engine/src/bind_groups.rs
@@ -1,3 +1 @@
-pub struct PerFrameBindGroup {
-    
-}
\ No newline at end of file
+pub struct PerFrameBindGroup {}
diff --git a/crates/dropbear-engine/src/buffer.rs b/crates/dropbear-engine/src/buffer.rs
index cc76cad..331a647 100644
--- a/crates/dropbear-engine/src/buffer.rs
+++ b/crates/dropbear-engine/src/buffer.rs
@@ -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
     }
-}
\ No newline at end of file
+}
diff --git a/crates/dropbear-engine/src/camera.rs b/crates/dropbear-engine/src/camera.rs
index efa9b0d..27e55a5 100644
--- a/crates/dropbear-engine/src/camera.rs
+++ b/crates/dropbear-engine/src/camera.rs
@@ -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,
diff --git a/crates/dropbear-engine/src/debug.rs b/crates/dropbear-engine/src/debug.rs
index 9a73a76..3a3b114 100644
--- a/crates/dropbear-engine/src/debug.rs
+++ b/crates/dropbear-engine/src/debug.rs
@@ -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,
         ],
     };
-}
\ No newline at end of file
+}
diff --git a/crates/dropbear-engine/src/egui_renderer.rs b/crates/dropbear-engine/src/egui_renderer.rs
index 1ecf70a..c460b93 100644
--- a/crates/dropbear-engine/src/egui_renderer.rs
+++ b/crates/dropbear-engine/src/egui_renderer.rs
@@ -112,6 +112,7 @@ impl EguiRenderer {
             timestamp_writes: None,
             label: Some("egui main render pass"),
             occlusion_query_set: None,
+            multiview_mask: None,
         });
 
         self.renderer
diff --git a/crates/dropbear-engine/src/entity.rs b/crates/dropbear-engine/src/entity.rs
index 8462d4f..0a988ae 100644
--- a/crates/dropbear-engine/src/entity.rs
+++ b/crates/dropbear-engine/src/entity.rs
@@ -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 {
diff --git a/crates/dropbear-engine/src/graphics.rs b/crates/dropbear-engine/src/graphics.rs
index c5072e8..15a3403 100644
--- a/crates/dropbear-engine/src/graphics.rs
+++ b/crates/dropbear-engine/src/graphics.rs
@@ -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(),
diff --git a/crates/dropbear-engine/src/lib.rs b/crates/dropbear-engine/src/lib.rs
index c4bb3ae..013da09 100644
--- a/crates/dropbear-engine/src/lib.rs
+++ b/crates/dropbear-engine/src/lib.rs
@@ -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));
                         }
                     }
                 }
diff --git a/crates/dropbear-engine/src/lighting.rs b/crates/dropbear-engine/src/lighting.rs
index d05d814..5391c61 100644
--- a/crates/dropbear-engine/src/lighting.rs
+++ b/crates/dropbear-engine/src/lighting.rs
@@ -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(
diff --git a/crates/dropbear-engine/src/mipmap.rs b/crates/dropbear-engine/src/mipmap.rs
index e1fc0d6..1ce4542 100644
--- a/crates/dropbear-engine/src/mipmap.rs
+++ b/crates/dropbear-engine/src/mipmap.rs
@@ -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, &[]);
diff --git a/crates/dropbear-engine/src/model.rs b/crates/dropbear-engine/src/model.rs
index 51612e9..59bf635 100644
--- a/crates/dropbear-engine/src/model.rs
+++ b/crates/dropbear-engine/src/model.rs
@@ -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,
-}
\ No newline at end of file
+}
diff --git a/crates/dropbear-engine/src/multisampling.rs b/crates/dropbear-engine/src/multisampling.rs
index 5f8c907..95841c7 100644
--- a/crates/dropbear-engine/src/multisampling.rs
+++ b/crates/dropbear-engine/src/multisampling.rs
@@ -15,4 +15,4 @@ impl Into<u32> for AntiAliasingMode {
             AntiAliasingMode::MSAA4 => 4,
         }
     }
-}
\ No newline at end of file
+}
diff --git a/crates/dropbear-engine/src/pipelines/globals.rs b/crates/dropbear-engine/src/pipelines/globals.rs
index 9167ba0..b6fb7df 100644
--- a/crates/dropbear-engine/src/pipelines/globals.rs
+++ b/crates/dropbear-engine/src/pipelines/globals.rs
@@ -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) {
diff --git a/crates/dropbear-engine/src/pipelines/hdr.rs b/crates/dropbear-engine/src/pipelines/hdr.rs
index bfab1c1..74ed195 100644
--- a/crates/dropbear-engine/src/pipelines/hdr.rs
+++ b/crates/dropbear-engine/src/pipelines/hdr.rs
@@ -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);
     }
-}
\ No newline at end of file
+}
diff --git a/crates/dropbear-engine/src/pipelines/light_cube.rs b/crates/dropbear-engine/src/pipelines/light_cube.rs
index 495c21b..8080be6 100644
--- a/crates/dropbear-engine/src/pipelines/light_cube.rs
+++ b/crates/dropbear-engine/src/pipelines/light_cube.rs
@@ -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);
                 }
diff --git a/crates/dropbear-engine/src/pipelines/mod.rs b/crates/dropbear-engine/src/pipelines/mod.rs
index 96f8721..8752a28 100644
--- a/crates/dropbear-engine/src/pipelines/mod.rs
+++ b/crates/dropbear-engine/src/pipelines/mod.rs
@@ -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,
     })
 }
diff --git a/crates/dropbear-engine/src/pipelines/shader.rs b/crates/dropbear-engine/src/pipelines/shader.rs
index eaad988..eda5b21 100644
--- a/crates/dropbear-engine/src/pipelines/shader.rs
+++ b/crates/dropbear-engine/src/pipelines/shader.rs
@@ -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);
         }
diff --git a/crates/dropbear-engine/src/procedural/mod.rs b/crates/dropbear-engine/src/procedural/mod.rs
index 0549ced..91c8787 100644
--- a/crates/dropbear-engine/src/procedural/mod.rs
+++ b/crates/dropbear-engine/src/procedural/mod.rs
@@ -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,
diff --git a/crates/dropbear-engine/src/scene.rs b/crates/dropbear-engine/src/scene.rs
index 2529a39..943d613 100644
--- a/crates/dropbear-engine/src/scene.rs
+++ b/crates/dropbear-engine/src/scene.rs
@@ -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};
 
diff --git a/crates/dropbear-engine/src/sky.rs b/crates/dropbear-engine/src/sky.rs
index bac163e..0a05d92 100644
--- a/crates/dropbear-engine/src/sky.rs
+++ b/crates/dropbear-engine/src/sky.rs
@@ -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(
diff --git a/crates/dropbear-engine/src/texture.rs b/crates/dropbear-engine/src/texture.rs
index d8ff172..60491ee 100644
--- a/crates/dropbear-engine/src/texture.rs
+++ b/crates/dropbear-engine/src/texture.rs
@@ -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,
diff --git a/crates/dropbear-engine/src/utils.rs b/crates/dropbear-engine/src/utils.rs
index f621c67..c946426 100644
--- a/crates/dropbear-engine/src/utils.rs
+++ b/crates/dropbear-engine/src/utils.rs
@@ -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
         }
     }
 }
-
diff --git a/crates/dropbear-macro/src/lib.rs b/crates/dropbear-macro/src/lib.rs
index 74b193a..bf61c49 100644
--- a/crates/dropbear-macro/src/lib.rs
+++ b/crates/dropbear-macro/src/lib.rs
@@ -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) },
     )
diff --git a/crates/dropbear-utils/src/either.rs b/crates/dropbear-utils/src/either.rs
index f37a38f..886f359 100644
--- a/crates/dropbear-utils/src/either.rs
+++ b/crates/dropbear-utils/src/either.rs
@@ -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
     }
-}
\ No newline at end of file
+}
diff --git a/crates/dropbear-utils/src/hashmap.rs b/crates/dropbear-utils/src/hashmap.rs
index 4f052ed..da2e3d7 100644
--- a/crates/dropbear-utils/src/hashmap.rs
+++ b/crates/dropbear-utils/src/hashmap.rs
@@ -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))
     }
 }
 
diff --git a/crates/dropbear-utils/src/lib.rs b/crates/dropbear-utils/src/lib.rs
index a9a451c..74d48ef 100644
--- a/crates/dropbear-utils/src/lib.rs
+++ b/crates/dropbear-utils/src/lib.rs
@@ -1,5 +1,5 @@
-mod hashmap;
 pub mod either;
+mod hashmap;
 
+pub use either::*;
 pub use hashmap::*;
-pub use either::*;
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/animation.rs b/crates/eucalyptus-core/src/animation.rs
index 9d5116a..682e82a 100644
--- a/crates/eucalyptus-core/src/animation.rs
+++ b/crates/eucalyptus-core/src/animation.rs
@@ -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();
diff --git a/crates/eucalyptus-core/src/asset/mod.rs b/crates/eucalyptus-core/src/asset/mod.rs
index 90416b0..3664bd4 100644
--- a/crates/eucalyptus-core/src/asset/mod.rs
+++ b/crates/eucalyptus-core/src/asset/mod.rs
@@ -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),
diff --git a/crates/eucalyptus-core/src/asset/model.rs b/crates/eucalyptus-core/src/asset/model.rs
index 151da5b..4b4bd22 100644
--- a/crates/eucalyptus-core/src/asset/model.rs
+++ b/crates/eucalyptus-core/src/asset/model.rs
@@ -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)),
     }
 }
 
diff --git a/crates/eucalyptus-core/src/billboard.rs b/crates/eucalyptus-core/src/billboard.rs
index d3fe2af..3079737 100644
--- a/crates/eucalyptus-core/src/billboard.rs
+++ b/crates/eucalyptus-core/src/billboard.rs
@@ -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,
                     );
                 }
-
             });
     }
-}
\ No newline at end of file
+}
diff --git a/crates/eucalyptus-core/src/camera.rs b/crates/eucalyptus-core/src/camera.rs
index 70aac75..40e46de 100644
--- a/crates/eucalyptus-core/src/camera.rs
+++ b/crates/eucalyptus-core/src/camera.rs
@@ -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();
-            }
-        });
     }
 }
 
diff --git a/crates/eucalyptus-core/src/component.rs b/crates/eucalyptus-core/src/component.rs
index 4e6116f..02d6cdb 100644
--- a/crates/eucalyptus-core/src/component.rs
+++ b/crates/eucalyptus-core/src/component.rs
@@ -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) {
diff --git a/crates/eucalyptus-core/src/config.rs b/crates/eucalyptus-core/src/config.rs
index 733e074..d22fb56 100644
--- a/crates/eucalyptus-core/src/config.rs
+++ b/crates/eucalyptus-core/src/config.rs
@@ -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() {
diff --git a/crates/eucalyptus-core/src/debug.rs b/crates/eucalyptus-core/src/debug.rs
index 578e229..2390d52 100644
--- a/crates/eucalyptus-core/src/debug.rs
+++ b/crates/eucalyptus-core/src/debug.rs
@@ -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(())
 }
diff --git a/crates/eucalyptus-core/src/entity.rs b/crates/eucalyptus-core/src/entity.rs
index f9d42ee..2c08349 100644
--- a/crates/eucalyptus-core/src/entity.rs
+++ b/crates/eucalyptus-core/src/entity.rs
@@ -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,
diff --git a/crates/eucalyptus-core/src/entity_status.rs b/crates/eucalyptus-core/src/entity_status.rs
index 3cead89..910d734 100644
--- a/crates/eucalyptus-core/src/entity_status.rs
+++ b/crates/eucalyptus-core/src/entity_status.rs
@@ -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...
     }
 }
diff --git a/crates/eucalyptus-core/src/input.rs b/crates/eucalyptus-core/src/input.rs
index 2eca9a2..3e26b6d 100644
--- a/crates/eucalyptus-core/src/input.rs
+++ b/crates/eucalyptus-core/src/input.rs
@@ -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))
     }
diff --git a/crates/eucalyptus-core/src/input/gamepad.rs b/crates/eucalyptus-core/src/input/gamepad.rs
index 627a83d..f94df4b 100644
--- a/crates/eucalyptus-core/src/input/gamepad.rs
+++ b/crates/eucalyptus-core/src/input/gamepad.rs
@@ -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)?;
diff --git a/crates/eucalyptus-core/src/input/ndc.rs b/crates/eucalyptus-core/src/input/ndc.rs
index fd1bc89..fae325a 100644
--- a/crates/eucalyptus-core/src/input/ndc.rs
+++ b/crates/eucalyptus-core/src/input/ndc.rs
@@ -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)
     }
-}
\ No newline at end of file
+}
diff --git a/crates/eucalyptus-core/src/lib.rs b/crates/eucalyptus-core/src/lib.rs
index 0a8ac4c..933cda9 100644
--- a/crates/eucalyptus-core/src/lib.rs
+++ b/crates/eucalyptus-core/src/lib.rs
@@ -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.
 ///
diff --git a/crates/eucalyptus-core/src/lighting.rs b/crates/eucalyptus-core/src/lighting.rs
index 65ea9c3..6e5da31 100644
--- a/crates/eucalyptus-core/src/lighting.rs
+++ b/crates/eucalyptus-core/src/lighting.rs
@@ -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)
     }
 }
diff --git a/crates/eucalyptus-core/src/logging.rs b/crates/eucalyptus-core/src/logging.rs
index 1cdbbad..79f1183 100644
--- a/crates/eucalyptus-core/src/logging.rs
+++ b/crates/eucalyptus-core/src/logging.rs
@@ -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);
 }
diff --git a/crates/eucalyptus-core/src/mesh.rs b/crates/eucalyptus-core/src/mesh.rs
index 781ab79..453ad62 100644
--- a/crates/eucalyptus-core/src/mesh.rs
+++ b/crates/eucalyptus-core/src/mesh.rs
@@ -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(
diff --git a/crates/eucalyptus-core/src/metadata.rs b/crates/eucalyptus-core/src/metadata.rs
index 558dac7..1046bed 100644
--- a/crates/eucalyptus-core/src/metadata.rs
+++ b/crates/eucalyptus-core/src/metadata.rs
@@ -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))
-}
\ No newline at end of file
+}
diff --git a/crates/eucalyptus-core/src/physics/collider.rs b/crates/eucalyptus-core/src/physics/collider.rs
index 5ad01c5..3df18ce 100644
--- a/crates/eucalyptus-core/src/physics/collider.rs
+++ b/crates/eucalyptus-core/src/physics/collider.rs
@@ -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);
diff --git a/crates/eucalyptus-core/src/physics/collider/shader.rs b/crates/eucalyptus-core/src/physics/collider/shader.rs
index fdab37b..265051c 100644
--- a/crates/eucalyptus-core/src/physics/collider/shader.rs
+++ b/crates/eucalyptus-core/src/physics/collider/shader.rs
@@ -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 {
diff --git a/crates/eucalyptus-core/src/physics/kcc.rs b/crates/eucalyptus-core/src/physics/kcc.rs
index e3ea6f0..db0b620 100644
--- a/crates/eucalyptus-core/src/physics/kcc.rs
+++ b/crates/eucalyptus-core/src/physics/kcc.rs
@@ -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))
-}
\ No newline at end of file
+    world
+        .get::<&KCC>(entity)
+        .map(|kcc| kcc.movement.clone())
+        .map(Ok)
+        .unwrap_or(Ok(None))
+}
diff --git a/crates/eucalyptus-core/src/physics/kcc/character_collision.rs b/crates/eucalyptus-core/src/physics/kcc/character_collision.rs
index bf623ba..16ccad3 100644
--- a/crates/eucalyptus-core/src/physics/kcc/character_collision.rs
+++ b/crates/eucalyptus-core/src/physics/kcc/character_collision.rs
@@ -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()
diff --git a/crates/eucalyptus-core/src/physics/rigidbody.rs b/crates/eucalyptus-core/src/physics/rigidbody.rs
index a765446..028bca3 100644
--- a/crates/eucalyptus-core/src/physics/rigidbody.rs
+++ b/crates/eucalyptus-core/src/physics/rigidbody.rs
@@ -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)
diff --git a/crates/eucalyptus-core/src/plugin.rs b/crates/eucalyptus-core/src/plugin.rs
index e69de29..8b13789 100644
--- a/crates/eucalyptus-core/src/plugin.rs
+++ b/crates/eucalyptus-core/src/plugin.rs
@@ -0,0 +1 @@
+
diff --git a/crates/eucalyptus-core/src/properties.rs b/crates/eucalyptus-core/src/properties.rs
index 7cad385..169849a 100644
--- a/crates/eucalyptus-core/src/properties.rs
+++ b/crates/eucalyptus-core/src/properties.rs
@@ -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;
diff --git a/crates/eucalyptus-core/src/resource.rs b/crates/eucalyptus-core/src/resource.rs
index cb887d4..982657d 100644
--- a/crates/eucalyptus-core/src/resource.rs
+++ b/crates/eucalyptus-core/src/resource.rs
@@ -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
+        )
     }
-}
\ No newline at end of file
+}
diff --git a/crates/eucalyptus-core/src/scripting.rs b/crates/eucalyptus-core/src/scripting.rs
index f339264..28b7732 100644
--- a/crates/eucalyptus-core/src/scripting.rs
+++ b/crates/eucalyptus-core/src/scripting.rs
@@ -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.
     ///
diff --git a/crates/eucalyptus-core/src/scripting/components.rs b/crates/eucalyptus-core/src/scripting/components.rs
new file mode 100644
index 0000000..5cbbfbc
--- /dev/null
+++ b/crates/eucalyptus-core/src/scripting/components.rs
@@ -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(())
+    }
+}
diff --git a/crates/eucalyptus-core/src/scripting/jni.rs b/crates/eucalyptus-core/src/scripting/jni.rs
index 8df2bc2..a28cc3c 100644
--- a/crates/eucalyptus-core/src/scripting/jni.rs
+++ b/crates/eucalyptus-core/src/scripting/jni.rs
@@ -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(())
+        })
     }
 }
diff --git a/crates/eucalyptus-core/src/scripting/jni/primitives.rs b/crates/eucalyptus-core/src/scripting/jni/primitives.rs
index ab240f1..03ae77f 100644
--- a/crates/eucalyptus-core/src/scripting/jni/primitives.rs
+++ b/crates/eucalyptus-core/src/scripting/jni/primitives.rs
@@ -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)
     }
diff --git a/crates/eucalyptus-core/src/scripting/jni/utils.rs b/crates/eucalyptus-core/src/scripting/jni/utils.rs
index 07d0311..914be74 100644
--- a/crates/eucalyptus-core/src/scripting/jni/utils.rs
+++ b/crates/eucalyptus-core/src/scripting/jni/utils.rs
@@ -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);
diff --git a/crates/eucalyptus-core/src/scripting/native.rs b/crates/eucalyptus-core/src/scripting/native.rs
index c35be72..fcfe972 100644
--- a/crates/eucalyptus-core/src/scripting/native.rs
+++ b/crates/eucalyptus-core/src/scripting/native.rs
@@ -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,
         }
     }
 }
diff --git a/crates/eucalyptus-core/src/scripting/types.rs b/crates/eucalyptus-core/src/scripting/types.rs
index 475e737..4686ac2 100644
--- a/crates/eucalyptus-core/src/scripting/types.rs
+++ b/crates/eucalyptus-core/src/scripting/types.rs
@@ -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(())
-}
\ No newline at end of file
+}
diff --git a/crates/eucalyptus-core/src/ser/mod.rs b/crates/eucalyptus-core/src/ser/mod.rs
index 648c67d..d45b43d 100644
--- a/crates/eucalyptus-core/src/ser/mod.rs
+++ b/crates/eucalyptus-core/src/ser/mod.rs
@@ -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()
     }
-}
\ No newline at end of file
+}
diff --git a/crates/eucalyptus-core/src/ser/model.rs b/crates/eucalyptus-core/src/ser/model.rs
index bf0940c..94e0a00 100644
--- a/crates/eucalyptus-core/src/ser/model.rs
+++ b/crates/eucalyptus-core/src/ser/model.rs
@@ -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
     }
-}
\ No newline at end of file
+}
diff --git a/crates/eucalyptus-core/src/ser/templates.rs b/crates/eucalyptus-core/src/ser/templates.rs
index 6116cae..4fb1de4 100644
--- a/crates/eucalyptus-core/src/ser/templates.rs
+++ b/crates/eucalyptus-core/src/ser/templates.rs
@@ -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, ) {
-
-    }
-}
\ No newline at end of file
+    pub fn update(&mut self) {}
+}
diff --git a/crates/eucalyptus-core/src/states.rs b/crates/eucalyptus-core/src/states.rs
index 01c0f44..5f9e477 100644
--- a/crates/eucalyptus-core/src/states.rs
+++ b/crates/eucalyptus-core/src/states.rs
@@ -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;
diff --git a/crates/eucalyptus-core/src/transform.rs b/crates/eucalyptus-core/src/transform.rs
index acf34e7..1deeddf 100644
--- a/crates/eucalyptus-core/src/transform.rs
+++ b/crates/eucalyptus-core/src/transform.rs
@@ -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)
+    }
 }
diff --git a/crates/eucalyptus-core/src/types.rs b/crates/eucalyptus-core/src/types.rs
index b1d9dbb..f95f412 100644
--- a/crates/eucalyptus-core/src/types.rs
+++ b/crates/eucalyptus-core/src/types.rs
@@ -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),
diff --git a/crates/eucalyptus-core/src/ui.rs b/crates/eucalyptus-core/src/ui.rs
index 40db894..f5ea033 100644
--- a/crates/eucalyptus-core/src/ui.rs
+++ b/crates/eucalyptus-core/src/ui.rs
@@ -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),
+                        );
+                    });
+                }
+            });
     }
-}
\ No newline at end of file
+}
diff --git a/crates/eucalyptus-core/src/utils.rs b/crates/eucalyptus-core/src/utils.rs
index d716e70..f113544 100644
--- a/crates/eucalyptus-core/src/utils.rs
+++ b/crates/eucalyptus-core/src/utils.rs
@@ -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)?;
diff --git a/crates/eucalyptus-core/src/uuid.rs b/crates/eucalyptus-core/src/uuid.rs
index 1cd4156..6eb569a 100644
--- a/crates/eucalyptus-core/src/uuid.rs
+++ b/crates/eucalyptus-core/src/uuid.rs
@@ -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]);
diff --git a/crates/eucalyptus-editor/src/about.rs b/crates/eucalyptus-editor/src/about.rs
index d5c7c38..414aeb5 100644
--- a/crates/eucalyptus-editor/src/about.rs
+++ b/crates/eucalyptus-editor/src/about.rs
@@ -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);
             });
diff --git a/crates/eucalyptus-editor/src/build.rs b/crates/eucalyptus-editor/src/build.rs
index 5aa33a6..224134f 100644
--- a/crates/eucalyptus-editor/src/build.rs
+++ b/crates/eucalyptus-editor/src/build.rs
@@ -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
diff --git a/crates/eucalyptus-editor/src/debug.rs b/crates/eucalyptus-editor/src/debug.rs
index d52e288..675a6cc 100644
--- a/crates/eucalyptus-editor/src/debug.rs
+++ b/crates/eucalyptus-editor/src/debug.rs
@@ -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;
 
diff --git a/crates/eucalyptus-editor/src/debug/window.rs b/crates/eucalyptus-editor/src/debug/window.rs
index 16d9a24..1166df7 100644
--- a/crates/eucalyptus-editor/src/debug/window.rs
+++ b/crates/eucalyptus-editor/src/debug/window.rs
@@ -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!");
         });
 
diff --git a/crates/eucalyptus-editor/src/editor/dock.rs b/crates/eucalyptus-editor/src/editor/dock.rs
index b8853f4..54c842b 100644
--- a/crates/eucalyptus-editor/src/editor/dock.rs
+++ b/crates/eucalyptus-editor/src/editor/dock.rs
@@ -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,
diff --git a/crates/eucalyptus-editor/src/editor/docks/asset_viewer.rs b/crates/eucalyptus-editor/src/editor/docks/asset_viewer.rs
index b2e267f..5b57ef0 100644
--- a/crates/eucalyptus-editor/src/editor/docks/asset_viewer.rs
+++ b/crates/eucalyptus-editor/src/editor/docks/asset_viewer.rs
@@ -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;
diff --git a/crates/eucalyptus-editor/src/editor/docks/build_console.rs b/crates/eucalyptus-editor/src/editor/docks/build_console.rs
index bddd268..b8785f3 100644
--- a/crates/eucalyptus-editor/src/editor/docks/build_console.rs
+++ b/crates/eucalyptus-editor/src/editor/docks/build_console.rs
@@ -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
         }
diff --git a/crates/eucalyptus-editor/src/editor/docks/console.rs b/crates/eucalyptus-editor/src/editor/docks/console.rs
index 980d3c2..0a8af39 100644
--- a/crates/eucalyptus-editor/src/editor/docks/console.rs
+++ b/crates/eucalyptus-editor/src/editor/docks/console.rs
@@ -101,4 +101,4 @@ pub struct ConsoleItem {
     pub msg: String,
     pub file_location: Option<PathBuf>,
     pub line_ref: Option<String>,
-}
\ No newline at end of file
+}
diff --git a/crates/eucalyptus-editor/src/editor/docks/entity_list.rs b/crates/eucalyptus-editor/src/editor/docks/entity_list.rs
index 4eaf679..18c0311 100644
--- a/crates/eucalyptus-editor/src/editor/docks/entity_list.rs
+++ b/crates/eucalyptus-editor/src/editor/docks/entity_list.rs
@@ -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,
         }
diff --git a/crates/eucalyptus-editor/src/editor/docks/mod.rs b/crates/eucalyptus-editor/src/editor/docks/mod.rs
index c059535..5821398 100644
--- a/crates/eucalyptus-editor/src/editor/docks/mod.rs
+++ b/crates/eucalyptus-editor/src/editor/docks/mod.rs
@@ -3,4 +3,4 @@ pub mod build_console;
 pub mod console;
 pub mod entity_list;
 pub mod resource;
-pub mod viewport;
\ No newline at end of file
+pub mod viewport;
diff --git a/crates/eucalyptus-editor/src/editor/docks/resource.rs b/crates/eucalyptus-editor/src/editor/docks/resource.rs
index 0eaec04..b9faa26 100644
--- a/crates/eucalyptus-editor/src/editor/docks/resource.rs
+++ b/crates/eucalyptus-editor/src/editor/docks/resource.rs
@@ -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,
         }
diff --git a/crates/eucalyptus-editor/src/editor/docks/viewport.rs b/crates/eucalyptus-editor/src/editor/docks/viewport.rs
index 39bd591..b3328b4 100644
--- a/crates/eucalyptus-editor/src/editor/docks/viewport.rs
+++ b/crates/eucalyptus-editor/src/editor/docks/viewport.rs
@@ -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);
     }
-}
\ No newline at end of file
+}
diff --git a/crates/eucalyptus-editor/src/editor/input.rs b/crates/eucalyptus-editor/src/editor/input.rs
index baee7f6..c3615de 100644
--- a/crates/eucalyptus-editor/src/editor/input.rs
+++ b/crates/eucalyptus-editor/src/editor/input.rs
@@ -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));
diff --git a/crates/eucalyptus-editor/src/editor/mod.rs b/crates/eucalyptus-editor/src/editor/mod.rs
index e015971..b4c51e9 100644
--- a/crates/eucalyptus-editor/src/editor/mod.rs
+++ b/crates/eucalyptus-editor/src/editor/mod.rs
@@ -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)),
 }
diff --git a/crates/eucalyptus-editor/src/editor/page.rs b/crates/eucalyptus-editor/src/editor/page.rs
index a553af5..04b77c1 100644
--- a/crates/eucalyptus-editor/src/editor/page.rs
+++ b/crates/eucalyptus-editor/src/editor/page.rs
@@ -8,4 +8,4 @@ bitflags! {
         /// The editor for UI
         const UIEditor = 1 << 1;
     }
-}
\ No newline at end of file
+}
diff --git a/crates/eucalyptus-editor/src/editor/scene.rs b/crates/eucalyptus-editor/src/editor/scene.rs
index 8df8ecc..bd441e0 100644
--- a/crates/eucalyptus-editor/src/editor/scene.rs
+++ b/crates/eucalyptus-editor/src/editor/scene.rs
@@ -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);
     }
 }
diff --git a/crates/eucalyptus-editor/src/editor/settings.rs b/crates/eucalyptus-editor/src/editor/settings.rs
index d85be0a..a4831e6 100644
--- a/crates/eucalyptus-editor/src/editor/settings.rs
+++ b/crates/eucalyptus-editor/src/editor/settings.rs
@@ -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");
diff --git a/crates/eucalyptus-editor/src/editor/settings/editor.rs b/crates/eucalyptus-editor/src/editor/settings/editor.rs
index aef08b7..96f0af4 100644
--- a/crates/eucalyptus-editor/src/editor/settings/editor.rs
+++ b/crates/eucalyptus-editor/src/editor/settings/editor.rs
@@ -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",
+                                        );
                                     });
                             }
-
                         }
                         _ => {}
                     });
diff --git a/crates/eucalyptus-editor/src/editor/ui/inspector.rs b/crates/eucalyptus-editor/src/editor/ui/inspector.rs
index 067596c..1436da8 100644
--- a/crates/eucalyptus-editor/src/editor/ui/inspector.rs
+++ b/crates/eucalyptus-editor/src/editor/ui/inspector.rs
@@ -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.");
     }
-}
\ No newline at end of file
+}
diff --git a/crates/eucalyptus-editor/src/editor/ui/mod.rs b/crates/eucalyptus-editor/src/editor/ui/mod.rs
index 231da78..0516901 100644
--- a/crates/eucalyptus-editor/src/editor/ui/mod.rs
+++ b/crates/eucalyptus-editor/src/editor/ui/mod.rs
@@ -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],
-}
\ No newline at end of file
+}
diff --git a/crates/eucalyptus-editor/src/editor/ui/viewport.rs b/crates/eucalyptus-editor/src/editor/ui/viewport.rs
index b9bf4f1..93dcf18 100644
--- a/crates/eucalyptus-editor/src/editor/ui/viewport.rs
+++ b/crates/eucalyptus-editor/src/editor/ui/viewport.rs
@@ -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 {
             });
         }
     }
-}
\ No newline at end of file
+}
diff --git a/crates/eucalyptus-editor/src/editor/ui/widget_tree.rs b/crates/eucalyptus-editor/src/editor/ui/widget_tree.rs
index bdc0a65..65789f0 100644
--- a/crates/eucalyptus-editor/src/editor/ui/widget_tree.rs
+++ b/crates/eucalyptus-editor/src/editor/ui/widget_tree.rs
@@ -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.");
     }
-}
\ No newline at end of file
+}
diff --git a/crates/eucalyptus-editor/src/lib.rs b/crates/eucalyptus-editor/src/lib.rs
index b49a7a2..5f0d5c3 100644
--- a/crates/eucalyptus-editor/src/lib.rs
+++ b/crates/eucalyptus-editor/src/lib.rs
@@ -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 {
diff --git a/crates/eucalyptus-editor/src/main.rs b/crates/eucalyptus-editor/src/main.rs
index df515a2..48ce8aa 100644
--- a/crates/eucalyptus-editor/src/main.rs
+++ b/crates/eucalyptus-editor/src/main.rs
@@ -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)?
         );
     }
 
diff --git a/crates/eucalyptus-editor/src/menu.rs b/crates/eucalyptus-editor/src/menu.rs
index f0af525..59476a3 100644
--- a/crates/eucalyptus-editor/src/menu.rs
+++ b/crates/eucalyptus-editor/src/menu.rs
@@ -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) {
diff --git a/crates/eucalyptus-editor/src/signal.rs b/crates/eucalyptus-editor/src/signal.rs
index b3f1497..cba9d71 100644
--- a/crates/eucalyptus-editor/src/signal.rs
+++ b/crates/eucalyptus-editor/src/signal.rs
@@ -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);
             }
diff --git a/crates/eucalyptus-editor/src/spawn.rs b/crates/eucalyptus-editor/src/spawn.rs
index 49bdcdb..b54000b 100644
--- a/crates/eucalyptus-editor/src/spawn.rs
+++ b/crates/eucalyptus-editor/src/spawn.rs
@@ -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);
diff --git a/crates/kino-ui/src/camera.rs b/crates/kino-ui/src/camera.rs
index d87506b..45a85b7 100644
--- a/crates/kino-ui/src/camera.rs
+++ b/crates/kino-ui/src/camera.rs
@@ -1,4 +1,3 @@
-
 use crate::math::Rect;
 use bytemuck::{Pod, Zeroable};
 use glam::{Mat4, Vec2, Vec3};
diff --git a/crates/kino-ui/src/lib.rs b/crates/kino-ui/src/lib.rs
index b55b4b6..6c2c1bd 100644
--- a/crates/kino-ui/src/lib.rs
+++ b/crates/kino-ui/src/lib.rs
@@ -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>,
 }
 
diff --git a/crates/kino-ui/src/rendering.rs b/crates/kino-ui/src/rendering.rs
index f49b3c9..8e8b089 100644
--- a/crates/kino-ui/src/rendering.rs
+++ b/crates/kino-ui/src/rendering.rs
@@ -125,7 +125,7 @@ pub enum KinoRenderContext {
     Billboard {
         texture: texture::Texture,
         view: wgpu::TextureView,
-    }
+    },
 }
 
 #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
diff --git a/crates/kino-ui/src/rendering/pipeline.rs b/crates/kino-ui/src/rendering/pipeline.rs
index 4272c7a..80942fd 100644
--- a/crates/kino-ui/src/rendering/pipeline.rs
+++ b/crates/kino-ui/src/rendering/pipeline.rs
@@ -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");
diff --git a/crates/kino-ui/src/tree.rs b/crates/kino-ui/src/tree.rs
index 7cd5f14..3c60cf0 100644
--- a/crates/kino-ui/src/tree.rs
+++ b/crates/kino-ui/src/tree.rs
@@ -76,4 +76,4 @@ impl WidgetTree {
 
 pub(crate) fn submit_node(node: WidgetNode, kino: &mut KinoState) {
     node.widget.submit(node.children, kino);
-}
\ No newline at end of file
+}
diff --git a/crates/kino-ui/src/widgets/text.rs b/crates/kino-ui/src/widgets/text.rs
index 4497fe6..6c6ac00 100644
--- a/crates/kino-ui/src/widgets/text.rs
+++ b/crates/kino-ui/src/widgets/text.rs
@@ -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 {
diff --git a/crates/magna-carta/src/generator/jvm.rs b/crates/magna-carta/src/generator/jvm.rs
index 95d201f..1390b9f 100644
--- a/crates/magna-carta/src/generator/jvm.rs
+++ b/crates/magna-carta/src/generator/jvm.rs
@@ -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, "}}")?;
diff --git a/crates/magna-carta/src/generator/native.rs b/crates/magna-carta/src/generator/native.rs
index ac5508c..827161c 100644
--- a/crates/magna-carta/src/generator/native.rs
+++ b/crates/magna-carta/src/generator/native.rs
@@ -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, "}}")?;
diff --git a/crates/magna-carta/src/lib.rs b/crates/magna-carta/src/lib.rs
index 7d6bd29..aa496b3 100644
--- a/crates/magna-carta/src/lib.rs
+++ b/crates/magna-carta/src/lib.rs
@@ -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);
                 }
             }
diff --git a/crates/redback-runtime/src/lib.rs b/crates/redback-runtime/src/lib.rs
index 0ac45dc..765fb83 100644
--- a/crates/redback-runtime/src/lib.rs
+++ b/crates/redback-runtime/src/lib.rs
@@ -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!");
diff --git a/crates/redback-runtime/src/scene.rs b/crates/redback-runtime/src/scene.rs
index 01f8cda..5577f3a 100644
--- a/crates/redback-runtime/src/scene.rs
+++ b/crates/redback-runtime/src/scene.rs
@@ -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 {
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 9bbc975..1b33c55 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ca025c8..c61a118 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -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
diff --git a/gradlew b/gradlew
index faf9300..23d15a9 100755
--- a/gradlew
+++ b/gradlew
@@ -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.
diff --git a/gradlew.bat b/gradlew.bat
index 9d21a21..db3a6ac 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -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