kitgit

tirbofish/dropbear · commit

35bc1a76c075aaa12f24290ad3d693b215b69082

wip: added a new type to Component and removed old "u_fs_main" for the lighting. last commit for the night...

Unverified

Thribhu K <4tkbytes@pm.me> · 2026-02-16 13:57

view full diff

diff --git a/crates/dropbear-engine/src/lib.rs b/crates/dropbear-engine/src/lib.rs
index 5841f21..d5e81d5 100644
--- a/crates/dropbear-engine/src/lib.rs
+++ b/crates/dropbear-engine/src/lib.rs
@@ -23,15 +23,8 @@ pub mod features;
 pub mod animation;
 
 features! {
-    pub mod build {
-        const Debug = 0b00000001,
-        const Release = 0b00000000
-    }
-}
-
-features! {
-    pub mod graphics_features {
-        const SupportsStorage = 0b00000001
+    pub mod feature_list {
+        const EnablePuffinTracer = 0b00000001
     }
 }
 
@@ -165,12 +158,8 @@ impl State {
             .flags
             .contains(wgpu::DownlevelFlags::VERTEX_STORAGE)
             && device.limits().max_storage_buffers_per_shader_stage > 0;
-
-        if supports_storage_resources {
-            graphics_features::enable(graphics_features::SupportsStorage);
-        }
         
-        log::debug!("graphics device {} support storage resources", if !supports_storage_resources { "DOES NOT" } else { "DOES" });
+        log::debug!("graphics device {} support storage resources", if !supports_storage_resources { "DOES NOT" } else { "does" });
 
         if WGPU_BACKEND.get().is_none() {
             let info = adapter.get_info();
@@ -960,7 +949,8 @@ impl App {
     /// Creates a new instance of the application. It only sets the default for the struct + the
     /// window config.
     fn new(app_data: AppInfo, future_queue: Option<Arc<FutureQueue>>) -> Self {
-        if build::is_enabled(build::Debug) {
+        if feature_list::is_enabled(feature_list::EnablePuffinTracer) {
+            log::info!("Enabling puffin profiler");
             puffin::set_scopes_on(true);
 
             if let Err(e) = puffin_http::Server::new("127.0.0.1:8585") {
diff --git a/crates/dropbear-engine/src/lighting.rs b/crates/dropbear-engine/src/lighting.rs
index e970c5e..2f3d18f 100644
--- a/crates/dropbear-engine/src/lighting.rs
+++ b/crates/dropbear-engine/src/lighting.rs
@@ -6,7 +6,7 @@ use crate::{
     entity::Transform,
     model::Model,
 };
-use glam::{DMat4, DVec3};
+use glam::{DMat4, DQuat, DVec3};
 use std::fmt::{Display, Formatter};
 use std::sync::Arc;
 use wgpu::{BindGroup};
@@ -208,6 +208,36 @@ impl LightComponent {
         }
     }
 
+    pub fn to_transform(&self) -> Transform {
+        Transform {
+            position: self.position,
+            rotation: Self::direction_to_quaternion(self.direction),
+            scale: DVec3::ONE,
+        }
+    }
+
+    /// Converts a direction vector to a quaternion rotation
+    fn direction_to_quaternion(direction: DVec3) -> DQuat {
+        if direction.length_squared() < 1e-6 {
+            return DQuat::IDENTITY;
+        }
+
+        let forward = DVec3::new(0.0, 0.0, -1.0);
+        let normalized_direction = direction.normalize();
+
+        let dot = forward.dot(normalized_direction);
+
+        if dot > 0.9999 {
+            DQuat::IDENTITY
+        } else if dot < -0.9999 {
+            DQuat::from_axis_angle(DVec3::Y, std::f64::consts::PI)
+        } else {
+            let axis = forward.cross(normalized_direction).normalize();
+            let angle = dot.acos();
+            DQuat::from_axis_angle(axis, angle)
+        }
+    }
+
     pub fn directional(colour: DVec3, intensity: f32) -> Self {
         Self::new(colour, LightType::Directional, intensity, None)
     }
@@ -267,16 +297,13 @@ impl Light {
     pub async fn new(
         graphics: Arc<SharedGraphicsContext>,
         light: LightComponent,
-        transform: Transform,
         label: Option<&str>,
     ) -> Self {
         puffin::profile_function!();
-        let forward = DVec3::new(0.0, 0.0, -1.0);
-        let direction = transform.rotation * forward;
 
         let uniform = LightUniform {
-            position: dvec3_to_uniform_array(transform.position),
-            direction: dvec3_direction_to_uniform_array(direction, light.outer_cutoff_angle),
+            position: dvec3_to_uniform_array(light.position),
+            direction: dvec3_direction_to_uniform_array(light.direction, light.outer_cutoff_angle),
             colour: dvec3_colour_to_uniform_array(
                 light.colour * light.intensity as f64,
                 light.light_type,
@@ -312,7 +339,12 @@ impl Light {
                 label,
             });
 
-        let instance: InstanceInput = DMat4::from_scale_rotation_translation(transform.scale, transform.rotation, transform.position).into();
+        let transform = light.to_transform();
+        let instance: InstanceInput = DMat4::from_scale_rotation_translation(
+            transform.scale,
+            transform.rotation,
+            transform.position
+        ).into();
 
         let mut instance_buffer = ResizableBuffer::new(
             &graphics.device,
@@ -335,14 +367,13 @@ impl Light {
         }
     }
 
-    pub fn update(&mut self, graphics: &SharedGraphicsContext, light: &mut LightComponent, transform: &Transform) {
+    pub fn update(&mut self, graphics: &SharedGraphicsContext, light: &LightComponent) {
         puffin::profile_function!();
-        self.uniform.position = dvec3_to_uniform_array(transform.position);
 
-        let forward = DVec3::new(0.0, 0.0, -1.0);
-        let direction = transform.rotation * forward;
+        self.uniform.position = dvec3_to_uniform_array(light.position);
+
         self.uniform.direction =
-            dvec3_direction_to_uniform_array(direction, light.outer_cutoff_angle);
+            dvec3_direction_to_uniform_array(light.direction, light.outer_cutoff_angle);
 
         self.uniform.colour =
             dvec3_colour_to_uniform_array(light.colour * light.intensity as f64, light.light_type);
diff --git a/crates/dropbear-engine/src/pipelines/light_cube.rs b/crates/dropbear-engine/src/pipelines/light_cube.rs
index d19b34e..b6c97c5 100644
--- a/crates/dropbear-engine/src/pipelines/light_cube.rs
+++ b/crates/dropbear-engine/src/pipelines/light_cube.rs
@@ -2,7 +2,7 @@ use std::sync::Arc;
 use std::mem::size_of;
 use glam::DMat4;
 use slank::include_slang;
-use wgpu::{BufferAddress, VertexAttribute, VertexFormat};
+use wgpu::{BufferAddress, CompareFunction, DepthBiasState, StencilState, VertexAttribute, VertexFormat};
 use crate::buffer::{StorageBuffer, UniformBuffer};
 use crate::entity::{EntityTransform, Transform};
 use crate::graphics::SharedGraphicsContext;
@@ -10,17 +10,14 @@ use crate::lighting::{Light, LightArrayUniform, LightComponent, MAX_LIGHTS};
 use crate::model::Vertex;
 use crate::pipelines::DropbearShaderPipeline;
 use crate::shader::Shader;
+use crate::texture::Texture;
 
 pub struct LightCubePipeline {
     shader: Shader,
     pipeline_layout: wgpu::PipelineLayout,
     pipeline: wgpu::RenderPipeline,
     storage_buffer: Option<StorageBuffer<LightArrayUniform>>,
-    uniform_buffer: Option<UniformBuffer<LightArrayUniform>>,
-    /// Bind group, defined in `shaders/shader.wgsl` as @group(2). 
-    /// 
-    /// This can either be a storage buffer or a uniform buffer depending on if the backend
-    /// supports storage resources. 
+    /// Bind group, defined in `shaders/shader.wgsl` as @group(2)
     light_bind_group: wgpu::BindGroup,
 }
 
@@ -69,17 +66,17 @@ impl DropbearShaderPipeline for LightCubePipeline {
                 topology: wgpu::PrimitiveTopology::TriangleList,
                 strip_index_format: None,
                 front_face: wgpu::FrontFace::Cw,
-                    cull_mode: Some(wgpu::Face::Back),
+                cull_mode: Some(wgpu::Face::Back),
                 polygon_mode: wgpu::PolygonMode::Fill,
                 unclipped_depth: false,
                 conservative: false,
             },
             depth_stencil: Some(wgpu::DepthStencilState {
-                format: crate::Texture::DEPTH_FORMAT,
+                format: Texture::DEPTH_FORMAT,
                 depth_write_enabled: true,
-                depth_compare: wgpu::CompareFunction::Greater,
-                stencil: wgpu::StencilState::default(),
-                bias: wgpu::DepthBiasState::default(),
+                depth_compare: CompareFunction::Greater,
+                stencil: StencilState::default(),
+                bias: DepthBiasState::default(),
             }),
             multisample: wgpu::MultisampleState {
                 count: 1,
@@ -91,26 +88,16 @@ impl DropbearShaderPipeline for LightCubePipeline {
         });
 
         let mut storage_buffer = None;
-        let mut uniform_buffer = None;
 
-        if crate::graphics_features::is_enabled(crate::graphics_features::SupportsStorage) {
-            storage_buffer = Some(StorageBuffer::new(
-                &graphics.device,
-                "light cube pipeline storage buffer",
-            ));
-        } else {
-            uniform_buffer = Some(UniformBuffer::new(
-                &graphics.device,
-                "light cube pipeline uniform buffer",
-            ));
-        }
+        storage_buffer = Some(StorageBuffer::new(
+            &graphics.device,
+            "light cube pipeline storage buffer",
+        ));
 
         let light_buffer: &wgpu::Buffer = if let Some(buf) = &storage_buffer {
             buf.buffer()
-        } else if let Some(buf) = &uniform_buffer {
-            buf.buffer()
         } else {
-            panic!("Either a storage buffer or a uniform buffer should have been created");
+            panic!("A storage buffer should have been created");
         };
 
         let light_bind_group = graphics.device.create_bind_group(&wgpu::BindGroupDescriptor {
@@ -127,7 +114,6 @@ impl DropbearShaderPipeline for LightCubePipeline {
             pipeline_layout,
             pipeline,
             storage_buffer,
-            uniform_buffer,
             light_bind_group,
         }
     }
@@ -151,11 +137,6 @@ impl LightCubePipeline {
     }
 
     pub fn update(&mut self, graphics: Arc<SharedGraphicsContext>, world: &hecs::World) {
-        debug_assert!(
-            self.storage_buffer.is_some() ^ self.uniform_buffer.is_some(),
-            "Expected exactly one of storage_buffer/uniform_buffer to exist"
-        );
-
         let mut light_array = LightArrayUniform::default();
 
         let mut light_index: usize = 0;
@@ -189,20 +170,16 @@ impl LightCubePipeline {
 
         if let Some(buf) = &self.storage_buffer {
             buf.write(&graphics.queue, &light_array);
-        } else if let Some(buf) = &self.uniform_buffer {
-            buf.write(&graphics.queue, &light_array);
         } else {
-            panic!("Either a storage buffer or a uniform buffer should have been created");
+            panic!("A storage buffer should have been created");
         }
     }
 
     pub fn buffer(&self) -> &wgpu::Buffer {
         if let Some(s) = &self.storage_buffer {
             s.buffer()
-        } else if let Some(u) = &self.uniform_buffer {
-            u.buffer()
         } else {
-            panic!("Either a storage buffer or a uniform buffer should have been created");
+            panic!("A storage buffer should have been created");
         }
     }
 }
diff --git a/crates/dropbear-engine/src/pipelines/shader.rs b/crates/dropbear-engine/src/pipelines/shader.rs
index fb2135e..c1fd9cb 100644
--- a/crates/dropbear-engine/src/pipelines/shader.rs
+++ b/crates/dropbear-engine/src/pipelines/shader.rs
@@ -53,11 +53,7 @@ impl DropbearShaderPipeline for MainRenderPipeline {
                     },
                     fragment: Some(wgpu::FragmentState {
                         module: &shader.module,
-                        entry_point: if crate::graphics_features::is_enabled(crate::graphics_features::SupportsStorage) {
-                            Some("s_fs_main")
-                        } else {
-                            Some("u_fs_main")
-                        },
+                        entry_point: Some("s_fs_main"),
                         targets: &[Some(wgpu::ColorTargetState {
                             format: hdr_format,
                             blend: Some(wgpu::BlendState::REPLACE),
diff --git a/crates/eucalyptus-core/src/animation/mod.rs b/crates/eucalyptus-core/src/animation/mod.rs
index 0d8418b..3d4303b 100644
--- a/crates/eucalyptus-core/src/animation/mod.rs
+++ b/crates/eucalyptus-core/src/animation/mod.rs
@@ -12,6 +12,7 @@ impl SerializedComponent for AnimationComponent {}
 
 impl Component for AnimationComponent {
     type SerializedForm = Self;
+    type RequiredComponentTypes = (Self, );
 
     fn descriptor() -> ComponentDescriptor {
         ComponentDescriptor {
@@ -22,15 +23,15 @@ impl Component for AnimationComponent {
         }
     }
 
-    async fn first_time(_graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self>
+    async fn first_time(_graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes>
     where
         Self: Sized
     {
-        Ok(Self::default())
+        Ok((Self::default(), ))
     }
 
-    async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self> {
-        Ok(ser)
+    async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
+        Ok((ser, ))
     }
 
     fn update_component(&mut self, world: &World, entity: Entity, dt: f32, graphics: Arc<SharedGraphicsContext>) {
diff --git a/crates/eucalyptus-core/src/camera.rs b/crates/eucalyptus-core/src/camera.rs
index 0777ecd..5f9a49b 100644
--- a/crates/eucalyptus-core/src/camera.rs
+++ b/crates/eucalyptus-core/src/camera.rs
@@ -25,6 +25,7 @@ impl SerializedComponent for SerializableCamera {}
 
 impl Component for Camera {
     type SerializedForm = SerializableCamera;
+    type RequiredComponentTypes = (Self, CameraComponent);
 
     fn descriptor() -> ComponentDescriptor {
         ComponentDescriptor {
@@ -35,17 +36,19 @@ impl Component for Camera {
         }
     }
 
-    async fn first_time(graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self>
+    async fn first_time(graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes>
     where
         Self: Sized
     {
-        Ok(Camera::predetermined(graphics.clone(), None))
+        let comp = CameraComponent::new();
+        let cam = Camera::predetermined(graphics.clone(), Some("default camera"));
+        Ok((cam, comp))
     }
 
-    async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self> {
+    async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
         let label = ser.label.clone();
-        let builder = CameraBuilder::from(ser);
-        Ok(Camera::new(graphics.clone(), builder, Some(label.as_str())))
+        let builder = CameraBuilder::from(ser.clone());
+        Ok((Camera::new(graphics.clone(), builder, Some(label.as_str())), CameraComponent::from(ser)))
     }
 
     fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, graphics: Arc<SharedGraphicsContext>) {
@@ -62,7 +65,7 @@ impl Component for Camera {
 
     fn inspect(&mut self, ui: &mut Ui) {
         CollapsingHeader::new("Camera3D").show(ui, |ui| {
-            ui.label("Not implemented yet!"); 
+            ui.label("Not implemented yet!");
         });
     }
 }
diff --git a/crates/eucalyptus-core/src/component.rs b/crates/eucalyptus-core/src/component.rs
index e1b8ed4..f30e02c 100644
--- a/crates/eucalyptus-core/src/component.rs
+++ b/crates/eucalyptus-core/src/component.rs
@@ -198,15 +198,20 @@ pub trait Component: Sized {
     /// ```
     type SerializedForm: Serialize + for<'de> Deserialize<'de> + SerializedComponent;
 
+    /// Defines all output types for any type of init function or any world query.
+    ///
+    /// The default is typically `(Self, )`, however you can even define it as `(Self, Transform, ...)`.
+    type RequiredComponentTypes: hecs::DynamicBundle;
+
     fn descriptor() -> ComponentDescriptor;
 
     /// Creates a new instance of the component for times when there is no existing component to
     /// initialise from.
-    async fn first_time(graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self>;
+    async fn first_time(graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes>;
 
     /// Converts [`Self::SerializedForm`] into a [`Component`] instance that can be added to
     /// `hecs::EntityBuilder` during scene initialisation.
-    async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self>;
+    async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes>;
 
     /// Called every frame to update the component's state.
     fn update_component(&mut self, world: &hecs::World, entity: hecs::Entity, dt: f32, graphics: Arc<SharedGraphicsContext>);
@@ -225,6 +230,7 @@ impl SerializedComponent for SerializedMeshRenderer {}
 // sample for MeshRenderer
 impl Component for MeshRenderer {
     type SerializedForm = SerializedMeshRenderer;
+    type RequiredComponentTypes = (Self, );
 
     fn descriptor() -> ComponentDescriptor {
         ComponentDescriptor {
@@ -235,14 +241,14 @@ impl Component for MeshRenderer {
         }
     }
 
-    async fn first_time(_graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self>
+    async fn first_time(_graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes>
     where
         Self: Sized
     {
-        Ok(MeshRenderer::from_handle(Handle::NULL))
+        Ok((MeshRenderer::from_handle(Handle::NULL), ))
     }
 
-    async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self> {
+    async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
         let import_scale = ser.import_scale.unwrap_or(1.0);
 
         let handle = match &ser.handle.ref_type {
@@ -344,7 +350,7 @@ impl Component for MeshRenderer {
             }
         }
 
-        Ok(renderer)
+        Ok((renderer, ))
     }
 
     fn update_component(&mut self, world: &World, entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {
diff --git a/crates/eucalyptus-core/src/lighting.rs b/crates/eucalyptus-core/src/lighting.rs
index e7eae7d..ff3ba08 100644
--- a/crates/eucalyptus-core/src/lighting.rs
+++ b/crates/eucalyptus-core/src/lighting.rs
@@ -1,5 +1,5 @@
 use std::sync::Arc;
-use egui::Ui;
+use egui::{CollapsingHeader, Ui};
 use crate::ptr::WorldPtr;
 use crate::scripting::jni::utils::{FromJObject, ToJObject};
 use crate::scripting::native::DropbearNativeError;
@@ -20,6 +20,7 @@ impl SerializedComponent for SerializedLight {}
 
 impl Component for Light {
     type SerializedForm = SerializedLight;
+    type RequiredComponentTypes = (Self, LightComponent);
 
     fn descriptor() -> ComponentDescriptor {
         ComponentDescriptor {
@@ -30,35 +31,36 @@ impl Component for Light {
         }
     }
 
-    async fn first_time(graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self>
+    async fn first_time(graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes>
     where
         Self: Sized
     {
-        Ok(Light::new(graphics.clone(), LightComponent::default(), Transform::default(), None).await)
+        let comp = LightComponent::default();
+        let light = Light::new(graphics.clone(), comp.clone(), None).await;
+        Ok((light, comp))
     }
 
-    async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self> {
+    async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
         let light = Light::new(
             graphics.clone(),
-            ser.light_component,
-            ser.transform,
+            ser.light_component.clone(),
             Some(ser.label.as_str())
         ).await;
         
-        Ok(light)
+        Ok((light, ser.light_component))
     }
 
     fn update_component(&mut self, world: &World, entity: Entity, _dt: f32, graphics: Arc<SharedGraphicsContext>) {
-        if let Ok((light, comp, trans)) = world.query_one::<(&mut Light, &mut LightComponent, &mut Transform)>(entity).get() {
-            light.update(&graphics, comp, trans);
+        if let Ok((light, comp)) = world.query_one::<(&mut Light, &mut LightComponent)>(entity).get() {
+            light.update(&graphics, comp);
         }
     }
 
     fn save(&self, world: &World, entity: Entity) -> Box<dyn SerializedComponent> {
-        if let Ok((_, comp, transform)) = world.query_one::<(&Light, &LightComponent, &Transform)>(entity).get() {
+        if let Ok((_, comp)) = world.query_one::<(&Light, &LightComponent)>(entity).get() {
             Box::new(SerializedLight {
                 label: self.label.clone(),
-                transform: *transform,
+                transform: comp.to_transform(),
                 light_component: comp.clone(),
                 enabled: comp.enabled,
                 entity_id: Some(entity),
@@ -69,7 +71,9 @@ impl Component for Light {
     }
 
     fn inspect(&mut self, ui: &mut Ui) {
-        todo!()
+        CollapsingHeader::new("Light").default_open(true).show(ui, |ui| {
+            ui.label("Not implemented yet"); 
+        });
     }
 }
 
diff --git a/crates/eucalyptus-core/src/physics/collider.rs b/crates/eucalyptus-core/src/physics/collider.rs
index 46950b4..3d8218e 100644
--- a/crates/eucalyptus-core/src/physics/collider.rs
+++ b/crates/eucalyptus-core/src/physics/collider.rs
@@ -61,6 +61,7 @@ impl SerializedComponent for ColliderGroup {}
 
 impl Component for ColliderGroup {
     type SerializedForm = Self;
+    type RequiredComponentTypes = (Self, );
 
     fn descriptor() -> ComponentDescriptor {
         ComponentDescriptor {
@@ -71,15 +72,15 @@ impl Component for ColliderGroup {
         }
     }
 
-    async fn first_time(graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self>
+    async fn first_time(graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes>
     where
         Self: Sized
     {
-        Ok(Self::new())
+        Ok((Self::new(), ))
     }
 
-    async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self> {
-        Ok(ser)
+    async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
+        Ok((ser, ))
     }
 
     fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {}
diff --git a/crates/eucalyptus-core/src/physics/kcc.rs b/crates/eucalyptus-core/src/physics/kcc.rs
index 1d41390..ab5a269 100644
--- a/crates/eucalyptus-core/src/physics/kcc.rs
+++ b/crates/eucalyptus-core/src/physics/kcc.rs
@@ -39,6 +39,7 @@ impl SerializedComponent for KCC {}
 
 impl Component for KCC {
     type SerializedForm = Self;
+    type RequiredComponentTypes = (Self, );
 
     fn descriptor() -> ComponentDescriptor {
         ComponentDescriptor {
@@ -49,15 +50,15 @@ impl Component for KCC {
         }
     }
 
-    async fn first_time(graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self>
+    async fn first_time(_: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes>
     where
         Self: Sized
     {
-        Ok(Self::default())
+        Ok((Self::default(), ))
     }
 
-    async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self> {
-        Ok(ser)
+    async fn init(ser: Self::SerializedForm, _: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
+        Ok((ser, ))
     }
 
     fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {}
diff --git a/crates/eucalyptus-core/src/physics/rigidbody.rs b/crates/eucalyptus-core/src/physics/rigidbody.rs
index 6d87ea2..9d01624 100644
--- a/crates/eucalyptus-core/src/physics/rigidbody.rs
+++ b/crates/eucalyptus-core/src/physics/rigidbody.rs
@@ -191,6 +191,7 @@ impl SerializedComponent for RigidBody {}
 
 impl Component for RigidBody {
 	type SerializedForm = Self;
+	type RequiredComponentTypes = (Self, );
 
 	fn descriptor() -> ComponentDescriptor {
 		ComponentDescriptor {
@@ -201,15 +202,15 @@ impl Component for RigidBody {
 		}
 	}
 
-	async fn first_time(_graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self>
+	async fn first_time(_graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes>
 	where
 		Self: Sized
 	{
-		Ok(Self::default())
+		Ok((Self::default(), ))
 	}
 
-	async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self> {
-		Ok(ser)
+	async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
+		Ok((ser, ))
 	}
 
 	fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {}
diff --git a/crates/eucalyptus-core/src/properties.rs b/crates/eucalyptus-core/src/properties.rs
index e72ac56..0c0f658 100644
--- a/crates/eucalyptus-core/src/properties.rs
+++ b/crates/eucalyptus-core/src/properties.rs
@@ -26,6 +26,7 @@ impl SerializedComponent for CustomProperties {}
 
 impl Component for CustomProperties {
     type SerializedForm = Self;
+    type RequiredComponentTypes = (Self, );
 
     fn descriptor() -> ComponentDescriptor {
         ComponentDescriptor {
@@ -36,15 +37,15 @@ impl Component for CustomProperties {
         }
     }
 
-    async fn first_time(_graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self>
+    async fn first_time(_graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes>
     where
         Self: Sized
     {
-        Ok(Self::new())
+        Ok((Self::new(), ))
     }
 
-    async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self> {
-        Ok(ser)
+    async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
+        Ok((ser, ))
     }
 
     fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {}
diff --git a/crates/eucalyptus-core/src/states.rs b/crates/eucalyptus-core/src/states.rs
index effb359..21fdf6f 100644
--- a/crates/eucalyptus-core/src/states.rs
+++ b/crates/eucalyptus-core/src/states.rs
@@ -163,6 +163,7 @@ impl SerializedComponent for Script {}
 
 impl Component for Script {
     type SerializedForm = Self;
+    type RequiredComponentTypes = (Self, );
 
     fn descriptor() -> ComponentDescriptor {
         ComponentDescriptor {
@@ -173,15 +174,15 @@ impl Component for Script {
         }
     }
 
-    async fn first_time(_graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self>
+    async fn first_time(_graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes>
     where
         Self: Sized
     {
-        Ok(Self::default())
+        Ok((Self::default(), ))
     }
 
-    async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self> {
-        Ok(ser)
+    async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
+        Ok((ser, ))
     }
 
     fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {}
diff --git a/crates/eucalyptus-core/src/transform.rs b/crates/eucalyptus-core/src/transform.rs
index 79b6798..0f0249c 100644
--- a/crates/eucalyptus-core/src/transform.rs
+++ b/crates/eucalyptus-core/src/transform.rs
@@ -19,6 +19,7 @@ impl SerializedComponent for EntityTransform {}
 
 impl Component for EntityTransform {
     type SerializedForm = Self;
+    type RequiredComponentTypes = (Self, );
 
     fn descriptor() -> ComponentDescriptor {
         ComponentDescriptor {
@@ -29,12 +30,12 @@ impl Component for EntityTransform {
         }
     }
 
-    async fn first_time(_: Arc<SharedGraphicsContext>) -> anyhow::Result<Self> {
-        Ok(Self::default())
+    async fn first_time(_: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
+        Ok((Self::default(), ))
     }
 
-    async fn init(ser: Self::SerializedForm, _: Arc<SharedGraphicsContext>) -> anyhow::Result<Self> {
-        Ok(ser)
+    async fn init(ser: Self::SerializedForm, _: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
+        Ok((ser, ))
     }
 
     fn update_component(&mut self, _: &World, _: Entity, _: f32, _: Arc<SharedGraphicsContext>) {}
diff --git a/crates/eucalyptus-editor/src/main.rs b/crates/eucalyptus-editor/src/main.rs
index b5fbfa1..1e0bde9 100644
--- a/crates/eucalyptus-editor/src/main.rs
+++ b/crates/eucalyptus-editor/src/main.rs
@@ -128,6 +128,14 @@ async fn main() -> anyhow::Result<()> {
                 .global(true)
                 .required(false)
         )
+        .arg(
+            Arg::new("tracing")
+                .long("tracing")
+                .help("Enabled the puffin tracer. Makes the editor slower, but better debugging")
+                .action(clap::ArgAction::SetTrue)
+                .global(true)
+                .required(false)
+        )
         .subcommand(
             Command::new("build")
                 .about("Build a eucalyptus project, but only the .eupak file and its resources")
@@ -182,6 +190,7 @@ async fn main() -> anyhow::Result<()> {
 
     let jvm_args = matches.get_one::<String>("jvm-args");
     let await_jdb = matches.get_flag("await-jdb");
+    let tracing = matches.get_flag("tracing");
 
     if let Some(args) = jvm_args {
         let _ = JVM_ARGS.set(args.clone());
@@ -191,6 +200,10 @@ async fn main() -> anyhow::Result<()> {
         let _ = AWAIT_JDB.set(true);
     }
     
+    if tracing {
+        dropbear_engine::feature_list::enable(dropbear_engine::feature_list::EnablePuffinTracer)
+    }
+    
     EditorSettings::read()?;
 
     match matches.subcommand() {
diff --git a/include/dropbear.h b/include/dropbear.h
index a29e1f8..726dbf4 100644
--- a/include/dropbear.h
+++ b/include/dropbear.h
@@ -184,38 +184,28 @@ typedef struct IndexNative {
     uint32_t generation;
 } IndexNative;
 
-typedef struct NCollider {
-    IndexNative index;
-    uint64_t entity_id;
-    uint32_t id;
-} NCollider;
-
-typedef struct NShapeCastHit {
-    NCollider collider;
-    double distance;
-    NVector3 witness1;
-    NVector3 witness2;
-    NVector3 normal1;
-    NVector3 normal2;
-    NShapeCastStatus status;
-} NShapeCastHit;
+typedef struct IndexNativeArray {
+    IndexNative* values;
+    size_t length;
+    size_t capacity;
+} IndexNativeArray;
 
-typedef struct RigidBodyContext {
-    IndexNative index;
+typedef struct CharacterCollisionArray {
     uint64_t entity_id;
-} RigidBodyContext;
+    IndexNativeArray collisions;
+} CharacterCollisionArray;
 
-typedef struct AxisLock {
-    bool x;
-    bool y;
-    bool z;
-} AxisLock;
+typedef struct NAttenuation {
+    float constant;
+    float linear;
+    float quadratic;
+} NAttenuation;
 
-typedef struct Progress {
-    size_t current;
-    size_t total;
-    const char* message;
-} Progress;
+typedef struct NTransform {
+    NVector3 position;
+    NQuaternion rotation;
+    NVector3 scale;
+} NTransform;
 
 typedef struct NColour {
     uint8_t r;
@@ -229,57 +219,82 @@ typedef struct NRange {
     float end;
 } NRange;
 
+typedef struct NVector4 {
+    double x;
+    double y;
+    double z;
+    double w;
+} NVector4;
+
+typedef struct NVector2 {
+    double x;
+    double y;
+} NVector2;
+
 typedef struct i32Array {
     int32_t* values;
     size_t length;
     size_t capacity;
 } i32Array;
 
-typedef struct NNodeTransform {
-    NVector3 translation;
-    NQuaternion rotation;
-    NVector3 scale;
-} NNodeTransform;
+typedef struct NModelVertex {
+    NVector3 position;
+    NVector3 normal;
+    NVector4 tangent;
+    NVector2 tex_coords0;
+    NVector2 tex_coords1;
+    NVector4 colour0;
+    i32Array joints0;
+    NVector4 weights0;
+} NModelVertex;
 
-typedef struct NNode {
+typedef struct NModelVertexArray {
+    NModelVertex* values;
+    size_t length;
+    size_t capacity;
+} NModelVertexArray;
+
+typedef struct NMesh {
     const char* name;
-    const int32_t* parent;
-    i32Array children;
-    NNodeTransform transform;
-} NNode;
+    int32_t num_elements;
+    int32_t material_index;
+    NModelVertexArray vertices;
+} NMesh;
 
-typedef struct NNodeArray {
-    NNode* values;
+typedef struct NMeshArray {
+    NMesh* values;
     size_t length;
     size_t capacity;
-} NNodeArray;
+} NMeshArray;
 
-typedef void* WorldPtr;
+typedef struct NCollider {
+    IndexNative index;
+    uint64_t entity_id;
+    uint32_t id;
+} NCollider;
 
-typedef struct NTransform {
-    NVector3 position;
-    NQuaternion rotation;
-    NVector3 scale;
-} NTransform;
+typedef struct RayHit {
+    NCollider collider;
+    double distance;
+} RayHit;
 
-typedef struct f64ArrayArray {
-    double* values;
+typedef struct u64Array {
+    uint64_t* values;
     size_t length;
     size_t capacity;
-} f64ArrayArray;
+} u64Array;
 
-typedef struct NSkin {
-    const char* name;
-    i32Array joints;
-    f64ArrayArray inverse_bind_matrices;
-    const int32_t* skeleton_root;
-} NSkin;
+typedef struct ConnectedGamepadIds {
+    u64Array ids;
+} ConnectedGamepadIds;
 
-typedef struct NSkinArray {
-    NSkin* values;
-    size_t length;
-    size_t capacity;
-} NSkinArray;
+typedef struct AxisLock {
+    bool x;
+    bool y;
+    bool z;
+} AxisLock;
+
+typedef void* WorldPtr;
 
 typedef struct NColliderArray {
     NCollider* values;
@@ -287,6 +302,41 @@ typedef struct NColliderArray {
     size_t capacity;
 } NColliderArray;
 
+typedef void* InputStatePtr;
+
+typedef struct NShapeCastHit {
+    NCollider collider;
+    double distance;
+    NVector3 witness1;
+    NVector3 witness2;
+    NVector3 normal1;
+    NVector3 normal2;
+    NShapeCastStatus status;
+} NShapeCastHit;
+
+typedef void* PhysicsStatePtr;
+
+typedef void* AssetRegistryPtr;
+
+typedef struct NNodeTransform {
+    NVector3 translation;
+    NQuaternion rotation;
+    NVector3 scale;
+} NNodeTransform;
+
+typedef struct NNode {
+    const char* name;
+    const int32_t* parent;
+    i32Array children;
+    NNodeTransform transform;
+} NNode;
+
+typedef struct NNodeArray {
+    NNode* values;
+    size_t length;
+    size_t capacity;
+} NNodeArray;
+
 typedef struct f64Array {
     double* values;
     size_t length;
@@ -318,86 +368,14 @@ typedef struct NAnimationArray {
     size_t capacity;
 } NAnimationArray;
 
-typedef void* AssetRegistryPtr;
-
-typedef void* InputStatePtr;
-
-typedef struct IndexNativeArray {
-    IndexNative* values;
-    size_t length;
-    size_t capacity;
-} IndexNativeArray;
-
-typedef struct CharacterCollisionArray {
-    uint64_t entity_id;
-    IndexNativeArray collisions;
-} CharacterCollisionArray;
-
-typedef struct NVector2 {
-    double x;
-    double y;
-} NVector2;
-
-typedef void* GraphicsContextPtr;
-
-typedef struct NAttenuation {
-    float constant;
-    float linear;
-    float quadratic;
-} NAttenuation;
-
-typedef struct u64Array {
-    uint64_t* values;
-    size_t length;
-    size_t capacity;
-} u64Array;
-
-typedef void* SceneLoaderPtr;
+typedef struct Progress {
+    size_t current;
+    size_t total;
+    const char* message;
+} Progress;
 
 typedef void* CommandBufferPtr;
 
-typedef struct NVector4 {
-    double x;
-    double y;
-    double z;
-    double w;
-} NVector4;
-
-typedef struct NModelVertex {
-    NVector3 position;
-    NVector3 normal;
-    NVector4 tangent;
-    NVector2 tex_coords0;
-    NVector2 tex_coords1;
-    NVector4 colour0;
-    i32Array joints0;
-    NVector4 weights0;
-} NModelVertex;
-
-typedef struct NModelVertexArray {
-    NModelVertex* values;
-    size_t length;
-    size_t capacity;
-} NModelVertexArray;
-
-typedef struct NMesh {
-    const char* name;
-    int32_t num_elements;
-    int32_t material_index;
-    NModelVertexArray vertices;
-} NMesh;
-
-typedef struct NMeshArray {
-    NMesh* values;
-    size_t length;
-    size_t capacity;
-} NMeshArray;
-
-typedef struct RayHit {
-    NCollider collider;
-    double distance;
-} RayHit;
-
 typedef struct NMaterial {
     const char* name;
     uint64_t diffuse_texture;
@@ -422,11 +400,33 @@ typedef struct NMaterialArray {
     size_t capacity;
 } NMaterialArray;
 
-typedef void* PhysicsStatePtr;
+typedef void* SceneLoaderPtr;
 
-typedef struct ConnectedGamepadIds {
-    u64Array ids;
-} ConnectedGamepadIds;
+typedef struct RigidBodyContext {
+    IndexNative index;
+    uint64_t entity_id;
+} RigidBodyContext;
+
+typedef struct f64ArrayArray {
+    double* values;
+    size_t length;
+    size_t capacity;
+} f64ArrayArray;
+
+typedef struct NSkin {
+    const char* name;
+    i32Array joints;
+    f64ArrayArray inverse_bind_matrices;
+    const int32_t* skeleton_root;
+} NSkin;
+
+typedef struct NSkinArray {
+    NSkin* values;
+    size_t length;
+    size_t capacity;
+} NSkinArray;
+
+typedef void* GraphicsContextPtr;
 
 int32_t dropbear_gamepad_is_button_pressed(InputStatePtr input, uint64_t gamepad_id, int32_t button_ordinal, bool* out0);
 int32_t dropbear_gamepad_get_left_stick_position(InputStatePtr input, uint64_t gamepad_id, NVector2* out0);