kitgit

tirbofish/dropbear · commit

cf77be1dd3793de44fcaa6a7c0a47c09f3d9fb87

feature: implemented an import scale (fixes #59) fix: locked axis ci: removed mold support from windows and macos jarvis, run github actions

Unverified

Thribhu K <4tkbytes@pm.me> · 2026-01-11 15:12

view full diff

diff --git a/.cargo/config.toml b/.cargo/config.toml
index 00d1e99..5c0c649 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -1,6 +1,3 @@
 [target.x86_64-unknown-linux-gnu]
 linker = "clang"
-rustflags = ["-C", "link-arg=-fuse-ld=mold"]
-
-[target.x86_64-pc-windows-msvc]
 rustflags = ["-C", "link-arg=-fuse-ld=mold"]
\ No newline at end of file
diff --git a/dropbear-engine/src/entity.rs b/dropbear-engine/src/entity.rs
index 9525702..31d12fe 100644
--- a/dropbear-engine/src/entity.rs
+++ b/dropbear-engine/src/entity.rs
@@ -172,6 +172,7 @@ pub struct MeshRenderer {
     pub material_overrides: Vec<MaterialOverride>,
     original_material_snapshots: HashMap<String, MaterialSnapshot>,
     texture_identifier_cache: HashMap<String, String>,
+    import_scale: f32,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
@@ -196,12 +197,12 @@ impl MeshRenderer {
         label: Option<&str>,
     ) -> anyhow::Result<Self> {
         let path = path.as_ref().to_path_buf();
-        let handle = Model::load(graphics, &path, label).await?;
+        let handle = Model::load(graphics, &path, label, None).await?;
         Ok(Self::from_handle(handle))
     }
 
-    /// Creates a new [`MeshRenderer`] instance from a [`LoadedModel`] handle
-    pub fn from_handle(handle: LoadedModel) -> Self {
+    /// Creates a new [`MeshRenderer`] instance from a [`LoadedModel`] handle with an explicit per-renderer import scale.
+    pub fn from_handle_with_import_scale(handle: LoadedModel, import_scale: f32) -> Self {
         Self {
             handle,
             instance: Instance::new(DVec3::ZERO, DQuat::IDENTITY, DVec3::ONE),
@@ -210,9 +211,15 @@ impl MeshRenderer {
             material_overrides: Vec::new(),
             original_material_snapshots: HashMap::new(),
             texture_identifier_cache: HashMap::new(),
+            import_scale,
         }
     }
 
+    /// Creates a new [`MeshRenderer`] instance from a [`LoadedModel`] handle
+    pub fn from_handle(handle: LoadedModel) -> Self {
+        Self::from_handle_with_import_scale(handle, 1.0)
+    }
+
     pub fn model(&self) -> Arc<Model> {
         self.handle.get()
     }
@@ -238,10 +245,10 @@ impl MeshRenderer {
     }
 
     pub fn update(&mut self, transform: &Transform) {
-        let import_scale = ASSET_REGISTRY.model_import_scale(&self.handle.inner.path);
-        let scaled = transform.scale * glam::DVec3::splat(import_scale as f64);
+        // Import scaling is per-renderer and should not mutate shared model buffers.
+        let scale = transform.scale * glam::DVec3::splat(self.import_scale as f64);
         let current_matrix = DMat4::from_scale_rotation_translation(
-            scaled,
+            scale,
             transform.rotation,
             transform.position,
         );
@@ -599,6 +606,29 @@ impl MeshRenderer {
             cache.insert(key, Arc::clone(&current));
         }
     }
+
+    pub fn import_scale(&self) -> f32 {
+        self.import_scale
+    }
+
+    pub fn set_import_scale(&mut self, scale: f32) {
+        self.import_scale = scale;
+    }
+
+    // Backwards-compat helper names (kept for now).
+    pub fn effective_import_scale(&self) -> f32 {
+        self.import_scale
+    }
+
+    pub fn custom_import_scale(&self) -> Option<f32> {
+        Some(self.import_scale)
+    }
+
+    pub fn set_custom_import_scale(&mut self, scale: Option<f32>) {
+        if let Some(scale) = scale {
+            self.import_scale = scale;
+        }
+    }
 }
 
 #[repr(C)]
diff --git a/dropbear-engine/src/lighting.rs b/dropbear-engine/src/lighting.rs
index 0ea33bd..487210b 100644
--- a/dropbear-engine/src/lighting.rs
+++ b/dropbear-engine/src/lighting.rs
@@ -289,6 +289,7 @@ impl Light {
             graphics.clone(),
             include_bytes!("../../resources/models/cube.glb").to_vec(),
             label,
+            None
         )
         .await
         .expect("failed to load light cube model")
diff --git a/dropbear-engine/src/model.rs b/dropbear-engine/src/model.rs
index c17d54e..f30b6a1 100644
--- a/dropbear-engine/src/model.rs
+++ b/dropbear-engine/src/model.rs
@@ -302,6 +302,7 @@ pub struct Mesh {
     pub index_buffer: wgpu::Buffer,
     pub num_elements: u32,
     pub material: usize,
+    pub vertices: Vec<ModelVertex>,
 }
 
 impl Model {
@@ -414,6 +415,7 @@ impl Model {
         graphics: Arc<SharedGraphicsContext>,
         buffer: B,
         label: Option<&str>,
+        import_scale: Option<f64>,
     ) -> anyhow::Result<LoadedModel>
     where
         B: AsRef<[u8]>,
@@ -424,6 +426,7 @@ impl Model {
             label,
             &ASSET_REGISTRY,
             LazyLock::force(&MODEL_CACHE),
+            import_scale
         )
         .await
     }
@@ -434,6 +437,7 @@ impl Model {
         label: Option<&str>,
         registry: &AssetRegistry,
         cache: &Mutex<HashMap<String, Arc<Model>>>,
+        import_scale: Option<f64>,
     ) -> anyhow::Result<LoadedModel>
     where
         B: AsRef<[u8]>,
@@ -441,7 +445,12 @@ impl Model {
         let start = Instant::now();
         let mut hasher = DefaultHasher::new();
 
-        let cache_key = label.unwrap_or("default").to_string();
+        let scale_key = import_scale.unwrap_or(1.0);
+        let cache_key = format!(
+            "{}::import_scale={:.8}",
+            label.unwrap_or("default"),
+            scale_key
+        );
 
         if let Some(cached_model) = {
             let cache_guard = cache.lock();
@@ -640,6 +649,18 @@ impl Model {
                         bitangent: [0.0; 3],
                     })
                     .collect();
+
+                // Apply import scaling at load time so downstream systems (bounds, physics, etc.)
+                // see the baked geometry rather than a render-time transform hack.
+                let scale = import_scale.unwrap_or(1.0) as f32;
+                if (scale - 1.0).abs() > f32::EPSILON {
+                    for vertex in &mut vertices {
+                        vertex.position[0] *= scale;
+                        vertex.position[1] *= scale;
+                        vertex.position[2] *= scale;
+                    }
+                }
+
                 for v in &vertices {
                     let _ = v.position.iter().map(|v| (*v as i32).hash(&mut hasher));
                     let _ = v.normal.iter().map(|v| (*v as i32).hash(&mut hasher));
@@ -739,6 +760,7 @@ impl Model {
                     name: mesh.name().unwrap_or("Unnamed Mesh").to_string(),
                     vertex_buffer,
                     index_buffer,
+                    vertices,
                     num_elements: indices.len() as u32,
                     material: material_index,
                 });
@@ -772,6 +794,7 @@ impl Model {
         graphics: Arc<SharedGraphicsContext>,
         path: &PathBuf,
         label: Option<&str>,
+        import_scale: Option<f64>,
     ) -> anyhow::Result<LoadedModel> {
         Self::load_raw(
             graphics,
@@ -779,6 +802,7 @@ impl Model {
             label,
             &ASSET_REGISTRY,
             LazyLock::force(&MODEL_CACHE),
+            import_scale
         )
         .await
     }
@@ -789,11 +813,13 @@ impl Model {
         label: Option<&str>,
         registry: &AssetRegistry,
         cache: &Mutex<HashMap<String, Arc<Model>>>,
+        import_scale: Option<f64>,
     ) -> anyhow::Result<LoadedModel> {
         let file_name = path.file_name();
         log::debug!("Loading model [{:?}]", file_name);
 
-        let path_str = path.to_string_lossy().to_string();
+        let scale_key = import_scale.unwrap_or(1.0);
+        let path_str = format!("{}::import_scale={:.8}", path.to_string_lossy(), scale_key);
 
         log::debug!("Checking if model exists in cache");
         if let Some(cached_model) = {
@@ -808,7 +834,9 @@ impl Model {
         log::debug!("Path of model: {}", path.display());
 
         let buffer = std::fs::read(path)?;
-        let loaded = Self::load_from_memory_raw(graphics, buffer, label, registry, cache).await?;
+        let loaded =
+            Self::load_from_memory_raw(graphics, buffer, label, registry, cache, import_scale)
+                .await?;
 
         let mut model_clone: Model = (*loaded).clone();
         if let Ok(reference) = ResourceReference::from_path(path) {
@@ -823,7 +851,8 @@ impl Model {
             let mut cache_guard = cache.lock();
             cache_guard.insert(path_str.clone(), Arc::clone(&updated));
             if let Some(custom_label) = label {
-                cache_guard.insert(custom_label.to_string(), Arc::clone(&updated));
+                let label_key = format!("{}::import_scale={:.8}", custom_label, scale_key);
+                cache_guard.insert(label_key, Arc::clone(&updated));
             }
         }
 
diff --git a/dropbear-engine/src/procedural/mod.rs b/dropbear-engine/src/procedural/mod.rs
index a0bc91e..695c172 100644
--- a/dropbear-engine/src/procedural/mod.rs
+++ b/dropbear-engine/src/procedural/mod.rs
@@ -65,11 +65,14 @@ impl ProcedurallyGeneratedObject {
             return LoadedModel::new_raw(registry, cached_model);
         }
 
+        let vertices = self.vertices;
+        let indices = self.indices;
+
         let vertex_buffer = graphics
             .device
             .create_buffer_init(&wgpu::util::BufferInitDescriptor {
                 label: Some(&format!("{label} Vertex Buffer")),
-                contents: bytemuck::cast_slice(&self.vertices),
+                contents: bytemuck::cast_slice(&vertices),
                 usage: wgpu::BufferUsages::VERTEX,
             });
 
@@ -77,7 +80,7 @@ impl ProcedurallyGeneratedObject {
             .device
             .create_buffer_init(&wgpu::util::BufferInitDescriptor {
                 label: Some(&format!("{label} Index Buffer")),
-                contents: bytemuck::cast_slice(&self.indices),
+                contents: bytemuck::cast_slice(&indices),
                 usage: wgpu::BufferUsages::INDEX,
             });
 
@@ -85,8 +88,9 @@ impl ProcedurallyGeneratedObject {
             name: label.clone(),
             vertex_buffer,
             index_buffer,
-            num_elements: self.indices.len() as u32,
+            num_elements: indices.len() as u32,
             material: 0,
+            vertices,
         };
 
         let material = material.unwrap_or_else(|| {
diff --git a/eucalyptus-core/src/physics.rs b/eucalyptus-core/src/physics.rs
index b899bca..3fe01f6 100644
--- a/eucalyptus-core/src/physics.rs
+++ b/eucalyptus-core/src/physics.rs
@@ -99,6 +99,31 @@ impl PhysicsState {
         let pos = transform.position.as_vec3().to_array();
         let rot = transform.rotation.as_quat().to_array();
 
+        let mut bits = LockedAxes::empty();
+
+        let translation_lock = rigid_body.lock_translation;
+        if translation_lock.x {
+            bits = bits | LockedAxes::TRANSLATION_LOCKED_X;
+        }
+        if translation_lock.y {
+            bits = bits | LockedAxes::TRANSLATION_LOCKED_Y;
+        }
+        if translation_lock.z {
+            bits = bits | LockedAxes::TRANSLATION_LOCKED_Z;
+        }
+
+        let rotation_lock = rigid_body.lock_rotation;
+        if rotation_lock.x {
+            bits = bits | LockedAxes::ROTATION_LOCKED_X;
+        }
+        if rotation_lock.y {
+            bits = bits | LockedAxes::ROTATION_LOCKED_Y;
+        }
+        if rotation_lock.z {
+            bits = bits | LockedAxes::ROTATION_LOCKED_Z;
+        }
+
+
         let body = RigidBodyBuilder::new(mode)
             .translation(Vector::from_array(pos))
             .rotation(UnitQuaternion::from_quaternion(Quaternion::new(
@@ -112,8 +137,7 @@ impl PhysicsState {
             .angvel(Vector::from_array(rigid_body.angvel))
             .linear_damping(rigid_body.linear_damping)
             .angular_damping(rigid_body.angular_damping)
-            .enabled_translations(!rigid_body.lock_translation.x, !rigid_body.lock_translation.y, !rigid_body.lock_translation.z)
-            .enabled_rotations(!rigid_body.lock_rotation.x, !rigid_body.lock_rotation.y, !rigid_body.lock_rotation.z)
+            .locked_axes(bits)
             .build();
 
         let body_handle = self.bodies.insert(body);
diff --git a/eucalyptus-core/src/scene.rs b/eucalyptus-core/src/scene.rs
index 64265e6..3699478 100644
--- a/eucalyptus-core/src/scene.rs
+++ b/eucalyptus-core/src/scene.rs
@@ -142,6 +142,7 @@ impl SceneConfig {
             builder.add(*transform);
         } else if let Some(renderer) = component.as_any().downcast_ref::<SerializedMeshRenderer>() {
             let renderer = renderer.clone();
+            let import_scale = renderer.import_scale.unwrap_or(1.0);
             let mut model = match &renderer.handle.ref_type {
                 ResourceReferenceType::None => {
                     log::error!(
@@ -161,7 +162,7 @@ impl SceneConfig {
                         id: ModelId(*id),
                     });
                     let loaded = LoadedModel::new_raw(&ASSET_REGISTRY, model);
-                    MeshRenderer::from_handle(loaded)
+                    MeshRenderer::from_handle_with_import_scale(loaded, import_scale)
                 }
                 ResourceReferenceType::File(reference) => {
                     let path = &renderer.handle.resolve()?;
@@ -173,15 +174,16 @@ impl SceneConfig {
                             reference
                         );
 
-                    MeshRenderer::from_path(graphics.clone(), &path, Some(label)).await?
+                    let loaded = Model::load(graphics.clone(), path, Some(label), None).await?;
+                    MeshRenderer::from_handle_with_import_scale(loaded, import_scale)
                 }
                 ResourceReferenceType::Bytes(bytes) => {
                     log::info!("Loading entity from bytes [Len: {}]", bytes.len());
 
-                    let model =
-                        Model::load_from_memory(graphics.clone(), bytes.clone(), Some(label))
+                    let loaded =
+                        Model::load_from_memory(graphics.clone(), bytes.clone(), Some(label), None)
                             .await?;
-                    MeshRenderer::from_handle(model)
+                    MeshRenderer::from_handle_with_import_scale(loaded, import_scale)
                 }
                 ResourceReferenceType::Cuboid { size_bits } => {
                     let size = [
@@ -204,7 +206,7 @@ impl SceneConfig {
 
                     loaded_model.refresh_registry();
 
-                    MeshRenderer::from_handle(loaded_model)
+                    MeshRenderer::from_handle_with_import_scale(loaded_model, import_scale)
                 }
             };
 
@@ -220,7 +222,7 @@ impl SceneConfig {
                         ) {
                             let source_path = override_entry.source_model.resolve()?;
                             let label_hint = override_entry.source_model.as_uri();
-                            Model::load(graphics.clone(), &source_path, label_hint).await?;
+                            Model::load(graphics.clone(), &source_path, label_hint, None).await?;
                         } else {
                             log::warn!(
                                 "Material override for '{}' references unsupported resource {:?}",
diff --git a/eucalyptus-core/src/states.rs b/eucalyptus-core/src/states.rs
index 4f31dec..802da45 100644
--- a/eucalyptus-core/src/states.rs
+++ b/eucalyptus-core/src/states.rs
@@ -384,6 +384,12 @@ pub struct SerializedMeshRenderer {
 
     #[serde(default)]
     pub material_customisation: Vec<SerializedMaterialcustomisation>,
+
+    #[serde(default)]
+    #[serde(alias = "custom_import_scale")]
+    #[serde(alias = "editor_import_scale")]
+    #[serde(alias = "baked_import_scale")]
+    pub import_scale: Option<f32>,
 }
 
 #[derive(Default, Debug, Clone, Serialize, Deserialize)]
@@ -470,6 +476,7 @@ impl SerializedMeshRenderer {
             handle: handle.path.clone(),
             material_override: renderer.material_overrides.clone(),
             material_customisation,
+            import_scale: Some(renderer.import_scale()),
         }
     }
 }
\ No newline at end of file
diff --git a/eucalyptus-editor/src/editor/component.rs b/eucalyptus-editor/src/editor/component.rs
index 4762037..6c1b153 100644
--- a/eucalyptus-editor/src/editor/component.rs
+++ b/eucalyptus-editor/src/editor/component.rs
@@ -1147,17 +1147,21 @@ impl InspectableComponent for MeshRenderer {
 
                 if let ResourceReferenceType::File(uri) = &model_reference.ref_type {
                     if is_probably_model_uri(uri) {
-                        let mut import_scale = ASSET_REGISTRY.model_import_scale(&model_reference);
+                        let mut import_scale = self.import_scale();
                         ui.horizontal(|ui| {
-                            ui.label("Imported Scale");
+                            ui.label("Import Scale");
                             let resp = ui.add(
                                 egui::DragValue::new(&mut import_scale)
                                     .speed(0.01)
                                     .range(0.0001..=10_000.0),
                             );
 
-                            if resp.drag_stopped() || (resp.changed() && resp.lost_focus()) {
-                                *signal = Signal::SetModelImportScale(*entity, import_scale);
+                            if resp.changed() {
+                                self.set_import_scale(import_scale);
+                            }
+
+                            if ui.button("Reset").clicked() {
+                                self.set_import_scale(1.0);
                             }
                         });
                         ui.add_space(6.0);
diff --git a/eucalyptus-editor/src/editor/mod.rs b/eucalyptus-editor/src/editor/mod.rs
index 81ec732..f2746d0 100644
--- a/eucalyptus-editor/src/editor/mod.rs
+++ b/eucalyptus-editor/src/editor/mod.rs
@@ -1535,9 +1535,9 @@ pub enum Signal {
     /// Sets a material tint colour (RGBA, unmultiplied).
     SetMaterialTint(hecs::Entity, String, [f32; 4]),
 
-    /// Sets the import scale for the currently assigned model on an entity's MeshRenderer.
+    /// Sets (bakes) the import scale for an entity's MeshRenderer.
     ///
-    /// This is an asset-level setting keyed by the model's resource reference.
+    /// This updates the MeshRenderer's baked import scale (saved into the scene and used at runtime).
     SetModelImportScale(hecs::Entity, f32),
     RequestNewWindow(WindowData),
 }
diff --git a/eucalyptus-editor/src/signal.rs b/eucalyptus-editor/src/signal.rs
index 0f47faa..012e3d8 100644
--- a/eucalyptus-editor/src/signal.rs
+++ b/eucalyptus-editor/src/signal.rs
@@ -52,10 +52,8 @@ impl SignalController for Editor {
             }
 
             Signal::SetModelImportScale(entity, scale) => {
-                if let Ok(renderer) = self.world.get::<&MeshRenderer>(*entity) {
-                    let reference = renderer.model().path.clone();
-                    dropbear_engine::asset::ASSET_REGISTRY
-                        .set_model_import_scale(reference.clone(), *scale);
+                if let Ok(mut renderer) = self.world.get::<&mut MeshRenderer>(*entity) {
+                    renderer.set_import_scale(*scale);
                 }
                 self.signal = Signal::None;
                 Ok(())
@@ -656,7 +654,7 @@ impl SignalController for Editor {
                             PathBuf::from(&uri_clone)
                         };
 
-                        Model::load(graphics_clone.clone(), &path, Some(&uri_clone)).await?
+                        Model::load(graphics_clone.clone(), &path, Some(&uri_clone), None).await?
                     };
 
                     // Ensure imports start as pure white; users can tint later.
@@ -710,6 +708,7 @@ impl SignalController for Editor {
                             graphics_clone.clone(),
                             &path,
                             Some(&uri_clone),
+                            None,
                         )
                         .await?;
 
diff --git a/eucalyptus-editor/src/spawn.rs b/eucalyptus-editor/src/spawn.rs
index 925809c..51e3216 100644
--- a/eucalyptus-editor/src/spawn.rs
+++ b/eucalyptus-editor/src/spawn.rs
@@ -268,6 +268,8 @@ async fn load_renderer_from_serialized(
         uri.ends_with("internal/dropbear/models/cube")
     }
 
+    let import_scale = renderer.import_scale.unwrap_or(1.0);
+
     let mut mesh_renderer = match &renderer.handle.ref_type {
         ResourceReferenceType::None => anyhow::bail!(
             "Renderer for '{}' does not specify an asset reference",
@@ -283,7 +285,7 @@ async fn load_renderer_from_serialized(
             });
 
             let loaded = LoadedModel::new_raw(&ASSET_REGISTRY, model);
-            MeshRenderer::from_handle(loaded)
+            MeshRenderer::from_handle_with_import_scale(loaded, import_scale)
         }
         ResourceReferenceType::File(reference) => {
             if is_legacy_internal_cube_uri(reference) {
@@ -300,13 +302,15 @@ async fn load_renderer_from_serialized(
                 MeshRenderer::from_handle(loaded_model)
             } else {
                 let path = renderer.handle.resolve()?;
-                MeshRenderer::from_path(graphics.clone(), &path, Some(&label)).await?
+                let loaded = Model::load(graphics.clone(), &path, Some(&label), None).await?;
+                MeshRenderer::from_handle_with_import_scale(loaded, import_scale)
             }
         }
         ResourceReferenceType::Bytes(bytes) => {
-            let model =
-                Model::load_from_memory(graphics.clone(), bytes.clone(), Some(&label)).await?;
-            MeshRenderer::from_handle(model)
+            let loaded =
+                Model::load_from_memory(graphics.clone(), bytes.clone(), Some(&label), None)
+                    .await?;
+            MeshRenderer::from_handle_with_import_scale(loaded, import_scale)
         }
         ResourceReferenceType::Cuboid { size_bits } => {
             let size = [
@@ -323,7 +327,7 @@ async fn load_renderer_from_serialized(
 
             loaded_model.refresh_registry();
 
-            MeshRenderer::from_handle(loaded_model)
+            MeshRenderer::from_handle_with_import_scale(loaded_model, import_scale)
         }
     };
 
@@ -338,7 +342,7 @@ async fn load_renderer_from_serialized(
             ) {
                 let source_path = override_entry.source_model.resolve()?;
                 let label_hint = override_entry.source_model.as_uri();
-                if let Err(err) = Model::load(graphics.clone(), &source_path, label_hint).await {
+                if let Err(err) = Model::load(graphics.clone(), &source_path, label_hint, None).await {
                     log::warn!(
                         "Failed to preload source model {:?} for override '{}': {}",
                         override_entry.source_model,
@@ -432,5 +436,7 @@ async fn load_renderer_from_serialized(
         mesh_renderer.sync_asset_registry();
     }
 
+    mesh_renderer.set_import_scale(import_scale);
+
     Ok(mesh_renderer)
 }
diff --git a/redback-runtime/src/scene.rs b/redback-runtime/src/scene.rs
index ef0d154..5cfad19 100644
--- a/redback-runtime/src/scene.rs
+++ b/redback-runtime/src/scene.rs
@@ -271,21 +271,30 @@ impl Scene for Runtime {
             lights
         };
 
-        let renderers = {
-            let mut renderers = Vec::new();
-            let mut query = self.world.query::<&MeshRenderer>();
-            for (_, renderer) in query.iter() {
-                renderers.push(renderer.clone());
-            }
-            renderers
-        };
-
         let mut model_batches: HashMap<ModelId, Vec<InstanceRaw>> = HashMap::new();
-        for renderer in &renderers {
-            model_batches
-                .entry(renderer.model_id())
-                .or_default()
-                .push(renderer.instance.to_raw());
+        {
+            let mut query = self.world.query::<(
+                &mut MeshRenderer,
+                Option<&dropbear_engine::entity::Transform>,
+                Option<&dropbear_engine::entity::EntityTransform>,
+            )>();
+
+            for (_, (renderer, transform_opt, entity_transform_opt)) 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;
+                };
+
+                renderer.update(&transform);
+
+                model_batches
+                    .entry(renderer.model_id())
+                    .or_default()
+                    .push(renderer.instance.to_raw());
+            }
         }
 
         let mut prepared_models = Vec::new();