pub struct PointLightComponent {
pub intensity: f32,
pub distance: f32,
pub color: [f32; 3],
/* private fields */
}Expand description
Point light (local omnidirectional light).
This is purely an ECS representation for now; renderer integration will come later.
Fields§
§intensity: f32§distance: f32§color: [f32; 3]Linear RGB color in 0..1.
Implementations§
Source§impl PointLightComponent
impl PointLightComponent
Sourcepub fn new() -> Self
pub fn new() -> Self
Examples found in repository?
examples/folder-text.rs (line 270)
60fn main() {
61 mittens_engine::example_support::ensure_model_assets();
62 utils::logger::init();
63
64 // Usage:
65 // cargo run --example folder-text -- [folder] [spacing]
66 // Defaults:
67 // folder=src spacing=0.3
68 let mut args = std::env::args().skip(1);
69 let folder = args.next().unwrap_or_else(|| "src".to_string());
70 let spacing: f32 = args
71 .next()
72 .and_then(|s| s.parse::<f32>().ok())
73 .unwrap_or(1.0);
74
75 // Safety caps: this demo can explode the ECS if we load huge projects.
76 const MAX_FILES: usize = 42;
77 const MAX_CHARS_PER_FILE: usize = 10_000;
78 const WRAP_AT: usize = 90;
79 const TEXT_SCALE: f32 = 0.01;
80
81 let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
82 let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
83 let scan_root = {
84 let p = PathBuf::from(&folder);
85 if p.is_absolute() {
86 p
87 } else {
88 // Resolve relative paths from the crate root, not the process CWD.
89 manifest_dir.join(p)
90 }
91 };
92 eprintln!("[folder-text] cwd={:?}", cwd);
93 eprintln!("[folder-text] manifest_dir={:?}", manifest_dir);
94 eprintln!("[folder-text] scan_root={:?}", scan_root);
95
96 let mut files = Vec::new();
97 collect_rs_files(&scan_root, &mut files);
98 files.sort();
99
100 if files.is_empty() {
101 eprintln!(
102 "[folder-text] No .rs files found under folder={:?} (resolved={:?})",
103 folder, scan_root
104 );
105 }
106
107 let files: Vec<PathBuf> = files.into_iter().take(MAX_FILES).collect();
108 eprintln!(
109 "[folder-text] Loaded {} file(s) from {:?} (spacing={})",
110 files.len(),
111 folder,
112 spacing
113 );
114
115 #[derive(Debug, Clone)]
116 struct FileEntry {
117 path: PathBuf,
118 display_text: String,
119 max_cols: usize,
120 rows: usize,
121 }
122
123 // Group files by folder.
124 // Layout:
125 // - each folder becomes a column along +X
126 // - within a folder, files stack along +Y (8 high)
127 // - after 8, additional files start a new stack further along +Z
128 let mut files_by_folder: BTreeMap<PathBuf, Vec<FileEntry>> = BTreeMap::new();
129 for path in files {
130 let Ok(content) = std::fs::read_to_string(&path) else {
131 eprintln!("[folder-text] Failed to read {:?}", path);
132 continue;
133 };
134
135 let mut display_text = format!(
136 "// {}\n\n{}",
137 path.strip_prefix(&manifest_dir).unwrap_or(&path).display(),
138 content
139 );
140
141 if display_text.chars().count() > MAX_CHARS_PER_FILE {
142 display_text = display_text
143 .chars()
144 .take(MAX_CHARS_PER_FILE)
145 .collect::<String>();
146 display_text.push_str("\n\n// ... truncated ...\n");
147 }
148
149 let (max_cols, rows) = measure_text_bounds(&display_text, WRAP_AT);
150
151 let folder_key = path.parent().unwrap_or(&scan_root).to_path_buf();
152 files_by_folder
153 .entry(folder_key)
154 .or_default()
155 .push(FileEntry {
156 path,
157 display_text,
158 max_cols,
159 rows,
160 });
161 }
162
163 let column_count = files_by_folder.len().max(1);
164
165 let world = engine::ecs::World::default();
166 let mut universe = engine::Universe::new(world);
167
168 let bg_r = 0.25;
169 let bg_g = 0.25;
170 let bg_b = 0.25;
171 let background = universe
172 .world
173 .add_component(engine::ecs::component::BackgroundColorComponent::new());
174 let background_c = universe
175 .world
176 .add_component(engine::ecs::component::ColorComponent::rgba(
177 bg_r, bg_g, bg_b, 1.00,
178 ));
179 let _ = universe.world.add_child(background, background_c);
180 universe.add(background);
181
182 let ambient = universe
183 .world
184 .add_component(engine::ecs::component::AmbientLightComponent::rgb(
185 bg_r, bg_g, bg_b,
186 ));
187 universe.add(ambient);
188
189 // Input-driven camera rig.
190 // Topology: I { T { C3D } }
191 let input = universe
192 .world
193 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
194
195 // Center the camera horizontally over the spawned columns.
196 let center_x = if column_count <= 1 {
197 0.0
198 } else {
199 ((column_count - 1) as f32) * spacing * 0.5
200 };
201
202 // Bring camera closer: these text blocks are tiny.
203 let rig_transform = universe.world.add_component(
204 engine::ecs::component::TransformComponent::new().with_position(center_x, 0.0, 1.2),
205 );
206 let camera3d = universe
207 .world
208 .add_component(engine::ecs::component::Camera3DComponent::new());
209 let input_mode = universe.world.add_component(
210 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
211 );
212 let _ = universe.attach(input, input_mode);
213 let _ = universe.attach(input, rig_transform);
214 let _ = universe.attach(rig_transform, camera3d);
215
216 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
217 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
218 universe.add(input);
219
220 // Big floor plane under everything.
221 // RenderableComponent::square() is an XY quad facing +Z; rotate it to XZ facing +Y.
222 let max_stacks = files_by_folder
223 .values()
224 .map(|v| (v.len() + 7) / 8)
225 .max()
226 .unwrap_or(1)
227 .max(1);
228 let total_width = (column_count as f32) * spacing;
229 let total_depth = (max_stacks as f32) * spacing;
230 let floor_w = total_width.max(10.0);
231 let floor_h = total_depth.max(10.0);
232 let floor_transform = universe.world.add_component(
233 engine::ecs::component::TransformComponent::new()
234 .with_position(0.0, -2.0, 0.0)
235 .with_rotation_euler(-std::f32::consts::FRAC_PI_2, 0.0, 0.0)
236 .with_scale(floor_w, floor_h, 1.0),
237 );
238 let floor_renderable = universe
239 .world
240 .add_component(engine::ecs::component::RenderableComponent::square());
241 let floor_color = universe
242 .world
243 .add_component(engine::ecs::component::ColorComponent::rgba(
244 0.88, 0.88, 0.88, 1.0,
245 ));
246 let _ = universe.attach(floor_transform, floor_renderable);
247 let _ = universe.attach(floor_renderable, floor_color);
248 universe.add(floor_transform);
249
250 // 4 red cubes around the perimeter of the world (easy visual anchors).
251 fn spawn_red_cube(universe: &mut engine::Universe, x: f32, y: f32, z: f32, s: f32) {
252 let t = universe.world.add_component(
253 engine::ecs::component::TransformComponent::new()
254 .with_position(x, y, z)
255 .with_scale(s, s, s),
256 );
257 let r = universe
258 .world
259 .add_component(engine::ecs::component::RenderableComponent::cube());
260 let c = universe
261 .world
262 .add_component(engine::ecs::component::ColorComponent::rgba(
263 1.0, 0.0, 0.0, 1.0,
264 ));
265
266 let light_transform = universe.world.add_component(
267 engine::ecs::component::TransformComponent::new().with_position(0.0, s * 0.75, 0.0),
268 );
269 let light = universe.world.add_component(
270 engine::ecs::component::PointLightComponent::new()
271 .with_intensity(1.5)
272 .with_distance(20.0)
273 .with_color(1.0, 0.0, 0.0),
274 );
275
276 let _ = universe.attach(t, r);
277 let _ = universe.attach(r, c);
278 let _ = universe.attach(t, light_transform);
279 let _ = universe.attach(light_transform, light);
280
281 universe.add(t);
282 }
283
284 let p = 1.5;
285 let s = 0.25;
286 spawn_red_cube(&mut universe, -p, -p, 0.0, s);
287 spawn_red_cube(&mut universe, p, -p, 0.0, s);
288 spawn_red_cube(&mut universe, -p, p, 0.0, s);
289 spawn_red_cube(&mut universe, p, p, 0.0, s);
290
291 let start_x = -center_x;
292 let stack_depth = spacing;
293 let row_gap_world = 0.35;
294
295 // One global point light at the top of the overall text "tower".
296 let pad_y = 4.0;
297 let global_max_panel_h_world = files_by_folder
298 .values()
299 .flat_map(|v| v.iter())
300 .map(|e| ((e.rows as f32) + pad_y) * TEXT_SCALE)
301 .fold(0.0_f32, f32::max)
302 .max(0.6);
303 let global_slot_h_world = global_max_panel_h_world + row_gap_world;
304 let tower_top_y = (7.0 * global_slot_h_world) + 4.0;
305 let tower_center_z = total_depth * 0.5;
306
307 let tower_light_transform = universe.world.add_component(
308 engine::ecs::component::TransformComponent::new().with_position(
309 0.0,
310 tower_top_y,
311 tower_center_z,
312 ),
313 );
314 let tower_light = universe.world.add_component(
315 engine::ecs::component::PointLightComponent::new()
316 .with_intensity(2.0)
317 .with_distance(120.0)
318 .with_color(1.0, 1.0, 1.0),
319 );
320 let _ = universe.attach(tower_light_transform, tower_light);
321 universe.add(tower_light_transform);
322
323 for (col_idx, (_folder, mut entries)) in files_by_folder.into_iter().enumerate() {
324 entries.sort_by(|a, b| a.path.cmp(&b.path));
325
326 // Slot height (world units) based on the tallest panel in this folder.
327 let pad_y = 4.0;
328 let max_panel_h_world = entries
329 .iter()
330 .map(|e| ((e.rows as f32) + pad_y) * TEXT_SCALE)
331 .fold(0.0_f32, f32::max)
332 .max(0.6);
333 let slot_h_world = max_panel_h_world + row_gap_world;
334
335 let col_x = start_x + (col_idx as f32) * spacing;
336
337 for (i, entry) in entries.into_iter().enumerate() {
338 let row = (i % 8) as f32;
339 let stack = (i / 8) as f32;
340 let file_y = row * slot_h_world;
341 let file_z = stack * stack_depth;
342
343 let file_group = universe.world.add_component(
344 engine::ecs::component::TransformComponent::new()
345 .with_position(col_x, file_y, file_z),
346 );
347
348 // Text subtree (tiny scale).
349 let file_root = universe.world.add_component(
350 engine::ecs::component::TransformComponent::new()
351 .with_position(0.0, 1.0, 0.0)
352 .with_scale(TEXT_SCALE, TEXT_SCALE, 1.0)
353 .with_rotation_euler(0.0, std::f32::consts::PI / 6.0, 0.0),
354 );
355 let _ = universe.attach(file_group, file_root);
356
357 // Background panel behind the text.
358 // Size is based on wrapped line count (matches TextSystem's strict wrapping behavior).
359 let (max_cols, rows) = (entry.max_cols, entry.rows);
360 let pad_x = 4.0;
361 let pad_y = 4.0;
362 let w = (max_cols as f32) + pad_x;
363 let h = (rows as f32) + pad_y;
364
365 // Text glyph quads are centered at integer (col,row) positions and are 1x1, so the
366 // text's AABB (in text-space) is roughly:
367 // x: [-0.5, max_cols-0.5]
368 // y: [-(rows-1)-0.5, 0.5]
369 // Centering the background quad at those midpoints keeps it aligned as text grows.
370 let bg_x = (max_cols as f32 - 1.0) * 0.5;
371 let bg_y = -((rows as f32 - 1.0) * 0.5);
372
373 let bg_transform = universe.world.add_component(
374 engine::ecs::component::TransformComponent::new()
375 .with_position(bg_x, bg_y, -0.05)
376 .with_scale(w, h, 1.0),
377 );
378 let bg_renderable = universe
379 .world
380 .add_component(engine::ecs::component::RenderableComponent::square());
381 let bg_quant = universe.world.add_component(
382 engine::ecs::component::LightQuantizationComponent::steps(5.0),
383 );
384 let bg_color =
385 universe
386 .world
387 .add_component(engine::ecs::component::ColorComponent::rgba(
388 0.2, 0.2, 0.2, 1.0,
389 ));
390 let _ = universe.attach(file_root, bg_transform);
391 let _ = universe.attach(bg_transform, bg_renderable);
392 let _ = universe.attach(bg_renderable, bg_quant);
393 let _ = universe.attach(bg_renderable, bg_color);
394
395 let text =
396 universe
397 .world
398 .add_component(engine::ecs::component::TextComponent::with_wrap(
399 entry.display_text,
400 WRAP_AT,
401 ));
402 let cutout = universe
403 .world
404 .add_component(engine::ecs::component::TransparentCutoutComponent::new());
405 let filtering = universe.world.add_component(
406 engine::ecs::component::TextureFilteringComponent::nearest_magnification(),
407 );
408 // let color = universe
409 // .world
410 // .add_component(engine::ecs::component::ColorComponent::rgba(0.7, 0.7, 1.0, 1.0));
411 let emissive = universe
412 .world
413 .add_component(engine::ecs::component::EmissiveComponent::on());
414 let _ = universe.attach(file_root, text);
415 let _ = universe.attach(text, cutout);
416 let _ = universe.attach(text, filtering);
417 //let _ = universe.world.add_child(text, color);
418 let _ = universe.attach(text, emissive);
419
420 universe.add(file_group);
421 }
422 }
423
424 // Process init-time registrations (Text expands into glyph subtrees here).
425 universe.systems.process_commands(
426 &mut universe.world,
427 &mut universe.visuals,
428 &mut universe.render_assets,
429 &mut universe.command_queue,
430 );
431
432 engine::Windowing::run_app(universe).expect("Windowing failed");
433}More examples
examples/collision-perimeter.rs (line 104)
86 fn spawn_light(
87 universe: &mut engine::Universe,
88 x: f32,
89 y: f32,
90 z: f32,
91 r: f32,
92 g: f32,
93 b: f32,
94 intensity: f32,
95 distance: f32,
96 ) {
97 let t = universe.world.add_component(
98 engine::ecs::component::TransformComponent::new()
99 .with_position(x, y, z)
100 .with_scale(0.12, 0.12, 0.12),
101 );
102
103 let l = universe.world.add_component(
104 engine::ecs::component::PointLightComponent::new()
105 .with_color(r, g, b)
106 .with_intensity(intensity)
107 .with_distance(distance),
108 );
109 // Visual marker: a small white cube at the light position.
110 // White ensures it mostly shows the light color.
111 let marker = universe
112 .world
113 .add_component(engine::ecs::component::RenderableComponent::cube());
114 let marker_color =
115 universe
116 .world
117 .add_component(engine::ecs::component::ColorComponent::rgba(
118 1.0, 1.0, 1.0, 1.0,
119 ));
120
121 let _ = universe.attach(t, l);
122 let _ = universe.attach(t, marker);
123 let _ = universe.attach(marker, marker_color);
124 universe.add(t);
125 }examples/opacity-example.rs (line 389)
359fn main() {
360 mittens_engine::example_support::ensure_model_assets();
361 utils::logger::init();
362
363 let world = engine::ecs::World::default();
364 let mut universe = engine::Universe::new(world);
365
366 // Dark brown / pink background.
367 let bg = universe
368 .world
369 .add_component(BackgroundColorComponent::new());
370 let bg_c = universe
371 .world
372 .add_component(ColorComponent::rgba(0.22, 0.08, 0.10, 1.0));
373 let _ = universe.world.add_child(bg, bg_c);
374 universe.add(bg);
375
376 // Ambient so unlit areas aren't black.
377 let ambient = universe
378 .world
379 .add_component(AmbientLightComponent::rgb(0.35, 0.35, 0.40));
380 universe.add(ambient);
381
382 // A bright overhead light.
383 let light_t = universe.world.add_component(
384 TransformComponent::new()
385 .with_position(0.0, 18.0, 10.0)
386 .with_scale(0.2, 0.2, 0.2),
387 );
388 let light = universe.world.add_component(
389 PointLightComponent::new()
390 .with_color(1.0, 1.0, 1.0)
391 .with_intensity(1.6)
392 .with_distance(80.0),
393 );
394 let _ = universe.attach(light_t, light);
395 universe.add(light_t);
396
397 // --- Camera rig ---
398 // I { T { C3D { with_fps_rotation with_roll_axis_y } } }
399 let input = universe
400 .world
401 .add_component(InputComponent::new().with_speed(3.0));
402 let input_mode = universe.world.add_component(
403 InputTransformModeComponent::forward_z()
404 .with_roll_axis_y()
405 .with_fps_rotation(),
406 );
407 let _ = universe.attach(input, input_mode);
408
409 let rig_transform = universe
410 .world
411 .add_component(TransformComponent::new().with_position(0.0, 6.0, 20.0));
412 let _ = universe.attach(input, rig_transform);
413
414 let camera = universe.world.add_component(Camera3DComponent::new());
415 let _ = universe.attach(rig_transform, camera);
416
417 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
418 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
419
420 universe.add(input);
421
422 // --- Ground ---
423 let ground = universe.world.add_component(
424 TransformComponent::new()
425 .with_position(0.0, -0.6, 0.0)
426 .with_scale(60.0, 1.0, 60.0),
427 );
428 let ground_r = universe.world.add_component(RenderableComponent::cube());
429 let ground_c = universe
430 .world
431 .add_component(ColorComponent::rgba(0.95, 0.95, 0.95, 1.0));
432 let _ = universe.attach(ground, ground_r);
433 let _ = universe.attach(ground_r, ground_c);
434 universe.add(ground);
435
436 // --- Opaque yellow cubes behind (contrast reference) ---
437 for i in 0..6 {
438 let x = -10.0 + i as f32 * 4.0;
439 spawn_cube(
440 &mut universe,
441 ground,
442 (x, 1.2, -18.0),
443 (2.0, 2.0, 2.0),
444 Some((1.0, 0.92, 0.15, 1.0)),
445 None,
446 );
447 }
448
449 // --- Demo spots ---
450 // Left of the left big spot: a single-layer transparent XY plane (16x16).
451 spawn_demo_xy_plane(&mut universe, (-14.0, 0.0, 0.0), 0.50, 16, 16, 0.0);
452
453 // Big spots: 8x8x8
454 // Left: single-layer transparent (instanced)
455 spawn_demo_spot(&mut universe, (-4.0, 0.0, 0.0), 0.50, false, 8);
456
457 // Right: multi-layer transparent (sorted)
458 spawn_demo_spot(&mut universe, (4.0, 0.0, 0.0), 0.50, true, 8);
459
460 // Small spots: opaque 1x4 strip + transparent 1x4 strip (along Z).
461 spawn_demo_strip_pair(&mut universe, (-4.0, 0.0, 10.0), false);
462 spawn_demo_strip_pair(&mut universe, (4.0, 0.0, 10.0), true);
463
464 // Process init-time registrations.
465 universe.systems.process_commands(
466 &mut universe.world,
467 &mut universe.visuals,
468 &mut universe.render_assets,
469 &mut universe.command_queue,
470 );
471
472 universe.enable_repl();
473
474 engine::Windowing::run_app(universe).expect("Windowing failed");
475}examples/text-animation.rs (line 40)
6fn main() {
7 mittens_engine::example_support::ensure_model_assets();
8 utils::logger::init();
9
10 let world = engine::ecs::World::default();
11 let mut universe = engine::Universe::new(world);
12
13 // Input-driven camera rig.
14 // Topology: I { T { C3D } }
15 let input = universe
16 .world
17 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
18 let camera3d = universe
19 .world
20 .add_component(engine::ecs::component::Camera3DComponent::new());
21 let rig_transform = universe.world.add_component(
22 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.5),
23 );
24 let input_mode = universe.world.add_component(
25 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
26 );
27 let _ = universe.attach(input, input_mode);
28 let _ = universe.attach(input, rig_transform);
29 let _ = universe.attach(rig_transform, camera3d);
30
31 // Small on-screen help.
32 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
33 universe.add(input);
34
35 // Light so we can see non-emissive materials.
36 let light_transform = universe.world.add_component(
37 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.0),
38 );
39 let light = universe.world.add_component(
40 engine::ecs::component::PointLightComponent::new()
41 .with_distance(25.0)
42 .with_color(1.0, 1.0, 1.0),
43 );
44 let _ = universe.attach(light_transform, light);
45 universe.add(light_transform);
46
47 use engine::ecs::component::{
48 ColorComponent, TextComponent, TextShadowComponent, TextureComponent,
49 TextureFilteringComponent, TransformComponent, TransparentCutoutComponent,
50 };
51
52 // Styled text root.
53 let text_root = universe.world.add_component(
54 TransformComponent::new()
55 .with_position(0.0, 0.0, 0.0)
56 .with_scale(0.25, 0.25, 1.0),
57 );
58
59 // Color must be an ancestor of the glyph renderables.
60 let color_id = universe
61 .world
62 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
63 let _ = universe.attach(text_root, color_id);
64
65 // This is the component we animate via SetText.
66 let text_id = universe.world.add_component(TextComponent::new(">w<"));
67 let _ = universe.attach(color_id, text_id);
68
69 // Route into cutout pass for cleaner edges.
70 let cutout = universe
71 .world
72 .add_component(TransparentCutoutComponent::new());
73 let _ = universe.attach(text_id, cutout);
74
75 // Font atlas.
76 let tex = universe.world.add_component(TextureComponent::with_uri(
77 "assets/textures/font_system.dds",
78 ));
79 let _ = universe.attach(text_id, tex);
80
81 let shadow = universe.world.add_component(
82 TextShadowComponent::new()
83 .with_scale(1.35)
84 .with_offset([0.06, -0.06, 0.0015]),
85 );
86 let filtering = universe
87 .world
88 .add_component(TextureFilteringComponent::nearest_magnification());
89
90 let _ = universe.attach(text_id, shadow);
91 let _ = universe.attach(text_id, filtering);
92
93 universe.add(text_root);
94
95 let clock_component = universe
96 .world
97 .add_component(engine::ecs::component::ClockComponent::new().with_bpm(140.0));
98 let _ = universe.add(clock_component);
99
100 // Looping animation: 4 beats long, 4 keyframes.
101 let anim = universe
102 .world
103 .add_component(engine::ecs::component::AnimationComponent::new());
104
105 let faces = [">w<", "^w^", "-_-", "o_o"];
106 for (i, &face) in faces.iter().enumerate() {
107 let kf = universe
108 .world
109 .add_component(engine::ecs::component::KeyframeComponent::new(i as f64));
110
111 // Each keyframe emits a SetText intent.
112 let action = universe
113 .world
114 .add_component(engine::ecs::component::ActionComponent::new(
115 engine::ecs::IntentValue::SetText {
116 component_ids: vec![text_id],
117 text: face.to_string(),
118 },
119 ));
120
121 let _ = universe.attach(anim, kf);
122 let _ = universe.attach(kf, action);
123 }
124
125 universe.add(anim);
126
127 // Process init-time registrations (Text expands into glyph subtrees here).
128 universe.systems.process_commands(
129 &mut universe.world,
130 &mut universe.visuals,
131 &mut universe.render_assets,
132 &mut universe.command_queue,
133 );
134
135 engine::Windowing::run_app(universe).expect("Windowing failed");
136}examples/gravity-fields.rs (line 735)
607 fn spawn_street_light(universe: &mut engine::Universe) -> engine::ecs::ComponentId {
608 // Dimensions (world units).
609 let shaft_height = 6.0;
610 let shaft_thickness = 0.22;
611 let arm_length = shaft_height / 3.0;
612 let arm_thickness = 0.18;
613
614 // Colors.
615 let pole_color = [0.35, 0.35, 0.38, 1.0];
616 let housing_color = [0.18, 0.18, 0.20, 1.0];
617 let diffuser_color = [1.0, 1.0, 1.0, 1.0];
618
619 // Model root at origin.
620 // The caller is expected to position + rotate this model using an external placement transform.
621 // Street light is authored facing +X (arm extends toward +X).
622 let root_t = universe
623 .world
624 .add_component(engine::ecs::component::TransformComponent::new());
625
626 // Vertical shaft (split root/geom so scaling doesn't affect the arm).
627 let shaft_root_t = universe
628 .world
629 .add_component(engine::ecs::component::TransformComponent::new());
630 let shaft_geom_t = universe.world.add_component(
631 engine::ecs::component::TransformComponent::new()
632 .with_position(0.0, shaft_height * 0.5, 0.0)
633 .with_scale(shaft_thickness, shaft_height, shaft_thickness),
634 );
635 let shaft_r = universe
636 .world
637 .add_component(engine::ecs::component::RenderableComponent::cube());
638 let shaft_c = universe
639 .world
640 .add_component(engine::ecs::component::ColorComponent::rgba(
641 pole_color[0],
642 pole_color[1],
643 pole_color[2],
644 pole_color[3],
645 ));
646
647 let _ = universe.attach(root_t, shaft_root_t);
648 let _ = universe.attach(shaft_root_t, shaft_geom_t);
649 let _ = universe.attach(shaft_geom_t, shaft_r);
650 let _ = universe.attach(shaft_r, shaft_c);
651
652 // Horizontal arm at the top; starts at the pole and extends toward +X.
653 let arm_root_t = universe.world.add_component(
654 engine::ecs::component::TransformComponent::new().with_position(0.0, shaft_height, 0.0),
655 );
656 let arm_geom_t = universe.world.add_component(
657 engine::ecs::component::TransformComponent::new()
658 .with_position(arm_length * 0.5, -arm_thickness * 0.5, 0.0)
659 .with_scale(arm_length, arm_thickness, arm_thickness),
660 );
661 let arm_r = universe
662 .world
663 .add_component(engine::ecs::component::RenderableComponent::cube());
664 let arm_c = universe
665 .world
666 .add_component(engine::ecs::component::ColorComponent::rgba(
667 pole_color[0],
668 pole_color[1],
669 pole_color[2],
670 pole_color[3],
671 ));
672
673 let _ = universe.attach(shaft_root_t, arm_root_t);
674 let _ = universe.attach(arm_root_t, arm_geom_t);
675 let _ = universe.attach(arm_geom_t, arm_r);
676 let _ = universe.attach(arm_r, arm_c);
677
678 // Light housing at the end of the arm (+X end).
679 let housing_root_t = universe.world.add_component(
680 engine::ecs::component::TransformComponent::new().with_position(arm_length, 0.0, 0.0),
681 );
682 let housing_geom_t = universe.world.add_component(
683 engine::ecs::component::TransformComponent::new()
684 .with_position(0.5, 0.0, 0.0)
685 // 2x1x1 box in (z, x, y) -> (x, y, z) = (1, 1, 2)
686 .with_scale(2.0, 1.0, 1.0),
687 );
688 let housing_r = universe
689 .world
690 .add_component(engine::ecs::component::RenderableComponent::cube());
691 let housing_c = universe
692 .world
693 .add_component(engine::ecs::component::ColorComponent::rgba(
694 housing_color[0],
695 housing_color[1],
696 housing_color[2],
697 housing_color[3],
698 ));
699
700 let _ = universe.attach(arm_root_t, housing_root_t);
701 let _ = universe.attach(housing_root_t, housing_geom_t);
702 let _ = universe.attach(housing_geom_t, housing_r);
703 let _ = universe.attach(housing_r, housing_c);
704
705 // White diffuser cube (slightly smaller, slightly lower).
706 let diffuser_t = universe.world.add_component(
707 engine::ecs::component::TransformComponent::new()
708 .with_position(0.5, -0.35, 0.0)
709 .with_scale(1.85, 0.55, 0.70),
710 );
711 let diffuser_r = universe
712 .world
713 .add_component(engine::ecs::component::RenderableComponent::cube());
714 let diffuser_c =
715 universe
716 .world
717 .add_component(engine::ecs::component::ColorComponent::rgba(
718 diffuser_color[0],
719 diffuser_color[1],
720 diffuser_color[2],
721 diffuser_color[3],
722 ));
723
724 let diffuser_emissive = universe
725 .world
726 .add_component(engine::ecs::component::EmissiveComponent::on());
727 let _ = universe.attach(diffuser_r, diffuser_emissive);
728
729 let _ = universe.attach(housing_root_t, diffuser_t);
730 let _ = universe.attach(diffuser_t, diffuser_r);
731 let _ = universe.attach(diffuser_r, diffuser_c);
732
733 // Yellow (slightly red) point light.
734 let light = universe.world.add_component(
735 engine::ecs::component::PointLightComponent::new()
736 .with_color(1.0, 0.82, 0.55)
737 .with_intensity(5.0)
738 .with_distance(40.0),
739 );
740 let _ = universe.attach(diffuser_t, light);
741
742 root_t
743 }examples/vtuber-example.rs (line 79)
11fn main() {
12 mittens_engine::example_support::ensure_model_assets();
13 utils::logger::init();
14
15 let world = engine::ecs::World::default();
16 let mut universe = engine::Universe::new(world);
17
18 // Light pink background.
19 let background = universe
20 .world
21 .add_component(BackgroundColorComponent::new());
22 let background_c = universe
23 .world
24 .add_component(ColorComponent::rgba(1.0, 0.82, 0.90, 1.0));
25 let _ = universe.world.add_child(background, background_c);
26 universe.add(background);
27
28 // Small ambient so shadowed areas aren't pure black.
29 let ambient = universe
30 .world
31 .add_component(AmbientLightComponent::rgb(0.10, 0.10, 0.12));
32 universe.add(ambient);
33
34 // --- Camera rig (WASD + mouse) ---
35 // InputComponent is the root, and it owns a Transform (the camera rig).
36 let input = universe
37 .world
38 .add_component(InputComponent::new().with_speed(1.5));
39 let input_mode = universe.world.add_component(
40 InputTransformModeComponent::forward_z()
41 .with_fps_rotation()
42 .with_roll_axis_y(),
43 );
44 let _ = universe.attach(input, input_mode);
45
46 // Start slightly pulled back looking towards the origin.
47 let rig_transform = universe
48 .world
49 .add_component(TransformComponent::new().with_position(0.0, 0.0, 6.0));
50 let _ = universe.attach(input, rig_transform);
51
52 let camera3d = universe.world.add_component(Camera3DComponent::new());
53 let _ = universe.attach(rig_transform, camera3d);
54
55 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
56 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
57
58 universe.add(input);
59
60 // --- lighting ---
61 // Directional key light (slightly down + forward Z).
62 let sun = universe.world.add_component(
63 DirectionalLightComponent::new()
64 .with_intensity(1.2)
65 .with_color(1.0, 0.98, 0.94),
66 );
67 let sun_dir = universe
68 .world
69 .add_component(TransformComponent::new().with_position(0.0, -0.35, 1.0));
70 let _ = universe.attach(sun_dir, sun);
71 universe.add(sun_dir);
72
73 let light_transform = universe.world.add_component(
74 TransformComponent::new()
75 .with_position(1.0, 6.0, 3.0)
76 .with_scale(0.1, 0.1, 0.1),
77 );
78 let light = universe.world.add_component(
79 engine::ecs::component::PointLightComponent::new()
80 .with_distance(120.0)
81 .with_color(1.0, 1.0, 1.0),
82 );
83 let _ = universe.attach(light_transform, light);
84 universe.add(light_transform);
85
86 // --- VTuber model ---
87 let model_root = universe.world.add_component(TransformComponent::new());
88 let model = universe
89 .world
90 .add_component(GLTFComponent::new("assets/models/pc-rei.hoodie.glb"));
91 // emissive for pc-rei
92 let emissive = universe
93 .world
94 .add_component(engine::ecs::component::EmissiveComponent::on());
95
96 let _ = universe.attach(model, emissive);
97
98 let xr_input = universe.world.add_component(InputXRComponent::on());
99 let xr_gamepad = universe
100 .world
101 .add_component(engine::ecs::component::InputXRGamepadComponent::new().speed(1.5));
102 let xr_head = universe.world.add_component(TransformComponent::new());
103 let xr_camera = universe.world.add_component(CameraXRComponent::on());
104 let _ = universe.attach(xr_input, xr_head);
105 let _ = universe.attach(xr_input, xr_gamepad);
106 let _ = universe.attach(xr_head, xr_camera);
107 let _ = universe.attach(xr_head, model_root);
108
109 let _ = universe.attach(model_root, model);
110 universe.add(xr_input);
111 universe.add(model_root);
112
113 // --- Background clouds (occluded + lit) ---
114 let bg_root = universe.world.add_component(
115 engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
116 );
117 universe.add(bg_root);
118 let mut cloud_params = example_util::CloudRingParams::default();
119 cloud_params.cloud_count = 8; // +3 clusters
120 cloud_params.angle_jitter = 0.35;
121 cloud_params.high_y_probability = 0.5;
122 cloud_params.high_y_multiplier = 1.5;
123 cloud_params.seed = 0x57_55_B0_01u32;
124 example_util::spawn_cloud_ring(&mut universe, bg_root, cloud_params);
125
126 // --- Simple environment ---
127 let spawn_cube = |universe: &mut engine::Universe,
128 position: (f32, f32, f32),
129 scale: (f32, f32, f32),
130 color: (f32, f32, f32, f32)| {
131 let transform = universe.world.add_component(
132 TransformComponent::new()
133 .with_position(position.0, position.1, position.2)
134 .with_scale(scale.0, scale.1, scale.2),
135 );
136 let renderable = universe.world.add_component(RenderableComponent::cube());
137 let color = universe
138 .world
139 .add_component(ColorComponent::rgba(color.0, color.1, color.2, color.3));
140
141 let _ = universe.attach(transform, renderable);
142 let _ = universe.attach(renderable, color);
143
144 universe.add(transform);
145 };
146
147 // floor
148 spawn_cube(
149 &mut universe,
150 (0.0, -0.05, 0.0),
151 (10.0, 0.1, 10.0),
152 (0.92, 0.92, 0.92, 1.0),
153 );
154
155 // back wall
156 spawn_cube(
157 &mut universe,
158 (0.0, 1.5, -5.0),
159 (10.0, 3.0, 1.0),
160 (0.95, 0.94, 0.96, 1.0),
161 );
162
163 // desk
164 spawn_cube(
165 &mut universe,
166 (0.0, 0.35, 1.0),
167 (1.0, 0.75, 0.5),
168 (0.75, 0.70, 0.65, 1.0),
169 );
170
171 let xr_root = universe
172 .world
173 .add_component(engine::ecs::component::XrComponent::on());
174 universe.add(xr_root);
175
176 universe.systems.process_commands(
177 &mut universe.world,
178 &mut universe.visuals,
179 &mut universe.render_assets,
180 &mut universe.command_queue,
181 );
182
183 universe.enable_repl();
184 engine::Windowing::run_app(universe).expect("Windowing failed");
185}Additional examples can be found in:
- examples/openxr.rs
- examples/transparent-cutout-example.rs
- examples/text-example.rs
- examples/background-example.rs
- examples/mesh-factory-example.rs
- examples/simple-demo.rs
- examples/background-occlusion-example.rs
- examples/animation-for-topology.rs
- examples/vtuber-joints-example.rs
- examples/animation-example.rs
Sourcepub fn with_intensity(self, intensity: f32) -> Self
pub fn with_intensity(self, intensity: f32) -> Self
Examples found in repository?
examples/folder-text.rs (line 271)
60fn main() {
61 mittens_engine::example_support::ensure_model_assets();
62 utils::logger::init();
63
64 // Usage:
65 // cargo run --example folder-text -- [folder] [spacing]
66 // Defaults:
67 // folder=src spacing=0.3
68 let mut args = std::env::args().skip(1);
69 let folder = args.next().unwrap_or_else(|| "src".to_string());
70 let spacing: f32 = args
71 .next()
72 .and_then(|s| s.parse::<f32>().ok())
73 .unwrap_or(1.0);
74
75 // Safety caps: this demo can explode the ECS if we load huge projects.
76 const MAX_FILES: usize = 42;
77 const MAX_CHARS_PER_FILE: usize = 10_000;
78 const WRAP_AT: usize = 90;
79 const TEXT_SCALE: f32 = 0.01;
80
81 let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
82 let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
83 let scan_root = {
84 let p = PathBuf::from(&folder);
85 if p.is_absolute() {
86 p
87 } else {
88 // Resolve relative paths from the crate root, not the process CWD.
89 manifest_dir.join(p)
90 }
91 };
92 eprintln!("[folder-text] cwd={:?}", cwd);
93 eprintln!("[folder-text] manifest_dir={:?}", manifest_dir);
94 eprintln!("[folder-text] scan_root={:?}", scan_root);
95
96 let mut files = Vec::new();
97 collect_rs_files(&scan_root, &mut files);
98 files.sort();
99
100 if files.is_empty() {
101 eprintln!(
102 "[folder-text] No .rs files found under folder={:?} (resolved={:?})",
103 folder, scan_root
104 );
105 }
106
107 let files: Vec<PathBuf> = files.into_iter().take(MAX_FILES).collect();
108 eprintln!(
109 "[folder-text] Loaded {} file(s) from {:?} (spacing={})",
110 files.len(),
111 folder,
112 spacing
113 );
114
115 #[derive(Debug, Clone)]
116 struct FileEntry {
117 path: PathBuf,
118 display_text: String,
119 max_cols: usize,
120 rows: usize,
121 }
122
123 // Group files by folder.
124 // Layout:
125 // - each folder becomes a column along +X
126 // - within a folder, files stack along +Y (8 high)
127 // - after 8, additional files start a new stack further along +Z
128 let mut files_by_folder: BTreeMap<PathBuf, Vec<FileEntry>> = BTreeMap::new();
129 for path in files {
130 let Ok(content) = std::fs::read_to_string(&path) else {
131 eprintln!("[folder-text] Failed to read {:?}", path);
132 continue;
133 };
134
135 let mut display_text = format!(
136 "// {}\n\n{}",
137 path.strip_prefix(&manifest_dir).unwrap_or(&path).display(),
138 content
139 );
140
141 if display_text.chars().count() > MAX_CHARS_PER_FILE {
142 display_text = display_text
143 .chars()
144 .take(MAX_CHARS_PER_FILE)
145 .collect::<String>();
146 display_text.push_str("\n\n// ... truncated ...\n");
147 }
148
149 let (max_cols, rows) = measure_text_bounds(&display_text, WRAP_AT);
150
151 let folder_key = path.parent().unwrap_or(&scan_root).to_path_buf();
152 files_by_folder
153 .entry(folder_key)
154 .or_default()
155 .push(FileEntry {
156 path,
157 display_text,
158 max_cols,
159 rows,
160 });
161 }
162
163 let column_count = files_by_folder.len().max(1);
164
165 let world = engine::ecs::World::default();
166 let mut universe = engine::Universe::new(world);
167
168 let bg_r = 0.25;
169 let bg_g = 0.25;
170 let bg_b = 0.25;
171 let background = universe
172 .world
173 .add_component(engine::ecs::component::BackgroundColorComponent::new());
174 let background_c = universe
175 .world
176 .add_component(engine::ecs::component::ColorComponent::rgba(
177 bg_r, bg_g, bg_b, 1.00,
178 ));
179 let _ = universe.world.add_child(background, background_c);
180 universe.add(background);
181
182 let ambient = universe
183 .world
184 .add_component(engine::ecs::component::AmbientLightComponent::rgb(
185 bg_r, bg_g, bg_b,
186 ));
187 universe.add(ambient);
188
189 // Input-driven camera rig.
190 // Topology: I { T { C3D } }
191 let input = universe
192 .world
193 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
194
195 // Center the camera horizontally over the spawned columns.
196 let center_x = if column_count <= 1 {
197 0.0
198 } else {
199 ((column_count - 1) as f32) * spacing * 0.5
200 };
201
202 // Bring camera closer: these text blocks are tiny.
203 let rig_transform = universe.world.add_component(
204 engine::ecs::component::TransformComponent::new().with_position(center_x, 0.0, 1.2),
205 );
206 let camera3d = universe
207 .world
208 .add_component(engine::ecs::component::Camera3DComponent::new());
209 let input_mode = universe.world.add_component(
210 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
211 );
212 let _ = universe.attach(input, input_mode);
213 let _ = universe.attach(input, rig_transform);
214 let _ = universe.attach(rig_transform, camera3d);
215
216 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
217 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
218 universe.add(input);
219
220 // Big floor plane under everything.
221 // RenderableComponent::square() is an XY quad facing +Z; rotate it to XZ facing +Y.
222 let max_stacks = files_by_folder
223 .values()
224 .map(|v| (v.len() + 7) / 8)
225 .max()
226 .unwrap_or(1)
227 .max(1);
228 let total_width = (column_count as f32) * spacing;
229 let total_depth = (max_stacks as f32) * spacing;
230 let floor_w = total_width.max(10.0);
231 let floor_h = total_depth.max(10.0);
232 let floor_transform = universe.world.add_component(
233 engine::ecs::component::TransformComponent::new()
234 .with_position(0.0, -2.0, 0.0)
235 .with_rotation_euler(-std::f32::consts::FRAC_PI_2, 0.0, 0.0)
236 .with_scale(floor_w, floor_h, 1.0),
237 );
238 let floor_renderable = universe
239 .world
240 .add_component(engine::ecs::component::RenderableComponent::square());
241 let floor_color = universe
242 .world
243 .add_component(engine::ecs::component::ColorComponent::rgba(
244 0.88, 0.88, 0.88, 1.0,
245 ));
246 let _ = universe.attach(floor_transform, floor_renderable);
247 let _ = universe.attach(floor_renderable, floor_color);
248 universe.add(floor_transform);
249
250 // 4 red cubes around the perimeter of the world (easy visual anchors).
251 fn spawn_red_cube(universe: &mut engine::Universe, x: f32, y: f32, z: f32, s: f32) {
252 let t = universe.world.add_component(
253 engine::ecs::component::TransformComponent::new()
254 .with_position(x, y, z)
255 .with_scale(s, s, s),
256 );
257 let r = universe
258 .world
259 .add_component(engine::ecs::component::RenderableComponent::cube());
260 let c = universe
261 .world
262 .add_component(engine::ecs::component::ColorComponent::rgba(
263 1.0, 0.0, 0.0, 1.0,
264 ));
265
266 let light_transform = universe.world.add_component(
267 engine::ecs::component::TransformComponent::new().with_position(0.0, s * 0.75, 0.0),
268 );
269 let light = universe.world.add_component(
270 engine::ecs::component::PointLightComponent::new()
271 .with_intensity(1.5)
272 .with_distance(20.0)
273 .with_color(1.0, 0.0, 0.0),
274 );
275
276 let _ = universe.attach(t, r);
277 let _ = universe.attach(r, c);
278 let _ = universe.attach(t, light_transform);
279 let _ = universe.attach(light_transform, light);
280
281 universe.add(t);
282 }
283
284 let p = 1.5;
285 let s = 0.25;
286 spawn_red_cube(&mut universe, -p, -p, 0.0, s);
287 spawn_red_cube(&mut universe, p, -p, 0.0, s);
288 spawn_red_cube(&mut universe, -p, p, 0.0, s);
289 spawn_red_cube(&mut universe, p, p, 0.0, s);
290
291 let start_x = -center_x;
292 let stack_depth = spacing;
293 let row_gap_world = 0.35;
294
295 // One global point light at the top of the overall text "tower".
296 let pad_y = 4.0;
297 let global_max_panel_h_world = files_by_folder
298 .values()
299 .flat_map(|v| v.iter())
300 .map(|e| ((e.rows as f32) + pad_y) * TEXT_SCALE)
301 .fold(0.0_f32, f32::max)
302 .max(0.6);
303 let global_slot_h_world = global_max_panel_h_world + row_gap_world;
304 let tower_top_y = (7.0 * global_slot_h_world) + 4.0;
305 let tower_center_z = total_depth * 0.5;
306
307 let tower_light_transform = universe.world.add_component(
308 engine::ecs::component::TransformComponent::new().with_position(
309 0.0,
310 tower_top_y,
311 tower_center_z,
312 ),
313 );
314 let tower_light = universe.world.add_component(
315 engine::ecs::component::PointLightComponent::new()
316 .with_intensity(2.0)
317 .with_distance(120.0)
318 .with_color(1.0, 1.0, 1.0),
319 );
320 let _ = universe.attach(tower_light_transform, tower_light);
321 universe.add(tower_light_transform);
322
323 for (col_idx, (_folder, mut entries)) in files_by_folder.into_iter().enumerate() {
324 entries.sort_by(|a, b| a.path.cmp(&b.path));
325
326 // Slot height (world units) based on the tallest panel in this folder.
327 let pad_y = 4.0;
328 let max_panel_h_world = entries
329 .iter()
330 .map(|e| ((e.rows as f32) + pad_y) * TEXT_SCALE)
331 .fold(0.0_f32, f32::max)
332 .max(0.6);
333 let slot_h_world = max_panel_h_world + row_gap_world;
334
335 let col_x = start_x + (col_idx as f32) * spacing;
336
337 for (i, entry) in entries.into_iter().enumerate() {
338 let row = (i % 8) as f32;
339 let stack = (i / 8) as f32;
340 let file_y = row * slot_h_world;
341 let file_z = stack * stack_depth;
342
343 let file_group = universe.world.add_component(
344 engine::ecs::component::TransformComponent::new()
345 .with_position(col_x, file_y, file_z),
346 );
347
348 // Text subtree (tiny scale).
349 let file_root = universe.world.add_component(
350 engine::ecs::component::TransformComponent::new()
351 .with_position(0.0, 1.0, 0.0)
352 .with_scale(TEXT_SCALE, TEXT_SCALE, 1.0)
353 .with_rotation_euler(0.0, std::f32::consts::PI / 6.0, 0.0),
354 );
355 let _ = universe.attach(file_group, file_root);
356
357 // Background panel behind the text.
358 // Size is based on wrapped line count (matches TextSystem's strict wrapping behavior).
359 let (max_cols, rows) = (entry.max_cols, entry.rows);
360 let pad_x = 4.0;
361 let pad_y = 4.0;
362 let w = (max_cols as f32) + pad_x;
363 let h = (rows as f32) + pad_y;
364
365 // Text glyph quads are centered at integer (col,row) positions and are 1x1, so the
366 // text's AABB (in text-space) is roughly:
367 // x: [-0.5, max_cols-0.5]
368 // y: [-(rows-1)-0.5, 0.5]
369 // Centering the background quad at those midpoints keeps it aligned as text grows.
370 let bg_x = (max_cols as f32 - 1.0) * 0.5;
371 let bg_y = -((rows as f32 - 1.0) * 0.5);
372
373 let bg_transform = universe.world.add_component(
374 engine::ecs::component::TransformComponent::new()
375 .with_position(bg_x, bg_y, -0.05)
376 .with_scale(w, h, 1.0),
377 );
378 let bg_renderable = universe
379 .world
380 .add_component(engine::ecs::component::RenderableComponent::square());
381 let bg_quant = universe.world.add_component(
382 engine::ecs::component::LightQuantizationComponent::steps(5.0),
383 );
384 let bg_color =
385 universe
386 .world
387 .add_component(engine::ecs::component::ColorComponent::rgba(
388 0.2, 0.2, 0.2, 1.0,
389 ));
390 let _ = universe.attach(file_root, bg_transform);
391 let _ = universe.attach(bg_transform, bg_renderable);
392 let _ = universe.attach(bg_renderable, bg_quant);
393 let _ = universe.attach(bg_renderable, bg_color);
394
395 let text =
396 universe
397 .world
398 .add_component(engine::ecs::component::TextComponent::with_wrap(
399 entry.display_text,
400 WRAP_AT,
401 ));
402 let cutout = universe
403 .world
404 .add_component(engine::ecs::component::TransparentCutoutComponent::new());
405 let filtering = universe.world.add_component(
406 engine::ecs::component::TextureFilteringComponent::nearest_magnification(),
407 );
408 // let color = universe
409 // .world
410 // .add_component(engine::ecs::component::ColorComponent::rgba(0.7, 0.7, 1.0, 1.0));
411 let emissive = universe
412 .world
413 .add_component(engine::ecs::component::EmissiveComponent::on());
414 let _ = universe.attach(file_root, text);
415 let _ = universe.attach(text, cutout);
416 let _ = universe.attach(text, filtering);
417 //let _ = universe.world.add_child(text, color);
418 let _ = universe.attach(text, emissive);
419
420 universe.add(file_group);
421 }
422 }
423
424 // Process init-time registrations (Text expands into glyph subtrees here).
425 universe.systems.process_commands(
426 &mut universe.world,
427 &mut universe.visuals,
428 &mut universe.render_assets,
429 &mut universe.command_queue,
430 );
431
432 engine::Windowing::run_app(universe).expect("Windowing failed");
433}More examples
examples/collision-perimeter.rs (line 106)
86 fn spawn_light(
87 universe: &mut engine::Universe,
88 x: f32,
89 y: f32,
90 z: f32,
91 r: f32,
92 g: f32,
93 b: f32,
94 intensity: f32,
95 distance: f32,
96 ) {
97 let t = universe.world.add_component(
98 engine::ecs::component::TransformComponent::new()
99 .with_position(x, y, z)
100 .with_scale(0.12, 0.12, 0.12),
101 );
102
103 let l = universe.world.add_component(
104 engine::ecs::component::PointLightComponent::new()
105 .with_color(r, g, b)
106 .with_intensity(intensity)
107 .with_distance(distance),
108 );
109 // Visual marker: a small white cube at the light position.
110 // White ensures it mostly shows the light color.
111 let marker = universe
112 .world
113 .add_component(engine::ecs::component::RenderableComponent::cube());
114 let marker_color =
115 universe
116 .world
117 .add_component(engine::ecs::component::ColorComponent::rgba(
118 1.0, 1.0, 1.0, 1.0,
119 ));
120
121 let _ = universe.attach(t, l);
122 let _ = universe.attach(t, marker);
123 let _ = universe.attach(marker, marker_color);
124 universe.add(t);
125 }examples/opacity-example.rs (line 391)
359fn main() {
360 mittens_engine::example_support::ensure_model_assets();
361 utils::logger::init();
362
363 let world = engine::ecs::World::default();
364 let mut universe = engine::Universe::new(world);
365
366 // Dark brown / pink background.
367 let bg = universe
368 .world
369 .add_component(BackgroundColorComponent::new());
370 let bg_c = universe
371 .world
372 .add_component(ColorComponent::rgba(0.22, 0.08, 0.10, 1.0));
373 let _ = universe.world.add_child(bg, bg_c);
374 universe.add(bg);
375
376 // Ambient so unlit areas aren't black.
377 let ambient = universe
378 .world
379 .add_component(AmbientLightComponent::rgb(0.35, 0.35, 0.40));
380 universe.add(ambient);
381
382 // A bright overhead light.
383 let light_t = universe.world.add_component(
384 TransformComponent::new()
385 .with_position(0.0, 18.0, 10.0)
386 .with_scale(0.2, 0.2, 0.2),
387 );
388 let light = universe.world.add_component(
389 PointLightComponent::new()
390 .with_color(1.0, 1.0, 1.0)
391 .with_intensity(1.6)
392 .with_distance(80.0),
393 );
394 let _ = universe.attach(light_t, light);
395 universe.add(light_t);
396
397 // --- Camera rig ---
398 // I { T { C3D { with_fps_rotation with_roll_axis_y } } }
399 let input = universe
400 .world
401 .add_component(InputComponent::new().with_speed(3.0));
402 let input_mode = universe.world.add_component(
403 InputTransformModeComponent::forward_z()
404 .with_roll_axis_y()
405 .with_fps_rotation(),
406 );
407 let _ = universe.attach(input, input_mode);
408
409 let rig_transform = universe
410 .world
411 .add_component(TransformComponent::new().with_position(0.0, 6.0, 20.0));
412 let _ = universe.attach(input, rig_transform);
413
414 let camera = universe.world.add_component(Camera3DComponent::new());
415 let _ = universe.attach(rig_transform, camera);
416
417 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
418 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
419
420 universe.add(input);
421
422 // --- Ground ---
423 let ground = universe.world.add_component(
424 TransformComponent::new()
425 .with_position(0.0, -0.6, 0.0)
426 .with_scale(60.0, 1.0, 60.0),
427 );
428 let ground_r = universe.world.add_component(RenderableComponent::cube());
429 let ground_c = universe
430 .world
431 .add_component(ColorComponent::rgba(0.95, 0.95, 0.95, 1.0));
432 let _ = universe.attach(ground, ground_r);
433 let _ = universe.attach(ground_r, ground_c);
434 universe.add(ground);
435
436 // --- Opaque yellow cubes behind (contrast reference) ---
437 for i in 0..6 {
438 let x = -10.0 + i as f32 * 4.0;
439 spawn_cube(
440 &mut universe,
441 ground,
442 (x, 1.2, -18.0),
443 (2.0, 2.0, 2.0),
444 Some((1.0, 0.92, 0.15, 1.0)),
445 None,
446 );
447 }
448
449 // --- Demo spots ---
450 // Left of the left big spot: a single-layer transparent XY plane (16x16).
451 spawn_demo_xy_plane(&mut universe, (-14.0, 0.0, 0.0), 0.50, 16, 16, 0.0);
452
453 // Big spots: 8x8x8
454 // Left: single-layer transparent (instanced)
455 spawn_demo_spot(&mut universe, (-4.0, 0.0, 0.0), 0.50, false, 8);
456
457 // Right: multi-layer transparent (sorted)
458 spawn_demo_spot(&mut universe, (4.0, 0.0, 0.0), 0.50, true, 8);
459
460 // Small spots: opaque 1x4 strip + transparent 1x4 strip (along Z).
461 spawn_demo_strip_pair(&mut universe, (-4.0, 0.0, 10.0), false);
462 spawn_demo_strip_pair(&mut universe, (4.0, 0.0, 10.0), true);
463
464 // Process init-time registrations.
465 universe.systems.process_commands(
466 &mut universe.world,
467 &mut universe.visuals,
468 &mut universe.render_assets,
469 &mut universe.command_queue,
470 );
471
472 universe.enable_repl();
473
474 engine::Windowing::run_app(universe).expect("Windowing failed");
475}examples/gravity-fields.rs (line 737)
607 fn spawn_street_light(universe: &mut engine::Universe) -> engine::ecs::ComponentId {
608 // Dimensions (world units).
609 let shaft_height = 6.0;
610 let shaft_thickness = 0.22;
611 let arm_length = shaft_height / 3.0;
612 let arm_thickness = 0.18;
613
614 // Colors.
615 let pole_color = [0.35, 0.35, 0.38, 1.0];
616 let housing_color = [0.18, 0.18, 0.20, 1.0];
617 let diffuser_color = [1.0, 1.0, 1.0, 1.0];
618
619 // Model root at origin.
620 // The caller is expected to position + rotate this model using an external placement transform.
621 // Street light is authored facing +X (arm extends toward +X).
622 let root_t = universe
623 .world
624 .add_component(engine::ecs::component::TransformComponent::new());
625
626 // Vertical shaft (split root/geom so scaling doesn't affect the arm).
627 let shaft_root_t = universe
628 .world
629 .add_component(engine::ecs::component::TransformComponent::new());
630 let shaft_geom_t = universe.world.add_component(
631 engine::ecs::component::TransformComponent::new()
632 .with_position(0.0, shaft_height * 0.5, 0.0)
633 .with_scale(shaft_thickness, shaft_height, shaft_thickness),
634 );
635 let shaft_r = universe
636 .world
637 .add_component(engine::ecs::component::RenderableComponent::cube());
638 let shaft_c = universe
639 .world
640 .add_component(engine::ecs::component::ColorComponent::rgba(
641 pole_color[0],
642 pole_color[1],
643 pole_color[2],
644 pole_color[3],
645 ));
646
647 let _ = universe.attach(root_t, shaft_root_t);
648 let _ = universe.attach(shaft_root_t, shaft_geom_t);
649 let _ = universe.attach(shaft_geom_t, shaft_r);
650 let _ = universe.attach(shaft_r, shaft_c);
651
652 // Horizontal arm at the top; starts at the pole and extends toward +X.
653 let arm_root_t = universe.world.add_component(
654 engine::ecs::component::TransformComponent::new().with_position(0.0, shaft_height, 0.0),
655 );
656 let arm_geom_t = universe.world.add_component(
657 engine::ecs::component::TransformComponent::new()
658 .with_position(arm_length * 0.5, -arm_thickness * 0.5, 0.0)
659 .with_scale(arm_length, arm_thickness, arm_thickness),
660 );
661 let arm_r = universe
662 .world
663 .add_component(engine::ecs::component::RenderableComponent::cube());
664 let arm_c = universe
665 .world
666 .add_component(engine::ecs::component::ColorComponent::rgba(
667 pole_color[0],
668 pole_color[1],
669 pole_color[2],
670 pole_color[3],
671 ));
672
673 let _ = universe.attach(shaft_root_t, arm_root_t);
674 let _ = universe.attach(arm_root_t, arm_geom_t);
675 let _ = universe.attach(arm_geom_t, arm_r);
676 let _ = universe.attach(arm_r, arm_c);
677
678 // Light housing at the end of the arm (+X end).
679 let housing_root_t = universe.world.add_component(
680 engine::ecs::component::TransformComponent::new().with_position(arm_length, 0.0, 0.0),
681 );
682 let housing_geom_t = universe.world.add_component(
683 engine::ecs::component::TransformComponent::new()
684 .with_position(0.5, 0.0, 0.0)
685 // 2x1x1 box in (z, x, y) -> (x, y, z) = (1, 1, 2)
686 .with_scale(2.0, 1.0, 1.0),
687 );
688 let housing_r = universe
689 .world
690 .add_component(engine::ecs::component::RenderableComponent::cube());
691 let housing_c = universe
692 .world
693 .add_component(engine::ecs::component::ColorComponent::rgba(
694 housing_color[0],
695 housing_color[1],
696 housing_color[2],
697 housing_color[3],
698 ));
699
700 let _ = universe.attach(arm_root_t, housing_root_t);
701 let _ = universe.attach(housing_root_t, housing_geom_t);
702 let _ = universe.attach(housing_geom_t, housing_r);
703 let _ = universe.attach(housing_r, housing_c);
704
705 // White diffuser cube (slightly smaller, slightly lower).
706 let diffuser_t = universe.world.add_component(
707 engine::ecs::component::TransformComponent::new()
708 .with_position(0.5, -0.35, 0.0)
709 .with_scale(1.85, 0.55, 0.70),
710 );
711 let diffuser_r = universe
712 .world
713 .add_component(engine::ecs::component::RenderableComponent::cube());
714 let diffuser_c =
715 universe
716 .world
717 .add_component(engine::ecs::component::ColorComponent::rgba(
718 diffuser_color[0],
719 diffuser_color[1],
720 diffuser_color[2],
721 diffuser_color[3],
722 ));
723
724 let diffuser_emissive = universe
725 .world
726 .add_component(engine::ecs::component::EmissiveComponent::on());
727 let _ = universe.attach(diffuser_r, diffuser_emissive);
728
729 let _ = universe.attach(housing_root_t, diffuser_t);
730 let _ = universe.attach(diffuser_t, diffuser_r);
731 let _ = universe.attach(diffuser_r, diffuser_c);
732
733 // Yellow (slightly red) point light.
734 let light = universe.world.add_component(
735 engine::ecs::component::PointLightComponent::new()
736 .with_color(1.0, 0.82, 0.55)
737 .with_intensity(5.0)
738 .with_distance(40.0),
739 );
740 let _ = universe.attach(diffuser_t, light);
741
742 root_t
743 }examples/transparent-cutout-example.rs (line 85)
34fn main() {
35 mittens_engine::example_support::ensure_model_assets();
36 utils::logger::init();
37
38 let world = engine::ecs::World::default();
39 let mut universe = engine::Universe::new(world);
40
41 // Orange/yellow-ish clear color so cutout edges read.
42 let clear = universe
43 .world
44 .add_component(BackgroundColorComponent::new());
45 let clear_c = universe
46 .world
47 .add_component(ColorComponent::rgba(0.98, 0.72, 0.22, 1.0));
48 let _ = universe.world.add_child(clear, clear_c);
49 universe.add(clear);
50
51 // Warm-ish ambient so the gold cubes don’t go too dark.
52 let ambient = universe
53 .world
54 .add_component(AmbientLightComponent::rgb(0.22, 0.16, 0.08));
55 universe.add(ambient);
56
57 // --- Camera rig (WASD/QE) ---
58 let input = universe
59 .world
60 .add_component(InputComponent::new().with_speed(2.0));
61 let input_mode = universe
62 .world
63 .add_component(InputTransformModeComponent::forward_z().with_roll_axis_y());
64 let _ = universe.attach(input, input_mode);
65
66 // Start a bit pulled back, looking toward the origin.
67 let rig_transform = universe
68 .world
69 .add_component(TransformComponent::new().with_position(0.0, 0.0, 5.0));
70 let _ = universe.attach(input, rig_transform);
71
72 let camera3d = universe
73 .world
74 .add_component(Camera3DComponent::new().with_far(200.0).with_fov(55.0));
75 let _ = universe.attach(rig_transform, camera3d);
76
77 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
78 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
79 universe.add(input);
80
81 // Key light for toon shading.
82 let light = universe.world.add_component(
83 PointLightComponent::new()
84 .with_distance(50.0)
85 .with_intensity(2.2)
86 .with_color(1.0, 0.98, 0.92),
87 );
88 let light_transform = universe
89 .world
90 .add_component(TransformComponent::new().with_position(2.0, 3.0, 4.0));
91 let _ = universe.attach(light_transform, light);
92 universe.add(light_transform);
93
94 // --- Transparent cutout: 100 cat faces in a 10x10 grid ---
95 // Place them *behind* the starting camera position (camera starts at z=+5.0).
96 // Not attached to the camera rig.
97 let cat_grid_root = universe
98 .world
99 .add_component(TransformComponent::new().with_position(0.0, 0.0, 9.0));
100 universe.add(cat_grid_root);
101
102 let grid_w: i32 = 10;
103 let grid_h: i32 = 10;
104 let spacing: f32 = 0.7;
105 let half_w = (grid_w as f32 - 1.0) * spacing * 0.5;
106 let half_h = (grid_h as f32 - 1.0) * spacing * 0.5;
107
108 for y in 0..grid_h {
109 for x in 0..grid_w {
110 // Topology: cat_grid_root { T_quad { R_quad { Texture + Filtering + Cutout + Color } } }
111 let px = x as f32 * spacing - half_w;
112 let py = y as f32 * spacing - half_h;
113
114 let pz: f32 = (x as f32) % half_w;
115
116 let quad_t = universe.world.add_component(
117 TransformComponent::new()
118 .with_position(px, py, pz)
119 .with_scale(0.55, 0.55, 1.0),
120 );
121 let quad_r = universe.world.add_component(RenderableComponent::square());
122
123 let quad_tex = universe.world.add_component(TextureComponent::with_uri(
124 "assets/textures/cat-face-amused.dds",
125 ));
126 let quad_filtering = universe
127 .world
128 .add_component(TextureFilteringComponent::linear());
129 let quad_cutout = universe
130 .world
131 .add_component(TransparentCutoutComponent::new());
132 let quad_color = universe
133 .world
134 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
135
136 let _ = universe.attach(cat_grid_root, quad_t);
137 let _ = universe.attach(quad_t, quad_r);
138 let _ = universe.attach(quad_r, quad_tex);
139 let _ = universe.attach(quad_r, quad_filtering);
140 let _ = universe.attach(quad_r, quad_cutout);
141 let _ = universe.attach(quad_r, quad_color);
142 }
143 }
144
145 // --- Gold/yellow cubes behind the quad ---
146 // Parent them under a transform so it’s easy to tweak their depth.
147 let cubes_root = universe
148 .world
149 .add_component(TransformComponent::new().with_position(0.0, 0.0, 4.6));
150
151 // point light for cats
152 let cat_light_tx = universe
153 .world
154 .add_component(TransformComponent::new().with_position(0.0, 2.0, 7.0));
155
156 let cat_light = universe.world.add_component(
157 PointLightComponent::new()
158 .with_distance(150.0)
159 .with_intensity(1.5)
160 .with_color(1.0, 0.98, 0.92),
161 );
162 let _ = universe.attach(cat_light_tx, cat_light);
163 let _ = universe.attach(cubes_root, cat_light_tx);
164
165 universe.add(cubes_root);
166
167 let gold_a = (1.0, 0.86, 0.22);
168 let gold_b = (1.0, 0.74, 0.10);
169 let gold_c = (0.95, 0.92, 0.32);
170
171 // A loose cluster that’s visible through the cutout (transparent) area.
172 spawn_gold_cube(&mut universe, cubes_root, (-1.1, -0.3, -0.2), 0.45, gold_a);
173 spawn_gold_cube(&mut universe, cubes_root, (1.0, -0.4, -0.4), 0.40, gold_b);
174 spawn_gold_cube(&mut universe, cubes_root, (-0.2, 0.9, -0.6), 0.38, gold_c);
175 spawn_gold_cube(&mut universe, cubes_root, (0.7, 0.6, -0.9), 0.32, gold_a);
176 spawn_gold_cube(&mut universe, cubes_root, (-0.8, 0.4, -1.1), 0.36, gold_b);
177
178 // Bigger orange/yellow cubes behind the cluster (to make the cutout depth obvious).
179 let orange_gold_a = (1.0, 0.62, 0.10);
180 let orange_gold_b = (1.0, 0.78, 0.18);
181 spawn_gold_cube(
182 &mut universe,
183 cubes_root,
184 (0.0, -0.1, -2.1),
185 1.15,
186 orange_gold_b,
187 );
188 spawn_gold_cube(
189 &mut universe,
190 cubes_root,
191 (-1.8, 0.6, -2.6),
192 0.95,
193 orange_gold_a,
194 );
195 spawn_gold_cube(
196 &mut universe,
197 cubes_root,
198 (1.9, 0.7, -2.9),
199 1.05,
200 orange_gold_b,
201 );
202 spawn_gold_cube(
203 &mut universe,
204 cubes_root,
205 (0.9, 1.8, -3.2),
206 0.90,
207 orange_gold_a,
208 );
209 spawn_gold_cube(
210 &mut universe,
211 cubes_root,
212 (-0.9, 1.7, -3.5),
213 1.10,
214 orange_gold_b,
215 );
216
217 // Process init-time registrations (loads textures, registers renderables, etc.).
218 universe.systems.process_commands(
219 &mut universe.world,
220 &mut universe.visuals,
221 &mut universe.render_assets,
222 &mut universe.command_queue,
223 );
224
225 universe.enable_repl();
226 engine::Windowing::run_app(universe).expect("Windowing failed");
227}examples/background-occlusion-example.rs (line 89)
19fn main() {
20 mittens_engine::example_support::ensure_model_assets();
21 utils::logger::init();
22
23 let world = engine::ecs::World::default();
24 let mut universe = engine::Universe::new(world);
25
26 // Light purple-red background so the occlusion reads.
27 let clear = universe
28 .world
29 .add_component(engine::ecs::component::BackgroundColorComponent::new());
30 let clear_c = universe
31 .world
32 .add_component(engine::ecs::component::ColorComponent::rgba(
33 0.62, 0.38, 0.56, 1.0,
34 ));
35 let _ = universe.world.add_child(clear, clear_c);
36 universe.add(clear);
37
38 // A bit of ambient so the cluster volume reads.
39 let ambient = universe
40 .world
41 .add_component(engine::ecs::component::AmbientLightComponent::rgb(
42 0.20, 0.15, 0.3,
43 ));
44 universe.add(ambient);
45
46 // --- Camera rig (WASD/QE) ---
47 let input = universe
48 .world
49 .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
50 let input_mode = universe.world.add_component(
51 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
52 );
53 let _ = universe.attach(input, input_mode);
54
55 let rig_transform = universe.world.add_component(
56 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 6.0),
57 );
58 let _ = universe.attach(input, rig_transform);
59
60 let camera3d = universe.world.add_component(
61 engine::ecs::component::Camera3DComponent::new()
62 .with_far(200.0)
63 .with_fov(70.0),
64 );
65 let _ = universe.attach(rig_transform, camera3d);
66
67 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
68 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
69
70 // Key directional light toward the cloud volume.
71 //
72 // Directional lights encode their direction in the node's world position.
73 // The shader normalizes this vector.
74 let sun = universe.world.add_component(
75 engine::ecs::component::DirectionalLightComponent::new()
76 .with_intensity(1.6)
77 .with_color(1.0, 0.98, 0.92),
78 );
79 // Pointing from the clouds back toward the camera a bit (+Z), and slightly from above.
80 let sun_dir = universe.world.add_component(
81 engine::ecs::component::TransformComponent::new().with_position(0.15, 0.65, 0.75),
82 );
83 let _ = universe.attach(sun_dir, sun);
84
85 // A couple point lights to fill/shade the cloud volume.
86 let light_a = universe.world.add_component(
87 engine::ecs::component::PointLightComponent::new()
88 .with_distance(80.0)
89 .with_intensity(3.0)
90 .with_color(0.9, 0.95, 1.0),
91 );
92 let light_a_tx = universe.world.add_component(
93 engine::ecs::component::TransformComponent::new().with_position(8.0, 8.0, 5.0),
94 );
95 let _ = universe.attach(light_a_tx, light_a);
96
97 let light_b = universe.world.add_component(
98 engine::ecs::component::PointLightComponent::new()
99 .with_distance(80.0)
100 .with_intensity(2.2)
101 .with_color(1.0, 0.9, 0.85),
102 );
103 let light_b_tx = universe.world.add_component(
104 engine::ecs::component::TransformComponent::new().with_position(-10.0, 4.0, -6.0),
105 );
106 let _ = universe.attach(light_b_tx, light_b);
107
108 universe.add(input);
109 universe.add(sun_dir);
110 universe.add(light_a_tx);
111 universe.add(light_b_tx);
112
113 let cube_mesh = universe
114 .render_assets
115 .get_mesh(engine::graphics::BuiltinMeshType::Cube);
116
117 // --- Background occluded+lit world ---
118 // Renderables under this node participate in a background stage that depth-writes for
119 // self-occlusion + uses the normal lighting shader inputs.
120 let bg_root = universe.world.add_component(
121 engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
122 );
123 universe.add(bg_root);
124
125 // Create overlapping cube clusters like cloud puffs.
126 // Place them generally in front of the camera (negative Z).
127 // Spawn two groups on opposite X sides.
128 let cluster_count: u32 = 9;
129 for (group_i, group_x) in [-16.0_f32, 16.0_f32].into_iter().enumerate() {
130 let group_tx = universe.world.add_component(
131 engine::ecs::component::TransformComponent::new().with_position(group_x, 0.0, 0.0),
132 );
133 let _ = universe.attach(bg_root, group_tx);
134
135 for cluster_i in 0..cluster_count {
136 let seed = (0xC10u32 ^ (group_i as u32).wrapping_mul(0x9E37_79B9))
137 ^ (cluster_i.wrapping_mul(7919));
138
139 let cx = (rand01(seed ^ 0xA341_316C) - 0.5) * 18.0;
140 let cy = (rand01(seed ^ 0xC801_3EA4) - 0.5) * 10.0 + 2.0;
141 let cz = -18.0 - rand01(seed ^ 0xB529_7A4D) * 26.0;
142
143 let center_tx = universe.world.add_component(
144 engine::ecs::component::TransformComponent::new().with_position(cx, cy, cz),
145 );
146 let _ = universe.attach(group_tx, center_tx);
147
148 let puffs = 18u32;
149 for puff_i in 0..puffs {
150 let puff_seed = seed ^ puff_i.wrapping_mul(1_103_515_245);
151
152 // Slightly ellipsoidal distribution.
153 let ox = (rand01(puff_seed ^ 0x68bc_21eb) - 0.5) * 7.0;
154 let oy = (rand01(puff_seed ^ 0x02e5_be93) - 0.5) * 3.0;
155 let oz = (rand01(puff_seed ^ 0xa1d3_4f2b) - 0.5) * 7.0;
156
157 let base = 0.7 + rand01(puff_seed ^ 0x9e37_79b9) * 2.6;
158 let sx = base * (0.7 + rand01(puff_seed ^ 0x243f_6a88) * 0.8);
159 let sy = base * (0.6 + rand01(puff_seed ^ 0x85a3_08d3) * 0.9);
160 let sz = base * (0.7 + rand01(puff_seed ^ 0x1319_8a2e) * 0.8);
161
162 let tx = universe.world.add_component(
163 engine::ecs::component::TransformComponent::new()
164 .with_position(ox, oy, oz)
165 .with_scale(sx, sy, sz),
166 );
167 let renderable =
168 universe
169 .world
170 .add_component(engine::ecs::component::RenderableComponent::new(
171 engine::graphics::primitives::Renderable::new(
172 cube_mesh,
173 engine::graphics::primitives::MaterialHandle::TOON_MESH,
174 ),
175 ));
176
177 // Slight blue-grey variation.
178 let t = rand01(puff_seed ^ 0x7f4a_7c15);
179 let r = 0.55 + 0.10 * t;
180 let g = 0.58 + 0.10 * t;
181 let b = 0.66 + 0.12 * t;
182 let color = universe
183 .world
184 .add_component(engine::ecs::component::ColorComponent::rgba(r, g, b, 1.0));
185
186 let _ = universe.attach(center_tx, tx);
187 let _ = universe.attach(tx, renderable);
188 let _ = universe.attach(renderable, color);
189 }
190 }
191 }
192
193 // Foreground reference cube (should never be occluded by background depth).
194 let fg_tx = universe.world.add_component(
195 engine::ecs::component::TransformComponent::new()
196 .with_position(0.0, -0.5, -4.0)
197 .with_scale(1.2, 0.8, 1.2),
198 );
199 let fg_renderable =
200 universe
201 .world
202 .add_component(engine::ecs::component::RenderableComponent::new(
203 engine::graphics::primitives::Renderable::new(
204 cube_mesh,
205 engine::graphics::primitives::MaterialHandle::TOON_MESH,
206 ),
207 ));
208 let fg_color = universe
209 .world
210 .add_component(engine::ecs::component::ColorComponent::rgba(
211 0.9, 0.2, 0.2, 1.0,
212 ));
213
214 universe.add(fg_tx);
215 let _ = universe.attach(fg_tx, fg_renderable);
216 let _ = universe.attach(fg_renderable, fg_color);
217
218 // Foreground opaque floor: 16x16 larger white cubes, 10 units below the reference cube.
219 let floor_root = universe.world.add_component(
220 engine::ecs::component::TransformComponent::new().with_position(0.0, -10.5, -10.0),
221 );
222 universe.add(floor_root);
223
224 let spacing = 10_f32;
225 let half = 5_f32;
226 for x in 0..16u32 {
227 for z in 0..64u32 {
228 let px = (x as f32 - half) * spacing;
229 let pz = (z as f32 * -1.0 + half) * spacing;
230 let tx = universe.world.add_component(
231 engine::ecs::component::TransformComponent::new()
232 .with_position(px, -5.0, pz)
233 .with_scale(5.0, 0.5, 5.0),
234 );
235 let renderable =
236 universe
237 .world
238 .add_component(engine::ecs::component::RenderableComponent::new(
239 engine::graphics::primitives::Renderable::new(
240 cube_mesh,
241 engine::graphics::primitives::MaterialHandle::TOON_MESH,
242 ),
243 ));
244 let color = universe
245 .world
246 .add_component(engine::ecs::component::ColorComponent::rgba(
247 1.0, 1.0, 1.0, 1.0,
248 ));
249
250 let _ = universe.attach(floor_root, tx);
251 let _ = universe.attach(tx, renderable);
252 let _ = universe.attach(renderable, color);
253 }
254 }
255
256 universe.systems.process_commands(
257 &mut universe.world,
258 &mut universe.visuals,
259 &mut universe.render_assets,
260 &mut universe.command_queue,
261 );
262
263 universe.enable_repl();
264 engine::Windowing::run_app(universe).expect("Windowing failed");
265}Sourcepub fn with_distance(self, distance: f32) -> Self
pub fn with_distance(self, distance: f32) -> Self
Examples found in repository?
examples/folder-text.rs (line 272)
60fn main() {
61 mittens_engine::example_support::ensure_model_assets();
62 utils::logger::init();
63
64 // Usage:
65 // cargo run --example folder-text -- [folder] [spacing]
66 // Defaults:
67 // folder=src spacing=0.3
68 let mut args = std::env::args().skip(1);
69 let folder = args.next().unwrap_or_else(|| "src".to_string());
70 let spacing: f32 = args
71 .next()
72 .and_then(|s| s.parse::<f32>().ok())
73 .unwrap_or(1.0);
74
75 // Safety caps: this demo can explode the ECS if we load huge projects.
76 const MAX_FILES: usize = 42;
77 const MAX_CHARS_PER_FILE: usize = 10_000;
78 const WRAP_AT: usize = 90;
79 const TEXT_SCALE: f32 = 0.01;
80
81 let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
82 let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
83 let scan_root = {
84 let p = PathBuf::from(&folder);
85 if p.is_absolute() {
86 p
87 } else {
88 // Resolve relative paths from the crate root, not the process CWD.
89 manifest_dir.join(p)
90 }
91 };
92 eprintln!("[folder-text] cwd={:?}", cwd);
93 eprintln!("[folder-text] manifest_dir={:?}", manifest_dir);
94 eprintln!("[folder-text] scan_root={:?}", scan_root);
95
96 let mut files = Vec::new();
97 collect_rs_files(&scan_root, &mut files);
98 files.sort();
99
100 if files.is_empty() {
101 eprintln!(
102 "[folder-text] No .rs files found under folder={:?} (resolved={:?})",
103 folder, scan_root
104 );
105 }
106
107 let files: Vec<PathBuf> = files.into_iter().take(MAX_FILES).collect();
108 eprintln!(
109 "[folder-text] Loaded {} file(s) from {:?} (spacing={})",
110 files.len(),
111 folder,
112 spacing
113 );
114
115 #[derive(Debug, Clone)]
116 struct FileEntry {
117 path: PathBuf,
118 display_text: String,
119 max_cols: usize,
120 rows: usize,
121 }
122
123 // Group files by folder.
124 // Layout:
125 // - each folder becomes a column along +X
126 // - within a folder, files stack along +Y (8 high)
127 // - after 8, additional files start a new stack further along +Z
128 let mut files_by_folder: BTreeMap<PathBuf, Vec<FileEntry>> = BTreeMap::new();
129 for path in files {
130 let Ok(content) = std::fs::read_to_string(&path) else {
131 eprintln!("[folder-text] Failed to read {:?}", path);
132 continue;
133 };
134
135 let mut display_text = format!(
136 "// {}\n\n{}",
137 path.strip_prefix(&manifest_dir).unwrap_or(&path).display(),
138 content
139 );
140
141 if display_text.chars().count() > MAX_CHARS_PER_FILE {
142 display_text = display_text
143 .chars()
144 .take(MAX_CHARS_PER_FILE)
145 .collect::<String>();
146 display_text.push_str("\n\n// ... truncated ...\n");
147 }
148
149 let (max_cols, rows) = measure_text_bounds(&display_text, WRAP_AT);
150
151 let folder_key = path.parent().unwrap_or(&scan_root).to_path_buf();
152 files_by_folder
153 .entry(folder_key)
154 .or_default()
155 .push(FileEntry {
156 path,
157 display_text,
158 max_cols,
159 rows,
160 });
161 }
162
163 let column_count = files_by_folder.len().max(1);
164
165 let world = engine::ecs::World::default();
166 let mut universe = engine::Universe::new(world);
167
168 let bg_r = 0.25;
169 let bg_g = 0.25;
170 let bg_b = 0.25;
171 let background = universe
172 .world
173 .add_component(engine::ecs::component::BackgroundColorComponent::new());
174 let background_c = universe
175 .world
176 .add_component(engine::ecs::component::ColorComponent::rgba(
177 bg_r, bg_g, bg_b, 1.00,
178 ));
179 let _ = universe.world.add_child(background, background_c);
180 universe.add(background);
181
182 let ambient = universe
183 .world
184 .add_component(engine::ecs::component::AmbientLightComponent::rgb(
185 bg_r, bg_g, bg_b,
186 ));
187 universe.add(ambient);
188
189 // Input-driven camera rig.
190 // Topology: I { T { C3D } }
191 let input = universe
192 .world
193 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
194
195 // Center the camera horizontally over the spawned columns.
196 let center_x = if column_count <= 1 {
197 0.0
198 } else {
199 ((column_count - 1) as f32) * spacing * 0.5
200 };
201
202 // Bring camera closer: these text blocks are tiny.
203 let rig_transform = universe.world.add_component(
204 engine::ecs::component::TransformComponent::new().with_position(center_x, 0.0, 1.2),
205 );
206 let camera3d = universe
207 .world
208 .add_component(engine::ecs::component::Camera3DComponent::new());
209 let input_mode = universe.world.add_component(
210 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
211 );
212 let _ = universe.attach(input, input_mode);
213 let _ = universe.attach(input, rig_transform);
214 let _ = universe.attach(rig_transform, camera3d);
215
216 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
217 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
218 universe.add(input);
219
220 // Big floor plane under everything.
221 // RenderableComponent::square() is an XY quad facing +Z; rotate it to XZ facing +Y.
222 let max_stacks = files_by_folder
223 .values()
224 .map(|v| (v.len() + 7) / 8)
225 .max()
226 .unwrap_or(1)
227 .max(1);
228 let total_width = (column_count as f32) * spacing;
229 let total_depth = (max_stacks as f32) * spacing;
230 let floor_w = total_width.max(10.0);
231 let floor_h = total_depth.max(10.0);
232 let floor_transform = universe.world.add_component(
233 engine::ecs::component::TransformComponent::new()
234 .with_position(0.0, -2.0, 0.0)
235 .with_rotation_euler(-std::f32::consts::FRAC_PI_2, 0.0, 0.0)
236 .with_scale(floor_w, floor_h, 1.0),
237 );
238 let floor_renderable = universe
239 .world
240 .add_component(engine::ecs::component::RenderableComponent::square());
241 let floor_color = universe
242 .world
243 .add_component(engine::ecs::component::ColorComponent::rgba(
244 0.88, 0.88, 0.88, 1.0,
245 ));
246 let _ = universe.attach(floor_transform, floor_renderable);
247 let _ = universe.attach(floor_renderable, floor_color);
248 universe.add(floor_transform);
249
250 // 4 red cubes around the perimeter of the world (easy visual anchors).
251 fn spawn_red_cube(universe: &mut engine::Universe, x: f32, y: f32, z: f32, s: f32) {
252 let t = universe.world.add_component(
253 engine::ecs::component::TransformComponent::new()
254 .with_position(x, y, z)
255 .with_scale(s, s, s),
256 );
257 let r = universe
258 .world
259 .add_component(engine::ecs::component::RenderableComponent::cube());
260 let c = universe
261 .world
262 .add_component(engine::ecs::component::ColorComponent::rgba(
263 1.0, 0.0, 0.0, 1.0,
264 ));
265
266 let light_transform = universe.world.add_component(
267 engine::ecs::component::TransformComponent::new().with_position(0.0, s * 0.75, 0.0),
268 );
269 let light = universe.world.add_component(
270 engine::ecs::component::PointLightComponent::new()
271 .with_intensity(1.5)
272 .with_distance(20.0)
273 .with_color(1.0, 0.0, 0.0),
274 );
275
276 let _ = universe.attach(t, r);
277 let _ = universe.attach(r, c);
278 let _ = universe.attach(t, light_transform);
279 let _ = universe.attach(light_transform, light);
280
281 universe.add(t);
282 }
283
284 let p = 1.5;
285 let s = 0.25;
286 spawn_red_cube(&mut universe, -p, -p, 0.0, s);
287 spawn_red_cube(&mut universe, p, -p, 0.0, s);
288 spawn_red_cube(&mut universe, -p, p, 0.0, s);
289 spawn_red_cube(&mut universe, p, p, 0.0, s);
290
291 let start_x = -center_x;
292 let stack_depth = spacing;
293 let row_gap_world = 0.35;
294
295 // One global point light at the top of the overall text "tower".
296 let pad_y = 4.0;
297 let global_max_panel_h_world = files_by_folder
298 .values()
299 .flat_map(|v| v.iter())
300 .map(|e| ((e.rows as f32) + pad_y) * TEXT_SCALE)
301 .fold(0.0_f32, f32::max)
302 .max(0.6);
303 let global_slot_h_world = global_max_panel_h_world + row_gap_world;
304 let tower_top_y = (7.0 * global_slot_h_world) + 4.0;
305 let tower_center_z = total_depth * 0.5;
306
307 let tower_light_transform = universe.world.add_component(
308 engine::ecs::component::TransformComponent::new().with_position(
309 0.0,
310 tower_top_y,
311 tower_center_z,
312 ),
313 );
314 let tower_light = universe.world.add_component(
315 engine::ecs::component::PointLightComponent::new()
316 .with_intensity(2.0)
317 .with_distance(120.0)
318 .with_color(1.0, 1.0, 1.0),
319 );
320 let _ = universe.attach(tower_light_transform, tower_light);
321 universe.add(tower_light_transform);
322
323 for (col_idx, (_folder, mut entries)) in files_by_folder.into_iter().enumerate() {
324 entries.sort_by(|a, b| a.path.cmp(&b.path));
325
326 // Slot height (world units) based on the tallest panel in this folder.
327 let pad_y = 4.0;
328 let max_panel_h_world = entries
329 .iter()
330 .map(|e| ((e.rows as f32) + pad_y) * TEXT_SCALE)
331 .fold(0.0_f32, f32::max)
332 .max(0.6);
333 let slot_h_world = max_panel_h_world + row_gap_world;
334
335 let col_x = start_x + (col_idx as f32) * spacing;
336
337 for (i, entry) in entries.into_iter().enumerate() {
338 let row = (i % 8) as f32;
339 let stack = (i / 8) as f32;
340 let file_y = row * slot_h_world;
341 let file_z = stack * stack_depth;
342
343 let file_group = universe.world.add_component(
344 engine::ecs::component::TransformComponent::new()
345 .with_position(col_x, file_y, file_z),
346 );
347
348 // Text subtree (tiny scale).
349 let file_root = universe.world.add_component(
350 engine::ecs::component::TransformComponent::new()
351 .with_position(0.0, 1.0, 0.0)
352 .with_scale(TEXT_SCALE, TEXT_SCALE, 1.0)
353 .with_rotation_euler(0.0, std::f32::consts::PI / 6.0, 0.0),
354 );
355 let _ = universe.attach(file_group, file_root);
356
357 // Background panel behind the text.
358 // Size is based on wrapped line count (matches TextSystem's strict wrapping behavior).
359 let (max_cols, rows) = (entry.max_cols, entry.rows);
360 let pad_x = 4.0;
361 let pad_y = 4.0;
362 let w = (max_cols as f32) + pad_x;
363 let h = (rows as f32) + pad_y;
364
365 // Text glyph quads are centered at integer (col,row) positions and are 1x1, so the
366 // text's AABB (in text-space) is roughly:
367 // x: [-0.5, max_cols-0.5]
368 // y: [-(rows-1)-0.5, 0.5]
369 // Centering the background quad at those midpoints keeps it aligned as text grows.
370 let bg_x = (max_cols as f32 - 1.0) * 0.5;
371 let bg_y = -((rows as f32 - 1.0) * 0.5);
372
373 let bg_transform = universe.world.add_component(
374 engine::ecs::component::TransformComponent::new()
375 .with_position(bg_x, bg_y, -0.05)
376 .with_scale(w, h, 1.0),
377 );
378 let bg_renderable = universe
379 .world
380 .add_component(engine::ecs::component::RenderableComponent::square());
381 let bg_quant = universe.world.add_component(
382 engine::ecs::component::LightQuantizationComponent::steps(5.0),
383 );
384 let bg_color =
385 universe
386 .world
387 .add_component(engine::ecs::component::ColorComponent::rgba(
388 0.2, 0.2, 0.2, 1.0,
389 ));
390 let _ = universe.attach(file_root, bg_transform);
391 let _ = universe.attach(bg_transform, bg_renderable);
392 let _ = universe.attach(bg_renderable, bg_quant);
393 let _ = universe.attach(bg_renderable, bg_color);
394
395 let text =
396 universe
397 .world
398 .add_component(engine::ecs::component::TextComponent::with_wrap(
399 entry.display_text,
400 WRAP_AT,
401 ));
402 let cutout = universe
403 .world
404 .add_component(engine::ecs::component::TransparentCutoutComponent::new());
405 let filtering = universe.world.add_component(
406 engine::ecs::component::TextureFilteringComponent::nearest_magnification(),
407 );
408 // let color = universe
409 // .world
410 // .add_component(engine::ecs::component::ColorComponent::rgba(0.7, 0.7, 1.0, 1.0));
411 let emissive = universe
412 .world
413 .add_component(engine::ecs::component::EmissiveComponent::on());
414 let _ = universe.attach(file_root, text);
415 let _ = universe.attach(text, cutout);
416 let _ = universe.attach(text, filtering);
417 //let _ = universe.world.add_child(text, color);
418 let _ = universe.attach(text, emissive);
419
420 universe.add(file_group);
421 }
422 }
423
424 // Process init-time registrations (Text expands into glyph subtrees here).
425 universe.systems.process_commands(
426 &mut universe.world,
427 &mut universe.visuals,
428 &mut universe.render_assets,
429 &mut universe.command_queue,
430 );
431
432 engine::Windowing::run_app(universe).expect("Windowing failed");
433}More examples
examples/collision-perimeter.rs (line 107)
86 fn spawn_light(
87 universe: &mut engine::Universe,
88 x: f32,
89 y: f32,
90 z: f32,
91 r: f32,
92 g: f32,
93 b: f32,
94 intensity: f32,
95 distance: f32,
96 ) {
97 let t = universe.world.add_component(
98 engine::ecs::component::TransformComponent::new()
99 .with_position(x, y, z)
100 .with_scale(0.12, 0.12, 0.12),
101 );
102
103 let l = universe.world.add_component(
104 engine::ecs::component::PointLightComponent::new()
105 .with_color(r, g, b)
106 .with_intensity(intensity)
107 .with_distance(distance),
108 );
109 // Visual marker: a small white cube at the light position.
110 // White ensures it mostly shows the light color.
111 let marker = universe
112 .world
113 .add_component(engine::ecs::component::RenderableComponent::cube());
114 let marker_color =
115 universe
116 .world
117 .add_component(engine::ecs::component::ColorComponent::rgba(
118 1.0, 1.0, 1.0, 1.0,
119 ));
120
121 let _ = universe.attach(t, l);
122 let _ = universe.attach(t, marker);
123 let _ = universe.attach(marker, marker_color);
124 universe.add(t);
125 }examples/opacity-example.rs (line 392)
359fn main() {
360 mittens_engine::example_support::ensure_model_assets();
361 utils::logger::init();
362
363 let world = engine::ecs::World::default();
364 let mut universe = engine::Universe::new(world);
365
366 // Dark brown / pink background.
367 let bg = universe
368 .world
369 .add_component(BackgroundColorComponent::new());
370 let bg_c = universe
371 .world
372 .add_component(ColorComponent::rgba(0.22, 0.08, 0.10, 1.0));
373 let _ = universe.world.add_child(bg, bg_c);
374 universe.add(bg);
375
376 // Ambient so unlit areas aren't black.
377 let ambient = universe
378 .world
379 .add_component(AmbientLightComponent::rgb(0.35, 0.35, 0.40));
380 universe.add(ambient);
381
382 // A bright overhead light.
383 let light_t = universe.world.add_component(
384 TransformComponent::new()
385 .with_position(0.0, 18.0, 10.0)
386 .with_scale(0.2, 0.2, 0.2),
387 );
388 let light = universe.world.add_component(
389 PointLightComponent::new()
390 .with_color(1.0, 1.0, 1.0)
391 .with_intensity(1.6)
392 .with_distance(80.0),
393 );
394 let _ = universe.attach(light_t, light);
395 universe.add(light_t);
396
397 // --- Camera rig ---
398 // I { T { C3D { with_fps_rotation with_roll_axis_y } } }
399 let input = universe
400 .world
401 .add_component(InputComponent::new().with_speed(3.0));
402 let input_mode = universe.world.add_component(
403 InputTransformModeComponent::forward_z()
404 .with_roll_axis_y()
405 .with_fps_rotation(),
406 );
407 let _ = universe.attach(input, input_mode);
408
409 let rig_transform = universe
410 .world
411 .add_component(TransformComponent::new().with_position(0.0, 6.0, 20.0));
412 let _ = universe.attach(input, rig_transform);
413
414 let camera = universe.world.add_component(Camera3DComponent::new());
415 let _ = universe.attach(rig_transform, camera);
416
417 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
418 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
419
420 universe.add(input);
421
422 // --- Ground ---
423 let ground = universe.world.add_component(
424 TransformComponent::new()
425 .with_position(0.0, -0.6, 0.0)
426 .with_scale(60.0, 1.0, 60.0),
427 );
428 let ground_r = universe.world.add_component(RenderableComponent::cube());
429 let ground_c = universe
430 .world
431 .add_component(ColorComponent::rgba(0.95, 0.95, 0.95, 1.0));
432 let _ = universe.attach(ground, ground_r);
433 let _ = universe.attach(ground_r, ground_c);
434 universe.add(ground);
435
436 // --- Opaque yellow cubes behind (contrast reference) ---
437 for i in 0..6 {
438 let x = -10.0 + i as f32 * 4.0;
439 spawn_cube(
440 &mut universe,
441 ground,
442 (x, 1.2, -18.0),
443 (2.0, 2.0, 2.0),
444 Some((1.0, 0.92, 0.15, 1.0)),
445 None,
446 );
447 }
448
449 // --- Demo spots ---
450 // Left of the left big spot: a single-layer transparent XY plane (16x16).
451 spawn_demo_xy_plane(&mut universe, (-14.0, 0.0, 0.0), 0.50, 16, 16, 0.0);
452
453 // Big spots: 8x8x8
454 // Left: single-layer transparent (instanced)
455 spawn_demo_spot(&mut universe, (-4.0, 0.0, 0.0), 0.50, false, 8);
456
457 // Right: multi-layer transparent (sorted)
458 spawn_demo_spot(&mut universe, (4.0, 0.0, 0.0), 0.50, true, 8);
459
460 // Small spots: opaque 1x4 strip + transparent 1x4 strip (along Z).
461 spawn_demo_strip_pair(&mut universe, (-4.0, 0.0, 10.0), false);
462 spawn_demo_strip_pair(&mut universe, (4.0, 0.0, 10.0), true);
463
464 // Process init-time registrations.
465 universe.systems.process_commands(
466 &mut universe.world,
467 &mut universe.visuals,
468 &mut universe.render_assets,
469 &mut universe.command_queue,
470 );
471
472 universe.enable_repl();
473
474 engine::Windowing::run_app(universe).expect("Windowing failed");
475}examples/text-animation.rs (line 41)
6fn main() {
7 mittens_engine::example_support::ensure_model_assets();
8 utils::logger::init();
9
10 let world = engine::ecs::World::default();
11 let mut universe = engine::Universe::new(world);
12
13 // Input-driven camera rig.
14 // Topology: I { T { C3D } }
15 let input = universe
16 .world
17 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
18 let camera3d = universe
19 .world
20 .add_component(engine::ecs::component::Camera3DComponent::new());
21 let rig_transform = universe.world.add_component(
22 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.5),
23 );
24 let input_mode = universe.world.add_component(
25 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
26 );
27 let _ = universe.attach(input, input_mode);
28 let _ = universe.attach(input, rig_transform);
29 let _ = universe.attach(rig_transform, camera3d);
30
31 // Small on-screen help.
32 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
33 universe.add(input);
34
35 // Light so we can see non-emissive materials.
36 let light_transform = universe.world.add_component(
37 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.0),
38 );
39 let light = universe.world.add_component(
40 engine::ecs::component::PointLightComponent::new()
41 .with_distance(25.0)
42 .with_color(1.0, 1.0, 1.0),
43 );
44 let _ = universe.attach(light_transform, light);
45 universe.add(light_transform);
46
47 use engine::ecs::component::{
48 ColorComponent, TextComponent, TextShadowComponent, TextureComponent,
49 TextureFilteringComponent, TransformComponent, TransparentCutoutComponent,
50 };
51
52 // Styled text root.
53 let text_root = universe.world.add_component(
54 TransformComponent::new()
55 .with_position(0.0, 0.0, 0.0)
56 .with_scale(0.25, 0.25, 1.0),
57 );
58
59 // Color must be an ancestor of the glyph renderables.
60 let color_id = universe
61 .world
62 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
63 let _ = universe.attach(text_root, color_id);
64
65 // This is the component we animate via SetText.
66 let text_id = universe.world.add_component(TextComponent::new(">w<"));
67 let _ = universe.attach(color_id, text_id);
68
69 // Route into cutout pass for cleaner edges.
70 let cutout = universe
71 .world
72 .add_component(TransparentCutoutComponent::new());
73 let _ = universe.attach(text_id, cutout);
74
75 // Font atlas.
76 let tex = universe.world.add_component(TextureComponent::with_uri(
77 "assets/textures/font_system.dds",
78 ));
79 let _ = universe.attach(text_id, tex);
80
81 let shadow = universe.world.add_component(
82 TextShadowComponent::new()
83 .with_scale(1.35)
84 .with_offset([0.06, -0.06, 0.0015]),
85 );
86 let filtering = universe
87 .world
88 .add_component(TextureFilteringComponent::nearest_magnification());
89
90 let _ = universe.attach(text_id, shadow);
91 let _ = universe.attach(text_id, filtering);
92
93 universe.add(text_root);
94
95 let clock_component = universe
96 .world
97 .add_component(engine::ecs::component::ClockComponent::new().with_bpm(140.0));
98 let _ = universe.add(clock_component);
99
100 // Looping animation: 4 beats long, 4 keyframes.
101 let anim = universe
102 .world
103 .add_component(engine::ecs::component::AnimationComponent::new());
104
105 let faces = [">w<", "^w^", "-_-", "o_o"];
106 for (i, &face) in faces.iter().enumerate() {
107 let kf = universe
108 .world
109 .add_component(engine::ecs::component::KeyframeComponent::new(i as f64));
110
111 // Each keyframe emits a SetText intent.
112 let action = universe
113 .world
114 .add_component(engine::ecs::component::ActionComponent::new(
115 engine::ecs::IntentValue::SetText {
116 component_ids: vec![text_id],
117 text: face.to_string(),
118 },
119 ));
120
121 let _ = universe.attach(anim, kf);
122 let _ = universe.attach(kf, action);
123 }
124
125 universe.add(anim);
126
127 // Process init-time registrations (Text expands into glyph subtrees here).
128 universe.systems.process_commands(
129 &mut universe.world,
130 &mut universe.visuals,
131 &mut universe.render_assets,
132 &mut universe.command_queue,
133 );
134
135 engine::Windowing::run_app(universe).expect("Windowing failed");
136}examples/gravity-fields.rs (line 738)
607 fn spawn_street_light(universe: &mut engine::Universe) -> engine::ecs::ComponentId {
608 // Dimensions (world units).
609 let shaft_height = 6.0;
610 let shaft_thickness = 0.22;
611 let arm_length = shaft_height / 3.0;
612 let arm_thickness = 0.18;
613
614 // Colors.
615 let pole_color = [0.35, 0.35, 0.38, 1.0];
616 let housing_color = [0.18, 0.18, 0.20, 1.0];
617 let diffuser_color = [1.0, 1.0, 1.0, 1.0];
618
619 // Model root at origin.
620 // The caller is expected to position + rotate this model using an external placement transform.
621 // Street light is authored facing +X (arm extends toward +X).
622 let root_t = universe
623 .world
624 .add_component(engine::ecs::component::TransformComponent::new());
625
626 // Vertical shaft (split root/geom so scaling doesn't affect the arm).
627 let shaft_root_t = universe
628 .world
629 .add_component(engine::ecs::component::TransformComponent::new());
630 let shaft_geom_t = universe.world.add_component(
631 engine::ecs::component::TransformComponent::new()
632 .with_position(0.0, shaft_height * 0.5, 0.0)
633 .with_scale(shaft_thickness, shaft_height, shaft_thickness),
634 );
635 let shaft_r = universe
636 .world
637 .add_component(engine::ecs::component::RenderableComponent::cube());
638 let shaft_c = universe
639 .world
640 .add_component(engine::ecs::component::ColorComponent::rgba(
641 pole_color[0],
642 pole_color[1],
643 pole_color[2],
644 pole_color[3],
645 ));
646
647 let _ = universe.attach(root_t, shaft_root_t);
648 let _ = universe.attach(shaft_root_t, shaft_geom_t);
649 let _ = universe.attach(shaft_geom_t, shaft_r);
650 let _ = universe.attach(shaft_r, shaft_c);
651
652 // Horizontal arm at the top; starts at the pole and extends toward +X.
653 let arm_root_t = universe.world.add_component(
654 engine::ecs::component::TransformComponent::new().with_position(0.0, shaft_height, 0.0),
655 );
656 let arm_geom_t = universe.world.add_component(
657 engine::ecs::component::TransformComponent::new()
658 .with_position(arm_length * 0.5, -arm_thickness * 0.5, 0.0)
659 .with_scale(arm_length, arm_thickness, arm_thickness),
660 );
661 let arm_r = universe
662 .world
663 .add_component(engine::ecs::component::RenderableComponent::cube());
664 let arm_c = universe
665 .world
666 .add_component(engine::ecs::component::ColorComponent::rgba(
667 pole_color[0],
668 pole_color[1],
669 pole_color[2],
670 pole_color[3],
671 ));
672
673 let _ = universe.attach(shaft_root_t, arm_root_t);
674 let _ = universe.attach(arm_root_t, arm_geom_t);
675 let _ = universe.attach(arm_geom_t, arm_r);
676 let _ = universe.attach(arm_r, arm_c);
677
678 // Light housing at the end of the arm (+X end).
679 let housing_root_t = universe.world.add_component(
680 engine::ecs::component::TransformComponent::new().with_position(arm_length, 0.0, 0.0),
681 );
682 let housing_geom_t = universe.world.add_component(
683 engine::ecs::component::TransformComponent::new()
684 .with_position(0.5, 0.0, 0.0)
685 // 2x1x1 box in (z, x, y) -> (x, y, z) = (1, 1, 2)
686 .with_scale(2.0, 1.0, 1.0),
687 );
688 let housing_r = universe
689 .world
690 .add_component(engine::ecs::component::RenderableComponent::cube());
691 let housing_c = universe
692 .world
693 .add_component(engine::ecs::component::ColorComponent::rgba(
694 housing_color[0],
695 housing_color[1],
696 housing_color[2],
697 housing_color[3],
698 ));
699
700 let _ = universe.attach(arm_root_t, housing_root_t);
701 let _ = universe.attach(housing_root_t, housing_geom_t);
702 let _ = universe.attach(housing_geom_t, housing_r);
703 let _ = universe.attach(housing_r, housing_c);
704
705 // White diffuser cube (slightly smaller, slightly lower).
706 let diffuser_t = universe.world.add_component(
707 engine::ecs::component::TransformComponent::new()
708 .with_position(0.5, -0.35, 0.0)
709 .with_scale(1.85, 0.55, 0.70),
710 );
711 let diffuser_r = universe
712 .world
713 .add_component(engine::ecs::component::RenderableComponent::cube());
714 let diffuser_c =
715 universe
716 .world
717 .add_component(engine::ecs::component::ColorComponent::rgba(
718 diffuser_color[0],
719 diffuser_color[1],
720 diffuser_color[2],
721 diffuser_color[3],
722 ));
723
724 let diffuser_emissive = universe
725 .world
726 .add_component(engine::ecs::component::EmissiveComponent::on());
727 let _ = universe.attach(diffuser_r, diffuser_emissive);
728
729 let _ = universe.attach(housing_root_t, diffuser_t);
730 let _ = universe.attach(diffuser_t, diffuser_r);
731 let _ = universe.attach(diffuser_r, diffuser_c);
732
733 // Yellow (slightly red) point light.
734 let light = universe.world.add_component(
735 engine::ecs::component::PointLightComponent::new()
736 .with_color(1.0, 0.82, 0.55)
737 .with_intensity(5.0)
738 .with_distance(40.0),
739 );
740 let _ = universe.attach(diffuser_t, light);
741
742 root_t
743 }examples/vtuber-example.rs (line 80)
11fn main() {
12 mittens_engine::example_support::ensure_model_assets();
13 utils::logger::init();
14
15 let world = engine::ecs::World::default();
16 let mut universe = engine::Universe::new(world);
17
18 // Light pink background.
19 let background = universe
20 .world
21 .add_component(BackgroundColorComponent::new());
22 let background_c = universe
23 .world
24 .add_component(ColorComponent::rgba(1.0, 0.82, 0.90, 1.0));
25 let _ = universe.world.add_child(background, background_c);
26 universe.add(background);
27
28 // Small ambient so shadowed areas aren't pure black.
29 let ambient = universe
30 .world
31 .add_component(AmbientLightComponent::rgb(0.10, 0.10, 0.12));
32 universe.add(ambient);
33
34 // --- Camera rig (WASD + mouse) ---
35 // InputComponent is the root, and it owns a Transform (the camera rig).
36 let input = universe
37 .world
38 .add_component(InputComponent::new().with_speed(1.5));
39 let input_mode = universe.world.add_component(
40 InputTransformModeComponent::forward_z()
41 .with_fps_rotation()
42 .with_roll_axis_y(),
43 );
44 let _ = universe.attach(input, input_mode);
45
46 // Start slightly pulled back looking towards the origin.
47 let rig_transform = universe
48 .world
49 .add_component(TransformComponent::new().with_position(0.0, 0.0, 6.0));
50 let _ = universe.attach(input, rig_transform);
51
52 let camera3d = universe.world.add_component(Camera3DComponent::new());
53 let _ = universe.attach(rig_transform, camera3d);
54
55 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
56 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
57
58 universe.add(input);
59
60 // --- lighting ---
61 // Directional key light (slightly down + forward Z).
62 let sun = universe.world.add_component(
63 DirectionalLightComponent::new()
64 .with_intensity(1.2)
65 .with_color(1.0, 0.98, 0.94),
66 );
67 let sun_dir = universe
68 .world
69 .add_component(TransformComponent::new().with_position(0.0, -0.35, 1.0));
70 let _ = universe.attach(sun_dir, sun);
71 universe.add(sun_dir);
72
73 let light_transform = universe.world.add_component(
74 TransformComponent::new()
75 .with_position(1.0, 6.0, 3.0)
76 .with_scale(0.1, 0.1, 0.1),
77 );
78 let light = universe.world.add_component(
79 engine::ecs::component::PointLightComponent::new()
80 .with_distance(120.0)
81 .with_color(1.0, 1.0, 1.0),
82 );
83 let _ = universe.attach(light_transform, light);
84 universe.add(light_transform);
85
86 // --- VTuber model ---
87 let model_root = universe.world.add_component(TransformComponent::new());
88 let model = universe
89 .world
90 .add_component(GLTFComponent::new("assets/models/pc-rei.hoodie.glb"));
91 // emissive for pc-rei
92 let emissive = universe
93 .world
94 .add_component(engine::ecs::component::EmissiveComponent::on());
95
96 let _ = universe.attach(model, emissive);
97
98 let xr_input = universe.world.add_component(InputXRComponent::on());
99 let xr_gamepad = universe
100 .world
101 .add_component(engine::ecs::component::InputXRGamepadComponent::new().speed(1.5));
102 let xr_head = universe.world.add_component(TransformComponent::new());
103 let xr_camera = universe.world.add_component(CameraXRComponent::on());
104 let _ = universe.attach(xr_input, xr_head);
105 let _ = universe.attach(xr_input, xr_gamepad);
106 let _ = universe.attach(xr_head, xr_camera);
107 let _ = universe.attach(xr_head, model_root);
108
109 let _ = universe.attach(model_root, model);
110 universe.add(xr_input);
111 universe.add(model_root);
112
113 // --- Background clouds (occluded + lit) ---
114 let bg_root = universe.world.add_component(
115 engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
116 );
117 universe.add(bg_root);
118 let mut cloud_params = example_util::CloudRingParams::default();
119 cloud_params.cloud_count = 8; // +3 clusters
120 cloud_params.angle_jitter = 0.35;
121 cloud_params.high_y_probability = 0.5;
122 cloud_params.high_y_multiplier = 1.5;
123 cloud_params.seed = 0x57_55_B0_01u32;
124 example_util::spawn_cloud_ring(&mut universe, bg_root, cloud_params);
125
126 // --- Simple environment ---
127 let spawn_cube = |universe: &mut engine::Universe,
128 position: (f32, f32, f32),
129 scale: (f32, f32, f32),
130 color: (f32, f32, f32, f32)| {
131 let transform = universe.world.add_component(
132 TransformComponent::new()
133 .with_position(position.0, position.1, position.2)
134 .with_scale(scale.0, scale.1, scale.2),
135 );
136 let renderable = universe.world.add_component(RenderableComponent::cube());
137 let color = universe
138 .world
139 .add_component(ColorComponent::rgba(color.0, color.1, color.2, color.3));
140
141 let _ = universe.attach(transform, renderable);
142 let _ = universe.attach(renderable, color);
143
144 universe.add(transform);
145 };
146
147 // floor
148 spawn_cube(
149 &mut universe,
150 (0.0, -0.05, 0.0),
151 (10.0, 0.1, 10.0),
152 (0.92, 0.92, 0.92, 1.0),
153 );
154
155 // back wall
156 spawn_cube(
157 &mut universe,
158 (0.0, 1.5, -5.0),
159 (10.0, 3.0, 1.0),
160 (0.95, 0.94, 0.96, 1.0),
161 );
162
163 // desk
164 spawn_cube(
165 &mut universe,
166 (0.0, 0.35, 1.0),
167 (1.0, 0.75, 0.5),
168 (0.75, 0.70, 0.65, 1.0),
169 );
170
171 let xr_root = universe
172 .world
173 .add_component(engine::ecs::component::XrComponent::on());
174 universe.add(xr_root);
175
176 universe.systems.process_commands(
177 &mut universe.world,
178 &mut universe.visuals,
179 &mut universe.render_assets,
180 &mut universe.command_queue,
181 );
182
183 universe.enable_repl();
184 engine::Windowing::run_app(universe).expect("Windowing failed");
185}Additional examples can be found in:
- examples/openxr.rs
- examples/transparent-cutout-example.rs
- examples/text-example.rs
- examples/background-example.rs
- examples/mesh-factory-example.rs
- examples/simple-demo.rs
- examples/background-occlusion-example.rs
- examples/animation-for-topology.rs
- examples/vtuber-joints-example.rs
- examples/animation-example.rs
Sourcepub fn with_color(self, r: f32, g: f32, b: f32) -> Self
pub fn with_color(self, r: f32, g: f32, b: f32) -> Self
Examples found in repository?
examples/folder-text.rs (line 273)
60fn main() {
61 mittens_engine::example_support::ensure_model_assets();
62 utils::logger::init();
63
64 // Usage:
65 // cargo run --example folder-text -- [folder] [spacing]
66 // Defaults:
67 // folder=src spacing=0.3
68 let mut args = std::env::args().skip(1);
69 let folder = args.next().unwrap_or_else(|| "src".to_string());
70 let spacing: f32 = args
71 .next()
72 .and_then(|s| s.parse::<f32>().ok())
73 .unwrap_or(1.0);
74
75 // Safety caps: this demo can explode the ECS if we load huge projects.
76 const MAX_FILES: usize = 42;
77 const MAX_CHARS_PER_FILE: usize = 10_000;
78 const WRAP_AT: usize = 90;
79 const TEXT_SCALE: f32 = 0.01;
80
81 let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
82 let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
83 let scan_root = {
84 let p = PathBuf::from(&folder);
85 if p.is_absolute() {
86 p
87 } else {
88 // Resolve relative paths from the crate root, not the process CWD.
89 manifest_dir.join(p)
90 }
91 };
92 eprintln!("[folder-text] cwd={:?}", cwd);
93 eprintln!("[folder-text] manifest_dir={:?}", manifest_dir);
94 eprintln!("[folder-text] scan_root={:?}", scan_root);
95
96 let mut files = Vec::new();
97 collect_rs_files(&scan_root, &mut files);
98 files.sort();
99
100 if files.is_empty() {
101 eprintln!(
102 "[folder-text] No .rs files found under folder={:?} (resolved={:?})",
103 folder, scan_root
104 );
105 }
106
107 let files: Vec<PathBuf> = files.into_iter().take(MAX_FILES).collect();
108 eprintln!(
109 "[folder-text] Loaded {} file(s) from {:?} (spacing={})",
110 files.len(),
111 folder,
112 spacing
113 );
114
115 #[derive(Debug, Clone)]
116 struct FileEntry {
117 path: PathBuf,
118 display_text: String,
119 max_cols: usize,
120 rows: usize,
121 }
122
123 // Group files by folder.
124 // Layout:
125 // - each folder becomes a column along +X
126 // - within a folder, files stack along +Y (8 high)
127 // - after 8, additional files start a new stack further along +Z
128 let mut files_by_folder: BTreeMap<PathBuf, Vec<FileEntry>> = BTreeMap::new();
129 for path in files {
130 let Ok(content) = std::fs::read_to_string(&path) else {
131 eprintln!("[folder-text] Failed to read {:?}", path);
132 continue;
133 };
134
135 let mut display_text = format!(
136 "// {}\n\n{}",
137 path.strip_prefix(&manifest_dir).unwrap_or(&path).display(),
138 content
139 );
140
141 if display_text.chars().count() > MAX_CHARS_PER_FILE {
142 display_text = display_text
143 .chars()
144 .take(MAX_CHARS_PER_FILE)
145 .collect::<String>();
146 display_text.push_str("\n\n// ... truncated ...\n");
147 }
148
149 let (max_cols, rows) = measure_text_bounds(&display_text, WRAP_AT);
150
151 let folder_key = path.parent().unwrap_or(&scan_root).to_path_buf();
152 files_by_folder
153 .entry(folder_key)
154 .or_default()
155 .push(FileEntry {
156 path,
157 display_text,
158 max_cols,
159 rows,
160 });
161 }
162
163 let column_count = files_by_folder.len().max(1);
164
165 let world = engine::ecs::World::default();
166 let mut universe = engine::Universe::new(world);
167
168 let bg_r = 0.25;
169 let bg_g = 0.25;
170 let bg_b = 0.25;
171 let background = universe
172 .world
173 .add_component(engine::ecs::component::BackgroundColorComponent::new());
174 let background_c = universe
175 .world
176 .add_component(engine::ecs::component::ColorComponent::rgba(
177 bg_r, bg_g, bg_b, 1.00,
178 ));
179 let _ = universe.world.add_child(background, background_c);
180 universe.add(background);
181
182 let ambient = universe
183 .world
184 .add_component(engine::ecs::component::AmbientLightComponent::rgb(
185 bg_r, bg_g, bg_b,
186 ));
187 universe.add(ambient);
188
189 // Input-driven camera rig.
190 // Topology: I { T { C3D } }
191 let input = universe
192 .world
193 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
194
195 // Center the camera horizontally over the spawned columns.
196 let center_x = if column_count <= 1 {
197 0.0
198 } else {
199 ((column_count - 1) as f32) * spacing * 0.5
200 };
201
202 // Bring camera closer: these text blocks are tiny.
203 let rig_transform = universe.world.add_component(
204 engine::ecs::component::TransformComponent::new().with_position(center_x, 0.0, 1.2),
205 );
206 let camera3d = universe
207 .world
208 .add_component(engine::ecs::component::Camera3DComponent::new());
209 let input_mode = universe.world.add_component(
210 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
211 );
212 let _ = universe.attach(input, input_mode);
213 let _ = universe.attach(input, rig_transform);
214 let _ = universe.attach(rig_transform, camera3d);
215
216 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
217 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
218 universe.add(input);
219
220 // Big floor plane under everything.
221 // RenderableComponent::square() is an XY quad facing +Z; rotate it to XZ facing +Y.
222 let max_stacks = files_by_folder
223 .values()
224 .map(|v| (v.len() + 7) / 8)
225 .max()
226 .unwrap_or(1)
227 .max(1);
228 let total_width = (column_count as f32) * spacing;
229 let total_depth = (max_stacks as f32) * spacing;
230 let floor_w = total_width.max(10.0);
231 let floor_h = total_depth.max(10.0);
232 let floor_transform = universe.world.add_component(
233 engine::ecs::component::TransformComponent::new()
234 .with_position(0.0, -2.0, 0.0)
235 .with_rotation_euler(-std::f32::consts::FRAC_PI_2, 0.0, 0.0)
236 .with_scale(floor_w, floor_h, 1.0),
237 );
238 let floor_renderable = universe
239 .world
240 .add_component(engine::ecs::component::RenderableComponent::square());
241 let floor_color = universe
242 .world
243 .add_component(engine::ecs::component::ColorComponent::rgba(
244 0.88, 0.88, 0.88, 1.0,
245 ));
246 let _ = universe.attach(floor_transform, floor_renderable);
247 let _ = universe.attach(floor_renderable, floor_color);
248 universe.add(floor_transform);
249
250 // 4 red cubes around the perimeter of the world (easy visual anchors).
251 fn spawn_red_cube(universe: &mut engine::Universe, x: f32, y: f32, z: f32, s: f32) {
252 let t = universe.world.add_component(
253 engine::ecs::component::TransformComponent::new()
254 .with_position(x, y, z)
255 .with_scale(s, s, s),
256 );
257 let r = universe
258 .world
259 .add_component(engine::ecs::component::RenderableComponent::cube());
260 let c = universe
261 .world
262 .add_component(engine::ecs::component::ColorComponent::rgba(
263 1.0, 0.0, 0.0, 1.0,
264 ));
265
266 let light_transform = universe.world.add_component(
267 engine::ecs::component::TransformComponent::new().with_position(0.0, s * 0.75, 0.0),
268 );
269 let light = universe.world.add_component(
270 engine::ecs::component::PointLightComponent::new()
271 .with_intensity(1.5)
272 .with_distance(20.0)
273 .with_color(1.0, 0.0, 0.0),
274 );
275
276 let _ = universe.attach(t, r);
277 let _ = universe.attach(r, c);
278 let _ = universe.attach(t, light_transform);
279 let _ = universe.attach(light_transform, light);
280
281 universe.add(t);
282 }
283
284 let p = 1.5;
285 let s = 0.25;
286 spawn_red_cube(&mut universe, -p, -p, 0.0, s);
287 spawn_red_cube(&mut universe, p, -p, 0.0, s);
288 spawn_red_cube(&mut universe, -p, p, 0.0, s);
289 spawn_red_cube(&mut universe, p, p, 0.0, s);
290
291 let start_x = -center_x;
292 let stack_depth = spacing;
293 let row_gap_world = 0.35;
294
295 // One global point light at the top of the overall text "tower".
296 let pad_y = 4.0;
297 let global_max_panel_h_world = files_by_folder
298 .values()
299 .flat_map(|v| v.iter())
300 .map(|e| ((e.rows as f32) + pad_y) * TEXT_SCALE)
301 .fold(0.0_f32, f32::max)
302 .max(0.6);
303 let global_slot_h_world = global_max_panel_h_world + row_gap_world;
304 let tower_top_y = (7.0 * global_slot_h_world) + 4.0;
305 let tower_center_z = total_depth * 0.5;
306
307 let tower_light_transform = universe.world.add_component(
308 engine::ecs::component::TransformComponent::new().with_position(
309 0.0,
310 tower_top_y,
311 tower_center_z,
312 ),
313 );
314 let tower_light = universe.world.add_component(
315 engine::ecs::component::PointLightComponent::new()
316 .with_intensity(2.0)
317 .with_distance(120.0)
318 .with_color(1.0, 1.0, 1.0),
319 );
320 let _ = universe.attach(tower_light_transform, tower_light);
321 universe.add(tower_light_transform);
322
323 for (col_idx, (_folder, mut entries)) in files_by_folder.into_iter().enumerate() {
324 entries.sort_by(|a, b| a.path.cmp(&b.path));
325
326 // Slot height (world units) based on the tallest panel in this folder.
327 let pad_y = 4.0;
328 let max_panel_h_world = entries
329 .iter()
330 .map(|e| ((e.rows as f32) + pad_y) * TEXT_SCALE)
331 .fold(0.0_f32, f32::max)
332 .max(0.6);
333 let slot_h_world = max_panel_h_world + row_gap_world;
334
335 let col_x = start_x + (col_idx as f32) * spacing;
336
337 for (i, entry) in entries.into_iter().enumerate() {
338 let row = (i % 8) as f32;
339 let stack = (i / 8) as f32;
340 let file_y = row * slot_h_world;
341 let file_z = stack * stack_depth;
342
343 let file_group = universe.world.add_component(
344 engine::ecs::component::TransformComponent::new()
345 .with_position(col_x, file_y, file_z),
346 );
347
348 // Text subtree (tiny scale).
349 let file_root = universe.world.add_component(
350 engine::ecs::component::TransformComponent::new()
351 .with_position(0.0, 1.0, 0.0)
352 .with_scale(TEXT_SCALE, TEXT_SCALE, 1.0)
353 .with_rotation_euler(0.0, std::f32::consts::PI / 6.0, 0.0),
354 );
355 let _ = universe.attach(file_group, file_root);
356
357 // Background panel behind the text.
358 // Size is based on wrapped line count (matches TextSystem's strict wrapping behavior).
359 let (max_cols, rows) = (entry.max_cols, entry.rows);
360 let pad_x = 4.0;
361 let pad_y = 4.0;
362 let w = (max_cols as f32) + pad_x;
363 let h = (rows as f32) + pad_y;
364
365 // Text glyph quads are centered at integer (col,row) positions and are 1x1, so the
366 // text's AABB (in text-space) is roughly:
367 // x: [-0.5, max_cols-0.5]
368 // y: [-(rows-1)-0.5, 0.5]
369 // Centering the background quad at those midpoints keeps it aligned as text grows.
370 let bg_x = (max_cols as f32 - 1.0) * 0.5;
371 let bg_y = -((rows as f32 - 1.0) * 0.5);
372
373 let bg_transform = universe.world.add_component(
374 engine::ecs::component::TransformComponent::new()
375 .with_position(bg_x, bg_y, -0.05)
376 .with_scale(w, h, 1.0),
377 );
378 let bg_renderable = universe
379 .world
380 .add_component(engine::ecs::component::RenderableComponent::square());
381 let bg_quant = universe.world.add_component(
382 engine::ecs::component::LightQuantizationComponent::steps(5.0),
383 );
384 let bg_color =
385 universe
386 .world
387 .add_component(engine::ecs::component::ColorComponent::rgba(
388 0.2, 0.2, 0.2, 1.0,
389 ));
390 let _ = universe.attach(file_root, bg_transform);
391 let _ = universe.attach(bg_transform, bg_renderable);
392 let _ = universe.attach(bg_renderable, bg_quant);
393 let _ = universe.attach(bg_renderable, bg_color);
394
395 let text =
396 universe
397 .world
398 .add_component(engine::ecs::component::TextComponent::with_wrap(
399 entry.display_text,
400 WRAP_AT,
401 ));
402 let cutout = universe
403 .world
404 .add_component(engine::ecs::component::TransparentCutoutComponent::new());
405 let filtering = universe.world.add_component(
406 engine::ecs::component::TextureFilteringComponent::nearest_magnification(),
407 );
408 // let color = universe
409 // .world
410 // .add_component(engine::ecs::component::ColorComponent::rgba(0.7, 0.7, 1.0, 1.0));
411 let emissive = universe
412 .world
413 .add_component(engine::ecs::component::EmissiveComponent::on());
414 let _ = universe.attach(file_root, text);
415 let _ = universe.attach(text, cutout);
416 let _ = universe.attach(text, filtering);
417 //let _ = universe.world.add_child(text, color);
418 let _ = universe.attach(text, emissive);
419
420 universe.add(file_group);
421 }
422 }
423
424 // Process init-time registrations (Text expands into glyph subtrees here).
425 universe.systems.process_commands(
426 &mut universe.world,
427 &mut universe.visuals,
428 &mut universe.render_assets,
429 &mut universe.command_queue,
430 );
431
432 engine::Windowing::run_app(universe).expect("Windowing failed");
433}More examples
examples/collision-perimeter.rs (line 105)
86 fn spawn_light(
87 universe: &mut engine::Universe,
88 x: f32,
89 y: f32,
90 z: f32,
91 r: f32,
92 g: f32,
93 b: f32,
94 intensity: f32,
95 distance: f32,
96 ) {
97 let t = universe.world.add_component(
98 engine::ecs::component::TransformComponent::new()
99 .with_position(x, y, z)
100 .with_scale(0.12, 0.12, 0.12),
101 );
102
103 let l = universe.world.add_component(
104 engine::ecs::component::PointLightComponent::new()
105 .with_color(r, g, b)
106 .with_intensity(intensity)
107 .with_distance(distance),
108 );
109 // Visual marker: a small white cube at the light position.
110 // White ensures it mostly shows the light color.
111 let marker = universe
112 .world
113 .add_component(engine::ecs::component::RenderableComponent::cube());
114 let marker_color =
115 universe
116 .world
117 .add_component(engine::ecs::component::ColorComponent::rgba(
118 1.0, 1.0, 1.0, 1.0,
119 ));
120
121 let _ = universe.attach(t, l);
122 let _ = universe.attach(t, marker);
123 let _ = universe.attach(marker, marker_color);
124 universe.add(t);
125 }examples/opacity-example.rs (line 390)
359fn main() {
360 mittens_engine::example_support::ensure_model_assets();
361 utils::logger::init();
362
363 let world = engine::ecs::World::default();
364 let mut universe = engine::Universe::new(world);
365
366 // Dark brown / pink background.
367 let bg = universe
368 .world
369 .add_component(BackgroundColorComponent::new());
370 let bg_c = universe
371 .world
372 .add_component(ColorComponent::rgba(0.22, 0.08, 0.10, 1.0));
373 let _ = universe.world.add_child(bg, bg_c);
374 universe.add(bg);
375
376 // Ambient so unlit areas aren't black.
377 let ambient = universe
378 .world
379 .add_component(AmbientLightComponent::rgb(0.35, 0.35, 0.40));
380 universe.add(ambient);
381
382 // A bright overhead light.
383 let light_t = universe.world.add_component(
384 TransformComponent::new()
385 .with_position(0.0, 18.0, 10.0)
386 .with_scale(0.2, 0.2, 0.2),
387 );
388 let light = universe.world.add_component(
389 PointLightComponent::new()
390 .with_color(1.0, 1.0, 1.0)
391 .with_intensity(1.6)
392 .with_distance(80.0),
393 );
394 let _ = universe.attach(light_t, light);
395 universe.add(light_t);
396
397 // --- Camera rig ---
398 // I { T { C3D { with_fps_rotation with_roll_axis_y } } }
399 let input = universe
400 .world
401 .add_component(InputComponent::new().with_speed(3.0));
402 let input_mode = universe.world.add_component(
403 InputTransformModeComponent::forward_z()
404 .with_roll_axis_y()
405 .with_fps_rotation(),
406 );
407 let _ = universe.attach(input, input_mode);
408
409 let rig_transform = universe
410 .world
411 .add_component(TransformComponent::new().with_position(0.0, 6.0, 20.0));
412 let _ = universe.attach(input, rig_transform);
413
414 let camera = universe.world.add_component(Camera3DComponent::new());
415 let _ = universe.attach(rig_transform, camera);
416
417 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
418 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
419
420 universe.add(input);
421
422 // --- Ground ---
423 let ground = universe.world.add_component(
424 TransformComponent::new()
425 .with_position(0.0, -0.6, 0.0)
426 .with_scale(60.0, 1.0, 60.0),
427 );
428 let ground_r = universe.world.add_component(RenderableComponent::cube());
429 let ground_c = universe
430 .world
431 .add_component(ColorComponent::rgba(0.95, 0.95, 0.95, 1.0));
432 let _ = universe.attach(ground, ground_r);
433 let _ = universe.attach(ground_r, ground_c);
434 universe.add(ground);
435
436 // --- Opaque yellow cubes behind (contrast reference) ---
437 for i in 0..6 {
438 let x = -10.0 + i as f32 * 4.0;
439 spawn_cube(
440 &mut universe,
441 ground,
442 (x, 1.2, -18.0),
443 (2.0, 2.0, 2.0),
444 Some((1.0, 0.92, 0.15, 1.0)),
445 None,
446 );
447 }
448
449 // --- Demo spots ---
450 // Left of the left big spot: a single-layer transparent XY plane (16x16).
451 spawn_demo_xy_plane(&mut universe, (-14.0, 0.0, 0.0), 0.50, 16, 16, 0.0);
452
453 // Big spots: 8x8x8
454 // Left: single-layer transparent (instanced)
455 spawn_demo_spot(&mut universe, (-4.0, 0.0, 0.0), 0.50, false, 8);
456
457 // Right: multi-layer transparent (sorted)
458 spawn_demo_spot(&mut universe, (4.0, 0.0, 0.0), 0.50, true, 8);
459
460 // Small spots: opaque 1x4 strip + transparent 1x4 strip (along Z).
461 spawn_demo_strip_pair(&mut universe, (-4.0, 0.0, 10.0), false);
462 spawn_demo_strip_pair(&mut universe, (4.0, 0.0, 10.0), true);
463
464 // Process init-time registrations.
465 universe.systems.process_commands(
466 &mut universe.world,
467 &mut universe.visuals,
468 &mut universe.render_assets,
469 &mut universe.command_queue,
470 );
471
472 universe.enable_repl();
473
474 engine::Windowing::run_app(universe).expect("Windowing failed");
475}examples/text-animation.rs (line 42)
6fn main() {
7 mittens_engine::example_support::ensure_model_assets();
8 utils::logger::init();
9
10 let world = engine::ecs::World::default();
11 let mut universe = engine::Universe::new(world);
12
13 // Input-driven camera rig.
14 // Topology: I { T { C3D } }
15 let input = universe
16 .world
17 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
18 let camera3d = universe
19 .world
20 .add_component(engine::ecs::component::Camera3DComponent::new());
21 let rig_transform = universe.world.add_component(
22 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.5),
23 );
24 let input_mode = universe.world.add_component(
25 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
26 );
27 let _ = universe.attach(input, input_mode);
28 let _ = universe.attach(input, rig_transform);
29 let _ = universe.attach(rig_transform, camera3d);
30
31 // Small on-screen help.
32 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
33 universe.add(input);
34
35 // Light so we can see non-emissive materials.
36 let light_transform = universe.world.add_component(
37 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.0),
38 );
39 let light = universe.world.add_component(
40 engine::ecs::component::PointLightComponent::new()
41 .with_distance(25.0)
42 .with_color(1.0, 1.0, 1.0),
43 );
44 let _ = universe.attach(light_transform, light);
45 universe.add(light_transform);
46
47 use engine::ecs::component::{
48 ColorComponent, TextComponent, TextShadowComponent, TextureComponent,
49 TextureFilteringComponent, TransformComponent, TransparentCutoutComponent,
50 };
51
52 // Styled text root.
53 let text_root = universe.world.add_component(
54 TransformComponent::new()
55 .with_position(0.0, 0.0, 0.0)
56 .with_scale(0.25, 0.25, 1.0),
57 );
58
59 // Color must be an ancestor of the glyph renderables.
60 let color_id = universe
61 .world
62 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
63 let _ = universe.attach(text_root, color_id);
64
65 // This is the component we animate via SetText.
66 let text_id = universe.world.add_component(TextComponent::new(">w<"));
67 let _ = universe.attach(color_id, text_id);
68
69 // Route into cutout pass for cleaner edges.
70 let cutout = universe
71 .world
72 .add_component(TransparentCutoutComponent::new());
73 let _ = universe.attach(text_id, cutout);
74
75 // Font atlas.
76 let tex = universe.world.add_component(TextureComponent::with_uri(
77 "assets/textures/font_system.dds",
78 ));
79 let _ = universe.attach(text_id, tex);
80
81 let shadow = universe.world.add_component(
82 TextShadowComponent::new()
83 .with_scale(1.35)
84 .with_offset([0.06, -0.06, 0.0015]),
85 );
86 let filtering = universe
87 .world
88 .add_component(TextureFilteringComponent::nearest_magnification());
89
90 let _ = universe.attach(text_id, shadow);
91 let _ = universe.attach(text_id, filtering);
92
93 universe.add(text_root);
94
95 let clock_component = universe
96 .world
97 .add_component(engine::ecs::component::ClockComponent::new().with_bpm(140.0));
98 let _ = universe.add(clock_component);
99
100 // Looping animation: 4 beats long, 4 keyframes.
101 let anim = universe
102 .world
103 .add_component(engine::ecs::component::AnimationComponent::new());
104
105 let faces = [">w<", "^w^", "-_-", "o_o"];
106 for (i, &face) in faces.iter().enumerate() {
107 let kf = universe
108 .world
109 .add_component(engine::ecs::component::KeyframeComponent::new(i as f64));
110
111 // Each keyframe emits a SetText intent.
112 let action = universe
113 .world
114 .add_component(engine::ecs::component::ActionComponent::new(
115 engine::ecs::IntentValue::SetText {
116 component_ids: vec![text_id],
117 text: face.to_string(),
118 },
119 ));
120
121 let _ = universe.attach(anim, kf);
122 let _ = universe.attach(kf, action);
123 }
124
125 universe.add(anim);
126
127 // Process init-time registrations (Text expands into glyph subtrees here).
128 universe.systems.process_commands(
129 &mut universe.world,
130 &mut universe.visuals,
131 &mut universe.render_assets,
132 &mut universe.command_queue,
133 );
134
135 engine::Windowing::run_app(universe).expect("Windowing failed");
136}examples/gravity-fields.rs (line 736)
607 fn spawn_street_light(universe: &mut engine::Universe) -> engine::ecs::ComponentId {
608 // Dimensions (world units).
609 let shaft_height = 6.0;
610 let shaft_thickness = 0.22;
611 let arm_length = shaft_height / 3.0;
612 let arm_thickness = 0.18;
613
614 // Colors.
615 let pole_color = [0.35, 0.35, 0.38, 1.0];
616 let housing_color = [0.18, 0.18, 0.20, 1.0];
617 let diffuser_color = [1.0, 1.0, 1.0, 1.0];
618
619 // Model root at origin.
620 // The caller is expected to position + rotate this model using an external placement transform.
621 // Street light is authored facing +X (arm extends toward +X).
622 let root_t = universe
623 .world
624 .add_component(engine::ecs::component::TransformComponent::new());
625
626 // Vertical shaft (split root/geom so scaling doesn't affect the arm).
627 let shaft_root_t = universe
628 .world
629 .add_component(engine::ecs::component::TransformComponent::new());
630 let shaft_geom_t = universe.world.add_component(
631 engine::ecs::component::TransformComponent::new()
632 .with_position(0.0, shaft_height * 0.5, 0.0)
633 .with_scale(shaft_thickness, shaft_height, shaft_thickness),
634 );
635 let shaft_r = universe
636 .world
637 .add_component(engine::ecs::component::RenderableComponent::cube());
638 let shaft_c = universe
639 .world
640 .add_component(engine::ecs::component::ColorComponent::rgba(
641 pole_color[0],
642 pole_color[1],
643 pole_color[2],
644 pole_color[3],
645 ));
646
647 let _ = universe.attach(root_t, shaft_root_t);
648 let _ = universe.attach(shaft_root_t, shaft_geom_t);
649 let _ = universe.attach(shaft_geom_t, shaft_r);
650 let _ = universe.attach(shaft_r, shaft_c);
651
652 // Horizontal arm at the top; starts at the pole and extends toward +X.
653 let arm_root_t = universe.world.add_component(
654 engine::ecs::component::TransformComponent::new().with_position(0.0, shaft_height, 0.0),
655 );
656 let arm_geom_t = universe.world.add_component(
657 engine::ecs::component::TransformComponent::new()
658 .with_position(arm_length * 0.5, -arm_thickness * 0.5, 0.0)
659 .with_scale(arm_length, arm_thickness, arm_thickness),
660 );
661 let arm_r = universe
662 .world
663 .add_component(engine::ecs::component::RenderableComponent::cube());
664 let arm_c = universe
665 .world
666 .add_component(engine::ecs::component::ColorComponent::rgba(
667 pole_color[0],
668 pole_color[1],
669 pole_color[2],
670 pole_color[3],
671 ));
672
673 let _ = universe.attach(shaft_root_t, arm_root_t);
674 let _ = universe.attach(arm_root_t, arm_geom_t);
675 let _ = universe.attach(arm_geom_t, arm_r);
676 let _ = universe.attach(arm_r, arm_c);
677
678 // Light housing at the end of the arm (+X end).
679 let housing_root_t = universe.world.add_component(
680 engine::ecs::component::TransformComponent::new().with_position(arm_length, 0.0, 0.0),
681 );
682 let housing_geom_t = universe.world.add_component(
683 engine::ecs::component::TransformComponent::new()
684 .with_position(0.5, 0.0, 0.0)
685 // 2x1x1 box in (z, x, y) -> (x, y, z) = (1, 1, 2)
686 .with_scale(2.0, 1.0, 1.0),
687 );
688 let housing_r = universe
689 .world
690 .add_component(engine::ecs::component::RenderableComponent::cube());
691 let housing_c = universe
692 .world
693 .add_component(engine::ecs::component::ColorComponent::rgba(
694 housing_color[0],
695 housing_color[1],
696 housing_color[2],
697 housing_color[3],
698 ));
699
700 let _ = universe.attach(arm_root_t, housing_root_t);
701 let _ = universe.attach(housing_root_t, housing_geom_t);
702 let _ = universe.attach(housing_geom_t, housing_r);
703 let _ = universe.attach(housing_r, housing_c);
704
705 // White diffuser cube (slightly smaller, slightly lower).
706 let diffuser_t = universe.world.add_component(
707 engine::ecs::component::TransformComponent::new()
708 .with_position(0.5, -0.35, 0.0)
709 .with_scale(1.85, 0.55, 0.70),
710 );
711 let diffuser_r = universe
712 .world
713 .add_component(engine::ecs::component::RenderableComponent::cube());
714 let diffuser_c =
715 universe
716 .world
717 .add_component(engine::ecs::component::ColorComponent::rgba(
718 diffuser_color[0],
719 diffuser_color[1],
720 diffuser_color[2],
721 diffuser_color[3],
722 ));
723
724 let diffuser_emissive = universe
725 .world
726 .add_component(engine::ecs::component::EmissiveComponent::on());
727 let _ = universe.attach(diffuser_r, diffuser_emissive);
728
729 let _ = universe.attach(housing_root_t, diffuser_t);
730 let _ = universe.attach(diffuser_t, diffuser_r);
731 let _ = universe.attach(diffuser_r, diffuser_c);
732
733 // Yellow (slightly red) point light.
734 let light = universe.world.add_component(
735 engine::ecs::component::PointLightComponent::new()
736 .with_color(1.0, 0.82, 0.55)
737 .with_intensity(5.0)
738 .with_distance(40.0),
739 );
740 let _ = universe.attach(diffuser_t, light);
741
742 root_t
743 }examples/vtuber-example.rs (line 81)
11fn main() {
12 mittens_engine::example_support::ensure_model_assets();
13 utils::logger::init();
14
15 let world = engine::ecs::World::default();
16 let mut universe = engine::Universe::new(world);
17
18 // Light pink background.
19 let background = universe
20 .world
21 .add_component(BackgroundColorComponent::new());
22 let background_c = universe
23 .world
24 .add_component(ColorComponent::rgba(1.0, 0.82, 0.90, 1.0));
25 let _ = universe.world.add_child(background, background_c);
26 universe.add(background);
27
28 // Small ambient so shadowed areas aren't pure black.
29 let ambient = universe
30 .world
31 .add_component(AmbientLightComponent::rgb(0.10, 0.10, 0.12));
32 universe.add(ambient);
33
34 // --- Camera rig (WASD + mouse) ---
35 // InputComponent is the root, and it owns a Transform (the camera rig).
36 let input = universe
37 .world
38 .add_component(InputComponent::new().with_speed(1.5));
39 let input_mode = universe.world.add_component(
40 InputTransformModeComponent::forward_z()
41 .with_fps_rotation()
42 .with_roll_axis_y(),
43 );
44 let _ = universe.attach(input, input_mode);
45
46 // Start slightly pulled back looking towards the origin.
47 let rig_transform = universe
48 .world
49 .add_component(TransformComponent::new().with_position(0.0, 0.0, 6.0));
50 let _ = universe.attach(input, rig_transform);
51
52 let camera3d = universe.world.add_component(Camera3DComponent::new());
53 let _ = universe.attach(rig_transform, camera3d);
54
55 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
56 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
57
58 universe.add(input);
59
60 // --- lighting ---
61 // Directional key light (slightly down + forward Z).
62 let sun = universe.world.add_component(
63 DirectionalLightComponent::new()
64 .with_intensity(1.2)
65 .with_color(1.0, 0.98, 0.94),
66 );
67 let sun_dir = universe
68 .world
69 .add_component(TransformComponent::new().with_position(0.0, -0.35, 1.0));
70 let _ = universe.attach(sun_dir, sun);
71 universe.add(sun_dir);
72
73 let light_transform = universe.world.add_component(
74 TransformComponent::new()
75 .with_position(1.0, 6.0, 3.0)
76 .with_scale(0.1, 0.1, 0.1),
77 );
78 let light = universe.world.add_component(
79 engine::ecs::component::PointLightComponent::new()
80 .with_distance(120.0)
81 .with_color(1.0, 1.0, 1.0),
82 );
83 let _ = universe.attach(light_transform, light);
84 universe.add(light_transform);
85
86 // --- VTuber model ---
87 let model_root = universe.world.add_component(TransformComponent::new());
88 let model = universe
89 .world
90 .add_component(GLTFComponent::new("assets/models/pc-rei.hoodie.glb"));
91 // emissive for pc-rei
92 let emissive = universe
93 .world
94 .add_component(engine::ecs::component::EmissiveComponent::on());
95
96 let _ = universe.attach(model, emissive);
97
98 let xr_input = universe.world.add_component(InputXRComponent::on());
99 let xr_gamepad = universe
100 .world
101 .add_component(engine::ecs::component::InputXRGamepadComponent::new().speed(1.5));
102 let xr_head = universe.world.add_component(TransformComponent::new());
103 let xr_camera = universe.world.add_component(CameraXRComponent::on());
104 let _ = universe.attach(xr_input, xr_head);
105 let _ = universe.attach(xr_input, xr_gamepad);
106 let _ = universe.attach(xr_head, xr_camera);
107 let _ = universe.attach(xr_head, model_root);
108
109 let _ = universe.attach(model_root, model);
110 universe.add(xr_input);
111 universe.add(model_root);
112
113 // --- Background clouds (occluded + lit) ---
114 let bg_root = universe.world.add_component(
115 engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
116 );
117 universe.add(bg_root);
118 let mut cloud_params = example_util::CloudRingParams::default();
119 cloud_params.cloud_count = 8; // +3 clusters
120 cloud_params.angle_jitter = 0.35;
121 cloud_params.high_y_probability = 0.5;
122 cloud_params.high_y_multiplier = 1.5;
123 cloud_params.seed = 0x57_55_B0_01u32;
124 example_util::spawn_cloud_ring(&mut universe, bg_root, cloud_params);
125
126 // --- Simple environment ---
127 let spawn_cube = |universe: &mut engine::Universe,
128 position: (f32, f32, f32),
129 scale: (f32, f32, f32),
130 color: (f32, f32, f32, f32)| {
131 let transform = universe.world.add_component(
132 TransformComponent::new()
133 .with_position(position.0, position.1, position.2)
134 .with_scale(scale.0, scale.1, scale.2),
135 );
136 let renderable = universe.world.add_component(RenderableComponent::cube());
137 let color = universe
138 .world
139 .add_component(ColorComponent::rgba(color.0, color.1, color.2, color.3));
140
141 let _ = universe.attach(transform, renderable);
142 let _ = universe.attach(renderable, color);
143
144 universe.add(transform);
145 };
146
147 // floor
148 spawn_cube(
149 &mut universe,
150 (0.0, -0.05, 0.0),
151 (10.0, 0.1, 10.0),
152 (0.92, 0.92, 0.92, 1.0),
153 );
154
155 // back wall
156 spawn_cube(
157 &mut universe,
158 (0.0, 1.5, -5.0),
159 (10.0, 3.0, 1.0),
160 (0.95, 0.94, 0.96, 1.0),
161 );
162
163 // desk
164 spawn_cube(
165 &mut universe,
166 (0.0, 0.35, 1.0),
167 (1.0, 0.75, 0.5),
168 (0.75, 0.70, 0.65, 1.0),
169 );
170
171 let xr_root = universe
172 .world
173 .add_component(engine::ecs::component::XrComponent::on());
174 universe.add(xr_root);
175
176 universe.systems.process_commands(
177 &mut universe.world,
178 &mut universe.visuals,
179 &mut universe.render_assets,
180 &mut universe.command_queue,
181 );
182
183 universe.enable_repl();
184 engine::Windowing::run_app(universe).expect("Windowing failed");
185}Additional examples can be found in:
- examples/openxr.rs
- examples/transparent-cutout-example.rs
- examples/text-example.rs
- examples/background-example.rs
- examples/mesh-factory-example.rs
- examples/simple-demo.rs
- examples/background-occlusion-example.rs
- examples/animation-for-topology.rs
- examples/vtuber-joints-example.rs
- examples/animation-example.rs
pub fn id(&self) -> Option<ComponentId>
Trait Implementations§
Source§impl Clone for PointLightComponent
impl Clone for PointLightComponent
Source§fn clone(&self) -> PointLightComponent
fn clone(&self) -> PointLightComponent
Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreSource§impl Component for PointLightComponent
impl Component for PointLightComponent
fn set_id(&mut self, component: ComponentId)
Source§fn name(&self) -> &'static str
fn name(&self) -> &'static str
Short debug/type name for this component kind (e.g. “transform”, “camera”).
Source§fn init(&mut self, emit: &mut dyn SignalEmitter, component: ComponentId)
fn init(&mut self, emit: &mut dyn SignalEmitter, component: ComponentId)
Called when component is added to the World
fn as_any(&self) -> &dyn Any
fn as_any_mut(&mut self) -> &mut dyn Any
Source§fn to_mms_ast(&self, _world: &World) -> ComponentExpression
fn to_mms_ast(&self, _world: &World) -> ComponentExpression
Encode this component as an MMS Component Expression AST node. Read more
Source§fn cleanup(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)
fn cleanup(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)
Called when component is removed from the World.
impl Copy for PointLightComponent
Source§impl Debug for PointLightComponent
impl Debug for PointLightComponent
Auto Trait Implementations§
impl Freeze for PointLightComponent
impl RefUnwindSafe for PointLightComponent
impl Send for PointLightComponent
impl Sync for PointLightComponent
impl Unpin for PointLightComponent
impl UnsafeUnpin for PointLightComponent
impl UnwindSafe for PointLightComponent
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Convert
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.