kitgit

tirbofish/dropbear · diff

b5ee86a · Thribhu K

refactor: eucalyptus-editor is finally compilable.

Unverified

diff --git a/crates/dropbear-engine/src/entity.rs b/crates/dropbear-engine/src/entity.rs
index a8422cf..da0b2a7 100644
--- a/crates/dropbear-engine/src/entity.rs
+++ b/crates/dropbear-engine/src/entity.rs
@@ -1,22 +1,18 @@
 use glam::{DMat4, DQuat, DVec3, Mat4, Quat, Vec3};
-use parking_lot::Mutex;
 use serde::{Deserialize, Serialize};
 use std::{
-    collections::{HashMap, hash_map::Entry},
+    collections::{HashMap},
     path::Path,
-    sync::{Arc, LazyLock},
+    sync::{Arc},
 };
 
 use crate::{
-    asset::{ASSET_REGISTRY, AssetRegistry},
+    asset::{ASSET_REGISTRY},
     graphics::{Instance, SharedGraphicsContext},
     model::Model,
     texture::Texture,
-    utils::{ResourceReference, ResourceReferenceType, EUCA_SCHEME},
 };
-use anyhow::anyhow;
-use egui::{CollapsingHeader, Ui};
-use std::any::Any;
+use egui::{Ui};
 use crate::asset::Handle;
 use crate::model::Material;
 
