kitgit

tirbofish/dropbear · commit

060c236f3f92d75a8b55d31a979217fc1025d799

feature: multisampling with MSAA4 feature: increase active camera speed with mouse wheel scroll fix: when TRS animation was run, it would rotate the entire model, making it look like it was standing on its nose jarvis, run github actions.

Unverified

Thribhu K <4tkbytes@pm.me> · 2026-02-22 14:15

view full diff

diff --git a/crates/dropbear-engine/src/entity.rs b/crates/dropbear-engine/src/entity.rs
index 605e45f..01cd9d2 100644
--- a/crates/dropbear-engine/src/entity.rs
+++ b/crates/dropbear-engine/src/entity.rs
@@ -18,12 +18,18 @@ use egui::Ui;
 pub struct EntityTransform {
     local: Transform,
     world: Transform,
+    #[serde(default)]
+    animation: Transform,
 }
 
 impl EntityTransform {
     /// Creates a new [EntityTransform] from a local and world [Transform]
     pub fn new(local: Transform, world: Transform) -> Self {
-        Self { local, world }
+        Self {
+            local,
+            world,
+            animation: Transform::default(),
+        }
     }
 
     /// Creates a new [EntityTransform] from a world [Transform] and a default local transform.
@@ -33,6 +39,7 @@ impl EntityTransform {
         Self {
             world,
             local: Transform::default(),
+            animation: Transform::default(),
         }
     }
 
@@ -59,22 +66,26 @@ impl EntityTransform {
     /// Combines both transforms into one, propagating the local transform
     /// to the world transform and returning a uniform [Transform]
     pub fn sync(&self) -> Transform {
-        let scaled_pos = self.local.position * self.world.scale;
-        let rotated_pos = self.world.rotation * scaled_pos;
-        let position = self.world.position + rotated_pos;
+        let combined = self.world.matrix() * self.local.matrix() * self.animation.matrix();
+        let (scale, rotation, position) = combined.to_scale_rotation_translation();
 
         Transform {
             position,
-            rotation: self.world.rotation * self.local.rotation,
-            scale: self.world.scale * self.local.scale,
+            rotation,
+            scale,
         }
     }
 
     /// Applies a node transform for TRS animation as an absolute local transform.
     pub fn apply_animation(&mut self, node_transform: &NodeTransform) {
-        self.local.position = node_transform.translation.as_dvec3();
-        self.local.rotation = node_transform.rotation.as_dquat();
-        self.local.scale = node_transform.scale.as_dvec3();
+        self.animation.position = node_transform.translation.as_dvec3();
+        self.animation.rotation = node_transform.rotation.as_dquat();
+        self.animation.scale = node_transform.scale.as_dvec3();
+    }
+
+    /// Clears the animation contribution to the local transform.
+    pub fn clear_animation(&mut self) {
+        self.animation = Transform::default();
     }
 }
 
@@ -183,6 +194,9 @@ impl Transform {
                     .fixed_decimals(2),
             );
         });
+        if ui.button("Reset Position").clicked() {
+            self.position = DVec3::ZERO;
+        }
 
         ui.add_space(4.0);
 
@@ -231,6 +245,10 @@ impl Transform {
             })
             .inner;
 
+        if ui.button("Reset Rotation").clicked() {
+            self.rotation = DQuat::IDENTITY;
+        }
+
         if changed {
             self.rotation = DQuat::from_euler(
                 glam::EulerRot::XYZ,
@@ -269,6 +287,10 @@ impl Transform {
                     .fixed_decimals(3),
             );
         });
+
+        if ui.button("Reset Scale").clicked() {
+            self.scale = DVec3::ONE;
+        }
     }
 }
 
