kitgit

tirbofish/dropbear · diff

e9c9f39 · Thribhu K

refactor: got redback-runtime compilable

Unverified

diff --git a/Cargo.toml b/Cargo.toml
index 1e5e384..ca1cbaa 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -87,6 +87,7 @@ bitflags = "2.10"
 features = "0.10"
 puffin_http = "0.16"
 dyn-clone = "1.0"
+downcast-rs = "2.0"
 
 [workspace.dependencies.image]
 version = "0.24"
diff --git a/crates/eucalyptus-core/Cargo.toml b/crates/eucalyptus-core/Cargo.toml
index bf8ce15..d91d577 100644
--- a/crates/eucalyptus-core/Cargo.toml
+++ b/crates/eucalyptus-core/Cargo.toml
@@ -46,6 +46,7 @@ bytemuck.workspace = true
 thiserror.workspace = true
 combine.workspace = true
 dyn-clone.workspace = true
+downcast-rs.workspace = true
 
 [features]
 default = []
diff --git a/crates/eucalyptus-core/src/animation/mod.rs b/crates/eucalyptus-core/src/animation/mod.rs
index 3d4303b..ef925a1 100644
--- a/crates/eucalyptus-core/src/animation/mod.rs
+++ b/crates/eucalyptus-core/src/animation/mod.rs
@@ -30,8 +30,11 @@ impl Component for AnimationComponent {
         Ok((Self::default(), ))
     }
 
-    async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
-        Ok((ser, ))
+    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>> {
+        Box::pin(async move { Ok((ser.clone(), )) })
     }
 
     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 5f9a49b..6e38fe5 100644
--- a/crates/eucalyptus-core/src/camera.rs
+++ b/crates/eucalyptus-core/src/camera.rs
@@ -45,10 +45,18 @@ impl Component for Camera {
         Ok((cam, comp))
     }
 
-    async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
-        let label = ser.label.clone();
-        let builder = CameraBuilder::from(ser.clone());
-        Ok((Camera::new(graphics.clone(), builder, Some(label.as_str())), CameraComponent::from(ser)))
+    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>> {
+        Box::pin(async move {
+            let label = ser.label.clone();
+            let builder = CameraBuilder::from(ser.clone());
+            Ok((
+                Camera::new(graphics.clone(), builder, Some(label.as_str())),
+                CameraComponent::from(ser.clone()),
+            ))
+        })
     }
 
     fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, graphics: Arc<SharedGraphicsContext>) {
diff --git a/crates/eucalyptus-core/src/component.rs b/crates/eucalyptus-core/src/component.rs
index f30e02c..107ff0d 100644
--- a/crates/eucalyptus-core/src/component.rs
+++ b/crates/eucalyptus-core/src/component.rs
@@ -1,5 +1,7 @@
 use std::any::TypeId;
 use std::collections::HashMap;
+use std::future::Future;
+use std::pin::Pin;
 use std::sync::Arc;
 use egui::{CollapsingHeader, Ui};
 use hecs::{Entity, World};
@@ -14,6 +16,7 @@ use dropbear_engine::utils::{ResourceReference, ResourceReferenceType};
 use crate::hierarchy::EntityTransformExt;
 use crate::states::{SerializedMaterialCustomisation, SerializedMeshRenderer};
 use crate::utils::ResolveReference;
+use downcast_rs::{Downcast, impl_downcast};
 
 pub use typetag::*;
 
@@ -26,9 +29,22 @@ pub struct ComponentRegistry {
     categories: HashMap<String, Vec<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>,
 }
 
+type LoaderFuture<'a> = Pin<Box<
+    dyn Future<Output = anyhow::Result<Box<dyn for<'b> FnOnce(&'b mut hecs::EntityBuilder)>>> + Send + 'a
+>>;
+type LoaderFn = Box<
+    dyn for<'a> Fn(&'a dyn SerializedComponent, Arc<SharedGraphicsContext>) -> LoaderFuture<'a>
+        + Send
+        + Sync
+>;
 type ExtractorFn = Box<dyn Fn(&hecs::World, hecs::Entity) -> Option<Box<dyn SerializedComponent>> + Send + Sync>;
+type UpdateFn = Box<dyn Fn(&mut hecs::World, f32, Arc<SharedGraphicsContext>) + Send + Sync>;
 
 impl ComponentRegistry {
     pub fn new() -> Self {
@@ -36,34 +52,56 @@ impl ComponentRegistry {
             descriptors: HashMap::new(),
             fqtn_to_type: HashMap::new(),
             categories: HashMap::new(),
-            extractors: Default::default(),
+            extractors: HashMap::new(),
+            loaders: HashMap::new(),
+            updaters: HashMap::new(),
         }
     }
 
     /// Register a component type with the registry
-    pub fn register<T: Component + 'static + Sync + Send>(&mut self) {
+    pub fn register<T>(&mut self)
+    where
+        T: Component + Send + Sync + 'static,
+        T::SerializedForm: 'static,
+    {
         let type_id = TypeId::of::<T>();
-        let descriptor = T::descriptor();
-
-        self.fqtn_to_type.insert(descriptor.fqtn.clone(), type_id);
+        let desc = T::descriptor();
 
-        if let Some(ref category) = descriptor.category {
-            self.categories
-                .entry(category.clone())
-                .or_insert_with(Vec::new)
-                .push(type_id);
+        self.fqtn_to_type.insert(desc.fqtn.clone(), type_id);
+        if let Some(ref cat) = desc.category {
+            self.categories.entry(cat.clone()).or_default().push(type_id);
         }
-
-        self.extractors.insert(
-            type_id,
-            Box::new(|world: &hecs::World, entity: hecs::Entity| {
-                world.get::<&T>(entity).ok().map(|component| {
-                    component.save(world, entity)
-                })
+        self.descriptors.insert(type_id, desc);
+
+        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| {
+            let serialized = serialized
+                .as_any()
+                .downcast_ref::<T::SerializedForm>()
+                .expect("type mismatch in loader — registry bug");
+
+            Box::pin(async move {
+                let bundle = T::init(serialized, graphics).await?;
+                let applier: Box<dyn FnOnce(&mut hecs::EntityBuilder)> =
+                    Box::new(move |builder: &mut hecs::EntityBuilder| {
+                        builder.add_bundle(bundle);
+                    });
+                Ok(applier)
             })
-        );
-
-        self.descriptors.insert(type_id, descriptor);
+        }));
+
+        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)>();
+            for (entity, component) in query.iter() {
+                let world_ref = unsafe { &*world_ptr };
+                component.update_component(world_ref, entity, dt, graphics.clone());
+            }
+        }));
     }
 
     /// Get descriptor for a specific component type
@@ -161,6 +199,30 @@ impl ComponentRegistry {
             })
             .unwrap_or_default()
     }
