Skip to main content

mesh_factory_example/
mesh-factory-example.rs

1use mittens_engine::{engine, utils};
2
3#[path = "example_util/mod.rs"]
4mod example_util;
5
6fn main() {
7    mittens_engine::example_support::ensure_model_assets();
8    utils::logger::init();
9
10    const LABEL_WRAP_AT: usize = 13;
11
12    let world = engine::ecs::World::default();
13    let mut universe = engine::Universe::new(world);
14
15    // add a clock
16    let clock = universe
17        .world
18        .add_component(engine::ecs::component::ClockComponent::new().with_bpm(60.0));
19    universe.add(clock);
20
21    // Input-driven camera rig.
22    // Topology: I { T { C3D } }
23    let input = universe
24        .world
25        .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
26    let camera3d = universe
27        .world
28        .add_component(engine::ecs::component::Camera3DComponent::new());
29    let rig_transform = universe.world.add_component(
30        engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 11.0),
31    );
32    let input_mode = universe.world.add_component(
33        engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
34    );
35    let _ = universe.attach(input, input_mode);
36    let _ = universe.attach(input, rig_transform);
37    let _ = universe.attach(rig_transform, camera3d);
38
39    // Topology: I { T { C3D } } — add a small camera-attached controls hint.
40    example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
41    universe.add(input);
42
43    // Light.
44    let light_transform = universe.world.add_component(
45        engine::ecs::component::TransformComponent::new().with_position(0.0, 2.0, 2.0),
46    );
47    let light = universe.world.add_component(
48        engine::ecs::component::PointLightComponent::new()
49            .with_distance(50.0)
50            .with_color(1.0, 1.0, 1.0),
51    );
52    let _ = universe.attach(light_transform, light);
53    universe.add(light_transform);
54
55    fn spawn_labeled_mesh(
56        universe: &mut engine::Universe,
57        x: f32,
58        y: f32,
59        label: &str,
60        mesh: engine::graphics::primitives::CpuMeshHandle,
61        scale: [f32; 3],
62        color: [f32; 4],
63    ) {
64        use engine::ecs::component::{
65            ActionComponent, AnimationComponent, AnimationState, ColorComponent, EmissiveComponent,
66            KeyframeComponent, RenderableComponent, TextComponent, TransformComponent,
67        };
68        use engine::graphics::primitives::{MaterialHandle, Renderable};
69
70        // Mesh.
71        let root = universe.world.add_component(
72            TransformComponent::new()
73                .with_position(x, y, 0.0)
74                .with_scale(scale[0], scale[1], scale[2]),
75        );
76
77        // Spin each shape around its own +Y axis using AnimationComponent + keyframes.
78        // We fill [0, 2) beats densely so it looks smooth.
79        let anim = universe
80            .world
81            .add_component(AnimationComponent::new().with_state(AnimationState::Looping));
82        let _ = universe.attach(root, anim);
83
84        let steps: usize = 64;
85        for i in 0..steps {
86            let beat = (i as f64) * (2.0 / (steps as f64));
87            let kf = universe.world.add_component(KeyframeComponent::new(beat));
88            let _ = universe.attach(anim, kf);
89
90            // Full turn over 2 beats.
91            let angle = (std::f64::consts::TAU * (beat / 2.0)) as f32;
92            let rotation = utils::math::quat_from_axis_angle([0.0, 1.0, 0.0], angle);
93
94            let action_cid = universe.world.add_component(ActionComponent::new(
95                engine::ecs::IntentValue::UpdateTransform {
96                    component_ids: vec![root],
97                    translation: [x, y, 0.0],
98                    rotation_quat_xyzw: rotation,
99                    scale,
100                },
101            ));
102            let _ = universe.attach(kf, action_cid);
103        }
104
105        let renderable = universe
106            .world
107            .add_component(RenderableComponent::new(Renderable::new(
108                mesh,
109                MaterialHandle::TOON_MESH,
110            )));
111        let color_c = universe
112            .world
113            .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
114        let emissive = universe.world.add_component(EmissiveComponent::on());
115
116        let _ = universe.attach(root, renderable);
117        let _ = universe.attach(renderable, color_c);
118        let _ = universe.attach(renderable, emissive);
119
120        universe.add(root);
121
122        // Label (separate transform so we can scale text independently).
123        let text_root = universe.world.add_component(
124            TransformComponent::new()
125                .with_position(x, y + 0.75, 0.05)
126                .with_scale(0.09, 0.09, 1.0),
127        );
128        let text = universe
129            .world
130            .add_component(TextComponent::with_word_wrap_tokens(
131                label,
132                LABEL_WRAP_AT,
133                ["::", "(", ")", ",", "."],
134            ));
135        let text_color = universe
136            .world
137            .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
138        let text_emissive = universe.world.add_component(EmissiveComponent::on());
139        let _ = universe.attach(text_root, text);
140        let _ = universe.attach(text, text_color);
141        let _ = universe.attach(text, text_emissive);
142        universe.add(text_root);
143    }
144
145    // Built-in meshes (stable ids).
146    let tri = universe
147        .render_assets
148        .get_mesh(engine::graphics::BuiltinMeshType::Triangle2D);
149    let quad = universe
150        .render_assets
151        .get_mesh(engine::graphics::BuiltinMeshType::Quad2D);
152    let cube = universe
153        .render_assets
154        .get_mesh(engine::graphics::BuiltinMeshType::Cube);
155    let tetra = universe
156        .render_assets
157        .get_mesh(engine::graphics::BuiltinMeshType::Tetrahedron);
158    let sphere = universe
159        .render_assets
160        .get_mesh(engine::graphics::BuiltinMeshType::Sphere);
161    let cone = universe
162        .render_assets
163        .get_mesh(engine::graphics::BuiltinMeshType::Cone);
164    let circle = universe
165        .render_assets
166        .get_mesh(engine::graphics::BuiltinMeshType::Circle2D);
167
168    // Layout.
169    let y = 0.0;
170    let dx = 1.8;
171    let x0 = -dx * 3.0;
172
173    spawn_labeled_mesh(
174        &mut universe,
175        x0 + dx * 0.0,
176        y,
177        "Triangle2D\nMeshFactory::triangle_2d()",
178        tri,
179        [1.0, 1.0, 1.0],
180        [1.0, 1.0, 1.0, 1.0],
181    );
182    spawn_labeled_mesh(
183        &mut universe,
184        x0 + dx * 1.0,
185        y,
186        "Quad2D\nMeshFactory::quad_2d()",
187        quad,
188        [1.0, 1.0, 1.0],
189        [1.0, 1.0, 1.0, 1.0],
190    );
191    spawn_labeled_mesh(
192        &mut universe,
193        x0 + dx * 2.0,
194        y,
195        "Cube\nMeshFactory::cube()",
196        cube,
197        [0.9, 0.9, 0.9],
198        [1.0, 1.0, 1.0, 1.0],
199    );
200    spawn_labeled_mesh(
201        &mut universe,
202        x0 + dx * 3.0,
203        y,
204        "Tetrahedron\nMeshFactory::tetrahedron()",
205        tetra,
206        [1.0, 1.0, 1.0],
207        [1.0, 1.0, 1.0, 1.0],
208    );
209    spawn_labeled_mesh(
210        &mut universe,
211        x0 + dx * 4.0,
212        y,
213        "Sphere\nMeshFactory::sphere()",
214        sphere,
215        [1.0, 1.0, 1.0],
216        [1.0, 1.0, 1.0, 1.0],
217    );
218    spawn_labeled_mesh(
219        &mut universe,
220        x0 + dx * 5.0,
221        y,
222        "Cone\nMeshFactory::cone(32)",
223        cone,
224        [1.0, 1.0, 1.0],
225        [1.0, 1.0, 1.0, 1.0],
226    );
227    spawn_labeled_mesh(
228        &mut universe,
229        x0 + dx * 6.0,
230        y,
231        "Circle2D\nMeshFactory::circle_2d(0.45, 0.5, 64)",
232        circle,
233        [1.0, 1.0, 1.0],
234        [1.0, 1.0, 1.0, 1.0],
235    );
236
237    // Process init-time registrations (Text expands into glyph subtrees here).
238    universe.systems.process_commands(
239        &mut universe.world,
240        &mut universe.visuals,
241        &mut universe.render_assets,
242        &mut universe.command_queue,
243    );
244
245    engine::Windowing::run_app(universe).expect("Windowing failed");
246}