Skip to main content

mittens_engine/engine/ecs/component/
normal_visualisation.rs

1use crate::engine::ecs::ComponentId;
2use crate::engine::ecs::component::Component;
3
4/// Debug component: visualises vertex normals of the parent `RenderableComponent`.
5///
6/// Spawns one thin cyan emissive cube per vertex, oriented along the vertex normal.
7/// Cubes are 10× taller along Y than X/Z, making them read as needle-like indicators.
8///
9/// Attach as a child of a `RenderableComponent`. The visualisation subtree is spawned
10/// automatically on init and torn down on cleanup.
11#[derive(Debug, Clone)]
12pub struct NormalVisualisationComponent {
13    /// Thickness of each indicator cube (X and Z scale).
14    /// Y scale = thickness * 10.
15    pub thickness: f32,
16
17    /// Root ComponentIds of spawned indicator subtrees, stored for cleanup.
18    pub spawned_roots: Vec<ComponentId>,
19
20    /// Internal: own ComponentId, stored by set_id.
21    component: Option<ComponentId>,
22}
23
24impl NormalVisualisationComponent {
25    pub fn new() -> Self {
26        Self {
27            thickness: 0.02,
28            spawned_roots: Vec::new(),
29            component: None,
30        }
31    }
32
33    pub fn with_thickness(mut self, t: f32) -> Self {
34        self.thickness = t;
35        self
36    }
37
38    pub fn id(&self) -> Option<ComponentId> {
39        self.component
40    }
41}
42
43impl Default for NormalVisualisationComponent {
44    fn default() -> Self {
45        Self::new()
46    }
47}
48
49impl Component for NormalVisualisationComponent {
50    fn set_id(&mut self, component: ComponentId) {
51        self.component = Some(component);
52    }
53
54    fn name(&self) -> &'static str {
55        "normal_visualisation"
56    }
57
58    fn as_any(&self) -> &dyn std::any::Any {
59        self
60    }
61
62    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
63        self
64    }
65
66    fn init(&mut self, emit: &mut dyn crate::engine::ecs::SignalEmitter, component: ComponentId) {
67        emit.push_intent_now(
68            component,
69            crate::engine::ecs::IntentValue::RegisterNormalVis {
70                component_ids: vec![component],
71            },
72        );
73    }
74
75    fn cleanup(
76        &mut self,
77        emit: &mut dyn crate::engine::ecs::SignalEmitter,
78        _component: ComponentId,
79    ) {
80        for root in self.spawned_roots.drain(..) {
81            emit.push_intent_now(
82                root,
83                crate::engine::ecs::IntentValue::RemoveSubtree {
84                    component_ids: vec![root],
85                },
86            );
87        }
88    }
89
90    fn to_mms_ast(
91        &self,
92        _world: &crate::engine::ecs::World,
93    ) -> crate::scripting::ast::ComponentExpression {
94        use crate::engine::ecs::component::ce_helpers::*;
95        ce_call("NormalVis", "thickness", vec![num(self.thickness as f64)])
96    }
97}