+
+    /// 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();
+        self.loaders
+            .get(&type_id)
+            .map(|loader| loader(serialized, graphics))
+    }
+
+    /// Updates all registered components that exist in the world.
+    pub fn update_components(
+        &self,
+        world: &mut hecs::World,
+        dt: f32,
+        graphics: Arc<SharedGraphicsContext>,
+    ) {
+        for updater in self.updaters.values() {
+            updater(world, dt, graphics.clone());
+        }
+    }
 }
 
 impl Default for ComponentRegistry {
@@ -171,8 +233,9 @@ impl Default for ComponentRegistry {
 
 /// A blanket trait for types that can be serialized as a component.
 #[typetag::serde(tag = "type")]
-pub trait SerializedComponent: dyn_clone::DynClone + Send + Sync {}
+pub trait SerializedComponent: Downcast + dyn_clone::DynClone + Send + Sync {}
 
+impl_downcast!(SerializedComponent);
 dyn_clone::clone_trait_object!(SerializedComponent);
 
 #[derive(Clone, Debug)]
@@ -188,7 +251,7 @@ pub struct ComponentDescriptor {
 }
 
 /// Defines a type that can be considered a component of an entity.
-pub trait Component: Sized {
+pub trait Component: Sized + Sync + Send {
     /// A custom format of the component for saving the state of the component to disk.
     ///
     /// To have your type available, you must include this blanket trait:
@@ -211,7 +274,10 @@ pub trait Component: Sized {
 
     /// 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::RequiredComponentTypes>;
+    fn init<'a>(
+        ser: &'a Self::SerializedForm,
+        graphics: Arc<SharedGraphicsContext>,
+    ) -> Pin<Box<dyn Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>>;
 
     /// 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>);
@@ -248,109 +314,130 @@ impl Component for MeshRenderer {
         Ok((MeshRenderer::from_handle(Handle::NULL), ))
     }
 
-    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 {
-            ResourceReferenceType::None => {
-                log::debug!("ResourceReferenceType is None, setting to `Handle::NULL`");
-                Handle::NULL
-            }
-            ResourceReferenceType::File(_) => {
-                log::debug!("Loading model from file: {:?}", ser.handle);
-                let path = ser.handle.resolve()?;
-                let buffer = std::fs::read(&path)?;
-                Model::load_from_memory_raw(graphics.clone(), buffer, Some(ser.label.as_str()), ASSET_REGISTRY.clone()).await?
-            }
-            ResourceReferenceType::Bytes(bytes) => {
-                log::debug!("Loading model from bytes [Len: {}]", bytes.len());
-                Model::load_from_memory_raw(graphics.clone(), bytes, Some(ser.label.as_str()), ASSET_REGISTRY.clone()).await?
-            }
-            ResourceReferenceType::ProcObj(obj) => match obj {
-                dropbear_engine::procedural::ProcObj::Cuboid { size_bits } => {
-                    let size = [
-                        f32::from_bits(size_bits[0]),
-                        f32::from_bits(size_bits[1]),
-                        f32::from_bits(size_bits[2]),
-                    ];
-                    log::debug!("Loading model from cuboid: {:?}", size);
-
-                    let size_vec = glam::DVec3::new(size[0] as f64, size[1] as f64, size[2] as f64);
-                    ProcedurallyGeneratedObject::cuboid(size_vec).build_model(
+    fn init<'a>(
+        ser: &'a Self::SerializedForm,
+        graphics: Arc<SharedGraphicsContext>,
+    ) -> Pin<Box<dyn Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> {
+        Box::pin(async move {
+            let import_scale = ser.import_scale.unwrap_or(1.0);
+
+            let handle = match &ser.handle.ref_type {
+                ResourceReferenceType::None => {
+                    log::debug!("ResourceReferenceType is None, setting to `Handle::NULL`");
+                    Handle::NULL
+                }
+                ResourceReferenceType::File(_) => {
+                    log::debug!("Loading model from file: {:?}", ser.handle);
+                    let path = ser.handle.resolve()?;
+                    let buffer = std::fs::read(&path)?;
+                    Model::load_from_memory_raw(
                         graphics.clone(),
-                        None,
+                        buffer,
                         Some(ser.label.as_str()),
                         ASSET_REGISTRY.clone(),
                     )
+                    .await?
                 }
-            },
-        };
-
-        let mut renderer = MeshRenderer::from_handle(handle);
-        renderer.set_import_scale(import_scale);
-
-        for (label, m) in ser.texture_override {
-            if let Some(mat) = renderer.material_snapshot.get_mut(&label) {
-                mat.tint = m.tint;
-                mat.emissive_factor = m.emissive_factor;
-                mat.metallic_factor = m.metallic_factor;
-                mat.roughness_factor = m.roughness_factor;
-                mat.alpha_mode = m.alpha_mode;
-                mat.alpha_cutoff = m.alpha_cutoff;
-                mat.double_sided = m.double_sided;
-                mat.occlusion_strength = m.occlusion_strength;
-                mat.normal_scale = m.normal_scale;
-                mat.uv_tiling = m.uv_tiling;
-                mat.texture_tag = m.texture_tag;
-                mat.wrap_mode = m.wrap_mode;
-
-                let get_tex = async |resource: Option<ResourceReference>| -> Option<anyhow::Result<Texture>> {
-                    if let Some(dif) = resource {
-                        match dif.ref_type {
-                            ResourceReferenceType::None => {
-                                None
-                            }
-                            ResourceReferenceType::File(_) => {
-                                let path = dif.resolve().ok()?;
-                                let tex = Texture::from_file(graphics.clone(), &path, Some(&label)).await;
-                                Some(tex)
-                            }
-                            ResourceReferenceType::Bytes(bytes) => {
-                                let tex = Texture::from_bytes(graphics.clone(), &bytes, Some(&label));
-                                Some(Ok(tex))
-                            }
-                            ResourceReferenceType::ProcObj(_) => {
-                                Some(Err(anyhow::anyhow!("Using a ProcObj as a texture is not valid, for texture with label {}", label)))
+                ResourceReferenceType::Bytes(bytes) => {
+                    log::debug!("Loading model from bytes [Len: {}]", bytes.len());
+                    Model::load_from_memory_raw(
+                        graphics.clone(),
+                        bytes,
+                        Some(ser.label.as_str()),
+                        ASSET_REGISTRY.clone(),
+                    )
+                    .await?
+                }
+                ResourceReferenceType::ProcObj(obj) => match obj {
+                    dropbear_engine::procedural::ProcObj::Cuboid { size_bits } => {
+                        let size = [
+                            f32::from_bits(size_bits[0]),
+                            f32::from_bits(size_bits[1]),
+                            f32::from_bits(size_bits[2]),
+                        ];
+                        log::debug!("Loading model from cuboid: {:?}", size);
+
+                        let size_vec = glam::DVec3::new(
+                            size[0] as f64,
+                            size[1] as f64,
+                            size[2] as f64,
+                        );
+                        ProcedurallyGeneratedObject::cuboid(size_vec).build_model(
+                            graphics.clone(),
+                            None,
+                            Some(ser.label.as_str()),
+                            ASSET_REGISTRY.clone(),
+                        )
+                    }
+                },
+            };
+
+            let mut renderer = MeshRenderer::from_handle(handle);
+            renderer.set_import_scale(import_scale);
+
+            for (label, m) in &ser.texture_override {
+                if let Some(mat) = renderer.material_snapshot.get_mut(&label.clone()) {
+                    mat.tint = m.tint;
+                    mat.emissive_factor = m.emissive_factor;
+                    mat.metallic_factor = m.metallic_factor;
+                    mat.roughness_factor = m.roughness_factor;
+                    mat.alpha_mode = m.alpha_mode;
+                    mat.alpha_cutoff = m.alpha_cutoff;
+                    mat.double_sided = m.double_sided;
+                    mat.occlusion_strength = m.occlusion_strength;
+                    mat.normal_scale = m.normal_scale;
+                    mat.uv_tiling = m.uv_tiling;
+                    mat.texture_tag = m.texture_tag.clone();
+                    mat.wrap_mode = m.wrap_mode;
+
+                    let get_tex = async |resource: &Option<ResourceReference>| -> Option<anyhow::Result<Texture>> {
+                        if let Some(dif) = resource {
+                            match &dif.ref_type {
+                                ResourceReferenceType::None => {
+                                    None
+                                }
+                                ResourceReferenceType::File(_) => {
+                                    let path = dif.resolve().ok()?;
+                                    let tex = Texture::from_file(graphics.clone(), &path, Some(&label)).await;
+                                    Some(tex)
+                                }
+                                ResourceReferenceType::Bytes(bytes) => {
+                                    let tex = Texture::from_bytes(graphics.clone(), bytes, Some(&label));
+                                    Some(Ok(tex))
+                                }
+                                ResourceReferenceType::ProcObj(_) => {
+                                    Some(Err(anyhow::anyhow!("Using a ProcObj as a texture is not valid, for texture with label {}", label)))
+                                }
                             }
+                        } else {
+                            None
                         }
-                    } else {
-                        None
-                    }
-                };
+                    };
 
-                if let Some(tex) = get_tex(m.diffuse_texture).await {
-                    mat.diffuse_texture = tex?;
-                }
+                    if let Some(tex) = get_tex(&m.diffuse_texture).await {
+                        mat.diffuse_texture = tex?;
+                    }
 
-                if let Some(tex) = get_tex(m.emissive_texture).await {
-                    mat.emissive_texture = Some(tex?);
-                }
+                    if let Some(tex) = get_tex(&m.emissive_texture).await {
+                        mat.emissive_texture = Some(tex?);
+                    }
 
-                if let Some(tex) = get_tex(m.normal_texture).await {
-                    mat.normal_texture = tex?;
-                }
+                    if let Some(tex) = get_tex(&m.normal_texture).await {
+                        mat.normal_texture = tex?;
+                    }
 
-                if let Some(tex) = get_tex(m.occlusion_texture).await {
-                    mat.occlusion_texture = Some(tex?);
-                }
+                    if let Some(tex) = get_tex(&m.occlusion_texture).await {
+                        mat.occlusion_texture = Some(tex?);
+                    }
 
-                if let Some(tex) = get_tex(m.metallic_roughness_texture).await {
-                    mat.metallic_roughness_texture = Some(tex?);
+                    if let Some(tex) = get_tex(&m.metallic_roughness_texture).await {
+                        mat.metallic_roughness_texture = Some(tex?);
+                    }
                 }
             }
-        }
 
-        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 ff3ba08..7954ceb 100644
--- a/crates/eucalyptus-core/src/lighting.rs
+++ b/crates/eucalyptus-core/src/lighting.rs
@@ -40,14 +40,19 @@ impl Component for Light {
         Ok((light, comp))
     }
 
-    async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
-        let light = Light::new(
-            graphics.clone(),
-            ser.light_component.clone(),
-            Some(ser.label.as_str())
-        ).await;
-        
-        Ok((light, ser.light_component))
+    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>> {
+        Box::pin(async move {
+            let light = Light::new(
+                graphics.clone(),
+                ser.light_component.clone(),
+                Some(ser.label.as_str())
+            ).await;
+
+            Ok((light, ser.light_component.clone()))
+        })
     }
 
     fn update_component(&mut self, world: &World, entity: Entity, _dt: f32, graphics: Arc<SharedGraphicsContext>) {
@@ -60,7 +65,6 @@ impl Component for Light {
         if let Ok((_, comp)) = world.query_one::<(&Light, &LightComponent)>(entity).get() {
             Box::new(SerializedLight {
                 label: self.label.clone(),
-                transform: comp.to_transform(),
                 light_component: comp.clone(),
                 enabled: comp.enabled,
                 entity_id: Some(entity),
diff --git a/crates/eucalyptus-core/src/physics/collider.rs b/crates/eucalyptus-core/src/physics/collider.rs
index 3d8218e..daf109c 100644
--- a/crates/eucalyptus-core/src/physics/collider.rs
+++ b/crates/eucalyptus-core/src/physics/collider.rs
@@ -79,8 +79,11 @@ impl Component for ColliderGroup {
         Ok((Self::new(), ))
     }
 
-    async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
-        Ok((ser, ))
+    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>> {
+        Box::pin(async move { Ok((ser.clone(), )) })
     }
 
     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 ab5a269..fc959b2 100644
--- a/crates/eucalyptus-core/src/physics/kcc.rs
+++ b/crates/eucalyptus-core/src/physics/kcc.rs
@@ -57,8 +57,11 @@ impl Component for KCC {
         Ok((Self::default(), ))
     }
 
-    async fn init(ser: Self::SerializedForm, _: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
-        Ok((ser, ))
+    fn init<'a>(
+        ser: &'a Self::SerializedForm,
+        _: Arc<SharedGraphicsContext>,
+    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> {
+        Box::pin(async move { Ok((ser.clone(), )) })
     }
 
     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 9d01624..0c339b4 100644
--- a/crates/eucalyptus-core/src/physics/rigidbody.rs
+++ b/crates/eucalyptus-core/src/physics/rigidbody.rs
@@ -209,8 +209,11 @@ impl Component for RigidBody {
 		Ok((Self::default(), ))
 	}
 
-	async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
-		Ok((ser, ))
+	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>> {
+		Box::pin(async move { Ok((ser.clone(), )) })
 	}
 
 	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 0c0f658..561106d 100644
--- a/crates/eucalyptus-core/src/properties.rs
+++ b/crates/eucalyptus-core/src/properties.rs
@@ -44,8 +44,11 @@ impl Component for CustomProperties {
         Ok((Self::new(), ))
     }
 
-    async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
-        Ok((ser, ))
+    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>> {
+        Box::pin(async move { Ok((ser.clone(), )) })
     }
 
     fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {}
diff --git a/crates/eucalyptus-core/src/scene.rs b/crates/eucalyptus-core/src/scene.rs
index acf2ad3..63a365b 100644
--- a/crates/eucalyptus-core/src/scene.rs
+++ b/crates/eucalyptus-core/src/scene.rs
@@ -9,7 +9,7 @@ use crate::states::{SerializableCamera, Label, SerializedLight, Script, Serializ
 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::lighting::{Light, LightComponent};
 use glam::{DQuat, DVec3};
 use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
 use ron::ser::PrettyConfig;
@@ -20,7 +20,7 @@ use std::path::{Path, PathBuf};
 use std::sync::Arc;
 use crossbeam_channel::Sender;
 use egui::Ui;
-use hecs::Entity;
+use hecs::{Entity, EntityBuilder};
 use crate::component::{Component, ComponentRegistry, SerializedComponent};
 use crate::physics::collider::ColliderGroup;
 use crate::physics::kcc::KCC;
@@ -157,7 +157,7 @@ impl SceneConfig {
         &mut self,
         world: &mut hecs::World,
         graphics: Arc<SharedGraphicsContext>,
-        _registry: Option<&ComponentRegistry>,
+        registry: &ComponentRegistry,
         progress_sender: Option<Sender<WorldLoadingStatus>>,
         is_play_mode: bool,
     ) -> anyhow::Result<hecs::Entity> {
@@ -165,28 +165,19 @@ impl SceneConfig {
             let _ = s.send(WorldLoadingStatus::Idle);
         }
 
-        log::info!(
+        // clear world to make room for new entities
+        log::debug!(
             "Loading scene [{}], clearing world with {} entities",
             self.scene_name,
             world.len()
         );
         world.clear();
 
-        #[allow(unused_variables)]
-        let project_config = if cfg!(feature = "editor") {
-            let cfg = PROJECT.read();
-            cfg.project_path.clone()
-        } else {
-            log::debug!("Not using the editor feature, returning empty pathbuffer");
-            PathBuf::new()
-        };
-
         log::info!("World cleared, now has {} entities", world.len());
 
-        let mut resources = ComponentResources::new();
-        resources.insert(graphics.clone());
-        let resources = Arc::new(resources);
+        let mut label_to_entity: HashMap<Label, hecs::Entity> = HashMap::new();
 
+        // gather all entities
         let entity_configs: Vec<(usize, SceneEntity)> = {
             let cloned = self.entities.clone();
             cloned
@@ -196,8 +187,7 @@ impl SceneConfig {
                 .collect()
         };
 
-        let mut label_to_entity: HashMap<Label, hecs::Entity> = HashMap::new();
-
+        // fetch all entities
         for (index, entity_config) in entity_configs {
             let SceneEntity {
                 label,
@@ -220,18 +210,11 @@ impl SceneConfig {
                 });
             }
 
-            let entity = world.spawn((label_for_map.clone(),));
-
-            let mut has_entity_transform = false;
+            // all entities will ALWAYS have a label. if it doesnt, its not a valid entity
+            let mut builder = EntityBuilder::new();
+            builder.add(label_for_map.clone());
 
-            for component in components {
-                if component
-                    .as_any()
-                    .downcast_ref::<EntityTransform>()
-                    .is_some()
-                {
-                    has_entity_transform = true;
-                }
+            for component in components.iter() {
 
                 if component.as_any().downcast_ref::<Parent>().is_some() {
                     log::debug!(
@@ -241,79 +224,23 @@ impl SceneConfig {
                     continue;
                 }
 
-                Self::init_component(
-                    &component,
-                    entity,
-                    world,
-                    resources.clone(),
-                    &label_for_logs,
-                )
-                .await?;
-            }
-
-            if has_entity_transform {
-                if let Ok((
-                              entity_transform, 
-                              renderer_opt, 
-                              light_opt, 
-                              light_comp_opt
-                          )) = world.query_one::<(
-                    &EntityTransform,
-                    Option<&mut MeshRenderer>,
-                    Option<&mut EngineLight>,
-                    Option<&mut LightComponent>,
-                )>(entity).get()
-                {
-                    let transform = entity_transform.sync();
-
-                    if let Some(renderer) = renderer_opt {
-                        renderer.update(&transform);
-                        log::debug!("Updated renderer transform for '{}'", label_for_logs);
-                    }
+                let Some(loader_future) =
+                    registry.load_component(component.as_ref(), graphics.clone())
+                else {
+                    log::warn!(
+                        "Skipping unregistered serialized component for '{}'",
+                        label_for_logs
+                    );
+                    continue;
+                };
 
-                    if let (Some(light), Some(light_comp)) = (light_opt, light_comp_opt) {
-                        light.update(graphics.as_ref(), light_comp, &transform);
-                        log::debug!("Updated light transform for '{}'", label_for_logs);
-                    }
-                }
+                let applier = loader_future.await?;
+                applier(&mut builder);
             }
 
-            // adding to physics
-            if let Ok((
-                  label, 
-                  e_trans, 
-                  rigid, 
-                  col_group, 
-                  kcc
-              )) = world.query_one::<(
-                &Label, 
-                &EntityTransform, 
-                Option<&mut RigidBody>, 
-                Option<&mut ColliderGroup>, 
-                Option<&mut KCC>
-            )>(entity).get() {
-
-                // rigidbody
-                if let Some(body) = rigid {
-                    body.entity = label.clone();
-
-                    self.physics_state.register_rigidbody(body, e_trans.sync());
-                }
-
-                // collider group
-                if let Some(group) = col_group {
-                    for collider in &mut group.colliders {
-                        collider.entity = label.clone();
+            let entity = world.spawn(builder.build());
 
-                        self.physics_state.register_collider(collider);
-                    }
-                }
-
-                // kinematic character controller
-                if let Some(kcc) = kcc {
-                    kcc.entity = label.clone();
-                }
-            }
+            self.register_physics_for_entity(world, entity);
 
             if let Some(previous) = label_to_entity.insert(label_for_map.clone(), entity) {
                 log::warn!(
@@ -326,6 +253,53 @@ impl SceneConfig {
             log::debug!("Loaded entity '{}'", label_for_logs);
         }
 
+        self.rebuild_hierarchy(world, &label_to_entity);
+        self.ensure_default_light(world, graphics.clone(), progress_sender.as_ref()).await?;
+
+        log::info!("Loaded {} entities from scene", self.entities.len());
+
+        let camera_entity =
+            self.select_active_camera(world, graphics, progress_sender.as_ref(), is_play_mode)?;
+        Ok(camera_entity)
+    }
+
+    fn register_physics_for_entity(&mut self, world: &mut hecs::World, entity: hecs::Entity) {
+        if let Ok((
+              label,
+              e_trans,
+              rigid,
+              col_group,
+              kcc
+          )) = world.query_one::<(
+            &Label,
+            &EntityTransform,
+            Option<&mut RigidBody>,
+            Option<&mut ColliderGroup>,
+            Option<&mut KCC>
+        )>(entity).get() {
+            if let Some(body) = rigid {
+                body.entity = label.clone();
+                self.physics_state.register_rigidbody(body, e_trans.sync());
+            }
+
+            if let Some(group) = col_group {
+                for collider in &mut group.colliders {
+                    collider.entity = label.clone();
+                    self.physics_state.register_collider(collider);
+                }
+            }
+
+            if let Some(kcc) = kcc {
+                kcc.entity = label.clone();
+            }
+        }
+    }
+
+    fn rebuild_hierarchy(
+        &self,
+        world: &mut hecs::World,
+        label_to_entity: &HashMap<Label, hecs::Entity>,
+    ) {
         let mut parent_children_map: HashMap<Label, Vec<Label>> = HashMap::new();
 
         for entity_label in label_to_entity.keys() {
@@ -397,26 +371,144 @@ impl SceneConfig {
                 );
             }
         }
+    }
 
+    async fn ensure_default_light(
+        &self,
+        world: &mut hecs::World,
+        graphics: Arc<SharedGraphicsContext>,
+        progress_sender: Option<&Sender<WorldLoadingStatus>>,
+    ) -> anyhow::Result<()> {
+        let mut has_light = false;
+        if world
+            .query::<(&LightComponent, &Light)>()
+            .iter()
+            .next()
+            .is_some()
         {
-            let mut has_light = false;
-            if world
-                .query::<(&LightComponent, &EngineLight)>()
+            has_light = true;
+        }
+
+        if !has_light {
+            log::info!("No lights in scene, spawning default light");
+
+            let legacy_lights: Vec<hecs::Entity> = world
+                .query::<(Entity, &Label)>()
                 .iter()
-                .next()
-                .is_some()
-            {
-                has_light = true;
+                .filter_map(|(entity, label)| {
+                    if label.as_str() == "Default Light" {
+                        Some(entity)
+                    } else {
+                        None
+                    }
+                })
+                .collect();
+
+            for entity in legacy_lights {
+                if let Err(err) = world.despawn(entity) {
+                    log::warn!(
+                        "Failed to remove legacy 'Default Light' entity {:?}: {}",
+                        entity,
+                        err
+                    );
+                } else {
+                    log::debug!(
+                        "Removed legacy 'Default Light' placeholder entity {:?}",
+                        entity
+                    );
+                }
             }
 
-            if !has_light {
-                log::info!("No lights in scene, spawning default light");
+            if let Some(s) = progress_sender {
+                let _ = s.send(WorldLoadingStatus::LoadingEntity {
+                    index: 0,
+                    name: String::from("Default Light"),
+                    total: 1,
+                });
+            }
+            let comp = LightComponent::directional(glam::DVec3::ONE, 1.0);
+            let light_direction = LightComponent::default_direction();
+            let rotation =
+                DQuat::from_rotation_arc(DVec3::new(0.0, 0.0, -1.0), light_direction);
+            let light =
+                Light::new(graphics.clone(), comp.clone(), Some("Default Light"))
+                    .await;
+
+            let light_config = SerializedLight {
+                label: "Default Light".to_string(),
+                light_component: comp.clone(),
+                enabled: true,
+                entity_id: None,
+            };
 
-                let legacy_lights: Vec<hecs::Entity> = world
+            world.spawn((
+                Label::from("Default Light"),
+                comp,
+                light,
+                light_config,
+                CustomProperties::default(),
+            ));
+        }
+
+        Ok(())
+    }
+
+    fn select_active_camera(
+        &self,
+        world: &mut hecs::World,
+        graphics: Arc<SharedGraphicsContext>,
+        progress_sender: Option<&Sender<WorldLoadingStatus>>,
+        is_play_mode: bool,
+    ) -> anyhow::Result<hecs::Entity> {
+        use crate::camera::CameraType;
+
+        if is_play_mode {
+            log::debug!("Running in play mode");
+            let starting_camera = world
+                .query::<(Entity, &Camera, &CameraComponent)>()
+                .iter()
+                .find_map(|(entity, _, component)| {
+                    if component.starting_camera {
+                        log::debug!("Found starting camera: {:?}", entity);
+                        Some(entity)
+                    } else {
+                        None
+                    }
+                });
+
+            if let Some(camera_entity) = starting_camera {
+                log::debug!("Using starting camera for play mode");
+                Ok(camera_entity)
+            } else {
+                panic!("Unable to locate any starting camera while playing")
+            }
+        } else {
+            let debug_camera = {
+                world
+                    .query::<(Entity, &Camera, &CameraComponent)>()
+                    .iter()
+                    .find_map(|(entity, _, component)| {
+                        if matches!(component.camera_type, CameraType::Debug) {
+                            log::debug!("Found debug camera: {:?}", entity);
+                            Some(entity)
+                        } else {
+                            None
+                        }
+                    })
+            };
+
+            if let Some(camera_entity) = debug_camera {
+                log::info!("Using existing debug camera for editor");
+                Ok(camera_entity)
+            } else {
+                log::info!("No debug or starting camera found, creating viewport camera for editor");
+
+                let legacy_cameras: Vec<hecs::Entity> = world
                     .query::<(Entity, &Label)>()
                     .iter()
                     .filter_map(|(entity, label)| {
-                        if label.as_str() == "Default Light" {
+                        if label.as_str() == "Viewport Camera" {
+                            log::debug!("Found 'Viewport Camera' entity: {:?}", entity);
                             Some(entity)
                         } else {
                             None
@@ -424,150 +516,33 @@ impl SceneConfig {
                     })
                     .collect();
 
-                for entity in legacy_lights {
+                for entity in legacy_cameras {
                     if let Err(err) = world.despawn(entity) {
                         log::warn!(
-                            "Failed to remove legacy 'Default Light' entity {:?}: {}",
+                            "Failed to remove legacy 'Viewport Camera' entity {:?}: {}",
                             entity,
                             err
                         );
                     } else {
                         log::debug!(
-                            "Removed legacy 'Default Light' placeholder entity {:?}",
+                            "Removed legacy 'Viewport Camera' placeholder entity {:?}",
                             entity
                         );
                     }
                 }
 
-                if let Some(ref s) = progress_sender {
+                if let Some(s) = progress_sender {
                     let _ = s.send(WorldLoadingStatus::LoadingEntity {
                         index: 0,
-                        name: String::from("Default Light"),
+                        name: String::from("Viewport Camera"),
                         total: 1,
                     });
                 }
-                let comp = LightComponent::directional(glam::DVec3::ONE, 1.0);
-                let light_direction = LightComponent::default_direction();
-                let rotation =
-                    DQuat::from_rotation_arc(DVec3::new(0.0, 0.0, -1.0), light_direction);
-                let trans = Transform {
-                    position: glam::DVec3::new(2.0, 4.0, 2.0),
-                    rotation,
-                    ..Default::default()
-                };
-                let light =
-                    EngineLight::new(graphics.clone(), comp.clone(), trans, Some("Default Light"))
-                        .await;
-
-                let light_config = SerializedLight {
-                    label: "Default Light".to_string(),
-                    transform: trans,
-                    light_component: comp.clone(),
-                    enabled: true,
-                    entity_id: None,
-                };
-
-                {
-                    world.spawn((
-                        Label::from("Default Light"),
-                        comp,
-                        trans,
-                        light,
-                        light_config,
-                        CustomProperties::default(),
-                    ));
-                }
-            }
-        }
-
-        log::info!("Loaded {} entities from scene", self.entities.len());
-        {
-            use crate::camera::CameraType;
-
-            if is_play_mode {
-                log::debug!("Running in play mode");
-                let starting_camera = world
-                    .query::<(Entity, &Camera, &CameraComponent)>()
-                    .iter()
-                    .find_map(|(entity, _, component)| {
-                        if component.starting_camera {
-                            log::debug!("Found starting camera: {:?}", entity);
-                            Some(entity)
-                        } else {
-                            None
-                        }
-                    });
-
-                if let Some(camera_entity) = starting_camera {
-                    log::debug!("Using starting camera for play mode");
-                    Ok(camera_entity)
-                } else {
-                    panic!("Unable to locate any starting camera while playing")
-                }
-            } else {
-                let debug_camera = {
-                    world
-                        .query::<(Entity, &Camera, &CameraComponent)>()
-                        .iter()
-                        .find_map(|(entity, _, component)| {
-                            if matches!(component.camera_type, CameraType::Debug) {
-                                log::debug!("Found debug camera: {:?}", entity);
-                                Some(entity)
-                            } else {
-                                None
-                            }
-                        })
-                };
-
-                {
-                    if let Some(camera_entity) = debug_camera {
-                        log::info!("Using existing debug camera for editor");
-                        Ok(camera_entity)
-                    } else {
-                        log::info!("No debug or starting camera found, creating viewport camera for editor");
-
-                        let legacy_cameras: Vec<hecs::Entity> = world
-                            .query::<(Entity, &Label)>()
-                            .iter()
-                            .filter_map(|(entity, label)| {
-                                if label.as_str() == "Viewport Camera" {
-                                    log::debug!("Found 'Viewport Camera' entity: {:?}", entity);
-                                    Some(entity)
-                                } else {
-                                    None
-                                }
-                            })
-                            .collect();
-
-                        for entity in legacy_cameras {
-                            if let Err(err) = world.despawn(entity) {
-                                log::warn!(
-                                "Failed to remove legacy 'Viewport Camera' entity {:?}: {}",
-                                entity,
-                                err
-                            );
-                            } else {
-                                log::debug!(
-                                "Removed legacy 'Viewport Camera' placeholder entity {:?}",
-                                entity
-                            );
-                            }
-                        }
-
-                        if let Some(ref s) = progress_sender {
-                            let _ = s.send(WorldLoadingStatus::LoadingEntity {
-                                index: 0,
-                                name: String::from("Viewport Camera"),
-                                total: 1,
-                            });
-                        }
-                        let camera = Camera::predetermined(graphics.clone(), Some("Viewport Camera"));
-                        let component = crate::camera::DebugCamera::new();
-                        let label = Label::new("Viewport Camera");
-                        let camera_entity = { world.spawn((label, camera, component)) };
-                        Ok(camera_entity)
-                    }
-                }
+                let camera = Camera::predetermined(graphics.clone(), Some("Viewport Camera"));
+                let component = crate::camera::DebugCamera::new();
+                let label = Label::new("Viewport Camera");
+                let camera_entity = world.spawn((label, camera, component));
+                Ok(camera_entity)
             }
         }
     }
diff --git a/crates/eucalyptus-core/src/states.rs b/crates/eucalyptus-core/src/states.rs
index 21fdf6f..238baa9 100644
--- a/crates/eucalyptus-core/src/states.rs
+++ b/crates/eucalyptus-core/src/states.rs
@@ -181,8 +181,11 @@ impl Component for Script {
         Ok((Self::default(), ))
     }
 
-    async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
-        Ok((ser, ))
+    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>> {
+        Box::pin(async move { Ok((ser.clone(), )) })
     }
 
     fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {}
@@ -297,7 +300,6 @@ pub struct Property {
 #[derive(Debug, Serialize, Deserialize, Clone)]
 pub struct SerializedLight {
     pub label: String,
-    pub transform: Transform,
     pub light_component: LightComponent,
     pub enabled: bool,
 
@@ -309,7 +311,6 @@ impl Default for SerializedLight {
     fn default() -> Self {
         Self {
             label: "Default Light".to_string(),
-            transform: Transform::default(),
             light_component: LightComponent::default(),
             enabled: true,
             entity_id: None,
diff --git a/crates/eucalyptus-core/src/transform.rs b/crates/eucalyptus-core/src/transform.rs
index 0f0249c..17c9eda 100644
--- a/crates/eucalyptus-core/src/transform.rs
+++ b/crates/eucalyptus-core/src/transform.rs
@@ -34,8 +34,11 @@ impl Component for EntityTransform {
         Ok((Self::default(), ))
     }
 
-    async fn init(ser: Self::SerializedForm, _: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> {
-        Ok((ser, ))
+    fn init<'a>(
+        ser: &'a Self::SerializedForm,
+        _: Arc<SharedGraphicsContext>,
+    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> {
+        Box::pin(async move { Ok((ser.clone(), )) })
     }
 
     fn update_component(&mut self, _: &World, _: Entity, _: f32, _: Arc<SharedGraphicsContext>) {}
diff --git a/crates/eucalyptus-editor/src/editor/scene.rs b/crates/eucalyptus-editor/src/editor/scene.rs
index 485d4a8..3cd1607 100644
--- a/crates/eucalyptus-editor/src/editor/scene.rs
+++ b/crates/eucalyptus-editor/src/editor/scene.rs
@@ -22,7 +22,6 @@ use log;
 use parking_lot::Mutex;
 use wgpu::Color;
 use winit::{event_loop::ActiveEventLoop, keyboard::KeyCode};
-use dropbear_engine::component::ComponentResources;
 use eucalyptus_core::physics::collider::ColliderGroup;
 use eucalyptus_core::physics::collider::ColliderShapeKey;
 use eucalyptus_core::physics::collider::shader::{ColliderInstanceRaw, create_wireframe_geometry};
@@ -124,13 +123,8 @@ impl Scene for Editor {
             }
         }
 
-        {
-            let mut resources = ComponentResources::new();
-            resources.insert(graphics.clone());
-            let resources = Arc::new(resources);
-            self.component_registry
-                .update_components(self.world.as_mut(), dt, &resources);
-        }
+        self.component_registry
+            .update_components(self.world.as_mut(), dt, graphics.clone());
 
         if !self.is_world_loaded.is_fully_loaded() {
             log::debug!("Scene is not fully loaded, initialising...");
@@ -282,63 +276,7 @@ impl Scene for Editor {
             }
         }
 
-        {
-            let sim_world = self.world.as_mut();
-
-            for (_entity_id, camera, component) in sim_world
-                .query::<(Entity, &mut Camera, &mut CameraComponent)>()
-                .iter()
-            {
-                component.update(camera);
-                camera.update(graphics.clone());
-            }
-        }
-
-        {
-            let sim_world = self.world.as_mut();
-
-            {
-                let query = sim_world.query_mut::<(&mut MeshRenderer, &Transform)>();
-                for (renderer, transform) in query {
-                    renderer.update(transform);
-                }
-            }
-
-            {
-                let mut updates = Vec::new();
-                for (entity, transform) in sim_world.query::<(Entity, &EntityTransform)>().iter() {
-                    let final_transform = transform.propagate(sim_world, entity);
-                    updates.push((entity, final_transform));
-                }
-
-                for (entity, final_transform) in updates {
-                    if let Ok(mut renderer) = sim_world.get::<&mut MeshRenderer>(entity) {
-                        renderer.update(&final_transform);
-                    }
-                }
-            }
-
-            {
-                let light_query = sim_world.query_mut::<(
-                    &mut LightComponent,
-                    Option<&Transform>,
-                    Option<&EntityTransform>,
-                    &mut Light,
-                )>();
-
-                for (light_component, transform_opt, entity_transform_opt, light) in light_query {
-                    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);
-                }
-            }
-        }
+        
 
         if let Some(l) = &mut self.light_cube_pipeline {
             l.update(graphics.clone(), &self.world);
diff --git a/crates/redback-runtime/src/lib.rs b/crates/redback-runtime/src/lib.rs
index 0535e9e..7f3623a 100644
--- a/crates/redback-runtime/src/lib.rs
+++ b/crates/redback-runtime/src/lib.rs
@@ -7,6 +7,7 @@ use dropbear_engine::pipelines::DropbearShaderPipeline;
 use dropbear_engine::pipelines::GlobalsUniform;
 use dropbear_engine::pipelines::light_cube::LightCubePipeline;
 use dropbear_engine::pipelines::shader::MainRenderPipeline;
+use eucalyptus_core::component::ComponentRegistry;
 use eucalyptus_core::physics::collider::shader::ColliderWireframePipeline;
 use eucalyptus_core::physics::collider::shader::ColliderInstanceRaw;
 use eucalyptus_core::physics::collider::{ColliderShapeKey, WireframeGeometry};
@@ -19,7 +20,6 @@ use eucalyptus_core::input::InputState;
 use eucalyptus_core::scripting::{ScriptManager, ScriptTarget};
 use eucalyptus_core::states::{WorldLoadingStatus, SCENES, Script};
 use eucalyptus_core::scene::loading::{SceneLoadResult, SCENE_LOADER};
-use eucalyptus_core::traits::registry::ComponentRegistry;
 use eucalyptus_core::ptr::{CommandBufferPtr, GraphicsContextPtr, InputStatePtr, PhysicsStatePtr, WorldPtr};
 use eucalyptus_core::command::COMMAND_BUFFER;
 use eucalyptus_core::scene::loading::IsSceneLoaded;
@@ -29,8 +29,6 @@ use log::error;
 use wgpu::SurfaceConfiguration;
 use winit::window::Fullscreen;
 use dropbear_engine::sky::{HdrLoader, SkyPipeline, DEFAULT_SKY_TEXTURE};
-// use yakui_winit::YakuiWinit;
-use dropbear_engine::texture::Texture;
 use eucalyptus_core::physics::PhysicsState;
 use eucalyptus_core::rapier3d::prelude::*;
 use eucalyptus_core::register_components;
@@ -352,7 +350,7 @@ impl PlayMode {
             let load_status = scene_to_load.load_into_world(
                 &mut temp_world,
                 graphics_cloned,
-                Some(&component_registry),
+                &component_registry.clone(),
                 Some(tx),
                 true,
             ).await;
@@ -420,7 +418,7 @@ impl PlayMode {
             let camera = scene_to_load.load_into_world(
                 &mut temp_world,
                 graphics_cloned,
-                Some(&component_registry),
+                &component_registry.clone(),
                 Some(tx),
                 true,
             ).await;
diff --git a/crates/redback-runtime/src/scene.rs b/crates/redback-runtime/src/scene.rs
index c584f38..ef4c56f 100644
--- a/crates/redback-runtime/src/scene.rs
+++ b/crates/redback-runtime/src/scene.rs
@@ -23,7 +23,6 @@ use dropbear_engine::lighting::{Light, LightComponent};
 use dropbear_engine::lighting::MAX_LIGHTS;
 use dropbear_engine::model::{DrawLight, DrawModel, Model};
 use dropbear_engine::scene::{Scene, SceneCommand};
-use eucalyptus_core::camera::CameraComponent;
 use eucalyptus_core::command::CommandBufferPoller;
 use eucalyptus_core::hierarchy::{EntityTransformExt, Parent};
 use eucalyptus_core::physics::kcc::KCC;
@@ -32,7 +31,6 @@ use eucalyptus_core::rapier3d::geometry::SharedShape;
 use eucalyptus_core::states::{Label, PROJECT};
 use eucalyptus_core::states::SCENES;
 use eucalyptus_core::scene::loading::{IsSceneLoaded, SceneLoadResult, SCENE_LOADER};
-use eucalyptus_core::traits::ComponentResources;
 use crate::PlayMode;
 use eucalyptus_core::physics::collider::shader::create_wireframe_geometry;
 use kino_ui::widgets::{Anchor, Border, Fill};
@@ -334,84 +332,10 @@ impl Scene for PlayMode {
             }
         }
 
-        {
-            let mut resources = ComponentResources::new();
-            resources.insert(graphics.clone());
-            let resources = Arc::new(resources);
-            self.component_registry
-                .update_components(self.world.as_mut(), dt, &resources);
-        }
-
-        {
-            let mut query = self.world.query::<(&mut MeshRenderer, &Transform)>();
-            for (renderer, transform) in query.iter() {
-                renderer.update(transform);
-            }
-        }
-
-        {
-            let mut updates = Vec::new();
-            for (entity, transform) in self.world.query::<(Entity, &EntityTransform)>().iter() {
-                let final_transform = transform.propagate(&self.world, entity);
-                updates.push((entity, final_transform));
-            }
-
-            for (entity, final_transform) in updates {
-                if let Ok(mut renderer) = self.world.get::<&mut MeshRenderer>(entity) {
-                    renderer.update(&final_transform);
-                }
-            }
-        }
-
-        {
-            let registry = ASSET_REGISTRY.read();
-            let mut query = self
-                .world
-                .query::<(&MeshRenderer, &mut AnimationComponent)>();
-
-            for (renderer, animation) in query.iter() {
-                let handle = renderer.model();
-                if handle.is_null() {
-                    continue;
-                }
-
-                let Some(model) = registry.get_model(handle) else {
-                    continue;
-                };
-
-                animation.update(dt, model);
-                animation.prepare_gpu_resources(graphics.clone());
-            }
-        }
-
-        {
-            let mut light_query = self
-                .world
-                .query::<(&mut LightComponent, Option<&Transform>, Option<&EntityTransform>, &mut Light)>();
-
-            for (light_comp, transform_opt, entity_transform_opt, light) in light_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;
-                };
+        self.component_registry
+            .update_components(self.world.as_mut(), dt, graphics.clone());
 
-                light.update(graphics.as_ref(), light_comp, &transform);
-            }
-        }
-
-        {
-            for (camera, component) in self
-                .world
-                .query::<(&mut Camera, &mut CameraComponent)>()
-                .iter()
-            {
-                component.update(camera);
-                camera.update(graphics.clone());
-            }
-        }
+        
 
         if let Some(l) = &mut self.light_cube_pipeline {
             l.update(graphics.clone(), &self.world);
@@ -759,21 +683,11 @@ impl Scene for PlayMode {
         {
             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);
+            for (light_component, light) in query.iter() {
+                light.update(graphics.as_ref(), light_component);
             }
         }
 
diff --git a/include/dropbear.h b/include/dropbear.h
index 726dbf4..48fbcb5 100644
--- a/include/dropbear.h
+++ b/include/dropbear.h
@@ -184,22 +184,11 @@ typedef struct IndexNative {
     uint32_t generation;
 } IndexNative;
 
-typedef struct IndexNativeArray {
-    IndexNative* values;
-    size_t length;
-    size_t capacity;
-} IndexNativeArray;
-
-typedef struct CharacterCollisionArray {
+typedef struct NCollider {
+    IndexNative index;
     uint64_t entity_id;
-    IndexNativeArray collisions;
-} CharacterCollisionArray;
-
-typedef struct NAttenuation {
-    float constant;
-    float linear;
-    float quadratic;
-} NAttenuation;
+    uint32_t id;
+} NCollider;
 
 typedef struct NTransform {
     NVector3 position;
@@ -207,17 +196,22 @@ typedef struct NTransform {
     NVector3 scale;
 } NTransform;
 
-typedef struct NColour {
-    uint8_t r;
-    uint8_t g;
-    uint8_t b;
-    uint8_t a;
-} NColour;
+typedef struct NColliderArray {
+    NCollider* values;
+    size_t length;
+    size_t capacity;
+} NColliderArray;
 
-typedef struct NRange {
-    float start;
-    float end;
-} NRange;
+typedef struct NVector2 {
+    double x;
+    double y;
+} NVector2;
+
+typedef struct AxisLock {
+    bool x;
+    bool y;
+    bool z;
+} AxisLock;
 
 typedef struct NVector4 {
     double x;
@@ -226,10 +220,29 @@ typedef struct NVector4 {
     double w;
 } NVector4;
 
-typedef struct NVector2 {
-    double x;
-    double y;
-} NVector2;
+typedef struct NMaterial {
+    const char* name;
+    uint64_t diffuse_texture;
+    uint64_t normal_texture;
+    NVector4 tint;
+    NVector3 emissive_factor;
+    float metallic_factor;
+    float roughness_factor;
+    const float* alpha_cutoff;
+    bool double_sided;
+    float occlusion_strength;
+    float normal_scale;
+    NVector2 uv_tiling;
+    const uint64_t* emissive_texture;
+    const uint64_t* metallic_roughness_texture;
+    const uint64_t* occlusion_texture;
+} NMaterial;
+
+typedef struct NMaterialArray {
+    NMaterial* values;
+    size_t length;
+    size_t capacity;
+} NMaterialArray;
 
 typedef struct i32Array {
     int32_t* values;
@@ -237,6 +250,52 @@ typedef struct i32Array {
     size_t capacity;
 } i32Array;
 
+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* InputStatePtr;
+
+typedef void* GraphicsContextPtr;
+
+typedef void* PhysicsStatePtr;
+
+typedef struct IndexNativeArray {
+    IndexNative* 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;
+
 typedef struct NModelVertex {
     NVector3 position;
     NVector3 normal;
@@ -267,16 +326,36 @@ typedef struct NMeshArray {
     size_t capacity;
 } NMeshArray;
 
-typedef struct NCollider {
-    IndexNative index;
-    uint64_t entity_id;
-    uint32_t id;
-} NCollider;
+typedef struct f64Array {
+    double* values;
+    size_t length;
+    size_t capacity;
+} f64Array;
 
-typedef struct RayHit {
-    NCollider collider;
-    double distance;
-} RayHit;
+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;
+
+typedef struct NAnimation {
+    const char* name;
+    NAnimationChannelArray channels;
+    float duration;
+} NAnimation;
+
+typedef struct NAnimationArray {
+    NAnimation* values;
+    size_t length;
+    size_t capacity;
+} NAnimationArray;
 
 typedef struct u64Array {
     uint64_t* values;
@@ -288,22 +367,6 @@ typedef struct ConnectedGamepadIds {
     u64Array ids;
 } ConnectedGamepadIds;
 
-typedef struct AxisLock {
-    bool x;
-    bool y;
-    bool z;
-} AxisLock;
-
-typedef void* WorldPtr;
-
-typedef struct NColliderArray {
-    NCollider* values;
-    size_t length;
-    size_t capacity;
-} NColliderArray;
-
-typedef void* InputStatePtr;
-
 typedef struct NShapeCastHit {
     NCollider collider;
     double distance;
@@ -314,9 +377,21 @@ typedef struct NShapeCastHit {
     NShapeCastStatus status;
 } NShapeCastHit;
 
-typedef void* PhysicsStatePtr;
+typedef struct NAttenuation {
+    float constant;
+    float linear;
+    float quadratic;
+} NAttenuation;
 
-typedef void* AssetRegistryPtr;
+typedef struct RigidBodyContext {
+    IndexNative index;
+    uint64_t entity_id;
+} RigidBodyContext;
+
+typedef struct RayHit {
+    NCollider collider;
+    double distance;
+} RayHit;
 
 typedef struct NNodeTransform {
     NVector3 translation;
@@ -337,96 +412,21 @@ typedef struct NNodeArray {
     size_t capacity;
 } NNodeArray;
 
-typedef struct f64Array {
-    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;
-
-typedef struct NAnimation {
-    const char* name;
-    NAnimationChannelArray channels;
-    float duration;
-} NAnimation;
-
-typedef struct NAnimationArray {
-    NAnimation* values;
-    size_t length;
-    size_t capacity;
-} NAnimationArray;
-
-typedef struct Progress {
-    size_t current;
-    size_t total;
-    const char* message;
-} Progress;
-
-typedef void* CommandBufferPtr;
-
-typedef struct NMaterial {
-    const char* name;
-    uint64_t diffuse_texture;
-    uint64_t normal_texture;
-    NVector4 tint;
-    NVector3 emissive_factor;
-    float metallic_factor;
-    float roughness_factor;
-    const float* alpha_cutoff;
-    bool double_sided;
-    float occlusion_strength;
-    float normal_scale;
-    NVector2 uv_tiling;
-    const uint64_t* emissive_texture;
-    const uint64_t* metallic_roughness_texture;
-    const uint64_t* occlusion_texture;
-} NMaterial;
+typedef struct NColour {
+    uint8_t r;
+    uint8_t g;
+    uint8_t b;
+    uint8_t a;
+} NColour;
 
-typedef struct NMaterialArray {
-    NMaterial* values;
-    size_t length;
-    size_t capacity;
-} NMaterialArray;
+typedef struct NRange {
+    float start;
+    float end;
+} NRange;
 
 typedef void* SceneLoaderPtr;
 
-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;
+typedef void* CommandBufferPtr;
 
 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);