diff --git a/crates/dropbear-engine/src/graphics.rs b/crates/dropbear-engine/src/graphics.rs
index 73cd01c..65993aa 100644
--- a/crates/dropbear-engine/src/graphics.rs
+++ b/crates/dropbear-engine/src/graphics.rs
@@ -32,8 +32,6 @@ pub struct SharedGraphicsContext {
     pub mipmapper: Arc<MipMapper>,
     pub hdr: Arc<RwLock<HdrPipeline>>,
     pub antialiasing: Arc<RwLock<AntiAliasingMode>>,
-    // pub yakui_renderer: Arc<Mutex<yakui_wgpu::YakuiWgpu>>,
-    // pub yakui_texture: yakui::TextureId,
 }
 
 impl SharedGraphicsContext {
diff --git a/crates/dropbear-engine/src/multisampling.rs b/crates/dropbear-engine/src/multisampling.rs
index 5e6a870..5f8c907 100644
--- a/crates/dropbear-engine/src/multisampling.rs
+++ b/crates/dropbear-engine/src/multisampling.rs
@@ -1,4 +1,6 @@
-#[derive(Debug, Clone, Copy, PartialEq, Default)]
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
 pub enum AntiAliasingMode {
     #[default]
     None,
diff --git a/crates/eucalyptus-core/src/animation.rs b/crates/eucalyptus-core/src/animation.rs
index 59018cd..adc6d1d 100644
--- a/crates/eucalyptus-core/src/animation.rs
+++ b/crates/eucalyptus-core/src/animation.rs
@@ -60,14 +60,28 @@ impl Component for AnimationComponent {
         let target_node = self
             .active_animation_index
             .and_then(|index| model.animations.get(index))
-            .and_then(|animation| animation.channels.first())
-            .map(|channel| channel.target_node);
+            .and_then(|animation| {
+                let root_node = model
+                    .skins
+                    .first()
+                    .and_then(|skin| skin.skeleton_root)
+                    .or_else(|| model.nodes.iter().position(|n| n.parent.is_none()));
+
+                root_node.or_else(|| animation.channels.first().map(|channel| channel.target_node))
+            });
 
-        if let Some(node_idx) = target_node {
-            if let Some(node_transform) = self.local_pose.get(&node_idx) {
-                if let Ok(mut entity_transform) = world.get::<&mut EntityTransform>(entity) {
-                    entity_transform.apply_animation(node_transform);
-                }
+        let node_transform = target_node.and_then(|node_idx| {
+            self.local_pose
+                .get(&node_idx)
+                .cloned()
+                .or_else(|| model.nodes.get(node_idx).map(|n| n.transform.clone()))
+        });
+
+        if let Ok(mut entity_transform) = world.get::<&mut EntityTransform>(entity) {
+            if let Some(node_transform) = node_transform.as_ref() {
+                entity_transform.apply_animation(node_transform);
+            } else {
+                entity_transform.clear_animation();
             }
         }
 
diff --git a/crates/eucalyptus-core/src/camera.rs b/crates/eucalyptus-core/src/camera.rs
index ada38d6..4f42dec 100644
--- a/crates/eucalyptus-core/src/camera.rs
+++ b/crates/eucalyptus-core/src/camera.rs
@@ -16,7 +16,6 @@ use std::sync::Arc;
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct CameraComponent {
-    pub settings: CameraSettings,
     pub camera_type: CameraType,
     pub starting_camera: bool,
 }
@@ -39,10 +38,10 @@ impl Component for Camera {
         }
     }
 
-    fn init<'a>(
-        ser: &'a Self::SerializedForm,
+    fn init(
+        ser: &'_ Self::SerializedForm,
         graphics: Arc<SharedGraphicsContext>,
-    ) -> ComponentInitFuture<'a, Self> {
+    ) -> ComponentInitFuture<'_, Self> {
         Box::pin(async move {
             let label = ser.label.clone();
             let builder = CameraBuilder::from(ser.clone());
@@ -196,15 +195,10 @@ impl Default for CameraComponent {
 impl CameraComponent {
     pub fn new() -> Self {
         Self {
-            settings: CameraSettings::default(),
             camera_type: CameraType::Normal,
             starting_camera: false,
         }
     }
-
-    pub fn update(&mut self, camera: &mut Camera) {
-        camera.settings = self.settings;
-    }
 }
 
 impl From<SerializableCamera> for CameraBuilder {
@@ -234,13 +228,7 @@ impl From<SerializableCamera> for CameraBuilder {
 
 impl From<SerializableCamera> for CameraComponent {
     fn from(value: SerializableCamera) -> Self {
-        let settings = CameraSettings::new(
-            value.speed as f64,
-            value.sensitivity as f64,
-            value.fov as f64,
-        );
         Self {
-            settings,
             camera_type: value.camera_type,
             starting_camera: value.starting_camera,
         }
diff --git a/crates/eucalyptus-core/src/states.rs b/crates/eucalyptus-core/src/states.rs
index c199292..443236b 100644
--- a/crates/eucalyptus-core/src/states.rs
+++ b/crates/eucalyptus-core/src/states.rs
@@ -9,7 +9,7 @@ use crate::component::{
 use crate::config::{ProjectConfig, ResourceConfig, SourceConfig};
 use crate::properties::Value;
 use crate::scene::SceneConfig;
-use dropbear_engine::camera::Camera;
+use dropbear_engine::camera::{Camera, CameraSettings};
 use dropbear_engine::entity::Transform;
 use dropbear_engine::graphics::SharedGraphicsContext;
 use dropbear_engine::lighting::LightComponent;
@@ -252,7 +252,7 @@ pub struct SerializableCamera {
 
 impl Default for SerializableCamera {
     fn default() -> Self {
-        let default = CameraComponent::new();
+        let settings = CameraSettings::default();
         Self {
             transform: Transform::default(),
             aspect: 16.0 / 9.0,
@@ -261,8 +261,8 @@ impl Default for SerializableCamera {
             far: 100.0,
             label: String::new(),
             camera_type: CameraType::Normal,
-            speed: default.settings.speed as f32,
-            sensitivity: default.settings.sensitivity as f32,
+            speed: settings.speed as f32,
+            sensitivity: settings.sensitivity as f32,
             starting_camera: false,
         }
     }
diff --git a/crates/eucalyptus-editor/src/editor/scene.rs b/crates/eucalyptus-editor/src/editor/scene.rs
index 85d6ec7..f4663b4 100644
--- a/crates/eucalyptus-editor/src/editor/scene.rs
+++ b/crates/eucalyptus-editor/src/editor/scene.rs
@@ -28,6 +28,7 @@ use std::{
 };
 use wgpu::util::DeviceExt;
 use winit::{event::WindowEvent, event_loop::ActiveEventLoop, keyboard::KeyCode};
+use winit::event::{MouseScrollDelta, TouchPhase};
 
 impl Scene for Editor {
     fn load(&mut self, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) {
@@ -838,6 +839,23 @@ impl Scene for Editor {
             WindowEvent::HoveredFileCancelled => {
                 log_once::debug_once!("Hover cancelled");
             }
+            WindowEvent::MouseWheel { delta, phase, .. } => {
+                if matches!(self.viewport_mode, ViewportMode::CameraMove)
+                    && !matches!(phase, TouchPhase::Cancelled)
+                {
+                    let active = { self.active_camera.lock().clone() };
+                    if let Some(e) = active {
+                        if let Ok(mut cam) = self.world.get::<&mut Camera>(e) {
+                            let speed_delta = match delta {
+                                MouseScrollDelta::LineDelta(_, y) => *y as f64 * 2.0,
+                                MouseScrollDelta::PixelDelta(pos) => pos.y * 0.015,
+                            };
+
+                            cam.settings.speed = (cam.settings.speed + speed_delta).max(0.1);
+                        }
+                    }
+                }
+            }
             _ => {}
         }
     }
diff --git a/crates/eucalyptus-editor/src/editor/settings/editor.rs b/crates/eucalyptus-editor/src/editor/settings/editor.rs
index 2cbcb16..bd77274 100644
--- a/crates/eucalyptus-editor/src/editor/settings/editor.rs
+++ b/crates/eucalyptus-editor/src/editor/settings/editor.rs
@@ -3,7 +3,7 @@
 use app_dirs2::AppDataType;
 use dropbear_engine::input::{Controller, Keyboard, Mouse};
 use dropbear_engine::scene::{Scene, SceneCommand};
-use egui::{CentralPanel, Id, Slider, SliderClamping};
+use egui::{CentralPanel, ComboBox, Id, Slider, SliderClamping};
 use egui_dock::DockState;
 use egui_ltreeview::{Action, NodeBuilder};
 use eucalyptus_core::input::InputState;
@@ -19,6 +19,7 @@ use winit::event::MouseButton;
 use winit::event_loop::ActiveEventLoop;
 use winit::keyboard::KeyCode;
 use winit::window::WindowId;
+use dropbear_engine::multisampling::AntiAliasingMode;
 
 pub static EDITOR_SETTINGS: Lazy<RwLock<EditorSettings>> =
     Lazy::new(|| RwLock::new(EditorSettings::new()));
@@ -35,6 +36,9 @@ pub struct EditorSettings {
     #[serde(default)]
     pub target_fps: HistoricalOption<u32>,
 
+    #[serde(default)]
+    pub anti_aliasing_mode: AntiAliasingMode,
+
     /// Is the debug menu shown?
     ///
     /// Primarily used internally for testing out features of the editor, however
@@ -52,6 +56,7 @@ impl EditorSettings {
             dock_layout: None,
             is_debug_menu_shown: false,
             target_fps: HistoricalOption::none(),
+            anti_aliasing_mode: AntiAliasingMode::MSAA4,
         }
     }
 
@@ -220,6 +225,24 @@ impl Scene for EditorSettingsWindow {
                                     );
                                 }
                             });
+                            {
+                                let mut antialiasing = graphics.antialiasing.write();
+                                ui.label("Anti-aliasing mode:");
+                                ComboBox::from_id_salt("anti-aliasing-mode-combobox")
+                                    .selected_text(match *antialiasing {
+                                        AntiAliasingMode::None => "None",
+                                        AntiAliasingMode::MSAA4 => "MSAA4"
+                                    })
+                                    .show_ui(ui, |ui| {
+                                        ui.selectable_value(&mut *antialiasing, AntiAliasingMode::None, "None");
+                                        ui.selectable_value(&mut *antialiasing, AntiAliasingMode::MSAA4, "MSAA4");
+                                    });
+
+                                if editor.anti_aliasing_mode != *antialiasing {
+                                    editor.anti_aliasing_mode = *antialiasing;
+                                }
+                            }
+
                         }
                         _ => {}
                     });
diff --git a/crates/redback-runtime/src/scene.rs b/crates/redback-runtime/src/scene.rs
index c817b60..0bf1b36 100644
--- a/crates/redback-runtime/src/scene.rs
+++ b/crates/redback-runtime/src/scene.rs
@@ -25,7 +25,7 @@ use eucalyptus_core::rapier3d::prelude::QueryFilter;
 use eucalyptus_core::scene::loading::{IsSceneLoaded, SCENE_LOADER, SceneLoadResult};
 use eucalyptus_core::states::SCENES;
 use eucalyptus_core::states::{Label, PROJECT};
-use glam::{DQuat, DVec3, Mat4, Quat, Vec2};
+use glam::{DVec3, Mat4, Quat, Vec2};
 use hecs::Entity;
 // use kino_ui::widgets::rect::Rectangle;
 // use kino_ui::widgets::{Border, Fill};
@@ -263,18 +263,10 @@ impl Scene for PlayMode {
                     let base = entity_transform.world_mut();
                     base.position = new_local_pos;
                     base.rotation = new_local_rot;
-
-                    let offset = entity_transform.local_mut();
-                    offset.position = DVec3::ZERO;
-                    offset.rotation = DQuat::IDENTITY;
                 } else {
                     let base = entity_transform.world_mut();
                     base.position = new_world_pos;
                     base.rotation = new_world_rot;
-
-                    let offset = entity_transform.local_mut();
-                    offset.position = DVec3::ZERO;
-                    offset.rotation = DQuat::IDENTITY;
                 }
             }
         }