Skip to main content

mittens_engine/engine/ecs/component/
mod.rs

1pub mod action;
2pub mod ambient_light;
3pub mod animation;
4pub mod asset_payload;
5pub mod audio_band_pass_filter;
6pub mod audio_buffer_size;
7pub mod audio_clip;
8pub mod audio_gain;
9pub mod audio_high_pass_filter;
10pub mod audio_limiter;
11pub mod audio_low_pass_filter;
12pub mod audio_mix;
13pub mod audio_oscillator;
14pub mod audio_output;
15pub mod avatar_body_yaw;
16pub mod avatar_control;
17pub mod background;
18pub mod background_color;
19pub mod bloom;
20pub mod blur_pass;
21pub mod bone_rest_pose;
22pub mod bounds;
23pub mod camera_2d;
24pub mod camera_3d;
25pub mod camera_xr;
26pub mod clock;
27pub mod collision;
28pub mod collision_shape;
29pub mod color;
30pub mod component_ref;
31pub mod controller_xr;
32pub mod data;
33pub mod directional_light;
34pub mod editor;
35pub mod emissive;
36pub mod emissive_pass;
37pub mod fit_bounds;
38pub mod gesture_coord_type;
39pub mod gizmo;
40pub mod gltf;
41pub mod gravity;
42pub mod grid;
43pub mod html_element;
44pub mod http_client;
45pub mod http_server;
46pub mod ik_chain;
47pub mod input;
48pub mod input_transform_mode;
49pub mod input_xr;
50pub mod input_xr_gamepad;
51pub mod inspect_layout;
52pub mod keyframe;
53pub mod kinetic_response;
54pub mod layout;
55pub mod layout_bounds;
56pub mod light_quantization;
57pub mod mesh;
58pub mod mirror;
59pub mod music_note;
60pub mod normal_visualisation;
61pub mod opacity;
62pub mod option;
63pub mod overlay;
64pub mod pose_capture;
65pub mod render_graph;
66pub mod router;
67pub mod scrolling;
68pub mod secondary_motion;
69pub mod selectable;
70pub mod selection;
71pub mod serialize;
72pub mod signal_observer_router;
73pub mod signal_route_upward;
74pub mod skinned_mesh;
75pub mod stencil_clip;
76pub mod style;
77pub mod transparent_cutout;
78pub mod xr;
79
80pub mod point_light;
81pub mod pointer;
82pub mod raycast;
83pub mod raycastable;
84pub mod raycastable_shape;
85pub mod renderable;
86pub mod renderer_settings;
87pub mod renderer_stats;
88pub mod text;
89pub mod text_input;
90pub mod text_shadow;
91pub mod texture;
92pub mod texture_filtering;
93pub mod transform;
94pub mod transform_camera_specific;
95pub mod transform_parent;
96pub mod transform_pipeline;
97pub mod transform_pipeline_map;
98pub mod transform_temporal_filter;
99pub mod transition;
100pub mod uv;
101
102pub use self::gltf::GLTFComponent;
103pub use crate::engine::ecs::system::model::collision_types::{CollisionMode, CollisionShape};
104pub use action::ActionComponent;
105pub use ambient_light::AmbientLightComponent;
106pub use animation::AnimationComponent;
107pub use animation::AnimationState;
108pub use animation::ResolveTargetsMode;
109pub use asset_payload::AssetPayloadComponent;
110pub use audio_band_pass_filter::*;
111pub use audio_buffer_size::AudioBufferSizeComponent;
112pub use audio_clip::{AudioClipComponent, AudioClipLoadState, AudioTriggerMode};
113pub use audio_gain::*;
114pub use audio_high_pass_filter::*;
115pub use audio_limiter::*;
116pub use audio_low_pass_filter::*;
117pub use audio_mix::AudioMixComponent;
118pub use audio_oscillator::{AudioOscillator, AudioOscillatorComponent, OscillatorType};
119pub use audio_output::AudioOutputComponent;
120pub use avatar_body_yaw::AvatarBodyYawComponent;
121pub use avatar_control::AvatarControlComponent;
122pub use background::BackgroundComponent;
123pub use background_color::BackgroundColorComponent;
124pub use bloom::BloomComponent;
125pub use blur_pass::BlurPassComponent;
126pub use bone_rest_pose::BoneRestPoseComponent;
127pub use bounds::BoundsComponent;
128pub use camera_2d::Camera2DComponent;
129pub use camera_3d::Camera3DComponent;
130pub use camera_xr::CameraXRComponent;
131pub use clock::ClockComponent;
132pub use collision::CollisionComponent;
133pub use collision_shape::CollisionShapeComponent;
134pub use color::ColorComponent;
135pub use component_ref::{
136    ComponentRef, QueryRootMode, ScopedQuery, parse_scoped_query, resolve_component_ref,
137    resolve_scoped_query,
138};
139pub use controller_xr::{ControllerHand, ControllerPoseKind, ControllerXRComponent};
140pub use data::{DataComponent, DataEntry, DataValue};
141pub use directional_light::DirectionalLightComponent;
142pub use editor::EditorComponent;
143pub use editor::EditorInteractionMode;
144pub use editor::TransformGizmoCoordSpace;
145pub use emissive::EmissiveComponent;
146pub use emissive_pass::EmissivePassComponent;
147pub use fit_bounds::{FitBoundsComponent, FitBoundsMode, FitBoundsTarget};
148pub use gesture_coord_type::{GestureCoordType, GestureCoordTypeComponent};
149pub use gizmo::{
150    TransformGizmoAxis, TransformGizmoComponent, TransformGizmoRotateComponent,
151    TransformGizmoScaleComponent, TransformGizmoTranslateComponent,
152};
153pub use gravity::GravityComponent;
154pub use grid::GridComponent;
155pub use html_element::{ElementType, HtmlElementComponent};
156pub use http_client::HttpClientComponent;
157pub use http_server::HttpServerComponent;
158pub use ik_chain::{IKChainComponent, IKSolver};
159pub use input::InputComponent;
160pub use input_transform_mode::{ForwardAxis, InputTransformModeComponent, RollAxis};
161pub use input_xr::InputXRComponent;
162pub use input_xr_gamepad::{
163    InputXRGamepadComponent, XrAxisControl, XrButtonControl, XrHandPreference,
164};
165pub use inspect_layout::InspectLayoutComponent;
166pub use keyframe::KeyframeComponent;
167pub use kinetic_response::{KineticResponseComponent, KineticResponseMode};
168pub use layout::LayoutComponent;
169pub use layout_bounds::LayoutBoundsComponent;
170pub use light_quantization::LightQuantizationComponent;
171pub use mesh::MeshComponent;
172pub use mirror::MirrorComponent;
173pub(crate) use music_note::NotePitch;
174pub use music_note::{MusicNote, MusicNoteComponent};
175pub use normal_visualisation::NormalVisualisationComponent;
176pub use opacity::OpacityComponent;
177pub use option::OptionComponent;
178pub use overlay::OverlayComponent;
179pub use point_light::PointLightComponent;
180pub use pointer::PointerComponent;
181pub use pose_capture::*;
182pub use raycast::{RayCastComponent, RayCastMode};
183pub use raycastable::{PointerEvents, RaycastableComponent};
184pub use raycastable_shape::{RaycastableShapeComponent, RaycastableShapeType};
185pub use render_graph::RenderGraphComponent;
186pub use renderable::RenderableComponent;
187pub use renderer_settings::RendererSettingsComponent;
188pub use renderer_stats::RendererStatsComponent;
189pub use router::RouterComponent;
190pub use scrolling::ScrollingComponent;
191pub use secondary_motion::{
192    SecondaryMotionComponent, SpringBoneComponent, SpringJointComponent,
193    export_secondary_motion_sidecar,
194};
195pub use selectable::SelectableComponent;
196pub use selection::{SelectionComponent, SelectionEntry, SelectionMode};
197pub use serialize::SerializeComponent;
198pub use signal_observer_router::SignalObserverRouterComponent;
199pub use signal_route_upward::SignalRouteUpwardComponent;
200pub use skinned_mesh::SkinnedMeshComponent;
201pub use stencil_clip::StencilClipComponent;
202pub use style::{
203    AlignItems, BoxSizing, Display, EdgeInsets, FlexDirection, FlexWrap, JustifyContent, Overflow,
204    Position, SizeDimension, StyleComponent, StylePatch, TextAlign, WordWrapMode,
205};
206pub use text::TextComponent;
207pub use text_input::{TextInputComponent, TextInputGlyphHitComponent};
208pub use text_shadow::TextShadowComponent;
209pub use texture::{CatEngineTextureFormat, TextureComponent};
210pub use texture_filtering::TextureFilteringComponent;
211pub use transform::TransformComponent;
212pub use transform_camera_specific::{
213    TransformCameraSpecificComponent, TransformCameraSpecificMode,
214};
215pub use transform_parent::TransformParentComponent;
216pub use transform_pipeline::{
217    TransformDropComponent, TransformForkTRSComponent, TransformMergeTRSComponent,
218    TransformSampleAncestorComponent,
219};
220pub use transform_pipeline_map::{
221    TransformMapRotationComponent, TransformMapScaleComponent, TransformMapTranslationComponent,
222};
223pub use transform_temporal_filter::{
224    QuatExtractYawComponent, QuatTemporalFilterComponent, QuatYawFollowComponent,
225    Vector3TemporalFilterComponent,
226};
227pub use transition::{TransitionComponent, TransitionEasing, TransitionReplacePolicy};
228pub use transparent_cutout::TransparentCutoutComponent;
229pub use uv::UVComponent;
230pub use xr::XrComponent;
231pub type XRHandComponent = ControllerXRComponent;
232
233/// For now, our "LightComponent" is a point light.
234pub type LightComponent = point_light::PointLightComponent;
235
236/// World-owned record for a component payload plus its topology.
237///
238/// This is the building block of the component-centric ECS: a single flat store of records
239/// in `World`, each record carrying its own parent/children handles.
240
241pub struct ComponentNode {
242    pub guid: uuid::Uuid,
243    /// Engine-side type identifier from `Component::name()` (e.g. `"transform"`, `"text"`).
244    /// Set at construction, never changes.
245    pub component_type: String,
246    /// User-assigned label for this node (e.g. `name = "catgirl"` in MMS).
247    /// Defaults to empty. Used for `#label` query selectors.
248    pub name: String,
249    /// CSS-style class membership (e.g. `class = "avatar"` in MMS).
250    /// Used for `.class` query selectors.
251    pub classes: Vec<String>,
252    pub component: Box<dyn Component>,
253    pub parent: Option<crate::engine::ecs::ComponentId>,
254    pub children: Vec<crate::engine::ecs::ComponentId>,
255    pub initialized: bool,
256}
257
258impl ComponentNode {
259    pub fn new(component: Box<dyn Component>) -> Self {
260        let component_type = component.name().to_string();
261        Self {
262            guid: uuid::Uuid::new_v4(),
263            component_type,
264            name: String::new(),
265            classes: Vec::new(),
266            component,
267            parent: None,
268            children: Vec::new(),
269            initialized: false,
270        }
271    }
272
273    /// Create a node with a user-assigned label (`name`).
274    /// `component_type` is still derived from `component.name()`.
275    pub fn new_named(name: impl Into<String>, component: Box<dyn Component>) -> Self {
276        let component_type = component.name().to_string();
277        Self {
278            guid: uuid::Uuid::new_v4(),
279            component_type,
280            name: name.into(),
281            classes: Vec::new(),
282            component,
283            parent: None,
284            children: Vec::new(),
285            initialized: false,
286        }
287    }
288
289    pub fn new_with_guid_named(
290        guid: uuid::Uuid,
291        name: impl Into<String>,
292        component: Box<dyn Component>,
293    ) -> Self {
294        let component_type = component.name().to_string();
295        Self {
296            guid,
297            component_type,
298            name: name.into(),
299            classes: Vec::new(),
300            component,
301            parent: None,
302            children: Vec::new(),
303            initialized: false,
304        }
305    }
306}
307
308/// Component interface.
309/// `init` runs when the component is registered
310pub trait Component: std::any::Any {
311    fn as_any(&self) -> &dyn std::any::Any;
312    fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
313
314    /// Short debug/type name for this component kind (e.g. "transform", "camera").
315    fn name(&self) -> &'static str;
316
317    fn set_id(&mut self, _component: crate::engine::ecs::ComponentId) {}
318
319    /// Called when component is added to the World
320    fn init(
321        &mut self,
322        _emit: &mut dyn crate::engine::ecs::SignalEmitter,
323        _component: crate::engine::ecs::ComponentId,
324    ) {
325    }
326
327    /// Called when component is removed from the World.
328    fn cleanup(
329        &mut self,
330        _emit: &mut dyn crate::engine::ecs::SignalEmitter,
331        _component: crate::engine::ecs::ComponentId,
332    ) {
333    }
334
335    /// Encode component data to a HashMap for serialization.
336    ///
337    /// Components should serialize their data fields (not runtime handles).
338    fn encode(&self) -> std::collections::HashMap<String, serde_json::Value> {
339        std::collections::HashMap::new()
340    }
341
342    /// Decode component data from a HashMap after deserialization.
343    ///
344    /// Components should restore their data fields from the map.
345    fn decode(
346        &mut self,
347        _data: &std::collections::HashMap<String, serde_json::Value>,
348    ) -> Result<(), String> {
349        Ok(())
350    }
351
352    /// Encode this component as an MMS Component Expression AST node.
353    ///
354    /// The returned `ComponentExpression` should round-trip through the
355    /// MMS pipeline: `unparse → parse → eval_ce → spawn_tree` reconstructs
356    /// an equivalent component. Default impl emits a bare CE with no
357    /// constructors/body, using `name()` (snake_case) as the type — this
358    /// works for "tag" components with no state but most overrides will
359    /// want to emit proper builder calls for their fields.
360    fn to_mms_ast(
361        &self,
362        _world: &crate::engine::ecs::World,
363    ) -> crate::scripting::ast::ComponentExpression {
364        crate::scripting::ast::ComponentExpression {
365            component_type: crate::scripting::ast::Ident(self.name().to_string()),
366            constructors: Vec::new(),
367            body: crate::scripting::ast::BlockStatement {
368                statements: Vec::new(),
369            },
370        }
371    }
372}
373
374// ---------------------------------------------------------------------------
375// Helpers for component `to_mms_ast` implementations.
376// Imported by each component file: `use super::ce_helpers::*;` (or fully qualified).
377// ---------------------------------------------------------------------------
378
379pub mod ce_helpers {
380    use crate::scripting::ast::{
381        BlockStatement, ComponentExpression, ConstructorCall, Expression, Ident, UnaryOpKind,
382    };
383
384    /// Empty CE: `Name {}` (no constructors, empty body).
385    pub fn ce(type_name: &str) -> ComponentExpression {
386        ComponentExpression {
387            component_type: Ident(type_name.to_string()),
388            constructors: Vec::new(),
389            body: BlockStatement {
390                statements: Vec::new(),
391            },
392        }
393    }
394
395    /// CE with a single primary constructor: `Name.method(args)`.
396    pub fn ce_call(type_name: &str, method: &str, args: Vec<Expression>) -> ComponentExpression {
397        ComponentExpression {
398            component_type: Ident(type_name.to_string()),
399            constructors: vec![ConstructorCall {
400                method: Ident(method.to_string()),
401                args,
402            }],
403            body: BlockStatement {
404                statements: Vec::new(),
405            },
406        }
407    }
408
409    pub trait CeBuilder {
410        fn with_call(self, method: &str, args: Vec<Expression>) -> Self;
411    }
412
413    impl CeBuilder for ComponentExpression {
414        fn with_call(mut self, method: &str, args: Vec<Expression>) -> Self {
415            self.constructors.push(ConstructorCall {
416                method: Ident(method.to_string()),
417                args,
418            });
419            self
420        }
421    }
422
423    pub fn num(n: f64) -> Expression {
424        if n < 0.0 {
425            Expression::UnaryOp {
426                op: UnaryOpKind::Neg,
427                operand: Box::new(Expression::Number(-n)),
428            }
429        } else {
430            Expression::Number(n)
431        }
432    }
433
434    pub fn nums<I: IntoIterator<Item = f64>>(values: I) -> Vec<Expression> {
435        values.into_iter().map(num).collect()
436    }
437
438    pub fn s(text: &str) -> Expression {
439        Expression::String(text.to_string())
440    }
441
442    pub fn b(value: bool) -> Expression {
443        Expression::Bool(value)
444    }
445
446    pub fn ident(name: &str) -> Expression {
447        Expression::Identifier(Ident(name.to_string()))
448    }
449
450    pub fn array(items: Vec<Expression>) -> Expression {
451        Expression::Array(items)
452    }
453
454    pub fn ls<I: IntoIterator<Item = S>, S: AsRef<str>>(values: I) -> Expression {
455        Expression::Array(values.into_iter().map(|v| s(v.as_ref())).collect())
456    }
457}