diff --git a/crates/dropbear-engine/src/features.rs b/crates/dropbear-engine/src/features.rs
index b905c5e..2a2ba81 100644
--- a/crates/dropbear-engine/src/features.rs
+++ b/crates/dropbear-engine/src/features.rs
@@ -190,6 +190,7 @@ macro_rules! features {
     }) => {
         #[allow(non_upper_case_globals)]
         pub mod $mod_name {
+            #[allow(unused_imports)]
             use $crate::features;
             features! {
                 @_impl mod $mod_name {
diff --git a/crates/dropbear-engine/src/lib.rs b/crates/dropbear-engine/src/lib.rs
index d5e81d5..af63ccc 100644
--- a/crates/dropbear-engine/src/lib.rs
+++ b/crates/dropbear-engine/src/lib.rs
@@ -487,6 +487,36 @@ Hardware:
             );
     }
 
+    /// Resizes the offscreen viewport texture without touching the window surface.
+    pub fn resize_viewport_texture(&mut self, width: u32, height: u32) {
+        if width == 0 || height == 0 {
+            return;
+        }
+
+        if self.viewport_texture.size.width == width && self.viewport_texture.size.height == height {
+            return;
+        }
+
+        let mut config = self.config.read().clone();
+        config.width = width;
+        config.height = height;
+
+        self.depth_texture =
+            Texture::depth_texture(&config, &self.device, Some("depth texture"));
+        self.viewport_texture =
+            Texture::viewport(&config, &self.device, Some("viewport texture"));
+        self.hdr.write().resize(&self.device, width, height);
+        self.egui_renderer
+            .lock()
+            .renderer()
+            .update_egui_texture_from_wgpu_texture(
+                &self.device,
+                &self.viewport_texture.view,
+                wgpu::FilterMode::Linear,
+                *self.texture_id,
+            );
+    }
+
     /// Renders the scene and the egui renderer. I don't know what else to say.
     /// Returns any window-level commands that need to be handled by the App.
     fn render(
@@ -943,21 +973,32 @@ pub struct App {
     windows: HashMap<WindowId, (State, Arc<SharedGraphicsContext>)>,
     root_window_id: Option<WindowId>,
     windows_to_create: Vec<WindowData>,
+
+    
+    #[allow(dead_code)] 
+    puffin_server: Option<Arc<puffin_http::Server>>,
 }
 
 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 {
+        let mut puffin_server: Option<Arc<puffin_http::Server>> = None;
+        
         if feature_list::is_enabled(feature_list::EnablePuffinTracer) {
             log::info!("Enabling puffin profiler");
             puffin::set_scopes_on(true);
 
-            if let Err(e) = puffin_http::Server::new("127.0.0.1:8585") {
-                log::error!("Unable to start puffin http server: {}", e);
-            } else {
-                log::info!("Started puffin http server at \"127.0.0.1:8585\"");
-            };
+            match puffin_http::Server::new("127.0.0.1:8585") {
+                Ok(v) => {
+                    log::info!("Started puffin http server at \"127.0.0.1:8585\"");
+
+                    puffin_server = Some(Arc::new(v)); // need to keep as Arc to keep it alive
+                },
+                Err(e) => {
+                    log::error!("Unable to start puffin http server: {}", e);
+                },
+            }
         }
 
         let instance = Arc::new(Instance::new(&wgpu::InstanceDescriptor {
@@ -979,6 +1020,7 @@ impl App {
             root_window_id: None,
             windows_to_create: Vec::new(),
             app_data,
+            puffin_server,
         };
         log::debug!("Created new instance of app");
         result
@@ -1112,58 +1154,67 @@ impl ApplicationHandler for App {
             return;
         }
 
-        let (state, graphics) = match self.windows.get_mut(&window_id) {
-            Some(canvas) => canvas,
-            None => return,
-        };
+        let request_all_redraws = matches!(&event, WindowEvent::RedrawRequested);
+        let mut window_commands = Vec::new();
 
-        state
-            .egui_renderer
-            .lock()
-            .handle_input(&state.window, &event);
+        {
+            let (state, graphics) = match self.windows.get_mut(&window_id) {
+                Some(canvas) => canvas,
+                None => return,
+            };
 
-        state.scene_manager.handle_event(&event);
+            state
+                .egui_renderer
+                .lock()
+                .handle_input(&state.window, &event);
 
-        match event {
-            WindowEvent::Resized(size) => {
-                state.resize(size.width, size.height);
+            state.scene_manager.handle_event(&event);
 
-                *graphics = Arc::new(graphics::SharedGraphicsContext::from_state(state));
-            }
-            WindowEvent::RedrawRequested => {
-                self.future_queue.poll();
+            match event {
+                WindowEvent::Resized(size) => {
+                    state.resize(size.width, size.height);
 
-                puffin::GlobalProfiler::lock().new_frame();
+                    *graphics = Arc::new(graphics::SharedGraphicsContext::from_state(state));
+                }
+                WindowEvent::RedrawRequested => {
+                    self.future_queue.poll();
 
-                let frame_start = Instant::now();
-                
-                let active_handlers = state.scene_manager.get_active_input_handlers();
-                self.input_manager.set_active_handlers(active_handlers);
+                    puffin::GlobalProfiler::lock().new_frame();
 
-                self.input_manager.update(&mut self.gilrs);
+                    let frame_start = Instant::now();
 
-                let render_result = state.render(self.delta_time, event_loop, graphics.clone());
+                    let active_handlers = state.scene_manager.get_active_input_handlers();
+                    self.input_manager.set_active_handlers(active_handlers);
 
-                let window_commands = render_result.unwrap_or_else(|e| {
-                    log::error!("Render failed: {:?}", e);
-                    Vec::new()
-                });
+                    self.input_manager.update(&mut self.gilrs);
 
-                let frame_elapsed = frame_start.elapsed();
-                let target_frame_time = Duration::from_secs_f32(1.0 / self.target_fps as f32);
+                    let render_result =
+                        state.render(self.delta_time, event_loop, graphics.clone());
 
-                if frame_elapsed < target_frame_time {
-                    SpinSleeper::default().sleep(target_frame_time - frame_elapsed);
-                }
+                    window_commands = render_result.unwrap_or_else(|e| {
+                        log::error!("Render failed: {:?}", e);
+                        Vec::new()
+                    });
 
-                let total_frame_time = frame_start.elapsed();
-                self.delta_time = total_frame_time.as_secs_f32();
+                    let frame_elapsed = frame_start.elapsed();
+                    let target_frame_time = Duration::from_secs_f32(1.0 / self.target_fps as f32);
 
-                state.window.request_redraw();
-                self.future_queue.cleanup();
+                    if frame_elapsed < target_frame_time {
+                        SpinSleeper::default().sleep(target_frame_time - frame_elapsed);
+                    }
 
-                for command in window_commands {
-                    match command {
+                    let total_frame_time = frame_start.elapsed();
+                    self.delta_time = total_frame_time.as_secs_f32();
+
+                    state.window.request_redraw();
+                    self.future_queue.cleanup();
+                }
+                _ => {}
+            }
+        }
+
+        for command in window_commands {
+            match command {
                         scene::SceneCommand::RequestWindow(window_data) => {
                             log::info!("Scene requested new window creation");
                             match self.create_window(event_loop, window_data.attributes) {
@@ -1214,16 +1265,24 @@ impl ApplicationHandler for App {
                         scene::SceneCommand::SetFPS(new_fps) => {
                             self.set_target_fps(new_fps);
                         }
+                        scene::SceneCommand::ResizeViewport((width, height)) => {
+                            if let Some((state, graphics)) = self.windows.get_mut(&window_id) {
+                                state.resize_viewport_texture(width, height);
+                                *graphics =
+                                    Arc::new(graphics::SharedGraphicsContext::from_state(state));
+                            }
+                        }
                         _ => {}
-                    }
-                }
-
-                for (state, _) in self.windows.values() {
-                    state.window.request_redraw();
-                }
+            }
+        }
 
-                return;
+        if request_all_redraws {
+            for (state, _) in self.windows.values() {
+                state.window.request_redraw();
             }
+        }
+
+        match event {
             WindowEvent::KeyboardInput {
                 event:
                     KeyEvent {
diff --git a/crates/dropbear-engine/src/model.rs b/crates/dropbear-engine/src/model.rs
index 6e3d3de..c3ef642 100644
--- a/crates/dropbear-engine/src/model.rs
+++ b/crates/dropbear-engine/src/model.rs
@@ -6,15 +6,12 @@ use crate::{
     texture::{Texture, TextureWrapMode}
 };
 // use image::GenericImageView;
-use parking_lot::{Mutex, RwLock};
+use parking_lot::{RwLock};
 use rayon::prelude::*;
 use serde::{Deserialize, Serialize};
-use std::collections::HashMap;
 use std::hash::{DefaultHasher, Hash, Hasher};
-use std::ops::Deref;
-use std::sync::{Arc, LazyLock};
-use std::time::Instant;
-use std::{mem, ops::Range, path::PathBuf};
+use std::sync::{Arc};
+use std::{mem, ops::Range};
 use gltf::image::Format;
 use gltf::texture::MinFilter;
 use puffin::profile_scope;
diff --git a/crates/dropbear-engine/src/pipelines/light_cube.rs b/crates/dropbear-engine/src/pipelines/light_cube.rs
index b6c97c5..53773b3 100644
--- a/crates/dropbear-engine/src/pipelines/light_cube.rs
+++ b/crates/dropbear-engine/src/pipelines/light_cube.rs
@@ -151,7 +151,7 @@ impl LightCubePipeline {
             } else if let Some(transform) = s_trans {
                 transform.matrix().into()
             } else {
-                panic!("Unable to locate either a \"Transform\" or an \"EntityTransform\" component for the light {}", light.label);
+                light_component.to_transform().matrix().into()
             };
 
             light.instance_buffer.write(&graphics.device, &graphics.queue, &[instance]);
diff --git a/crates/dropbear-engine/src/scene.rs b/crates/dropbear-engine/src/scene.rs
index b53e10b..92e5617 100644
--- a/crates/dropbear-engine/src/scene.rs
+++ b/crates/dropbear-engine/src/scene.rs
@@ -35,6 +35,7 @@ pub enum SceneCommand {
     RequestWindow(WindowData),
     CloseWindow(WindowId),
     SetFPS(u32),
+    ResizeViewport((u32, u32)),
 }
 
 impl Default for SceneCommand {
@@ -146,7 +147,7 @@ impl Manager {
                 }
                 SceneCommand::None => {}
                 SceneCommand::DebugMessage(msg) => log::debug!("{}", msg),
-                SceneCommand::RequestWindow(_) | SceneCommand::CloseWindow(_) | SceneCommand::SetFPS(_) => {
+                SceneCommand::RequestWindow(_) | SceneCommand::CloseWindow(_) | SceneCommand::SetFPS(_) | SceneCommand::ResizeViewport(_) => {
                     return vec![command];
                 }
             }
diff --git a/crates/eucalyptus-core/src/animation/mod.rs b/crates/eucalyptus-core/src/animation/mod.rs
index ef925a1..1868057 100644
--- a/crates/eucalyptus-core/src/animation/mod.rs
+++ b/crates/eucalyptus-core/src/animation/mod.rs
@@ -33,7 +33,7 @@ impl Component for AnimationComponent {
     fn init<'a>(
         ser: &'a Self::SerializedForm,
         _graphics: Arc<SharedGraphicsContext>,
-    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> {
+    ) -> crate::component::ComponentInitFuture<'a, Self> {
         Box::pin(async move { Ok((ser.clone(), )) })
     }
 
diff --git a/crates/eucalyptus-core/src/camera.rs b/crates/eucalyptus-core/src/camera.rs
index 6e38fe5..4bc055b 100644
--- a/crates/eucalyptus-core/src/camera.rs
+++ b/crates/eucalyptus-core/src/camera.rs
@@ -8,7 +8,7 @@ use std::sync::Arc;
 use egui::{CollapsingHeader, Ui};
 use hecs::{Entity, World};
 use dropbear_engine::graphics::SharedGraphicsContext;
-use crate::component::{Component, ComponentDescriptor, SerializedComponent};
+use crate::component::{Component, ComponentDescriptor, ComponentInitFuture, SerializedComponent};
 use crate::ptr::WorldPtr;
 use crate::scripting::result::DropbearNativeResult;
 use crate::types::NVector3;
@@ -48,7 +48,7 @@ impl Component for Camera {
     fn init<'a>(
         ser: &'a Self::SerializedForm,
         graphics: Arc<SharedGraphicsContext>,
-    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> {
+    ) -> ComponentInitFuture<'a, Self> {
         Box::pin(async move {
             let label = ser.label.clone();
             let builder = CameraBuilder::from(ser.clone());
diff --git a/crates/eucalyptus-core/src/component.rs b/crates/eucalyptus-core/src/component.rs
index 107ff0d..2f50ed4 100644
--- a/crates/eucalyptus-core/src/component.rs
+++ b/crates/eucalyptus-core/src/component.rs
@@ -27,16 +27,29 @@ pub struct ComponentRegistry {
     fqtn_to_type: HashMap<String, TypeId>,
     /// Maps category name to list of TypeIds in that category
     categories: HashMap<String, Vec<TypeId>>,
+    /// Maps serialized TypeId to component TypeId
+    serialized_to_component: HashMap<TypeId, TypeId>,
     /// Functions that extract and serialize components from entities
     extractors: HashMap<TypeId, ExtractorFn>,
     /// Functions that allow for the entity to load.
     loaders: HashMap<TypeId, LoaderFn>,
     /// Functions that update the contents of the component. 
     updaters: HashMap<TypeId, UpdateFn>,
+    /// Functions that create default serialized components.
+    defaults: HashMap<TypeId, DefaultFn>,
+    /// Functions that remove components by type.
+    removers: HashMap<TypeId, RemoveFn>,
+    /// Functions that find entities with a component.
+    finders: HashMap<TypeId, FindFn>,
 }
 
+/// Describes a handy little future for [`Component::init`], which deals with initialising a component from its serialized form. 
+/// 
+/// Typically thrown in as a return parameter as `-> ComponentInitFuture<'a, Self>`
+pub type ComponentInitFuture<'a, T: Component> = std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<T::RequiredComponentTypes>> + Send + Sync + 'a>>;
+
 type LoaderFuture<'a> = Pin<Box<
-    dyn Future<Output = anyhow::Result<Box<dyn for<'b> FnOnce(&'b mut hecs::EntityBuilder)>>> + Send + 'a
+    dyn Future<Output = anyhow::Result<Box<dyn for<'b> FnOnce(&'b mut hecs::EntityBuilder) + Send + Sync>>> + Send + Sync + 'a
 >>;
 type LoaderFn = Box<
     dyn for<'a> Fn(&'a dyn SerializedComponent, Arc<SharedGraphicsContext>) -> LoaderFuture<'a>
@@ -45,6 +58,9 @@ type LoaderFn = Box<
 >;
 type ExtractorFn = Box<dyn Fn(&hecs::World, hecs::Entity) -> Option<Box<dyn SerializedComponent>> + Send + Sync>;
 type UpdateFn = Box<dyn Fn(&mut hecs::World, f32, Arc<SharedGraphicsContext>) + Send + Sync>;
+type DefaultFn = Box<dyn Fn() -> Box<dyn SerializedComponent> + Send + Sync>;
+type RemoveFn = Box<dyn Fn(&mut hecs::World, hecs::Entity) + Send + Sync>;
+type FindFn = Box<dyn Fn(&hecs::World) -> Vec<hecs::Entity> + Send + Sync>;
 
 impl ComponentRegistry {
     pub fn new() -> Self {
@@ -52,9 +68,13 @@ impl ComponentRegistry {
             descriptors: HashMap::new(),
             fqtn_to_type: HashMap::new(),
             categories: HashMap::new(),
+            serialized_to_component: HashMap::new(),
             extractors: HashMap::new(),
             loaders: HashMap::new(),
             updaters: HashMap::new(),
+            defaults: HashMap::new(),
+            removers: HashMap::new(),
+            finders: HashMap::new(),
         }
     }
 
@@ -62,9 +82,11 @@ impl ComponentRegistry {
     pub fn register<T>(&mut self)
     where
         T: Component + Send + Sync + 'static,
-        T::SerializedForm: 'static,
+        T::SerializedForm: 'static + Default,
+        T::RequiredComponentTypes: Send + Sync,
     {
         let type_id = TypeId::of::<T>();
+        let serialized_type_id = TypeId::of::<T::SerializedForm>();
         let desc = T::descriptor();
 
         self.fqtn_to_type.insert(desc.fqtn.clone(), type_id);
@@ -72,13 +94,15 @@ impl ComponentRegistry {
             self.categories.entry(cat.clone()).or_default().push(type_id);
         }
         self.descriptors.insert(type_id, desc);
+        self.serialized_to_component
+            .insert(serialized_type_id, type_id);
 
         self.extractors.insert(type_id, Box::new(|world, entity| {
             let Ok(c) = world.get::<&T>(entity) else { return None };
             Some(c.save(world, entity))
         }));
 
-        self.loaders.insert(type_id, Box::new(|serialized, graphics| {
+        self.loaders.insert(serialized_type_id, Box::new(|serialized, graphics| {
             let serialized = serialized
                 .as_any()
                 .downcast_ref::<T::SerializedForm>()
@@ -86,7 +110,7 @@ impl ComponentRegistry {
 
             Box::pin(async move {
                 let bundle = T::init(serialized, graphics).await?;
-                let applier: Box<dyn FnOnce(&mut hecs::EntityBuilder)> =
+                let applier: Box<dyn FnOnce(&mut hecs::EntityBuilder) + Send + Sync> =
                     Box::new(move |builder: &mut hecs::EntityBuilder| {
                         builder.add_bundle(bundle);
                     });
@@ -94,6 +118,22 @@ impl ComponentRegistry {
             })
         }));
 
+        self.defaults.insert(type_id, Box::new(|| {
+            Box::new(T::SerializedForm::default())
+        }));
+
+        self.removers.insert(type_id, Box::new(|world, entity| {
+            let _ = world.remove_one::<T>(entity);
+        }));
+
+        self.finders.insert(type_id, Box::new(|world| {
+            world
+                .query::<(hecs::Entity, &T)>()
+                .iter()
+                .map(|(entity, _)| entity)
+                .collect()
+        }));
+
         self.updaters.insert(type_id, Box::new(|world, dt, graphics| {
             let world_ptr = world as *mut hecs::World; // safe assuming world is kept at the DropbearAppBuilder application level (lifetime)
             let mut query = world.query::<(hecs::Entity, &mut T)>();
@@ -154,6 +194,13 @@ impl ComponentRegistry {
         self.descriptors.len()
     }
 
+    /// Iterates available component descriptors alongside their numeric ids.
+    pub fn iter_available_components(&self) -> impl Iterator<Item = (u64, &ComponentDescriptor)> {
+        self.descriptors
+            .iter()
+            .map(|(type_id, desc)| (Self::numeric_id(*type_id), desc))
+    }
+
     /// Extract all registered components from an entity
     pub fn extract_all_components(
         &self,
@@ -200,15 +247,63 @@ impl ComponentRegistry {
             .unwrap_or_default()
     }
 
+    /// Gets the numeric id for a serialized component instance.
+    pub fn id_for_component(&self, component: &dyn SerializedComponent) -> Option<u64> {
+        let serialized_type_id = component.as_any().type_id();
+        self.serialized_to_component
+            .get(&serialized_type_id)
+            .copied()
+            .map(Self::numeric_id)
+    }
+
+    /// Creates a default serialized component by numeric id.
+    pub fn create_default_component(&self, id: u64) -> Option<Box<dyn SerializedComponent>> {
+        self.type_id_from_numeric_id(id)
+            .and_then(|type_id| self.defaults.get(&type_id))
+            .map(|create| create())
+    }
+
+    /// Removes a component by numeric id from an entity.
+    pub fn remove_component_by_id(
+        &self,
+        world: &mut hecs::World,
+        entity: hecs::Entity,
+        id: u64,
+    ) {
+        if let Some(type_id) = self.type_id_from_numeric_id(id) {
+            if let Some(remover) = self.removers.get(&type_id) {
+                remover(world, entity);
+            }
+        }
+    }
+
+    /// Finds entities with the component matching a numeric id.
+    pub fn find_entities_by_numeric_id(
+        &self,
+        world: &hecs::World,
+        id: u64,
+    ) -> Vec<hecs::Entity> {
+        self.type_id_from_numeric_id(id)
+            .and_then(|type_id| self.finders.get(&type_id))
+            .map(|finder| finder(world))
+            .unwrap_or_default()
+    }
+
+    /// Gets a component descriptor by numeric id.
+    pub fn get_descriptor_by_numeric_id(&self, id: u64) -> Option<&ComponentDescriptor> {
+        self.type_id_from_numeric_id(id)
+            .and_then(|type_id| self.descriptors.get(&type_id))
+    }
+
     /// Create a component applier from a serialized component using the registry loader.
     pub fn load_component<'a>(
         &'a self,
         serialized: &'a dyn SerializedComponent,
         graphics: Arc<SharedGraphicsContext>,
     ) -> Option<LoaderFuture<'a>> {
-        let type_id = serialized.as_any().type_id();
+        let serialized_type_id = serialized.as_any().type_id();
         self.loaders
-            .get(&type_id)
+            .get(&serialized_type_id)
             .map(|loader| loader(serialized, graphics))
     }
 
@@ -223,6 +318,107 @@ impl ComponentRegistry {
             updater(world, dt, graphics.clone());
         }
     }
+
+    /// Inspects all registered components attached to an entity.
+    pub fn inspect_components(
+        &self,
+        world: &mut hecs::World,
+        entity: hecs::Entity,
+        ui: &mut egui::Ui,
+    ) {
+        if let Ok(mut label) = world.get::<&mut crate::states::Label>(entity) {
+            ui.horizontal(|ui| {
+                ui.label("Label");
+                ui.add(egui::TextEdit::singleline(label.as_mut_string()));
+            });
+            ui.separator();
+        }
+
+        let type_ids = world
+            .entity(entity)
+            .map(|e| e.component_types().collect::<Vec<_>>())
+            .unwrap_or_default();
+
+        for type_id in type_ids {
+            let Some(desc) = self.descriptors.get(&type_id) else {
+                continue;
+            };
+
+            match desc.fqtn.as_str() {
+                "dropbear_engine::entity::EntityTransform" => {
+                    if let Ok(mut comp) = world.get::<&mut EntityTransform>(entity) {
+                        comp.inspect(ui);
+                    }
+                }
+                "eucalyptus_core::properties::CustomProperties" => {
+                    if let Ok(mut comp) = world.get::<&mut crate::properties::CustomProperties>(entity) {
+                        comp.inspect(ui);
+                    }
+                }
+                "dropbear_engine::lighting::Light" => {
+                    if let Ok(mut comp) = world.get::<&mut dropbear_engine::lighting::Light>(entity) {
+                        comp.inspect(ui);
+                    }
+                }
+                "eucalyptus_core::states::Script" => {
+                    if let Ok(mut comp) = world.get::<&mut crate::states::Script>(entity) {
+                        comp.inspect(ui);
+                    }
+                }
+                "dropbear_engine::entity::MeshRenderer" => {
+                    if let Ok(mut comp) = world.get::<&mut MeshRenderer>(entity) {
+                        comp.inspect(ui);
+                    }
+                }
+                "dropbear_engine::camera::Camera" => {
+                    if let Ok(mut comp) = world.get::<&mut dropbear_engine::camera::Camera>(entity) {
+                        comp.inspect(ui);
+                    }
+                }
+                "eucalyptus_core::physics::rigidbody::RigidBody" => {
+                    if let Ok(mut comp) = world.get::<&mut crate::physics::rigidbody::RigidBody>(entity) {
+                        comp.inspect(ui);
+                    }
+                }
+                "eucalyptus_core::physics::collider::ColliderGroup" => {
+                    if let Ok(mut comp) = world.get::<&mut crate::physics::collider::ColliderGroup>(entity) {
+                        comp.inspect(ui);
+                    }
+                }
+                "eucalyptus_core::physics::kcc::KCC" => {
+                    if let Ok(mut comp) = world.get::<&mut crate::physics::kcc::KCC>(entity) {
+                        comp.inspect(ui);
+                    }
+                }
+                "dropbear_engine::animation::AnimationComponent" => {
+                    if let Ok(mut comp) = world.get::<&mut dropbear_engine::animation::AnimationComponent>(entity) {
+                        comp.inspect(ui);
+                    }
+                }
+                _ => {
+                    ui.label(format!("{} (no inspector)", desc.type_name));
+                }
+            }
+        }
+    }
+
+    fn numeric_id(type_id: TypeId) -> u64 {
+        use std::hash::{Hash, Hasher};
+        let mut hasher = std::collections::hash_map::DefaultHasher::new();
+        type_id.hash(&mut hasher);
+        let mut id = hasher.finish();
+        if id == 0 {
+            id = 1;
+        }
+        id
+    }
+
+    fn type_id_from_numeric_id(&self, id: u64) -> Option<TypeId> {
+        self.descriptors
+            .keys()
+            .copied()
+            .find(|type_id| Self::numeric_id(*type_id) == id)
+    }
 }
 
 impl Default for ComponentRegistry {
@@ -277,7 +473,7 @@ pub trait Component: Sized + Sync + Send {
     fn init<'a>(
         ser: &'a Self::SerializedForm,
         graphics: Arc<SharedGraphicsContext>,
-    ) -> Pin<Box<dyn Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>>;
+    ) -> ComponentInitFuture<'a, Self>;
 
     /// 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>);
@@ -317,7 +513,7 @@ impl Component for MeshRenderer {
     fn init<'a>(
         ser: &'a Self::SerializedForm,
         graphics: Arc<SharedGraphicsContext>,
-    ) -> Pin<Box<dyn Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> {
+    ) -> ComponentInitFuture<'a, Self> {
         Box::pin(async move {
             let import_scale = ser.import_scale.unwrap_or(1.0);
 
diff --git a/crates/eucalyptus-core/src/lighting.rs b/crates/eucalyptus-core/src/lighting.rs
index 7954ceb..fe8738c 100644
--- a/crates/eucalyptus-core/src/lighting.rs
+++ b/crates/eucalyptus-core/src/lighting.rs
@@ -12,7 +12,7 @@ use hecs::{Entity, World};
 use jni::objects::{JObject, JValue};
 use jni::JNIEnv;
 use dropbear_engine::graphics::SharedGraphicsContext;
-use crate::component::{Component, ComponentDescriptor, SerializedComponent};
+use crate::component::{Component, ComponentDescriptor, ComponentInitFuture, SerializedComponent};
 use crate::states::SerializedLight;
 
 #[typetag::serde]
@@ -43,7 +43,7 @@ impl Component for Light {
     fn init<'a>(
         ser: &'a Self::SerializedForm,
         graphics: Arc<SharedGraphicsContext>,
-    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> {
+    ) -> ComponentInitFuture<'a, Self> {
         Box::pin(async move {
             let light = Light::new(
                 graphics.clone(),
@@ -56,13 +56,13 @@ impl Component for Light {
     }
 
     fn update_component(&mut self, world: &World, entity: Entity, _dt: f32, graphics: Arc<SharedGraphicsContext>) {
-        if let Ok((light, comp)) = world.query_one::<(&mut Light, &mut LightComponent)>(entity).get() {
-            light.update(&graphics, comp);
+        if let Ok(comp) = world.query_one::<&LightComponent>(entity).get() {
+            self.update(&graphics, comp);
         }
     }
 
     fn save(&self, world: &World, entity: Entity) -> Box<dyn SerializedComponent> {
-        if let Ok((_, comp)) = world.query_one::<(&Light, &LightComponent)>(entity).get() {
+        if let Ok(comp) = world.query_one::<&LightComponent>(entity).get() {
             Box::new(SerializedLight {
                 label: self.label.clone(),
                 light_component: comp.clone(),
diff --git a/crates/eucalyptus-core/src/physics/collider.rs b/crates/eucalyptus-core/src/physics/collider.rs
index daf109c..0d46a40 100644
--- a/crates/eucalyptus-core/src/physics/collider.rs
+++ b/crates/eucalyptus-core/src/physics/collider.rs
@@ -82,7 +82,7 @@ impl Component for ColliderGroup {
     fn init<'a>(
         ser: &'a Self::SerializedForm,
         _graphics: Arc<SharedGraphicsContext>,
-    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> {
+    ) -> crate::component::ComponentInitFuture<'a, Self> {
         Box::pin(async move { Ok((ser.clone(), )) })
     }
 
diff --git a/crates/eucalyptus-core/src/physics/kcc.rs b/crates/eucalyptus-core/src/physics/kcc.rs
index fc959b2..a6abe0f 100644
--- a/crates/eucalyptus-core/src/physics/kcc.rs
+++ b/crates/eucalyptus-core/src/physics/kcc.rs
@@ -60,7 +60,7 @@ impl Component for KCC {
     fn init<'a>(
         ser: &'a Self::SerializedForm,
         _: Arc<SharedGraphicsContext>,
-    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> {
+    ) -> crate::component::ComponentInitFuture<'a, Self> {
         Box::pin(async move { Ok((ser.clone(), )) })
     }
 
diff --git a/crates/eucalyptus-core/src/physics/rigidbody.rs b/crates/eucalyptus-core/src/physics/rigidbody.rs
index 0c339b4..7aee1e1 100644
--- a/crates/eucalyptus-core/src/physics/rigidbody.rs
+++ b/crates/eucalyptus-core/src/physics/rigidbody.rs
@@ -212,7 +212,7 @@ impl Component for RigidBody {
 	fn init<'a>(
 		ser: &'a Self::SerializedForm,
 		_graphics: Arc<SharedGraphicsContext>,
-	) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> {
+	) -> crate::component::ComponentInitFuture<'a, Self> {
 		Box::pin(async move { Ok((ser.clone(), )) })
 	}
 
diff --git a/crates/eucalyptus-core/src/properties.rs b/crates/eucalyptus-core/src/properties.rs
index 561106d..89ad566 100644
--- a/crates/eucalyptus-core/src/properties.rs
+++ b/crates/eucalyptus-core/src/properties.rs
@@ -6,7 +6,7 @@ use std::sync::Arc;
 use egui::{CollapsingHeader, ComboBox, DragValue, Grid, RichText, TextEdit, Ui};
 use hecs::{Entity, World};
 use dropbear_engine::graphics::SharedGraphicsContext;
-use crate::component::{Component, ComponentDescriptor, SerializedComponent};
+use crate::component::{Component, ComponentDescriptor, ComponentInitFuture, SerializedComponent};
 use crate::ptr::WorldPtr;
 use crate::scripting::native::DropbearNativeError;
 use crate::scripting::result::DropbearNativeResult;
@@ -47,7 +47,7 @@ impl Component for CustomProperties {
     fn init<'a>(
         ser: &'a Self::SerializedForm,
         _graphics: Arc<SharedGraphicsContext>,
-    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> {
+    ) -> ComponentInitFuture<'a, Self> {
         Box::pin(async move { Ok((ser.clone(), )) })
     }
 
diff --git a/crates/eucalyptus-core/src/states.rs b/crates/eucalyptus-core/src/states.rs
index 238baa9..74bd6c8 100644
--- a/crates/eucalyptus-core/src/states.rs
+++ b/crates/eucalyptus-core/src/states.rs
@@ -27,7 +27,7 @@ use egui::{CollapsingHeader, TextEdit, Ui};
 use hecs::{Entity, World};
 use dropbear_engine::model::AlphaMode;
 use dropbear_engine::texture::TextureWrapMode;
-use crate::component::{Component, ComponentDescriptor, SerializedComponent};
+use crate::component::{Component, ComponentDescriptor, ComponentInitFuture, SerializedComponent};
 use crate::properties::Value;
 
 /// A global "singleton" that contains the configuration of a project.
@@ -184,7 +184,7 @@ impl Component for Script {
     fn init<'a>(
         ser: &'a Self::SerializedForm,
         _graphics: Arc<SharedGraphicsContext>,
-    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> {
+    ) -> ComponentInitFuture<'a, Self> {
         Box::pin(async move { Ok((ser.clone(), )) })
     }
 
diff --git a/crates/eucalyptus-core/src/transform.rs b/crates/eucalyptus-core/src/transform.rs
index 17c9eda..4b18466 100644
--- a/crates/eucalyptus-core/src/transform.rs
+++ b/crates/eucalyptus-core/src/transform.rs
@@ -37,7 +37,7 @@ impl Component for EntityTransform {
     fn init<'a>(
         ser: &'a Self::SerializedForm,
         _: Arc<SharedGraphicsContext>,
-    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> {
+    ) -> crate::component::ComponentInitFuture<'a, Self> {
         Box::pin(async move { Ok((ser.clone(), )) })
     }
 
diff --git a/crates/eucalyptus-editor/src/debug.rs b/crates/eucalyptus-editor/src/debug.rs
index 8ba8e8a..1cc0a2c 100644
--- a/crates/eucalyptus-editor/src/debug.rs
+++ b/crates/eucalyptus-editor/src/debug.rs
@@ -17,11 +17,6 @@ pub(crate) fn show_menu_bar(ui: &mut Ui, signal: &mut Signal) {
             panic!("Testing out panicking with new panic module, this is a test")
         }
 
-        if ui_debug.button("Show Entities Loaded").clicked() {
-            log::debug!("Show Entities Loaded under Debug Menu is clicked");
-            *signal = Signal::LogEntities;
-        }
-
         if ui_debug.button("size_of::<Editor>()").clicked() {
             log::debug!("size_of::<Editor>() is clicked");
             let size = size_of::<crate::editor::Editor>();
diff --git a/crates/eucalyptus-editor/src/editor/asset_viewer.rs b/crates/eucalyptus-editor/src/editor/asset_viewer.rs
index 5cb28d7..d3b530f 100644
--- a/crates/eucalyptus-editor/src/editor/asset_viewer.rs
+++ b/crates/eucalyptus-editor/src/editor/asset_viewer.rs
@@ -563,17 +563,23 @@ impl<'a> EditorTabViewer<'a> {
         let component_id = selection.component_type_id;
         let matches = self
             .component_registry
-            .find_components_by_numeric_id(component_id);
+            .find_entities_by_numeric_id(self.world, component_id);
+        let descriptor = self
+            .component_registry
+            .get_descriptor_by_numeric_id(component_id);
 
         if matches.is_empty() {
             log::warn!("Component id #{} not found in world", component_id);
             return;
         }
 
-        for (entity, component) in matches {
+        let name = descriptor
+            .map(|desc| desc.fqtn.as_str())
+            .unwrap_or("<unknown>");
+        for entity in matches {
             log::debug!(
                 "Serializable component '{}' (id #{}) attached to entity {:?}",
-                component,
+                name,
                 component_id,
                 entity
             );
diff --git a/crates/eucalyptus-editor/src/editor/dock.rs b/crates/eucalyptus-editor/src/editor/dock.rs
index cb10585..0db1e9f 100644
--- a/crates/eucalyptus-editor/src/editor/dock.rs
+++ b/crates/eucalyptus-editor/src/editor/dock.rs
@@ -15,7 +15,6 @@ use dropbear_engine::utils::ResourceReference;
 use dropbear_engine::entity::{EntityTransform, Transform};
 use egui::{self};
 use egui_dock::TabViewer;
-use eucalyptus_core::traits::registry::ComponentRegistry;
 use hecs::{Entity, World};
 use parking_lot::Mutex;
 use transform_gizmo_egui::{EnumSet, Gizmo, GizmoMode, GizmoOrientation};
diff --git a/crates/eucalyptus-editor/src/editor/entity_list.rs b/crates/eucalyptus-editor/src/editor/entity_list.rs
index eaf3bfb..b8a1dac 100644
--- a/crates/eucalyptus-editor/src/editor/entity_list.rs
+++ b/crates/eucalyptus-editor/src/editor/entity_list.rs
@@ -1,5 +1,5 @@
 use egui_ltreeview::{NodeBuilder, TreeViewBuilder};
-use eucalyptus_core::{hierarchy::{Children, Hierarchy, Parent}, physics::{collider::ColliderGroup, rigidbody::RigidBody}, states::{Label, PROJECT}, traits::registry::ComponentRegistry};
+use eucalyptus_core::{component::ComponentRegistry, hierarchy::{Children, Hierarchy, Parent}, physics::{collider::ColliderGroup, rigidbody::RigidBody}, states::{Label, PROJECT}};
 use hecs::{Entity, World};
 
 use crate::editor::{EditorTabViewer, Signal, StaticallyKept, TABS_GLOBAL};
@@ -63,10 +63,10 @@ impl<'a> EditorTabViewer<'a> {
                                 });
                                 ui.menu_button("Add", |ui| {
                                     log_once::debug_once!("Available components: ");
-                                    for (id, fqtn) in registry.iter_available_components() {
-                                        log_once::debug_once!("id: {}, name: {}", id, fqtn);
+                                    for (id, desc) in registry.iter_available_components() {
+                                        log_once::debug_once!("id: {}, name: {}", id, desc.fqtn);
 
-                                        if ui.button(fqtn).clicked() {
+                                        if ui.button(desc.type_name.as_str()).clicked() {
                                             if let Some(component) = registry.create_default_component(id) {
                                                 *signal = Signal::AddComponent(entity, component);
                                             }
@@ -80,22 +80,20 @@ impl<'a> EditorTabViewer<'a> {
                     let components = registry.extract_all_components(world, entity);
 
                     for component in components.iter() {
-                        // if component.type_name().contains("EntityTransform") {
-                        //     continue;
-                        // }
                         let Some(component_type_id) =
                             registry.id_for_component(component.as_ref())
                         else {
                             log_once::warn_once!(
-                                    "Component '{}' missing registry id, skipping tree entry",
-                                    component.type_name()
+                                    "Component missing registry id, skipping tree entry"
                                 );
                             continue;
                         };
                         let component_node_id =
                             cfg.component_node_id(entity, component_type_id as u64);
-                        let display =
-                            format!("{} (id #{component_type_id})", component.display_name());
+                        let display = registry
+                            .get_descriptor_by_numeric_id(component_type_id)
+                            .map(|desc| format!("{} (id #{component_type_id})", desc.type_name))
+                            .unwrap_or_else(|| format!("Unknown (id #{component_type_id})"));
 
                         let has_rigidbody = world.get::<&RigidBody>(entity).is_ok();
                         let has_collider = world.get::<&ColliderGroup>(entity).is_ok();
@@ -104,7 +102,7 @@ impl<'a> EditorTabViewer<'a> {
                             .label_ui(|ui| {
                                 ui.label(display.clone());
 
-                                if has_rigidbody && !has_collider && component.type_name().contains("RigidBody") {
+                                if has_rigidbody && !has_collider && component.typetag_name().contains("RigidBody") {
                                     ui.add_space(4.0);
                                     ui.small_button("⚠")
                                         .on_hover_text("RigidBody has no colliders! Add the ColliderGroup component");
diff --git a/crates/eucalyptus-editor/src/editor/input.rs b/crates/eucalyptus-editor/src/editor/input.rs
index 925bf63..ef61f48 100644
--- a/crates/eucalyptus-editor/src/editor/input.rs
+++ b/crates/eucalyptus-editor/src/editor/input.rs
@@ -1,10 +1,6 @@
 use std::process::{Command, Stdio};
-use std::thread;
 use super::*;
-use dropbear_engine::{
-    entity::MeshRenderer,
-    input::{Controller, Keyboard, Mouse},
-};
+use dropbear_engine::input::{Controller, Keyboard, Mouse};
 use eucalyptus_core::states::Label;
 use eucalyptus_core::success_without_console;
 use gilrs::{Button, GamepadId};
@@ -13,7 +9,6 @@ use transform_gizmo_egui::{GizmoMode, GizmoOrientation};
 use winit::{
     dpi::PhysicalPosition, event::MouseButton, event_loop::ActiveEventLoop, keyboard::KeyCode,
 };
-use eucalyptus_core::properties::CustomProperties;
 
 impl Keyboard for Editor {
     fn key_down(&mut self, key: KeyCode, _event_loop: &ActiveEventLoop) {
@@ -126,55 +121,35 @@ impl Keyboard for Editor {
                             PROJECT.read().project_path.clone()
                         };
 
-                        thread::spawn(|| {
-                            #[cfg(unix)]
-                            {
-                                use daemonize::Daemonize;
-
-                                let daemonize = Daemonize::new()
-                                    .working_directory("/tmp")
-                                    .umask(0o027);
-
-                                match daemonize.start() {
-                                    Ok(_) => {
-                                        Command::new("gradlew")
-                                            .arg("--stop")
-                                            .current_dir(current_dir)
-                                            .stdin(Stdio::null())
-                                            .stdout(Stdio::null())
-                                            .stderr(Stdio::null())
-                                            .status()
-                                            .ok();
-                                        log::debug!("Stopping gradle threads");
-                                    }
-                                    Err(_) => {
-                                        Command::new("gradlew")
-                                            .arg("--stop")
-                                            .current_dir(current_dir)
-                                            .status()
-                                            .ok();
-                                        log::debug!("Stopping gradle threads");
-                                    }
-                                }
-                            }
+                        #[cfg(unix)]
+                        {
+                            Command::new("gradlew")
+                                .arg("--stop")
+                                .current_dir(current_dir)
+                                .stdin(Stdio::null())
+                                .stdout(Stdio::null())
+                                .stderr(Stdio::null())
+                                .spawn()
+                                .ok();
+                            log::debug!("Stopping gradle threads");
+                        }
 
-                            #[cfg(windows)]
-                            {
-                                use std::os::windows::process::CommandExt;
-                                const DETACHED_PROCESS: u32 = 0x00000008;
+                        #[cfg(windows)]
+                        {
+                            use std::os::windows::process::CommandExt;
+                            const DETACHED_PROCESS: u32 = 0x00000008;
 
-                                Command::new("cmd")
-                                    .args(["/C", "gradlew", "--stop"])
-                                    .current_dir(current_dir)
-                                    .creation_flags(DETACHED_PROCESS)
-                                    .stdin(Stdio::null())
-                                    .stdout(Stdio::null())
-                                    .stderr(Stdio::null())
-                                    .spawn()
-                                    .ok();
-                                log::debug!("Stopping gradle threads");
-                            }
-                        });
+                            Command::new("cmd")
+                                .args(["/C", "gradlew", "--stop"])
+                                .current_dir(current_dir)
+                                .creation_flags(DETACHED_PROCESS)
+                                .stdin(Stdio::null())
+                                .stdout(Stdio::null())
+                                .stderr(Stdio::null())
+                                .spawn()
+                                .ok();
+                            log::debug!("Stopping gradle threads");
+                        }
                     };
                     
                     self.scene_command = SceneCommand::Quit(Some(commands));
@@ -191,32 +166,24 @@ impl Keyboard for Editor {
                         && matches!(tab, EditorTab::ModelEntityList)
                     {
                         if let Some(entity) = &self.selected_entity {
-                            let mut query = self.world.query_one::<(
-                                &Label,
-                                &MeshRenderer,
-                                &EntityTransform,
-                                &CustomProperties,
-                            )>(*entity);
-                            if let Ok((label, renderer, t, props)) = query.get() {
-                                let s_entity = SceneEntity {
-                                    label: label.clone(),
-                                    components: vec![
-                                        Box::new(props.clone()),
-                                        Box::new(SerializedMeshRenderer::from_renderer(
-                                            &renderer,
-                                        )),
-                                        Box::new(t.clone()),
-                                    ],
-                                    entity_id: None,
-                                };
-                                self.signal = Signal::Copy(s_entity);
+                            let Ok(label) = self.world.get::<&Label>(*entity) else {
+                                warn!("Unable to copy entity: Unable to obtain label");
+                                return;
+                            };
 
-                                info!("Copied!");
+                            let components = self
+                                .component_registry
+                                .extract_all_components(self.world.as_ref(), *entity);
+                            let s_entity = SceneEntity {
+                                label: Label::new(label.as_str()),
+                                components,
+                                entity_id: None,
+                            };
+                            self.signal = Signal::Copy(s_entity);
 
-                                log::debug!("Copied selected entity");
-                            } else {
-                                warn!("Unable to copy entity: Unable to obtain lock");
-                            }
+                            info!("Copied!");
+
+                            log::debug!("Copied selected entity");
                         } else {
                             warn!("Unable to copy entity: None selected");
                         }
diff --git a/crates/eucalyptus-editor/src/editor/mod.rs b/crates/eucalyptus-editor/src/editor/mod.rs
index 96b5856..2e24c3e 100644
--- a/crates/eucalyptus-editor/src/editor/mod.rs
+++ b/crates/eucalyptus-editor/src/editor/mod.rs
@@ -22,16 +22,14 @@ use dropbear_engine::buffer::ResizableBuffer;
 use dropbear_engine::entity::EntityTransform;
 use dropbear_engine::graphics::InstanceRaw;
 use dropbear_engine::pipelines::light_cube::LightCubePipeline;
-use dropbear_engine::texture::{Texture, TextureWrapMode};
-use dropbear_engine::{camera::Camera, entity::{MeshRenderer, Transform}, future::FutureHandle, graphics::{SharedGraphicsContext}, scene::SceneCommand, DropbearWindowBuilder, WindowData};
+use dropbear_engine::{camera::Camera, entity::{Transform}, future::FutureHandle, graphics::{SharedGraphicsContext}, scene::SceneCommand, DropbearWindowBuilder, WindowData};
 use egui::{self, Context};
 use egui_dock::{DockArea, DockState, NodeIndex, Style};
+use eucalyptus_core::component::{ComponentRegistry, SerializedComponent};
 use eucalyptus_core::{register_components, APP_INFO};
 use eucalyptus_core::hierarchy::{Children, SceneHierarchy};
 use eucalyptus_core::scene::{SceneConfig, SceneEntity};
-use eucalyptus_core::states::{Label, SerializedMeshRenderer};
-use eucalyptus_core::traits::SerializableComponent;
-use eucalyptus_core::traits::registry::ComponentRegistry;
+use eucalyptus_core::states::Label;
 use eucalyptus_core::{
     camera::{CameraComponent, CameraType, DebugCamera},
     fatal, info,
@@ -39,7 +37,7 @@ use eucalyptus_core::{
     scripting::BuildStatus,
     states,
     states::{
-        EditorTab, Script, WorldLoadingStatus, PROJECT, SCENES,
+        EditorTab, WorldLoadingStatus, PROJECT, SCENES,
     },
     success,
     utils::ViewportMode,
@@ -64,14 +62,13 @@ use winit::window::{CursorGrabMode, WindowAttributes};
 use winit::{keyboard::KeyCode, window::Window};
 use winit::dpi::PhysicalSize;
 use dropbear_engine::mipmap::MipMapper;
-use dropbear_engine::pipelines::{create_render_pipeline, DropbearShaderPipeline};
+use dropbear_engine::pipelines::{DropbearShaderPipeline};
 use dropbear_engine::pipelines::shader::MainRenderPipeline;
 use dropbear_engine::pipelines::GlobalsUniform;
 use dropbear_engine::sky::{HdrLoader, SkyPipeline, DEFAULT_SKY_TEXTURE};
 use eucalyptus_core::physics::collider::{ColliderShapeKey, WireframeGeometry};
 use eucalyptus_core::physics::collider::shader::ColliderInstanceRaw;
 use eucalyptus_core::physics::collider::shader::ColliderWireframePipeline;
-use eucalyptus_core::properties::CustomProperties;
 use crate::about::AboutWindow;
 use crate::editor::console::EucalyptusConsole;
 use crate::editor::settings::editor::{EditorSettingsWindow, EDITOR_SETTINGS};
@@ -171,7 +168,7 @@ pub struct Editor {
     nerd_stats: Rc<RwLock<NerdStats>>,
 
     // component registry
-    component_registry: Arc<ComponentRegistry>,
+    pub component_registry: Arc<ComponentRegistry>,
 
     // play mode process tracking
     pub(crate) play_mode_process: Option<std::process::Child>,
@@ -203,15 +200,16 @@ impl Editor {
 
         for plugin in plugin_registry.plugins.values_mut() {
             let plugin_id = plugin.id().to_string();
-            let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
-                plugin.register_component(&mut component_registry);
-            }));
-
-            if result.is_ok() {
-                log::info!("Registered components for plugin '{plugin_id}'");
-            } else {
-                warn!("Plugin '{plugin_id}' panicked during component registration");
-            }
+            log::debug!("Located plugin: {}", plugin_id);
+            // let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
+            //     plugin.register_component(&mut component_registry);
+            // }));
+
+            // if result.is_ok() {
+            //     log::info!("Registered components for plugin '{plugin_id}'");
+            // } else {
+            //     warn!("Plugin '{plugin_id}' panicked during component registration");
+            // }
         }
 
         let component_registry = Arc::new(component_registry);
@@ -541,7 +539,7 @@ impl Editor {
                     .load_into_world(
                         world,
                         graphics,
-                        Some(component_registry.as_ref()),
+                        &component_registry.clone(),
                         sender.clone(),
                         false,
                     )
@@ -688,7 +686,7 @@ impl Editor {
                 .load_into_world(
                     &mut temp_world,
                     graphics_shared.clone(),
-                    Some(component_registry_clone.as_ref()),
+                    &component_registry_clone,
                     Some(progress_sender.clone()),
                     false,
                 )
@@ -917,33 +915,22 @@ impl Editor {
                 ui.menu_button("Edit", |ui| {
                     if ui.button("Copy").clicked() {
                         if let Some(entity) = &self.selected_entity {
-                            let mut query = self.world.query_one::<(
-                                &Label,
-                                &MeshRenderer,
-                                &EntityTransform,
-                                &CustomProperties,
-                            )>(*entity);
-                            if let Ok((entity_label, renderer, transform, props)) = query.get() {
-                                let mut components: Vec<Box<dyn SerializableComponent>> = Vec::new();
-
-                                components.push(Box::new(*transform));
-
-                                let serialized_renderer = SerializedMeshRenderer::from_renderer(renderer);
-                                components.push(Box::new(serialized_renderer));
-
-                                components.push(Box::new(props.clone()));
-
-                                let s_entity = SceneEntity {
-                                    label: entity_label.clone(),
-                                    components,
-                                    entity_id: None,
-                                };
-                                self.signal = Signal::Copy(s_entity);
-
-                                info!("Copied selected entity!");
-                            } else {
-                                warn!("Unable to copy entity: Unable to obtain lock");
-                            }
+                            let Ok(label) = self.world.get::<&Label>(*entity) else {
+                                warn!("Unable to copy entity: Unable to obtain label");
+                                return;
+                            };
+
+                            let components = self
+                                .component_registry
+                                .extract_all_components(self.world.as_ref(), *entity);
+                            let s_entity = SceneEntity {
+                                label: Label::new(label.as_str()),
+                                components,
+                                entity_id: None,
+                            };
+                            self.signal = Signal::Copy(s_entity);
+
+                            info!("Copied selected entity!");
                         } else {
                             warn!("Unable to copy entity: None selected");
                         }
@@ -1419,10 +1406,10 @@ pub enum Signal {
     Undo,
     Play,
     StopPlaying,
-    LogEntities,
     /// Adds a new component instance using the async init pipeline.
-    AddComponent(hecs::Entity, Box<dyn SerializableComponent>),
+    AddComponent(hecs::Entity, Box<dyn SerializedComponent>),
     RequestNewWindow(WindowData),
+    UpdateViewportSize((f32, f32)),
 }
 
 #[derive(Debug)]
diff --git a/crates/eucalyptus-editor/src/editor/scene.rs b/crates/eucalyptus-editor/src/editor/scene.rs
index 3cd1607..f9cee19 100644
--- a/crates/eucalyptus-editor/src/editor/scene.rs
+++ b/crates/eucalyptus-editor/src/editor/scene.rs
@@ -16,11 +16,9 @@ use dropbear_engine::{
     model::{DrawLight, DrawModel},
     scene::{Scene, SceneCommand},
 };
-use eucalyptus_core::hierarchy::EntityTransformExt;
 use eucalyptus_core::states::{Label, WorldLoadingStatus};
 use log;
 use parking_lot::Mutex;
-use wgpu::Color;
 use winit::{event_loop::ActiveEventLoop, keyboard::KeyCode};
 use eucalyptus_core::physics::collider::ColliderGroup;
 use eucalyptus_core::physics::collider::ColliderShapeKey;
@@ -409,27 +407,6 @@ impl Scene for Editor {
             prepared_models.push((model, instance_buffer, instances.len() as u32));
         }
 
-        {
-            let mut query = self.world.query::<(
-                &mut LightComponent,
-                Option<&dropbear_engine::entity::Transform>,
-                Option<&dropbear_engine::entity::EntityTransform>,
-                &mut Light,
-            )>();
-
-            for (light_component, transform_opt, entity_transform_opt, light) in query.iter() {
-                let transform = if let Some(entity_transform) = entity_transform_opt {
-                    entity_transform.sync()
-                } else if let Some(transform) = transform_opt {
-                    *transform
-                } else {
-                    continue;
-                };
-
-                light.update(graphics.as_ref(), light_component, &transform);
-            }
-        }
-
         let registry = ASSET_REGISTRY.read();
         {
             let mut render_pass = encoder
diff --git a/crates/eucalyptus-editor/src/editor/viewport.rs b/crates/eucalyptus-editor/src/editor/viewport.rs
index ed5596d..2a349f5 100644
--- a/crates/eucalyptus-editor/src/editor/viewport.rs
+++ b/crates/eucalyptus-editor/src/editor/viewport.rs
@@ -3,7 +3,7 @@ use dropbear_engine::camera::Camera;
 use dropbear_engine::entity::{EntityTransform, Transform};
 use eucalyptus_core::camera::CameraComponent;
 use eucalyptus_core::utils::ViewportMode;
-use crate::editor::{EditorTabViewer, StaticallyKept, UndoableAction, TABS_GLOBAL};
+use crate::editor::{EditorTabViewer, Signal, TABS_GLOBAL, UndoableAction};
 
 impl<'a> EditorTabViewer<'a> {
     pub(crate) fn viewport_tab(&mut self, ui: &mut egui::Ui) {
@@ -14,6 +14,8 @@ impl<'a> EditorTabViewer<'a> {
         let available_rect = ui.available_rect_before_wrap();
         let available_size = available_rect.size();
 
+        *self.signal = Signal::UpdateViewportSize((available_size.x, available_size.y));
+
         let tex_aspect = self.tex_size.width as f32 / self.tex_size.height as f32;
         let available_aspect = available_size.x / available_size.y;
 
diff --git a/crates/eucalyptus-editor/src/plugin.rs b/crates/eucalyptus-editor/src/plugin.rs
index a1bc90b..7305059 100644
--- a/crates/eucalyptus-editor/src/plugin.rs
+++ b/crates/eucalyptus-editor/src/plugin.rs
@@ -3,22 +3,19 @@ use app_dirs2::AppDataType;
 use egui::Ui;
 use eucalyptus_core::APP_INFO;
 use eucalyptus_core::states::PluginInfo;
-use eucalyptus_core::traits::registry::ComponentRegistry;
 use indexmap::IndexMap;
 use libloading as lib;
 use std::fs::ReadDir;
 use std::path::PathBuf;
 use std::time::Instant;
 
+// this is for another time its just a blabber of code
+
 pub trait EditorPlugin: Send + Sync {
     fn id(&self) -> &str;
     fn display_name(&self) -> &str;
     fn ui(&mut self, ui: &mut Ui, editor: &mut Editor);
     fn tab_title(&self) -> &str;
-
-    /// Allows you to register the struct [Self] as a component to the component registry.
-    /// This will then be listed as an option for a potential component a user could add.
-    fn register_component(&mut self, registry: &mut ComponentRegistry);
 }
 
 pub type PluginConstructor = fn() -> Box<dyn EditorPlugin>;
diff --git a/crates/eucalyptus-editor/src/signal.rs b/crates/eucalyptus-editor/src/signal.rs
index 43d5fce..8c0a5c8 100644
--- a/crates/eucalyptus-editor/src/signal.rs
+++ b/crates/eucalyptus-editor/src/signal.rs
@@ -1,24 +1,14 @@
 use crate::editor::{Editor, EditorState, Signal};
-use dropbear_engine::asset::ASSET_REGISTRY;
-use dropbear_engine::camera::Camera;
-use dropbear_engine::entity::{EntityTransform, MeshRenderer, Transform};
 use dropbear_engine::graphics::SharedGraphicsContext;
-use dropbear_engine::lighting::{Light as EngineLight, LightComponent};
-use dropbear_engine::model::{Material, Model};
-use dropbear_engine::procedural::{ProcedurallyGeneratedObject, ProcObj};
-use dropbear_engine::texture::{Texture, TextureWrapMode};
 use dropbear_engine::utils::{
-    relative_path_from_euca, EUCA_SCHEME, ResourceReference, ResourceReferenceType,
+    relative_path_from_euca, EUCA_SCHEME, 
 };
-use dropbear_engine::component::{ComponentInitContext, ComponentInsert, ComponentResources};
 use egui::Align2;
+use crate::spawn::{push_pending_spawn, PendingSpawn};
 use eucalyptus_core::camera::{CameraComponent, CameraType};
-use eucalyptus_core::properties::CustomProperties;
 use eucalyptus_core::scripting::{build_jvm, BuildStatus};
-use eucalyptus_core::spawn::{push_pending_spawn, PendingSpawn};
-use eucalyptus_core::states::{EditorTab, Label, Script, PROJECT};
+use eucalyptus_core::states::{EditorTab, PROJECT};
 use eucalyptus_core::{fatal, info, success, success_without_console, warn, warn_without_console};
-use std::any::TypeId;
 use std::path::PathBuf;
 use std::sync::Arc;
 use winit::keyboard::KeyCode;
@@ -34,33 +24,6 @@ fn resolve_editor_path(uri: &str) -> PathBuf {
     }
 }
 
-struct InsertMeshRenderer {
-    renderer: MeshRenderer,
-    ensure_transform: bool,
-}
-
-impl ComponentInsert for InsertMeshRenderer {
-    fn insert(
-        self: Box<Self>,
-        world: &mut hecs::World,
-        entity: hecs::Entity,
-    ) -> anyhow::Result<()> {
-        if world.get::<&MeshRenderer>(entity).is_ok() {
-            let _ = world.remove_one::<MeshRenderer>(entity);
-        }
-
-        world
-            .insert_one(entity, self.renderer)
-            .map_err(|e| anyhow::anyhow!(e.to_string()))?;
-
-        if self.ensure_transform && world.get::<&EntityTransform>(entity).is_err() {
-            let _ = world.insert_one(entity, EntityTransform::default());
-        }
-
-        Ok(())
-    }
-}
-
 pub trait SignalController {
     fn run_signal(&mut self, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<()>;
 }
@@ -491,88 +454,21 @@ impl SignalController for Editor {
                 self.signal = Signal::None;
                 Ok(())
             }
-            Signal::LogEntities => {
-                log::debug!("====================");
-                log::info!("world total items: {}", self.world.len());
-                log::info!("typeid of Label: {:?}", TypeId::of::<Label>());
-                log::info!("typeid of MeshRenderer: {:?}", TypeId::of::<MeshRenderer>());
-                log::info!("typeid of Transform: {:?}", TypeId::of::<Transform>());
-                log::info!(
-                    "typeid of ModelProperties: {:?}",
-                    TypeId::of::<CustomProperties>()
-                );
-                log::info!("typeid of Camera: {:?}", TypeId::of::<Camera>());
-                log::info!(
-                    "typeid of CameraComponent: {:?}",
-                    TypeId::of::<CameraComponent>()
-                );
-                log::info!("typeid of Script: {:?}", TypeId::of::<Script>());
-                log::info!("typeid of EngineLight: {:?}", TypeId::of::<EngineLight>());
-                log::info!(
-                    "typeid of LightComponent: {:?}",
-                    TypeId::of::<LightComponent>()
-                );
-                for i in self.world.iter() {
-                    log::info!("entity id: {:?}", i.entity().id());
-                    log::info!("entity bytes: {:?}", i.entity().to_bits().get());
-                    log::info!(
-                        "components [{}]: ",
-                        i.component_types().collect::<Vec<_>>().len()
-                    );
-                    let mut comp_builder = String::new();
-                    for j in i.component_types() {
-                        comp_builder.push_str(format!("{:?} ", j).as_str());
-                        if TypeId::of::<Label>() == j {
-                            log::info!(" |- Label");
-                        }
-
-                        if TypeId::of::<MeshRenderer>() == j {
-                            log::info!(" |- MeshRenderer");
-                        }
-
-                        if TypeId::of::<Transform>() == j {
-                            log::info!(" |- Transform");
-                        }
-
-                        if TypeId::of::<CustomProperties>() == j {
-                            log::info!(" |- ModelProperties");
-                        }
-
-                        if TypeId::of::<Camera>() == j {
-                            log::info!(" |- Camera");
-                        }
-
-                        if TypeId::of::<CameraComponent>() == j {
-                            log::info!(" |- CameraComponent");
-                        }
-
-                        if TypeId::of::<Script>() == j {
-                            log::info!(" |- Script");
-                        }
-
-                        if TypeId::of::<EngineLight>() == j {
-                            log::info!(" |- Light");
-                        }
-
-                        if TypeId::of::<LightComponent>() == j {
-                            log::info!(" |- LightComponent");
-                        }
-                        log::info!("----------")
-                    }
-                    log::info!("components (typeid) [{}]: ", comp_builder);
-                }
-                self.signal = Signal::None;
-                Ok(())
-            }
             Signal::AddComponent(entity, component) => {
-                let mut resources = ComponentResources::new();
-                resources.insert(graphics.clone());
-                let ctx = ComponentInitContext {
-                    entity: *entity,
-                    resources: Arc::new(resources),
-                };
+                let component = component.clone();
+                let registry = self.component_registry.clone();
+                let graphics_clone = graphics.clone();
+                let init_future = async move {
+                    let Some(loader_future) =
+                        registry.load_component(component.as_ref(), graphics_clone.clone())
+                    else {
+                        return Err(anyhow::anyhow!(
+                            "Component type is not registered in ComponentRegistry"
+                        ));
+                    };
 
-                let init_future = component.init(ctx);
+                    loader_future.await
+                };
                 let handle = graphics.future_queue.push(init_future);
                 self.pending_components.push((*entity, handle));
 
@@ -586,6 +482,19 @@ impl SignalController for Editor {
                 self.signal = Signal::None;
                 Ok(())
             }
+            Signal::UpdateViewportSize((x, y)) => {
+                let width = x.max(1.0).round() as u32;
+                let height = y.max(1.0).round() as u32;
+                let current_size = graphics.viewport_texture.size;
+
+                if current_size.width != width || current_size.height != height {
+                    self.scene_command = dropbear_engine::scene::SceneCommand::ResizeViewport((
+                        width, height,
+                    ));
+                }
+                self.signal = Signal::None;
+                Ok(())
+            }
         }?;
         if !show {
             self.signal = Signal::None;
diff --git a/crates/eucalyptus-editor/src/spawn.rs b/crates/eucalyptus-editor/src/spawn.rs
index df16315..006d9f6 100644
--- a/crates/eucalyptus-editor/src/spawn.rs
+++ b/crates/eucalyptus-editor/src/spawn.rs
@@ -1,14 +1,37 @@
 use crate::editor::Editor;
-use dropbear_engine::entity::{EntityTransform, MeshRenderer};
-use dropbear_engine::future::FutureQueue;
-use dropbear_engine::graphics::{SharedGraphicsContext};
-use dropbear_engine::model::{Model};
-pub(crate) use eucalyptus_core::spawn::{PendingSpawnController, PENDING_SPAWNS};
-use eucalyptus_core::{fatal, success};
-use hecs::Entity;
-use std::sync::Arc;
 use dropbear_engine::asset::Handle;
-use dropbear_engine::component::{ComponentInitContext, ComponentInsert, ComponentResources};
+use dropbear_engine::entity::{EntityTransform, MeshRenderer};
+use dropbear_engine::future::{FutureHandle, FutureQueue};
+use dropbear_engine::graphics::SharedGraphicsContext;
+use dropbear_engine::model::Model;
+use eucalyptus_core::hierarchy::Parent;
+use eucalyptus_core::scene::SceneEntity;
+use eucalyptus_core::states::Label;
+use eucalyptus_core::{fatal, success, warn};
+use hecs::{Entity, EntityBuilder};
+use parking_lot::Mutex;
+use std::sync::{Arc, LazyLock};
+
+#[derive(Clone)]
+pub struct PendingSpawn {
+    pub scene_entity: SceneEntity,
+    pub handle: Option<FutureHandle>,
+}
+
+pub static PENDING_SPAWNS: LazyLock<Mutex<Vec<PendingSpawn>>> =
+    LazyLock::new(|| Mutex::new(Vec::new()));
+
+pub fn push_pending_spawn(spawn: PendingSpawn) {
+    PENDING_SPAWNS.lock().push(spawn);
+}
+
+pub trait PendingSpawnController {
+    fn check_up(
+        &mut self,
+        graphics: Arc<SharedGraphicsContext>,
+        queue: Arc<FutureQueue>,
+    ) -> anyhow::Result<()>;
+}
 
 impl PendingSpawnController for Editor {
     fn check_up(
@@ -27,25 +50,34 @@ impl PendingSpawnController for Editor {
             );
 
             if spawn.handle.is_none() {
-                let entity = self.world.spawn((spawn.scene_entity.label.clone(),));
                 let components = spawn.scene_entity.components.clone();
-
-                let mut resources = ComponentResources::new();
-                resources.insert(graphics.clone());
-                let resources = Arc::new(resources);
+                let registry = self.component_registry.clone();
+                let label = spawn.scene_entity.label.clone();
+                let graphics = graphics.clone();
 
                 let future = async move {
-                    let mut inserts: Vec<Box<dyn ComponentInsert>> = Vec::new();
+                    let mut appliers: Vec<Box<dyn for<'a> FnOnce(&'a mut EntityBuilder) + Send + Sync>> =
+                        Vec::new();
                     for component in components {
-                        let ctx = ComponentInitContext {
-                            entity,
-                            resources: resources.clone(),
+                        if component.as_any().downcast_ref::<Parent>().is_some() {
+                            continue;
+                        }
+
+                        let Some(loader_future) =
+                            registry.load_component(component.as_ref(), graphics.clone())
+                        else {
+                            warn!("Skipping unregistered serialized component for '{}'", label);
+                            continue;
                         };
-                        let insert = component.init(ctx).await?;
-                        inserts.push(insert);
+
+                        let applier = loader_future.await?;
+                        appliers.push(applier);
                     }
 
-                    Ok::<(Entity, Vec<Box<dyn ComponentInsert>>), anyhow::Error>((entity, inserts))
+                    Ok::<(Label, Vec<Box<dyn for<'a> FnOnce(&'a mut EntityBuilder) + Send + Sync>>), anyhow::Error>((
+                        label,
+                        appliers,
+                    ))
                 };
 
                 let handle = queue.push(Box::pin(future));
@@ -54,21 +86,21 @@ impl PendingSpawnController for Editor {
 
             if let Some(handle) = &spawn.handle {
                 if let Some(result) = queue.exchange_owned(handle) {
-                    if let Ok(r) = result.downcast::<anyhow::Result<(Entity, Vec<Box<dyn ComponentInsert>>)>>() {
+                    if let Ok(r) = result.downcast::<anyhow::Result<(Label, Vec<Box<dyn for<'a> FnOnce(&'a mut EntityBuilder) + Send + Sync>>)>>() {
                         match Arc::try_unwrap(r) {
-                            Ok(Ok((entity, inserts))) => {
-                                for insert in inserts {
-                                    insert.insert(&mut self.world, entity)?;
+                            Ok(Ok((label, appliers))) => {
+                                let mut builder = EntityBuilder::new();
+                                builder.add(label.clone());
+                                for applier in appliers {
+                                    applier(&mut builder);
                                 }
 
+                                let entity = self.world.spawn(builder.build());
                                 if self.world.get::<&EntityTransform>(entity).is_err() {
                                     let _ = self.world.insert_one(entity, EntityTransform::default());
                                 }
 
-                                success!(
-                                    "Spawned '{}' from pending queue",
-                                    spawn.scene_entity.label
-                                );
+                                success!("Spawned '{}' from pending queue", label);
                                 completed.push(index);
                             }
                             Ok(Err(err)) => {
@@ -100,11 +132,16 @@ 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 ComponentInsert>>>() {
+                if let Ok(r) = result.downcast::<anyhow::Result<Box<dyn for<'a> FnOnce(&'a mut EntityBuilder) + Send + Sync>>>() {
                     match Arc::try_unwrap(r) {
-                        Ok(Ok(insert)) => {
-                            insert.insert(&mut self.world, *entity)?;
-                            success!("Added component to entity {:?}", entity);
+                        Ok(Ok(applier)) => {
+                            let mut builder = EntityBuilder::new();
+                            applier(&mut builder);
+                            if let Err(e) = self.world.insert(*entity, builder.build()) {
+                                fatal!("Failed to add component bundle: {}", e);
+                            } else {
+                                success!("Added component to entity {:?}", entity);
+                            }
                             completed_components.push(index);
                         }
                         Ok(Err(e)) => {
diff --git a/include/dropbear.h b/include/dropbear.h
index 48fbcb5..6420743 100644
--- a/include/dropbear.h
+++ b/include/dropbear.h
@@ -179,6 +179,8 @@ typedef struct NChannelValuesFfi {
 
 typedef NChannelValuesFfi NChannelValues;
 
+typedef void* GraphicsContextPtr;
+
 typedef struct IndexNative {
     uint32_t index;
     uint32_t generation;
@@ -190,22 +192,30 @@ typedef struct NCollider {
     uint32_t id;
 } NCollider;
 
-typedef struct NTransform {
-    NVector3 position;
-    NQuaternion rotation;
-    NVector3 scale;
-} NTransform;
-
 typedef struct NColliderArray {
     NCollider* values;
     size_t length;
     size_t capacity;
 } NColliderArray;
 
-typedef struct NVector2 {
-    double x;
-    double y;
-} NVector2;
+typedef struct u64Array {
+    uint64_t* values;
+    size_t length;
+    size_t capacity;
+} u64Array;
+
+typedef struct ConnectedGamepadIds {
+    u64Array ids;
+} ConnectedGamepadIds;
+
+typedef struct NColour {
+    uint8_t r;
+    uint8_t g;
+    uint8_t b;
+    uint8_t a;
+} NColour;
+
+typedef void* AssetRegistryPtr;
 
 typedef struct AxisLock {
     bool x;
@@ -220,6 +230,11 @@ typedef struct NVector4 {
     double w;
 } NVector4;
 
+typedef struct NVector2 {
+    double x;
+    double y;
+} NVector2;
+
 typedef struct NMaterial {
     const char* name;
     uint64_t diffuse_texture;
@@ -244,57 +259,48 @@ typedef struct NMaterialArray {
     size_t capacity;
 } NMaterialArray;
 
-typedef struct i32Array {
-    int32_t* values;
+typedef struct f64Array {
+    double* values;
     size_t length;
     size_t capacity;
-} i32Array;
+} f64Array;
 
-typedef struct f64ArrayArray {
-    double* values;
+typedef struct NAnimationChannel {
+    int32_t target_node;
+    f64Array times;
+    NChannelValues values;
+    NAnimationInterpolation interpolation;
+} NAnimationChannel;
+
+typedef struct NAnimationChannelArray {
+    NAnimationChannel* values;
     size_t length;
     size_t capacity;
-} f64ArrayArray;
+} NAnimationChannelArray;
 
-typedef struct NSkin {
+typedef struct NAnimation {
     const char* name;
-    i32Array joints;
-    f64ArrayArray inverse_bind_matrices;
-    const int32_t* skeleton_root;
-} NSkin;
+    NAnimationChannelArray channels;
+    float duration;
+} NAnimation;
 
-typedef struct NSkinArray {
-    NSkin* values;
+typedef struct NAnimationArray {
+    NAnimation* values;
     size_t length;
     size_t capacity;
-} NSkinArray;
+} NAnimationArray;
 
 typedef void* InputStatePtr;
 
-typedef void* GraphicsContextPtr;
+typedef void* SceneLoaderPtr;
 
 typedef void* PhysicsStatePtr;
 
-typedef struct IndexNativeArray {
-    IndexNative* values;
+typedef struct i32Array {
+    int32_t* values;
     size_t length;
     size_t capacity;
-} IndexNativeArray;
-
-typedef struct CharacterCollisionArray {
-    uint64_t entity_id;
-    IndexNativeArray collisions;
-} CharacterCollisionArray;
-
-typedef struct Progress {
-    size_t current;
-    size_t total;
-    const char* message;
-} Progress;
-
-typedef void* AssetRegistryPtr;
-
-typedef void* WorldPtr;
+} i32Array;
 
 typedef struct NModelVertex {
     NVector3 position;
@@ -326,56 +332,41 @@ typedef struct NMeshArray {
     size_t capacity;
 } NMeshArray;
 
-typedef struct f64Array {
+typedef struct f64ArrayArray {
     double* values;
     size_t length;
     size_t capacity;
-} f64Array;
-
-typedef struct NAnimationChannel {
-    int32_t target_node;
-    f64Array times;
-    NChannelValues values;
-    NAnimationInterpolation interpolation;
-} NAnimationChannel;
-
-typedef struct NAnimationChannelArray {
-    NAnimationChannel* values;
-    size_t length;
-    size_t capacity;
-} NAnimationChannelArray;
+} f64ArrayArray;
 
-typedef struct NAnimation {
+typedef struct NSkin {
     const char* name;
-    NAnimationChannelArray channels;
-    float duration;
-} NAnimation;
+    i32Array joints;
+    f64ArrayArray inverse_bind_matrices;
+    const int32_t* skeleton_root;
+} NSkin;
 
-typedef struct NAnimationArray {
-    NAnimation* values;
+typedef struct NSkinArray {
+    NSkin* values;
     size_t length;
     size_t capacity;
-} NAnimationArray;
+} NSkinArray;
 
-typedef struct u64Array {
-    uint64_t* values;
+typedef struct NTransform {
+    NVector3 position;
+    NQuaternion rotation;
+    NVector3 scale;
+} NTransform;
+
+typedef struct IndexNativeArray {
+    IndexNative* values;
     size_t length;
     size_t capacity;
-} u64Array;
-
-typedef struct ConnectedGamepadIds {
-    u64Array ids;
-} ConnectedGamepadIds;
+} IndexNativeArray;
 
-typedef struct NShapeCastHit {
-    NCollider collider;
-    double distance;
-    NVector3 witness1;
-    NVector3 witness2;
-    NVector3 normal1;
-    NVector3 normal2;
-    NShapeCastStatus status;
-} NShapeCastHit;
+typedef struct CharacterCollisionArray {
+    uint64_t entity_id;
+    IndexNativeArray collisions;
+} CharacterCollisionArray;
 
 typedef struct NAttenuation {
     float constant;
@@ -383,15 +374,36 @@ typedef struct NAttenuation {
     float quadratic;
 } NAttenuation;
 
+typedef struct RayHit {
+    NCollider collider;
+    double distance;
+} RayHit;
+
+typedef struct Progress {
+    size_t current;
+    size_t total;
+    const char* message;
+} Progress;
+
 typedef struct RigidBodyContext {
     IndexNative index;
     uint64_t entity_id;
 } RigidBodyContext;
 
-typedef struct RayHit {
+typedef struct NRange {
+    float start;
+    float end;
+} NRange;
+
+typedef struct NShapeCastHit {
     NCollider collider;
     double distance;
-} RayHit;
+    NVector3 witness1;
+    NVector3 witness2;
+    NVector3 normal1;
+    NVector3 normal2;
+    NShapeCastStatus status;
+} NShapeCastHit;
 
 typedef struct NNodeTransform {
     NVector3 translation;
@@ -412,22 +424,10 @@ typedef struct NNodeArray {
     size_t capacity;
 } NNodeArray;
 
-typedef struct NColour {
-    uint8_t r;
-    uint8_t g;
-    uint8_t b;
-    uint8_t a;
-} NColour;
-
-typedef struct NRange {
-    float start;
-    float end;
-} NRange;
-
-typedef void* SceneLoaderPtr;
-
 typedef void* CommandBufferPtr;
 
+typedef void* WorldPtr;
+
 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);
 int32_t dropbear_gamepad_get_right_stick_position(InputStatePtr input, uint64_t gamepad_id, NVector2* out0);