Skip to main content

GravityComponent

Struct GravityComponent 

Source
pub struct GravityComponent {
    pub enabled: bool,
    pub coefficient: f32,
    /* private fields */
}
Expand description

Gravity field component.

Any KineticResponseComponent nested under a GravityComponent will have gravity applied by KineticResponseSystem.

This component can live anywhere in the scene graph and can have arbitrary descendants. If multiple GravityComponents exist in the ancestor chain, the nearest enabled one wins.

Fields§

§enabled: bool§coefficient: f32

Multiplier applied to the system gravity (m/s^2).

  • 1.0 = earth-like gravity
  • 0.0 = no gravity
  • negative values invert gravity

Implementations§

Source§

impl GravityComponent

Source

pub fn new() -> Self

Examples found in repository?
examples/collision-perimeter.rs (line 28)
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    let bg_color = universe
14        .world
15        .add_component(engine::ecs::component::BackgroundColorComponent::new());
16    let bg_color_c = universe
17        .world
18        .add_component(engine::ecs::component::ColorComponent::rgba(
19            0.1, 0.02, 0.05, 1.0,
20        ));
21    let _ = universe.world.add_child(bg_color, bg_color_c);
22    universe.add(bg_color);
23
24    // Gravity field for the pushable cubes.
25    // Any KineticResponseComponent nested under this subtree will have gravity applied.
26    let gravity_field = universe
27        .world
28        .add_component(engine::ecs::component::GravityComponent::new().with_coefficient(0.5));
29    universe.add(gravity_field);
30
31    // Input-driven camera rig.
32    // Topology: I { T { C3D }  CN{Rigged { Sphere }} }
33    let input = universe
34        .world
35        .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
36
37    let rig_transform = universe.world.add_component(
38        engine::ecs::component::TransformComponent::new().with_position(0.0, 0.75, 0.0),
39    );
40    let camera3d = universe
41        .world
42        .add_component(engine::ecs::component::Camera3DComponent::new());
43    let input_mode = universe.world.add_component(
44        engine::ecs::component::InputTransformModeComponent::forward_z()
45            .with_roll_axis_y()
46            .with_fps_rotation(),
47    );
48
49    let rig_collision = universe
50        .world
51        .add_component(engine::ecs::component::CollisionComponent::RIGGED());
52
53    // Opt-in to default kinematic-vs-static collision response.
54    // Policy note: collisions still emit signals regardless; response only runs for entities
55    // that explicitly add a KineticResponseComponent.
56    let rig_response = universe
57        .world
58        .add_component(engine::ecs::component::KineticResponseComponent::slide());
59    let rig_shape =
60        universe
61            .world
62            .add_component(engine::ecs::component::CollisionShapeComponent::new(
63                engine::ecs::component::CollisionShape::sphere_radius(0.25),
64            ));
65
66    let _ = universe.attach(input, input_mode);
67    let _ = universe.attach(input, rig_transform);
68    let _ = universe.attach(rig_transform, camera3d);
69
70    // Click-to-pick: treat this camera rig as a pointer source.
71    let pointer = universe
72        .world
73        .add_component(engine::ecs::component::PointerComponent::new());
74    let _ = universe.attach(camera3d, pointer);
75
76    // Topology: I { T { C3D } } — add a small camera-attached controls hint.
77    example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
78
79    let _ = universe.attach(rig_transform, rig_collision);
80    let _ = universe.attach(rig_collision, rig_response);
81    let _ = universe.attach(rig_collision, rig_shape);
82
83    universe.add(input);
84
85    // Lights so we can see non-emissive meshes and verify attenuation/direction.
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    }
126
127    // Perimeter of cubes: 16 per side, 90deg turns.
128    const N: i32 = 16;
129    let spacing = 1.0;
130    let half = (N as f32 - 1.0) * spacing * 0.5;
131
132    // Place 4 very distinct colored lights near corners plus a white overhead fill.
133    // (High-ish saturation makes it easy to tell which light is contributing.)
134    spawn_light(&mut universe, -half, 2.8, -half, 1.0, 0.1, 0.1, 2.5, 14.0); // red
135    spawn_light(&mut universe, half, 2.8, -half, 0.1, 1.0, 0.1, 2.5, 14.0); // green
136    spawn_light(&mut universe, half, 2.8, half, 0.1, 0.2, 1.0, 2.5, 14.0); // blue
137    spawn_light(&mut universe, -half, 2.8, half, 1.0, 0.2, 1.0, 2.5, 14.0); // magenta-ish
138    spawn_light(&mut universe, 0.0, 5.5, 0.0, 1.0, 1.0, 1.0, 0.7, 40.0); // white fill
139
140    fn spawn_wall_cube(universe: &mut engine::Universe, x: f32, y: f32, z: f32) {
141        let t = universe.world.add_component(
142            engine::ecs::component::TransformComponent::new().with_position(x, y, z),
143        );
144        let r = universe
145            .world
146            .add_component(engine::ecs::component::RenderableComponent::cube());
147        let cn = universe
148            .world
149            .add_component(engine::ecs::component::CollisionComponent::STATIC());
150        let c = universe
151            .world
152            .add_component(engine::ecs::component::ColorComponent::rgba(
153                0.9, 0.2, 0.2, 1.0,
154            ));
155
156        let _ = universe.attach(t, r);
157        let _ = universe.attach(t, cn);
158        let _ = universe.attach(r, c);
159
160        universe.add(t);
161    }
162
163    fn spawn_cube(
164        universe: &mut engine::Universe,
165        x: f32,
166        y: f32,
167        z: f32,
168        sx: f32,
169        sy: f32,
170        sz: f32,
171        r: f32,
172        g: f32,
173        b: f32,
174    ) {
175        let t = universe.world.add_component(
176            engine::ecs::component::TransformComponent::new()
177                .with_position(x, y, z)
178                .with_scale(sx, sy, sz),
179        );
180        let renderable = universe
181            .world
182            .add_component(engine::ecs::component::RenderableComponent::cube());
183        let color = universe
184            .world
185            .add_component(engine::ecs::component::ColorComponent::rgba(r, g, b, 1.0));
186
187        let _ = universe.attach(t, renderable);
188        let _ = universe.attach(renderable, color);
189
190        universe.add(t);
191    }
192
193    fn spawn_pushable_cube(
194        universe: &mut engine::Universe,
195        parent: engine::ecs::ComponentId,
196        x: f32,
197        y: f32,
198        z: f32,
199        s: f32,
200        r: f32,
201        g: f32,
202        b: f32,
203    ) {
204        let t = universe.world.add_component(
205            engine::ecs::component::TransformComponent::new()
206                .with_position(x, y, z)
207                .with_scale(s, s, s),
208        );
209        let renderable = universe
210            .world
211            .add_component(engine::ecs::component::RenderableComponent::cube());
212        let color = universe
213            .world
214            .add_component(engine::ecs::component::ColorComponent::rgba(r, g, b, 1.0));
215
216        let cn = universe
217            .world
218            .add_component(engine::ecs::component::CollisionComponent::KINEMATIC());
219        let response = universe.world.add_component(
220            engine::ecs::component::KineticResponseComponent::push()
221                .with_push_strength(4.0)
222                .with_friction_y(10.0),
223        );
224        let shape =
225            universe
226                .world
227                .add_component(engine::ecs::component::CollisionShapeComponent::new(
228                    engine::ecs::component::CollisionShape::cube_half_extents([
229                        0.5 * s,
230                        0.5 * s,
231                        0.5 * s,
232                    ]),
233                ));
234
235        let _ = universe.attach(t, renderable);
236        let _ = universe.attach(renderable, color);
237
238        let _ = universe.attach(t, cn);
239        let _ = universe.attach(cn, response);
240        let _ = universe.attach(cn, shape);
241
242        let _ = universe.attach(parent, t);
243    }
244
245    fn spawn_invisible_static_wall(
246        universe: &mut engine::Universe,
247        x: f32,
248        y: f32,
249        z: f32,
250        half_extents: [f32; 3],
251    ) {
252        let t = universe.world.add_component(
253            engine::ecs::component::TransformComponent::new().with_position(x, y, z),
254        );
255
256        let cn = universe
257            .world
258            .add_component(engine::ecs::component::CollisionComponent::STATIC());
259        let shape =
260            universe
261                .world
262                .add_component(engine::ecs::component::CollisionShapeComponent::new(
263                    engine::ecs::component::CollisionShape::cube_half_extents(half_extents),
264                ));
265
266        // Requested: attach kinematic_response as well (even though STATIC colliders
267        // are not responders).
268        let response = universe
269            .world
270            .add_component(engine::ecs::component::KineticResponseComponent::slide());
271
272        let _ = universe.attach(t, cn);
273        let _ = universe.attach(cn, shape);
274        let _ = universe.attach(cn, response);
275        universe.add(t);
276    }
277
278    let y = 0.5;
279
280    // Static ground plane (thin cube). Top surface at y=0.
281    {
282        let ground_half = half + 2.0;
283        let thickness = 0.20;
284
285        // Split transforms so scaling the ground does not scale any potential children.
286        // Top surface at y=0.
287        let ground_root_t = universe
288            .world
289            .add_component(engine::ecs::component::TransformComponent::new());
290        let ground_geom_t = universe.world.add_component(
291            engine::ecs::component::TransformComponent::new()
292                .with_position(0.0, -thickness, 0.0)
293                .with_scale(ground_half * 2.0, thickness * 2.0, ground_half * 2.0),
294        );
295        let ground_r = universe
296            .world
297            .add_component(engine::ecs::component::RenderableComponent::cube());
298        let ground_c = universe
299            .world
300            .add_component(engine::ecs::component::ColorComponent::rgba(
301                0.08, 0.08, 0.09, 1.0,
302            ));
303        let ground_cn = universe
304            .world
305            .add_component(engine::ecs::component::CollisionComponent::STATIC());
306        let ground_shape =
307            universe
308                .world
309                .add_component(engine::ecs::component::CollisionShapeComponent::new(
310                    engine::ecs::component::CollisionShape::cube_half_extents([
311                        ground_half,
312                        thickness,
313                        ground_half,
314                    ]),
315                ));
316
317        let _ = universe.attach(ground_root_t, ground_geom_t);
318        let _ = universe.attach(ground_geom_t, ground_r);
319        let _ = universe.attach(ground_r, ground_c);
320        let _ = universe.attach(ground_geom_t, ground_cn);
321        let _ = universe.attach(ground_cn, ground_shape);
322        universe.add(ground_root_t);
323    }
324
325    // Bottom edge (left -> right)
326    for i in 0..N {
327        let x = -half + (i as f32) * spacing;
328        let z = -half;
329        spawn_wall_cube(&mut universe, x, y, z);
330    }
331
332    // Right edge (bottom -> top), skip first corner
333    for i in 1..N {
334        let x = half;
335        let z = -half + (i as f32) * spacing;
336        spawn_wall_cube(&mut universe, x, y, z);
337    }
338
339    // Top edge (right -> left), skip first corner
340    for i in 1..N {
341        let x = half - (i as f32) * spacing;
342        let z = half;
343        spawn_wall_cube(&mut universe, x, y, z);
344    }
345
346    // Left edge (top -> bottom), skip first and last corners
347    for i in 1..(N - 1) {
348        let x = -half;
349        let z = half - (i as f32) * spacing;
350        spawn_wall_cube(&mut universe, x, y, z);
351    }
352
353    // A few larger cubes outside the perimeter (visual landmarks + lighting test surfaces).
354    let outer = half + 3.0;
355    spawn_cube(
356        &mut universe,
357        0.0,
358        0.75,
359        -outer,
360        3.0,
361        1.5,
362        1.0,
363        0.15,
364        0.15,
365        0.18,
366    );
367    spawn_cube(
368        &mut universe,
369        0.0,
370        0.75,
371        outer,
372        3.0,
373        1.5,
374        1.0,
375        0.15,
376        0.15,
377        0.18,
378    );
379    spawn_cube(
380        &mut universe,
381        -outer,
382        0.75,
383        0.0,
384        1.0,
385        1.5,
386        3.0,
387        0.15,
388        0.15,
389        0.18,
390    );
391    spawn_cube(
392        &mut universe,
393        outer,
394        0.75,
395        0.0,
396        1.0,
397        1.5,
398        3.0,
399        0.15,
400        0.15,
401        0.18,
402    );
403
404    // Outer containment walls (invisible): keep runaway cubes near the scene.
405    // Collision shapes are in world units; transform scale is irrelevant for collision.
406    {
407        let wall_center_y = 6.0; // bottom at y=0, top at y=12
408        let wall_half_height = 6.0;
409        let wall_half_thickness = 0.25;
410        let wall_offset = half + 6.0;
411        let wall_half_len = wall_offset + 2.0;
412
413        // +Z / -Z walls
414        spawn_invisible_static_wall(
415            &mut universe,
416            0.0,
417            wall_center_y,
418            wall_offset,
419            [wall_half_len, wall_half_height, wall_half_thickness],
420        );
421        spawn_invisible_static_wall(
422            &mut universe,
423            0.0,
424            wall_center_y,
425            -wall_offset,
426            [wall_half_len, wall_half_height, wall_half_thickness],
427        );
428
429        // +X / -X walls
430        spawn_invisible_static_wall(
431            &mut universe,
432            wall_offset,
433            wall_center_y,
434            0.0,
435            [wall_half_thickness, wall_half_height, wall_half_len],
436        );
437        spawn_invisible_static_wall(
438            &mut universe,
439            -wall_offset,
440            wall_center_y,
441            0.0,
442            [wall_half_thickness, wall_half_height, wall_half_len],
443        );
444
445        // Roof
446        spawn_invisible_static_wall(
447            &mut universe,
448            0.0,
449            wall_center_y + wall_half_height + wall_half_thickness,
450            0.0,
451            [wall_half_len, wall_half_thickness, wall_half_len],
452        );
453    }
454
455    // Many small cubes inside the perimeter.
456    // Deterministic pseudo-random distribution so the scene is stable.
457    let mut seed: u32 = 0xC0111510;
458    let mut rng01 = || {
459        seed ^= seed << 13;
460        seed ^= seed >> 17;
461        seed ^= seed << 5;
462        (seed as f32) / (u32::MAX as f32)
463    };
464
465    // 3x more cubes, with three distinct size bands.
466    for band in 0..3 {
467        for _ in 0..60 {
468            let s = match band {
469                0 => 0.12 + rng01() * 0.20, // small
470                1 => 0.35 + rng01() * 0.35, // medium
471                _ => 0.75 + rng01() * 0.55, // large
472            };
473
474            let bound = (half - (1.0 + s)).max(0.5);
475            let x = (rng01() * 2.0 - 1.0) * bound;
476            let z = (rng01() * 2.0 - 1.0) * bound;
477            let y = 0.5 * s;
478
479            // Slightly varied colors to show multiple light contributions.
480            let cr = 0.25 + rng01() * 0.6;
481            let cg = 0.25 + rng01() * 0.6;
482            let cb = 0.25 + rng01() * 0.6;
483
484            spawn_pushable_cube(&mut universe, gravity_field, x, y, z, s, cr, cg, cb);
485        }
486    }
487
488    // Process init-time registrations.
489    universe.systems.process_commands(
490        &mut universe.world,
491        &mut universe.visuals,
492        &mut universe.render_assets,
493        &mut universe.command_queue,
494    );
495
496    engine::Windowing::run_app(universe).expect("Windowing failed");
497}
More examples
Hide additional examples
examples/gravity-fields.rs (line 965)
170fn main() {
171    mittens_engine::example_support::ensure_model_assets();
172    utils::logger::init();
173
174    let world = engine::ecs::World::default();
175    let mut universe = engine::Universe::new(world);
176
177    let bg_color = universe
178        .world
179        .add_component(engine::ecs::component::BackgroundColorComponent::new());
180    let bg_color_c = universe
181        .world
182        .add_component(engine::ecs::component::ColorComponent::rgba(
183            0.06, 0.04, 0.07, 1.0,
184        ));
185    let _ = universe.world.add_child(bg_color, bg_color_c);
186    universe.add(bg_color);
187
188    // overhead directional light
189    let directional_light = universe.world.add_component(
190        engine::ecs::component::DirectionalLightComponent::new()
191            .with_color(0.16, 0.14, 0.12)
192            .with_intensity(0.7),
193    );
194    let directional_light_t = universe.world.add_component(
195        engine::ecs::component::TransformComponent::new().with_position(0.0, 1.0, 0.0),
196    );
197    let _ = universe.attach(directional_light_t, directional_light);
198    universe.add(directional_light_t);
199
200    // Simple input-driven camera.
201    let input = universe
202        .world
203        .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
204
205    let cam_t = universe.world.add_component(
206        engine::ecs::component::TransformComponent::new().with_position(0.0, 4.0, 10.0),
207    );
208    let cam = universe.world.add_component(
209        engine::ecs::component::Camera3DComponent::new()
210            // The default (150) clips background dressing (city + clouds).
211            .with_far(600.0)
212            .with_fov(70.0),
213    );
214
215    // Make the camera affect the collision system (same pattern as collision-perimeter).
216    let cam_collision = universe
217        .world
218        .add_component(engine::ecs::component::CollisionComponent::RIGGED());
219    let cam_response = universe
220        .world
221        .add_component(engine::ecs::component::KineticResponseComponent::slide());
222    let cam_shape =
223        universe
224            .world
225            .add_component(engine::ecs::component::CollisionShapeComponent::new(
226                engine::ecs::component::CollisionShape::sphere_radius(0.25),
227            ));
228
229    let input_mode = universe.world.add_component(
230        engine::ecs::component::InputTransformModeComponent::forward_z()
231            .with_roll_axis_y()
232            .with_fps_rotation(),
233    );
234
235    let _ = universe.attach(input, input_mode);
236    let _ = universe.attach(input, cam_t);
237    let _ = universe.attach(cam_t, cam);
238    let _ = universe.attach(cam_t, cam_collision);
239    let _ = universe.attach(cam_collision, cam_response);
240    let _ = universe.attach(cam_collision, cam_shape);
241
242    // Topology: I { T { C3D } } — add a small camera-attached controls hint.
243    example_util::spawn_desktop_camera_controls_hint(&mut universe, cam_t);
244    universe.add(input);
245
246    let arena_half = 30.0;
247
248    // --- Background world (occluded + lit) ---
249    // Buildings are scene dressing and should not occlude foreground cubes.
250    let bg_root = universe.world.add_component(
251        engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
252    );
253    universe.add(bg_root);
254
255    // Background ground plane (rendered in background pass) with top surface at y=0.
256    // This should not occlude foreground due to depth clear between passes.
257    let bg_ground_thickness = 0.20;
258    let bg_ground_root_t = universe
259        .world
260        .add_component(engine::ecs::component::TransformComponent::new());
261    let bg_ground_geom_t = universe.world.add_component(
262        engine::ecs::component::TransformComponent::new()
263            .with_position(0.0, -bg_ground_thickness, 0.0)
264            .with_scale(
265                arena_half * 10.0,
266                bg_ground_thickness * 2.0,
267                arena_half * 10.0,
268            ),
269    );
270    let bg_ground_r = universe
271        .world
272        .add_component(engine::ecs::component::RenderableComponent::cube());
273    let bg_ground_c = universe
274        .world
275        .add_component(engine::ecs::component::ColorComponent::rgba(
276            0.03, 0.03, 0.035, 1.0,
277        ));
278    let _ = universe.attach(bg_root, bg_ground_root_t);
279    let _ = universe.attach(bg_ground_root_t, bg_ground_geom_t);
280    let _ = universe.attach(bg_ground_geom_t, bg_ground_r);
281    let _ = universe.attach(bg_ground_r, bg_ground_c);
282
283    // Background clouds (occluded + lit) using the shared example helper.
284    // NOTE: `spawn_cloud_ring` places a ring centered at the parent transform's origin.
285    // If we attach directly to `bg_root`, the ring can end up mostly out of view.
286    // So we offset a cloud root forward along -Z and keep the radius within the camera far clip.
287    let bg_cloud_root = universe.world.add_component(
288        engine::ecs::component::TransformComponent::new().with_position(
289            0.0,
290            0.0,
291            -arena_half * 2.8,
292        ),
293    );
294    let _ = universe.attach(bg_root, bg_cloud_root);
295
296    let mut cloud_params = example_util::CloudRingParams::default();
297    cloud_params.cloud_count = 9;
298    cloud_params.radius = arena_half * 1.9;
299    cloud_params.center_y = 18.0;
300    cloud_params.puffs_per_cloud = 26;
301    cloud_params.angle_jitter = 0.35;
302    cloud_params.high_y_probability = 0.35;
303    cloud_params.high_y_multiplier = 1.35;
304    cloud_params.seed = 0xC10_71A5u32;
305    example_util::spawn_cloud_ring(&mut universe, bg_cloud_root, cloud_params);
306
307    // Foreground city: NOT background-layer.
308    let fg_city_root = universe
309        .world
310        .add_component(engine::ecs::component::TransformComponent::new());
311    universe.add(fg_city_root);
312
313    // Background city: nested under the BackgroundComponent.
314    let bg_city_root = universe
315        .world
316        .add_component(engine::ecs::component::TransformComponent::new());
317    let _ = universe.attach(bg_root, bg_city_root);
318
319    fn hash_u32(mut x: u32) -> u32 {
320        // Small, deterministic integer hash (no external RNG dependency).
321        x ^= x >> 16;
322        x = x.wrapping_mul(0x7FEB_352D);
323        x ^= x >> 15;
324        x = x.wrapping_mul(0x846C_A68B);
325        x ^= x >> 16;
326        x
327    }
328
329    fn rand01(seed: u32) -> f32 {
330        let x = hash_u32(seed);
331        (x as f32) / (u32::MAX as f32)
332    }
333
334    fn spawn_floor(
335        universe: &mut engine::Universe,
336        building_root: engine::ecs::ComponentId,
337        floor_center_y: f32,
338        footprint_x: f32,
339        footprint_z: f32,
340        floor_h: f32,
341        floor_rgb: [f32; 4],
342        building_scale: f32,
343        window_mask: &[bool],
344        window_height_coeff: f32,
345        seed: u32,
346    ) {
347        // Floor geometry.
348        // IMPORTANT: don't put non-uniform scale on the node that windows attach to,
349        // otherwise child window meshes inherit the scale and look stretched.
350        let floor_root_t = universe.world.add_component(
351            engine::ecs::component::TransformComponent::new().with_position(
352                0.0,
353                floor_center_y,
354                0.0,
355            ),
356        );
357        let floor_geom_t = universe.world.add_component(
358            engine::ecs::component::TransformComponent::new().with_scale(
359                footprint_x,
360                floor_h,
361                footprint_z,
362            ),
363        );
364        let floor_r = universe
365            .world
366            .add_component(engine::ecs::component::RenderableComponent::cube());
367        let floor_c = universe
368            .world
369            .add_component(engine::ecs::component::ColorComponent::rgba(
370                floor_rgb[0],
371                floor_rgb[1],
372                floor_rgb[2],
373                floor_rgb[3],
374            ));
375
376        let _ = universe.attach(building_root, floor_root_t);
377        let _ = universe.attach(floor_root_t, floor_geom_t);
378        let _ = universe.attach(floor_geom_t, floor_r);
379        let _ = universe.attach(floor_r, floor_c);
380
381        // Windows: a single clean row per floor.
382        // Each floor receives a Vec<bool> (window_mask) that indicates which window slots are present.
383        let slots = window_mask.len().max(1);
384        let building_scale = building_scale.max(0.01);
385        let window_height_coeff = window_height_coeff.clamp(0.05, 2.0);
386
387        // Keep window size independent from floor height; floor height is already controlled by height_scale.
388        let nominal_window = 0.32 * building_scale;
389        let window_d = 0.06 * building_scale;
390
391        // Compute per-side spacing and clamp window size to fit.
392        let margin_x = (0.65 * nominal_window).min(0.18 * footprint_x);
393        let margin_z = (0.65 * nominal_window).min(0.18 * footprint_z);
394        let available_x = (footprint_x - 2.0 * margin_x).max(nominal_window);
395        let available_z = (footprint_z - 2.0 * margin_z).max(nominal_window);
396        let step_x = available_x / (slots as f32);
397        let step_z = available_z / (slots as f32);
398
399        let window_wx = nominal_window.min(step_x * 0.85);
400        let window_wz = nominal_window.min(step_z * 0.85);
401        let window_h = (nominal_window * window_height_coeff).min(floor_h * 0.80);
402
403        let warm = 0.70 + 0.25 * rand01(seed ^ 0x1F12_6A77);
404        let window_rgb = [1.0, 0.78 * warm, 0.48 * warm, 1.0];
405
406        let mut spawn_window = |local_pos: (f32, f32, f32), local_scale: (f32, f32, f32)| {
407            let window_t = universe.world.add_component(
408                engine::ecs::component::TransformComponent::new()
409                    .with_position(local_pos.0, local_pos.1, local_pos.2)
410                    .with_scale(local_scale.0, local_scale.1, local_scale.2),
411            );
412            let window_r = universe
413                .world
414                .add_component(engine::ecs::component::RenderableComponent::cube());
415            let window_c =
416                universe
417                    .world
418                    .add_component(engine::ecs::component::ColorComponent::rgba(
419                        window_rgb[0],
420                        window_rgb[1],
421                        window_rgb[2],
422                        window_rgb[3],
423                    ));
424            let window_e = universe
425                .world
426                .add_component(engine::ecs::component::EmissiveComponent::on());
427
428            let _ = universe.attach(floor_root_t, window_t);
429            let _ = universe.attach(window_t, window_r);
430            let _ = universe.attach(window_r, window_c);
431            let _ = universe.attach(window_r, window_e);
432        };
433
434        // Place the row at the center of the floor segment.
435        let wy = 0.0;
436
437        for i in 0..slots {
438            if !window_mask[i] {
439                continue;
440            }
441            let tx = -0.5 * footprint_x + margin_x + (i as f32 + 0.5) * step_x;
442            let tz = -0.5 * footprint_z + margin_z + (i as f32 + 0.5) * step_z;
443
444            // +Z/-Z faces: windows laid out along X.
445            for z_sign in [1.0_f32, -1.0_f32] {
446                spawn_window(
447                    (tx, wy, z_sign * (footprint_z * 0.5 + window_d * 0.6)),
448                    (window_wx, window_h, window_d),
449                );
450            }
451
452            // +X/-X faces: windows laid out along Z.
453            for x_sign in [1.0_f32, -1.0_f32] {
454                spawn_window(
455                    (x_sign * (footprint_x * 0.5 + window_d * 0.6), wy, tz),
456                    (window_d, window_h, window_wz),
457                );
458            }
459        }
460    }
461
462    fn spawn_building(
463        universe: &mut engine::Universe,
464        parent: engine::ecs::ComponentId,
465        x: f32,
466        y_offset: f32,
467        z: f32,
468        floors: u32,
469        seed: u32,
470        building_scale: f32,
471        height_scale: f32,
472        total_windows_per_floor_per_building_side: u32,
473        window_height_coeff: f32,
474    ) {
475        let building_scale = building_scale.max(0.01);
476        let height_scale = height_scale.max(0.01);
477
478        let floors = floors.max(1);
479        let windows_per_side = total_windows_per_floor_per_building_side.max(1) as usize;
480
481        let footprint_x = 3.2 * building_scale;
482        let footprint_z = 3.2 * building_scale;
483        let floor_h = 1.15 * building_scale * height_scale;
484
485        let base_t = universe.world.add_component(
486            engine::ecs::component::TransformComponent::new().with_position(x, y_offset, z),
487        );
488        let _ = universe.attach(parent, base_t);
489
490        // Slight per-building tint.
491        let tint = 0.03 * (rand01(seed ^ 0xA511_E9B3) - 0.5);
492        let base_rgb = [0.08 + tint, 0.085 + tint, 0.095 + tint, 1.0];
493
494        // Probability that a given window slot exists.
495        // (False means no window cube at that slot.)
496        let window_fill_probability = 0.80_f32;
497
498        for floor_i in 0..floors {
499            let floor_seed = seed ^ floor_i.wrapping_mul(0x9E37_79B9) ^ 0xB17D_1E55;
500
501            let mut window_mask = Vec::with_capacity(windows_per_side);
502            for i in 0..windows_per_side {
503                let s = floor_seed ^ (i as u32).wrapping_mul(0x85EB_CA6B) ^ 0x243F_6A88;
504                window_mask.push(rand01(s) < window_fill_probability);
505            }
506
507            let floor_center_y = floor_h * (floor_i as f32) + floor_h * 0.5;
508            spawn_floor(
509                universe,
510                base_t,
511                floor_center_y,
512                footprint_x,
513                footprint_z,
514                floor_h,
515                base_rgb,
516                building_scale,
517                &window_mask,
518                window_height_coeff,
519                floor_seed,
520            );
521        }
522    }
523
524    fn spawn_city(
525        universe: &mut engine::Universe,
526        parent: engine::ecs::ComponentId,
527        columns: u32,
528        spacing: f32,
529        position_scale: f32,
530        building_scale: f32,
531        height_scale: f32,
532        seed_base: u32,
533        y_offset: f32,
534        total_windows_per_floor_per_building_side: u32,
535        window_height_coeff: f32,
536    ) {
537        let columns = columns.max(1);
538        let half = (columns as i32) / 2;
539
540        for gz in -half..=half {
541            for gx in -half..=half {
542                // Hole in the middle.
543                if gx == 0 && gz == 0 {
544                    continue;
545                }
546
547                let seed = seed_base
548                    ^ ((gx + half) as u32).wrapping_mul(0x9E37_79B9)
549                    ^ ((gz + half) as u32).wrapping_mul(0x85EB_CA6B)
550                    ^ 0xB17D_1E55;
551
552                let floors = 3 + (hash_u32(seed) % 10); // 3..=12 floors
553
554                // Centered around the origin.
555                // - position_scale controls how far out the city sits (positions only)
556                // - building_scale controls building footprint/window sizes
557                let x = (gx as f32) * spacing * position_scale;
558                let z = (gz as f32) * spacing * position_scale;
559
560                spawn_building(
561                    universe,
562                    parent,
563                    x,
564                    y_offset,
565                    z,
566                    floors,
567                    seed,
568                    building_scale,
569                    height_scale,
570                    total_windows_per_floor_per_building_side,
571                    window_height_coeff,
572                );
573            }
574        }
575    }
576
577    // // Foreground city: 5x5 (hole in center), scaled out to sit around the floor.
578    // spawn_city(
579    //     &mut universe,
580    //     fg_city_root,
581    //     5,
582    //     7.0,
583    //     2.0,
584    //     1.0,
585    //     1.0,
586    //     0xC170_0001u32,
587    //     3,
588    //     2,
589    //     -0.0
590    // );
591
592    // Background city: larger grid, farther out, taller buildings.
593    spawn_city(
594        &mut universe,
595        bg_city_root,
596        11,
597        3.0,
598        3.0,
599        0.45,
600        1.0,
601        0xC170_0002u32,
602        -2.0,
603        6,
604        0.75,
605    );
606
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    }
744
745    // Spawn 6 street lights around the arena.
746    // Arms point inward from each side.
747    let light_x = arena_half - 2.0;
748    for z in [-10.0, 0.0, 10.0] {
749        // Left side: street light model faces +X by default, so it points inward.
750        let left_place = universe.world.add_component(
751            engine::ecs::component::TransformComponent::new().with_position(-light_x, 0.0, z),
752        );
753        let left_model = spawn_street_light(&mut universe);
754        let _ = universe.attach(left_place, left_model);
755        universe.add(left_place);
756
757        // Right side: rotate 180° around Y so the arm points toward -X (inward).
758        let right_place = universe.world.add_component(
759            engine::ecs::component::TransformComponent::new()
760                .with_position(light_x, 0.0, z)
761                .with_rotation_euler(0.0, std::f32::consts::PI, 0.0),
762        );
763        let right_model = spawn_street_light(&mut universe);
764        let _ = universe.attach(right_place, right_model);
765        universe.add(right_place);
766    }
767
768    // Big floor.
769    {
770        let floor_half = arena_half;
771        let thickness = 0.20;
772
773        // Split transforms so scaling the floor does not scale any potential children.
774        // Top surface at y=0.
775        let floor_root_t = universe
776            .world
777            .add_component(engine::ecs::component::TransformComponent::new());
778        let floor_geom_t = universe.world.add_component(
779            engine::ecs::component::TransformComponent::new()
780                .with_position(0.0, -thickness, 0.0)
781                .with_scale(floor_half * 2.0, thickness * 2.0, floor_half * 2.0),
782        );
783        let floor_r = universe
784            .world
785            .add_component(engine::ecs::component::RenderableComponent::cube());
786        let floor_color =
787            universe
788                .world
789                .add_component(engine::ecs::component::ColorComponent::rgba(
790                    0.08, 0.08, 0.09, 1.0,
791                ));
792
793        let floor_cn = universe
794            .world
795            .add_component(engine::ecs::component::CollisionComponent::STATIC());
796        let floor_shape =
797            universe
798                .world
799                .add_component(engine::ecs::component::CollisionShapeComponent::new(
800                    engine::ecs::component::CollisionShape::cube_half_extents([
801                        floor_half, thickness, floor_half,
802                    ]),
803                ));
804
805        let _ = universe.attach(floor_root_t, floor_geom_t);
806        let _ = universe.attach(floor_geom_t, floor_r);
807        let _ = universe.attach(floor_r, floor_color);
808        let _ = universe.attach(floor_geom_t, floor_cn);
809        let _ = universe.attach(floor_cn, floor_shape);
810        universe.add(floor_root_t);
811    }
812
813    // Square boundary walls around the floor edges.
814    // Note: STATIC colliders don't need KineticResponse; they still collide with kinematic/rigged.
815    fn spawn_boundary_wall(
816        universe: &mut engine::Universe,
817        x: f32,
818        y: f32,
819        z: f32,
820        half_extents: [f32; 3],
821        color: [f32; 4],
822    ) {
823        let t = universe.world.add_component(
824            engine::ecs::component::TransformComponent::new()
825                .with_position(x, y, z)
826                .with_scale(
827                    half_extents[0] * 2.0,
828                    half_extents[1] * 2.0,
829                    half_extents[2] * 2.0,
830                ),
831        );
832        let r = universe
833            .world
834            .add_component(engine::ecs::component::RenderableComponent::cube());
835        let c = universe
836            .world
837            .add_component(engine::ecs::component::ColorComponent::rgba(
838                color[0], color[1], color[2], color[3],
839            ));
840
841        let cn = universe
842            .world
843            .add_component(engine::ecs::component::CollisionComponent::STATIC());
844        let shape =
845            universe
846                .world
847                .add_component(engine::ecs::component::CollisionShapeComponent::new(
848                    engine::ecs::component::CollisionShape::cube_half_extents(half_extents),
849                ));
850
851        let _ = universe.attach(t, r);
852        let _ = universe.attach(r, c);
853        let _ = universe.attach(t, cn);
854        let _ = universe.attach(cn, shape);
855        universe.add(t);
856    }
857
858    {
859        let wall_half_height = 2.5;
860        let wall_half_thickness = 0.30;
861        let wall_center_y = wall_half_height; // bottom at y=0
862        let wall_half_len = arena_half;
863
864        // Subtle visible walls.
865        let wall_color = [0.10, 0.10, 0.12, 0.30];
866
867        // +Z / -Z
868        spawn_boundary_wall(
869            &mut universe,
870            0.0,
871            wall_center_y,
872            arena_half,
873            [wall_half_len, wall_half_height, wall_half_thickness],
874            wall_color,
875        );
876        spawn_boundary_wall(
877            &mut universe,
878            0.0,
879            wall_center_y,
880            -arena_half,
881            [wall_half_len, wall_half_height, wall_half_thickness],
882            wall_color,
883        );
884
885        // +X / -X
886        spawn_boundary_wall(
887            &mut universe,
888            arena_half,
889            wall_center_y,
890            0.0,
891            [wall_half_thickness, wall_half_height, wall_half_len],
892            wall_color,
893        );
894        spawn_boundary_wall(
895            &mut universe,
896            -arena_half,
897            wall_center_y,
898            0.0,
899            [wall_half_thickness, wall_half_height, wall_half_len],
900            wall_color,
901        );
902    }
903
904    fn spawn_falling_cube(
905        universe: &mut engine::Universe,
906        parent: engine::ecs::ComponentId,
907        x: f32,
908        y: f32,
909        z: f32,
910        s: f32,
911        r: f32,
912        g: f32,
913        b: f32,
914    ) {
915        let t = universe.world.add_component(
916            engine::ecs::component::TransformComponent::new()
917                .with_position(x, y, z)
918                .with_scale(s, s, s),
919        );
920
921        let renderable = universe
922            .world
923            .add_component(engine::ecs::component::RenderableComponent::cube());
924        let color = universe
925            .world
926            .add_component(engine::ecs::component::ColorComponent::rgba(r, g, b, 1.0));
927
928        let cn = universe
929            .world
930            .add_component(engine::ecs::component::CollisionComponent::KINEMATIC());
931
932        let response = universe.world.add_component({
933            let mut r = engine::ecs::component::KineticResponseComponent::push()
934                .with_push_strength(3.0)
935                .with_friction_y(18.0);
936            // Allow higher fall speeds so gravity coefficients are visible.
937            r.max_speed = 80.0;
938            r
939        });
940
941        let shape =
942            universe
943                .world
944                .add_component(engine::ecs::component::CollisionShapeComponent::new(
945                    engine::ecs::component::CollisionShape::cube_half_extents([
946                        0.5 * s,
947                        0.5 * s,
948                        0.5 * s,
949                    ]),
950                ));
951
952        let _ = universe.attach(t, renderable);
953        let _ = universe.attach(renderable, color);
954
955        let _ = universe.attach(t, cn);
956        let _ = universe.attach(cn, response);
957        let _ = universe.attach(cn, shape);
958
959        let _ = universe.attach(parent, t);
960    }
961
962    // Three gravity fields, each with 20 cubes.
963    let field_low = universe
964        .world
965        .add_component(engine::ecs::component::GravityComponent::new().with_coefficient(0.125));
966    let field_mid = universe
967        .world
968        .add_component(engine::ecs::component::GravityComponent::new().with_coefficient(0.5));
969    let field_high = universe
970        .world
971        .add_component(engine::ecs::component::GravityComponent::new().with_coefficient(1.0));
972
973    universe.add(field_low);
974    universe.add(field_mid);
975    universe.add(field_high);
976
977    fn spawn_field(
978        universe: &mut engine::Universe,
979        field: engine::ecs::ComponentId,
980        base_x: f32,
981        base_z: f32,
982        cube_color: [f32; 3],
983    ) {
984        let spacing = 0.7;
985        let start_x = base_x - 2.0 * spacing;
986        let start_z = base_z - 1.5 * spacing;
987
988        for i in 0..20 {
989            let ix = (i % 5) as f32;
990            let iz = (i / 5) as f32;
991
992            let x = start_x + ix * spacing;
993            let z = start_z + iz * spacing;
994            let y = 2.0 + iz * 0.6 + ix * 0.1;
995
996            spawn_falling_cube(
997                universe,
998                field,
999                x,
1000                y,
1001                z,
1002                0.5,
1003                cube_color[0],
1004                cube_color[1],
1005                cube_color[2],
1006            );
1007        }
1008
1009        // Visual marker for the field center.
1010        let marker_t = universe.world.add_component(
1011            engine::ecs::component::TransformComponent::new()
1012                .with_position(base_x, 0.1, base_z)
1013                .with_scale(0.3, 0.02, 0.3),
1014        );
1015        let marker_r = universe
1016            .world
1017            .add_component(engine::ecs::component::RenderableComponent::cube());
1018        let marker_c = universe
1019            .world
1020            .add_component(engine::ecs::component::ColorComponent::rgba(
1021                cube_color[0],
1022                cube_color[1],
1023                cube_color[2],
1024                1.0,
1025            ));
1026        let _ = universe.attach(marker_t, marker_r);
1027        let _ = universe.attach(marker_r, marker_c);
1028        universe.add(marker_t);
1029    }
1030
1031    spawn_field(&mut universe, field_low, -8.0, 0.0, [0.3, 0.6, 1.0]);
1032    spawn_field(&mut universe, field_mid, 0.0, 0.0, [0.4, 1.0, 0.4]);
1033    spawn_field(&mut universe, field_high, 8.0, 0.0, [1.0, 0.5, 0.2]);
1034
1035    // Group-scoped collision behaviors.
1036    // - Low + High fields: cubes turn white after colliding with another cube.
1037    // - Mid field: cubes lose gravity after colliding with another cube.
1038    universe.add_signal_handler(
1039        engine::ecs::SignalKind::CollisionStarted,
1040        field_low,
1041        on_collision_turn_white,
1042    );
1043    universe.add_signal_handler(
1044        engine::ecs::SignalKind::CollisionStarted,
1045        field_high,
1046        on_collision_turn_white,
1047    );
1048    universe.add_signal_handler(
1049        engine::ecs::SignalKind::CollisionStarted,
1050        field_mid,
1051        on_collision_freeze_gravity,
1052    );
1053
1054    universe.systems.process_commands(
1055        &mut universe.world,
1056        &mut universe.visuals,
1057        &mut universe.render_assets,
1058        &mut universe.command_queue,
1059    );
1060
1061    universe.enable_repl();
1062    engine::Windowing::run_app(universe).expect("Windowing failed");
1063}
Source

pub fn off() -> Self

Source

pub fn with_coefficient(self, coefficient: f32) -> Self

Examples found in repository?
examples/collision-perimeter.rs (line 28)
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    let bg_color = universe
14        .world
15        .add_component(engine::ecs::component::BackgroundColorComponent::new());
16    let bg_color_c = universe
17        .world
18        .add_component(engine::ecs::component::ColorComponent::rgba(
19            0.1, 0.02, 0.05, 1.0,
20        ));
21    let _ = universe.world.add_child(bg_color, bg_color_c);
22    universe.add(bg_color);
23
24    // Gravity field for the pushable cubes.
25    // Any KineticResponseComponent nested under this subtree will have gravity applied.
26    let gravity_field = universe
27        .world
28        .add_component(engine::ecs::component::GravityComponent::new().with_coefficient(0.5));
29    universe.add(gravity_field);
30
31    // Input-driven camera rig.
32    // Topology: I { T { C3D }  CN{Rigged { Sphere }} }
33    let input = universe
34        .world
35        .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
36
37    let rig_transform = universe.world.add_component(
38        engine::ecs::component::TransformComponent::new().with_position(0.0, 0.75, 0.0),
39    );
40    let camera3d = universe
41        .world
42        .add_component(engine::ecs::component::Camera3DComponent::new());
43    let input_mode = universe.world.add_component(
44        engine::ecs::component::InputTransformModeComponent::forward_z()
45            .with_roll_axis_y()
46            .with_fps_rotation(),
47    );
48
49    let rig_collision = universe
50        .world
51        .add_component(engine::ecs::component::CollisionComponent::RIGGED());
52
53    // Opt-in to default kinematic-vs-static collision response.
54    // Policy note: collisions still emit signals regardless; response only runs for entities
55    // that explicitly add a KineticResponseComponent.
56    let rig_response = universe
57        .world
58        .add_component(engine::ecs::component::KineticResponseComponent::slide());
59    let rig_shape =
60        universe
61            .world
62            .add_component(engine::ecs::component::CollisionShapeComponent::new(
63                engine::ecs::component::CollisionShape::sphere_radius(0.25),
64            ));
65
66    let _ = universe.attach(input, input_mode);
67    let _ = universe.attach(input, rig_transform);
68    let _ = universe.attach(rig_transform, camera3d);
69
70    // Click-to-pick: treat this camera rig as a pointer source.
71    let pointer = universe
72        .world
73        .add_component(engine::ecs::component::PointerComponent::new());
74    let _ = universe.attach(camera3d, pointer);
75
76    // Topology: I { T { C3D } } — add a small camera-attached controls hint.
77    example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
78
79    let _ = universe.attach(rig_transform, rig_collision);
80    let _ = universe.attach(rig_collision, rig_response);
81    let _ = universe.attach(rig_collision, rig_shape);
82
83    universe.add(input);
84
85    // Lights so we can see non-emissive meshes and verify attenuation/direction.
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    }
126
127    // Perimeter of cubes: 16 per side, 90deg turns.
128    const N: i32 = 16;
129    let spacing = 1.0;
130    let half = (N as f32 - 1.0) * spacing * 0.5;
131
132    // Place 4 very distinct colored lights near corners plus a white overhead fill.
133    // (High-ish saturation makes it easy to tell which light is contributing.)
134    spawn_light(&mut universe, -half, 2.8, -half, 1.0, 0.1, 0.1, 2.5, 14.0); // red
135    spawn_light(&mut universe, half, 2.8, -half, 0.1, 1.0, 0.1, 2.5, 14.0); // green
136    spawn_light(&mut universe, half, 2.8, half, 0.1, 0.2, 1.0, 2.5, 14.0); // blue
137    spawn_light(&mut universe, -half, 2.8, half, 1.0, 0.2, 1.0, 2.5, 14.0); // magenta-ish
138    spawn_light(&mut universe, 0.0, 5.5, 0.0, 1.0, 1.0, 1.0, 0.7, 40.0); // white fill
139
140    fn spawn_wall_cube(universe: &mut engine::Universe, x: f32, y: f32, z: f32) {
141        let t = universe.world.add_component(
142            engine::ecs::component::TransformComponent::new().with_position(x, y, z),
143        );
144        let r = universe
145            .world
146            .add_component(engine::ecs::component::RenderableComponent::cube());
147        let cn = universe
148            .world
149            .add_component(engine::ecs::component::CollisionComponent::STATIC());
150        let c = universe
151            .world
152            .add_component(engine::ecs::component::ColorComponent::rgba(
153                0.9, 0.2, 0.2, 1.0,
154            ));
155
156        let _ = universe.attach(t, r);
157        let _ = universe.attach(t, cn);
158        let _ = universe.attach(r, c);
159
160        universe.add(t);
161    }
162
163    fn spawn_cube(
164        universe: &mut engine::Universe,
165        x: f32,
166        y: f32,
167        z: f32,
168        sx: f32,
169        sy: f32,
170        sz: f32,
171        r: f32,
172        g: f32,
173        b: f32,
174    ) {
175        let t = universe.world.add_component(
176            engine::ecs::component::TransformComponent::new()
177                .with_position(x, y, z)
178                .with_scale(sx, sy, sz),
179        );
180        let renderable = universe
181            .world
182            .add_component(engine::ecs::component::RenderableComponent::cube());
183        let color = universe
184            .world
185            .add_component(engine::ecs::component::ColorComponent::rgba(r, g, b, 1.0));
186
187        let _ = universe.attach(t, renderable);
188        let _ = universe.attach(renderable, color);
189
190        universe.add(t);
191    }
192
193    fn spawn_pushable_cube(
194        universe: &mut engine::Universe,
195        parent: engine::ecs::ComponentId,
196        x: f32,
197        y: f32,
198        z: f32,
199        s: f32,
200        r: f32,
201        g: f32,
202        b: f32,
203    ) {
204        let t = universe.world.add_component(
205            engine::ecs::component::TransformComponent::new()
206                .with_position(x, y, z)
207                .with_scale(s, s, s),
208        );
209        let renderable = universe
210            .world
211            .add_component(engine::ecs::component::RenderableComponent::cube());
212        let color = universe
213            .world
214            .add_component(engine::ecs::component::ColorComponent::rgba(r, g, b, 1.0));
215
216        let cn = universe
217            .world
218            .add_component(engine::ecs::component::CollisionComponent::KINEMATIC());
219        let response = universe.world.add_component(
220            engine::ecs::component::KineticResponseComponent::push()
221                .with_push_strength(4.0)
222                .with_friction_y(10.0),
223        );
224        let shape =
225            universe
226                .world
227                .add_component(engine::ecs::component::CollisionShapeComponent::new(
228                    engine::ecs::component::CollisionShape::cube_half_extents([
229                        0.5 * s,
230                        0.5 * s,
231                        0.5 * s,
232                    ]),
233                ));
234
235        let _ = universe.attach(t, renderable);
236        let _ = universe.attach(renderable, color);
237
238        let _ = universe.attach(t, cn);
239        let _ = universe.attach(cn, response);
240        let _ = universe.attach(cn, shape);
241
242        let _ = universe.attach(parent, t);
243    }
244
245    fn spawn_invisible_static_wall(
246        universe: &mut engine::Universe,
247        x: f32,
248        y: f32,
249        z: f32,
250        half_extents: [f32; 3],
251    ) {
252        let t = universe.world.add_component(
253            engine::ecs::component::TransformComponent::new().with_position(x, y, z),
254        );
255
256        let cn = universe
257            .world
258            .add_component(engine::ecs::component::CollisionComponent::STATIC());
259        let shape =
260            universe
261                .world
262                .add_component(engine::ecs::component::CollisionShapeComponent::new(
263                    engine::ecs::component::CollisionShape::cube_half_extents(half_extents),
264                ));
265
266        // Requested: attach kinematic_response as well (even though STATIC colliders
267        // are not responders).
268        let response = universe
269            .world
270            .add_component(engine::ecs::component::KineticResponseComponent::slide());
271
272        let _ = universe.attach(t, cn);
273        let _ = universe.attach(cn, shape);
274        let _ = universe.attach(cn, response);
275        universe.add(t);
276    }
277
278    let y = 0.5;
279
280    // Static ground plane (thin cube). Top surface at y=0.
281    {
282        let ground_half = half + 2.0;
283        let thickness = 0.20;
284
285        // Split transforms so scaling the ground does not scale any potential children.
286        // Top surface at y=0.
287        let ground_root_t = universe
288            .world
289            .add_component(engine::ecs::component::TransformComponent::new());
290        let ground_geom_t = universe.world.add_component(
291            engine::ecs::component::TransformComponent::new()
292                .with_position(0.0, -thickness, 0.0)
293                .with_scale(ground_half * 2.0, thickness * 2.0, ground_half * 2.0),
294        );
295        let ground_r = universe
296            .world
297            .add_component(engine::ecs::component::RenderableComponent::cube());
298        let ground_c = universe
299            .world
300            .add_component(engine::ecs::component::ColorComponent::rgba(
301                0.08, 0.08, 0.09, 1.0,
302            ));
303        let ground_cn = universe
304            .world
305            .add_component(engine::ecs::component::CollisionComponent::STATIC());
306        let ground_shape =
307            universe
308                .world
309                .add_component(engine::ecs::component::CollisionShapeComponent::new(
310                    engine::ecs::component::CollisionShape::cube_half_extents([
311                        ground_half,
312                        thickness,
313                        ground_half,
314                    ]),
315                ));
316
317        let _ = universe.attach(ground_root_t, ground_geom_t);
318        let _ = universe.attach(ground_geom_t, ground_r);
319        let _ = universe.attach(ground_r, ground_c);
320        let _ = universe.attach(ground_geom_t, ground_cn);
321        let _ = universe.attach(ground_cn, ground_shape);
322        universe.add(ground_root_t);
323    }
324
325    // Bottom edge (left -> right)
326    for i in 0..N {
327        let x = -half + (i as f32) * spacing;
328        let z = -half;
329        spawn_wall_cube(&mut universe, x, y, z);
330    }
331
332    // Right edge (bottom -> top), skip first corner
333    for i in 1..N {
334        let x = half;
335        let z = -half + (i as f32) * spacing;
336        spawn_wall_cube(&mut universe, x, y, z);
337    }
338
339    // Top edge (right -> left), skip first corner
340    for i in 1..N {
341        let x = half - (i as f32) * spacing;
342        let z = half;
343        spawn_wall_cube(&mut universe, x, y, z);
344    }
345
346    // Left edge (top -> bottom), skip first and last corners
347    for i in 1..(N - 1) {
348        let x = -half;
349        let z = half - (i as f32) * spacing;
350        spawn_wall_cube(&mut universe, x, y, z);
351    }
352
353    // A few larger cubes outside the perimeter (visual landmarks + lighting test surfaces).
354    let outer = half + 3.0;
355    spawn_cube(
356        &mut universe,
357        0.0,
358        0.75,
359        -outer,
360        3.0,
361        1.5,
362        1.0,
363        0.15,
364        0.15,
365        0.18,
366    );
367    spawn_cube(
368        &mut universe,
369        0.0,
370        0.75,
371        outer,
372        3.0,
373        1.5,
374        1.0,
375        0.15,
376        0.15,
377        0.18,
378    );
379    spawn_cube(
380        &mut universe,
381        -outer,
382        0.75,
383        0.0,
384        1.0,
385        1.5,
386        3.0,
387        0.15,
388        0.15,
389        0.18,
390    );
391    spawn_cube(
392        &mut universe,
393        outer,
394        0.75,
395        0.0,
396        1.0,
397        1.5,
398        3.0,
399        0.15,
400        0.15,
401        0.18,
402    );
403
404    // Outer containment walls (invisible): keep runaway cubes near the scene.
405    // Collision shapes are in world units; transform scale is irrelevant for collision.
406    {
407        let wall_center_y = 6.0; // bottom at y=0, top at y=12
408        let wall_half_height = 6.0;
409        let wall_half_thickness = 0.25;
410        let wall_offset = half + 6.0;
411        let wall_half_len = wall_offset + 2.0;
412
413        // +Z / -Z walls
414        spawn_invisible_static_wall(
415            &mut universe,
416            0.0,
417            wall_center_y,
418            wall_offset,
419            [wall_half_len, wall_half_height, wall_half_thickness],
420        );
421        spawn_invisible_static_wall(
422            &mut universe,
423            0.0,
424            wall_center_y,
425            -wall_offset,
426            [wall_half_len, wall_half_height, wall_half_thickness],
427        );
428
429        // +X / -X walls
430        spawn_invisible_static_wall(
431            &mut universe,
432            wall_offset,
433            wall_center_y,
434            0.0,
435            [wall_half_thickness, wall_half_height, wall_half_len],
436        );
437        spawn_invisible_static_wall(
438            &mut universe,
439            -wall_offset,
440            wall_center_y,
441            0.0,
442            [wall_half_thickness, wall_half_height, wall_half_len],
443        );
444
445        // Roof
446        spawn_invisible_static_wall(
447            &mut universe,
448            0.0,
449            wall_center_y + wall_half_height + wall_half_thickness,
450            0.0,
451            [wall_half_len, wall_half_thickness, wall_half_len],
452        );
453    }
454
455    // Many small cubes inside the perimeter.
456    // Deterministic pseudo-random distribution so the scene is stable.
457    let mut seed: u32 = 0xC0111510;
458    let mut rng01 = || {
459        seed ^= seed << 13;
460        seed ^= seed >> 17;
461        seed ^= seed << 5;
462        (seed as f32) / (u32::MAX as f32)
463    };
464
465    // 3x more cubes, with three distinct size bands.
466    for band in 0..3 {
467        for _ in 0..60 {
468            let s = match band {
469                0 => 0.12 + rng01() * 0.20, // small
470                1 => 0.35 + rng01() * 0.35, // medium
471                _ => 0.75 + rng01() * 0.55, // large
472            };
473
474            let bound = (half - (1.0 + s)).max(0.5);
475            let x = (rng01() * 2.0 - 1.0) * bound;
476            let z = (rng01() * 2.0 - 1.0) * bound;
477            let y = 0.5 * s;
478
479            // Slightly varied colors to show multiple light contributions.
480            let cr = 0.25 + rng01() * 0.6;
481            let cg = 0.25 + rng01() * 0.6;
482            let cb = 0.25 + rng01() * 0.6;
483
484            spawn_pushable_cube(&mut universe, gravity_field, x, y, z, s, cr, cg, cb);
485        }
486    }
487
488    // Process init-time registrations.
489    universe.systems.process_commands(
490        &mut universe.world,
491        &mut universe.visuals,
492        &mut universe.render_assets,
493        &mut universe.command_queue,
494    );
495
496    engine::Windowing::run_app(universe).expect("Windowing failed");
497}
More examples
Hide additional examples
examples/gravity-fields.rs (line 965)
170fn main() {
171    mittens_engine::example_support::ensure_model_assets();
172    utils::logger::init();
173
174    let world = engine::ecs::World::default();
175    let mut universe = engine::Universe::new(world);
176
177    let bg_color = universe
178        .world
179        .add_component(engine::ecs::component::BackgroundColorComponent::new());
180    let bg_color_c = universe
181        .world
182        .add_component(engine::ecs::component::ColorComponent::rgba(
183            0.06, 0.04, 0.07, 1.0,
184        ));
185    let _ = universe.world.add_child(bg_color, bg_color_c);
186    universe.add(bg_color);
187
188    // overhead directional light
189    let directional_light = universe.world.add_component(
190        engine::ecs::component::DirectionalLightComponent::new()
191            .with_color(0.16, 0.14, 0.12)
192            .with_intensity(0.7),
193    );
194    let directional_light_t = universe.world.add_component(
195        engine::ecs::component::TransformComponent::new().with_position(0.0, 1.0, 0.0),
196    );
197    let _ = universe.attach(directional_light_t, directional_light);
198    universe.add(directional_light_t);
199
200    // Simple input-driven camera.
201    let input = universe
202        .world
203        .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
204
205    let cam_t = universe.world.add_component(
206        engine::ecs::component::TransformComponent::new().with_position(0.0, 4.0, 10.0),
207    );
208    let cam = universe.world.add_component(
209        engine::ecs::component::Camera3DComponent::new()
210            // The default (150) clips background dressing (city + clouds).
211            .with_far(600.0)
212            .with_fov(70.0),
213    );
214
215    // Make the camera affect the collision system (same pattern as collision-perimeter).
216    let cam_collision = universe
217        .world
218        .add_component(engine::ecs::component::CollisionComponent::RIGGED());
219    let cam_response = universe
220        .world
221        .add_component(engine::ecs::component::KineticResponseComponent::slide());
222    let cam_shape =
223        universe
224            .world
225            .add_component(engine::ecs::component::CollisionShapeComponent::new(
226                engine::ecs::component::CollisionShape::sphere_radius(0.25),
227            ));
228
229    let input_mode = universe.world.add_component(
230        engine::ecs::component::InputTransformModeComponent::forward_z()
231            .with_roll_axis_y()
232            .with_fps_rotation(),
233    );
234
235    let _ = universe.attach(input, input_mode);
236    let _ = universe.attach(input, cam_t);
237    let _ = universe.attach(cam_t, cam);
238    let _ = universe.attach(cam_t, cam_collision);
239    let _ = universe.attach(cam_collision, cam_response);
240    let _ = universe.attach(cam_collision, cam_shape);
241
242    // Topology: I { T { C3D } } — add a small camera-attached controls hint.
243    example_util::spawn_desktop_camera_controls_hint(&mut universe, cam_t);
244    universe.add(input);
245
246    let arena_half = 30.0;
247
248    // --- Background world (occluded + lit) ---
249    // Buildings are scene dressing and should not occlude foreground cubes.
250    let bg_root = universe.world.add_component(
251        engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
252    );
253    universe.add(bg_root);
254
255    // Background ground plane (rendered in background pass) with top surface at y=0.
256    // This should not occlude foreground due to depth clear between passes.
257    let bg_ground_thickness = 0.20;
258    let bg_ground_root_t = universe
259        .world
260        .add_component(engine::ecs::component::TransformComponent::new());
261    let bg_ground_geom_t = universe.world.add_component(
262        engine::ecs::component::TransformComponent::new()
263            .with_position(0.0, -bg_ground_thickness, 0.0)
264            .with_scale(
265                arena_half * 10.0,
266                bg_ground_thickness * 2.0,
267                arena_half * 10.0,
268            ),
269    );
270    let bg_ground_r = universe
271        .world
272        .add_component(engine::ecs::component::RenderableComponent::cube());
273    let bg_ground_c = universe
274        .world
275        .add_component(engine::ecs::component::ColorComponent::rgba(
276            0.03, 0.03, 0.035, 1.0,
277        ));
278    let _ = universe.attach(bg_root, bg_ground_root_t);
279    let _ = universe.attach(bg_ground_root_t, bg_ground_geom_t);
280    let _ = universe.attach(bg_ground_geom_t, bg_ground_r);
281    let _ = universe.attach(bg_ground_r, bg_ground_c);
282
283    // Background clouds (occluded + lit) using the shared example helper.
284    // NOTE: `spawn_cloud_ring` places a ring centered at the parent transform's origin.
285    // If we attach directly to `bg_root`, the ring can end up mostly out of view.
286    // So we offset a cloud root forward along -Z and keep the radius within the camera far clip.
287    let bg_cloud_root = universe.world.add_component(
288        engine::ecs::component::TransformComponent::new().with_position(
289            0.0,
290            0.0,
291            -arena_half * 2.8,
292        ),
293    );
294    let _ = universe.attach(bg_root, bg_cloud_root);
295
296    let mut cloud_params = example_util::CloudRingParams::default();
297    cloud_params.cloud_count = 9;
298    cloud_params.radius = arena_half * 1.9;
299    cloud_params.center_y = 18.0;
300    cloud_params.puffs_per_cloud = 26;
301    cloud_params.angle_jitter = 0.35;
302    cloud_params.high_y_probability = 0.35;
303    cloud_params.high_y_multiplier = 1.35;
304    cloud_params.seed = 0xC10_71A5u32;
305    example_util::spawn_cloud_ring(&mut universe, bg_cloud_root, cloud_params);
306
307    // Foreground city: NOT background-layer.
308    let fg_city_root = universe
309        .world
310        .add_component(engine::ecs::component::TransformComponent::new());
311    universe.add(fg_city_root);
312
313    // Background city: nested under the BackgroundComponent.
314    let bg_city_root = universe
315        .world
316        .add_component(engine::ecs::component::TransformComponent::new());
317    let _ = universe.attach(bg_root, bg_city_root);
318
319    fn hash_u32(mut x: u32) -> u32 {
320        // Small, deterministic integer hash (no external RNG dependency).
321        x ^= x >> 16;
322        x = x.wrapping_mul(0x7FEB_352D);
323        x ^= x >> 15;
324        x = x.wrapping_mul(0x846C_A68B);
325        x ^= x >> 16;
326        x
327    }
328
329    fn rand01(seed: u32) -> f32 {
330        let x = hash_u32(seed);
331        (x as f32) / (u32::MAX as f32)
332    }
333
334    fn spawn_floor(
335        universe: &mut engine::Universe,
336        building_root: engine::ecs::ComponentId,
337        floor_center_y: f32,
338        footprint_x: f32,
339        footprint_z: f32,
340        floor_h: f32,
341        floor_rgb: [f32; 4],
342        building_scale: f32,
343        window_mask: &[bool],
344        window_height_coeff: f32,
345        seed: u32,
346    ) {
347        // Floor geometry.
348        // IMPORTANT: don't put non-uniform scale on the node that windows attach to,
349        // otherwise child window meshes inherit the scale and look stretched.
350        let floor_root_t = universe.world.add_component(
351            engine::ecs::component::TransformComponent::new().with_position(
352                0.0,
353                floor_center_y,
354                0.0,
355            ),
356        );
357        let floor_geom_t = universe.world.add_component(
358            engine::ecs::component::TransformComponent::new().with_scale(
359                footprint_x,
360                floor_h,
361                footprint_z,
362            ),
363        );
364        let floor_r = universe
365            .world
366            .add_component(engine::ecs::component::RenderableComponent::cube());
367        let floor_c = universe
368            .world
369            .add_component(engine::ecs::component::ColorComponent::rgba(
370                floor_rgb[0],
371                floor_rgb[1],
372                floor_rgb[2],
373                floor_rgb[3],
374            ));
375
376        let _ = universe.attach(building_root, floor_root_t);
377        let _ = universe.attach(floor_root_t, floor_geom_t);
378        let _ = universe.attach(floor_geom_t, floor_r);
379        let _ = universe.attach(floor_r, floor_c);
380
381        // Windows: a single clean row per floor.
382        // Each floor receives a Vec<bool> (window_mask) that indicates which window slots are present.
383        let slots = window_mask.len().max(1);
384        let building_scale = building_scale.max(0.01);
385        let window_height_coeff = window_height_coeff.clamp(0.05, 2.0);
386
387        // Keep window size independent from floor height; floor height is already controlled by height_scale.
388        let nominal_window = 0.32 * building_scale;
389        let window_d = 0.06 * building_scale;
390
391        // Compute per-side spacing and clamp window size to fit.
392        let margin_x = (0.65 * nominal_window).min(0.18 * footprint_x);
393        let margin_z = (0.65 * nominal_window).min(0.18 * footprint_z);
394        let available_x = (footprint_x - 2.0 * margin_x).max(nominal_window);
395        let available_z = (footprint_z - 2.0 * margin_z).max(nominal_window);
396        let step_x = available_x / (slots as f32);
397        let step_z = available_z / (slots as f32);
398
399        let window_wx = nominal_window.min(step_x * 0.85);
400        let window_wz = nominal_window.min(step_z * 0.85);
401        let window_h = (nominal_window * window_height_coeff).min(floor_h * 0.80);
402
403        let warm = 0.70 + 0.25 * rand01(seed ^ 0x1F12_6A77);
404        let window_rgb = [1.0, 0.78 * warm, 0.48 * warm, 1.0];
405
406        let mut spawn_window = |local_pos: (f32, f32, f32), local_scale: (f32, f32, f32)| {
407            let window_t = universe.world.add_component(
408                engine::ecs::component::TransformComponent::new()
409                    .with_position(local_pos.0, local_pos.1, local_pos.2)
410                    .with_scale(local_scale.0, local_scale.1, local_scale.2),
411            );
412            let window_r = universe
413                .world
414                .add_component(engine::ecs::component::RenderableComponent::cube());
415            let window_c =
416                universe
417                    .world
418                    .add_component(engine::ecs::component::ColorComponent::rgba(
419                        window_rgb[0],
420                        window_rgb[1],
421                        window_rgb[2],
422                        window_rgb[3],
423                    ));
424            let window_e = universe
425                .world
426                .add_component(engine::ecs::component::EmissiveComponent::on());
427
428            let _ = universe.attach(floor_root_t, window_t);
429            let _ = universe.attach(window_t, window_r);
430            let _ = universe.attach(window_r, window_c);
431            let _ = universe.attach(window_r, window_e);
432        };
433
434        // Place the row at the center of the floor segment.
435        let wy = 0.0;
436
437        for i in 0..slots {
438            if !window_mask[i] {
439                continue;
440            }
441            let tx = -0.5 * footprint_x + margin_x + (i as f32 + 0.5) * step_x;
442            let tz = -0.5 * footprint_z + margin_z + (i as f32 + 0.5) * step_z;
443
444            // +Z/-Z faces: windows laid out along X.
445            for z_sign in [1.0_f32, -1.0_f32] {
446                spawn_window(
447                    (tx, wy, z_sign * (footprint_z * 0.5 + window_d * 0.6)),
448                    (window_wx, window_h, window_d),
449                );
450            }
451
452            // +X/-X faces: windows laid out along Z.
453            for x_sign in [1.0_f32, -1.0_f32] {
454                spawn_window(
455                    (x_sign * (footprint_x * 0.5 + window_d * 0.6), wy, tz),
456                    (window_d, window_h, window_wz),
457                );
458            }
459        }
460    }
461
462    fn spawn_building(
463        universe: &mut engine::Universe,
464        parent: engine::ecs::ComponentId,
465        x: f32,
466        y_offset: f32,
467        z: f32,
468        floors: u32,
469        seed: u32,
470        building_scale: f32,
471        height_scale: f32,
472        total_windows_per_floor_per_building_side: u32,
473        window_height_coeff: f32,
474    ) {
475        let building_scale = building_scale.max(0.01);
476        let height_scale = height_scale.max(0.01);
477
478        let floors = floors.max(1);
479        let windows_per_side = total_windows_per_floor_per_building_side.max(1) as usize;
480
481        let footprint_x = 3.2 * building_scale;
482        let footprint_z = 3.2 * building_scale;
483        let floor_h = 1.15 * building_scale * height_scale;
484
485        let base_t = universe.world.add_component(
486            engine::ecs::component::TransformComponent::new().with_position(x, y_offset, z),
487        );
488        let _ = universe.attach(parent, base_t);
489
490        // Slight per-building tint.
491        let tint = 0.03 * (rand01(seed ^ 0xA511_E9B3) - 0.5);
492        let base_rgb = [0.08 + tint, 0.085 + tint, 0.095 + tint, 1.0];
493
494        // Probability that a given window slot exists.
495        // (False means no window cube at that slot.)
496        let window_fill_probability = 0.80_f32;
497
498        for floor_i in 0..floors {
499            let floor_seed = seed ^ floor_i.wrapping_mul(0x9E37_79B9) ^ 0xB17D_1E55;
500
501            let mut window_mask = Vec::with_capacity(windows_per_side);
502            for i in 0..windows_per_side {
503                let s = floor_seed ^ (i as u32).wrapping_mul(0x85EB_CA6B) ^ 0x243F_6A88;
504                window_mask.push(rand01(s) < window_fill_probability);
505            }
506
507            let floor_center_y = floor_h * (floor_i as f32) + floor_h * 0.5;
508            spawn_floor(
509                universe,
510                base_t,
511                floor_center_y,
512                footprint_x,
513                footprint_z,
514                floor_h,
515                base_rgb,
516                building_scale,
517                &window_mask,
518                window_height_coeff,
519                floor_seed,
520            );
521        }
522    }
523
524    fn spawn_city(
525        universe: &mut engine::Universe,
526        parent: engine::ecs::ComponentId,
527        columns: u32,
528        spacing: f32,
529        position_scale: f32,
530        building_scale: f32,
531        height_scale: f32,
532        seed_base: u32,
533        y_offset: f32,
534        total_windows_per_floor_per_building_side: u32,
535        window_height_coeff: f32,
536    ) {
537        let columns = columns.max(1);
538        let half = (columns as i32) / 2;
539
540        for gz in -half..=half {
541            for gx in -half..=half {
542                // Hole in the middle.
543                if gx == 0 && gz == 0 {
544                    continue;
545                }
546
547                let seed = seed_base
548                    ^ ((gx + half) as u32).wrapping_mul(0x9E37_79B9)
549                    ^ ((gz + half) as u32).wrapping_mul(0x85EB_CA6B)
550                    ^ 0xB17D_1E55;
551
552                let floors = 3 + (hash_u32(seed) % 10); // 3..=12 floors
553
554                // Centered around the origin.
555                // - position_scale controls how far out the city sits (positions only)
556                // - building_scale controls building footprint/window sizes
557                let x = (gx as f32) * spacing * position_scale;
558                let z = (gz as f32) * spacing * position_scale;
559
560                spawn_building(
561                    universe,
562                    parent,
563                    x,
564                    y_offset,
565                    z,
566                    floors,
567                    seed,
568                    building_scale,
569                    height_scale,
570                    total_windows_per_floor_per_building_side,
571                    window_height_coeff,
572                );
573            }
574        }
575    }
576
577    // // Foreground city: 5x5 (hole in center), scaled out to sit around the floor.
578    // spawn_city(
579    //     &mut universe,
580    //     fg_city_root,
581    //     5,
582    //     7.0,
583    //     2.0,
584    //     1.0,
585    //     1.0,
586    //     0xC170_0001u32,
587    //     3,
588    //     2,
589    //     -0.0
590    // );
591
592    // Background city: larger grid, farther out, taller buildings.
593    spawn_city(
594        &mut universe,
595        bg_city_root,
596        11,
597        3.0,
598        3.0,
599        0.45,
600        1.0,
601        0xC170_0002u32,
602        -2.0,
603        6,
604        0.75,
605    );
606
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    }
744
745    // Spawn 6 street lights around the arena.
746    // Arms point inward from each side.
747    let light_x = arena_half - 2.0;
748    for z in [-10.0, 0.0, 10.0] {
749        // Left side: street light model faces +X by default, so it points inward.
750        let left_place = universe.world.add_component(
751            engine::ecs::component::TransformComponent::new().with_position(-light_x, 0.0, z),
752        );
753        let left_model = spawn_street_light(&mut universe);
754        let _ = universe.attach(left_place, left_model);
755        universe.add(left_place);
756
757        // Right side: rotate 180° around Y so the arm points toward -X (inward).
758        let right_place = universe.world.add_component(
759            engine::ecs::component::TransformComponent::new()
760                .with_position(light_x, 0.0, z)
761                .with_rotation_euler(0.0, std::f32::consts::PI, 0.0),
762        );
763        let right_model = spawn_street_light(&mut universe);
764        let _ = universe.attach(right_place, right_model);
765        universe.add(right_place);
766    }
767
768    // Big floor.
769    {
770        let floor_half = arena_half;
771        let thickness = 0.20;
772
773        // Split transforms so scaling the floor does not scale any potential children.
774        // Top surface at y=0.
775        let floor_root_t = universe
776            .world
777            .add_component(engine::ecs::component::TransformComponent::new());
778        let floor_geom_t = universe.world.add_component(
779            engine::ecs::component::TransformComponent::new()
780                .with_position(0.0, -thickness, 0.0)
781                .with_scale(floor_half * 2.0, thickness * 2.0, floor_half * 2.0),
782        );
783        let floor_r = universe
784            .world
785            .add_component(engine::ecs::component::RenderableComponent::cube());
786        let floor_color =
787            universe
788                .world
789                .add_component(engine::ecs::component::ColorComponent::rgba(
790                    0.08, 0.08, 0.09, 1.0,
791                ));
792
793        let floor_cn = universe
794            .world
795            .add_component(engine::ecs::component::CollisionComponent::STATIC());
796        let floor_shape =
797            universe
798                .world
799                .add_component(engine::ecs::component::CollisionShapeComponent::new(
800                    engine::ecs::component::CollisionShape::cube_half_extents([
801                        floor_half, thickness, floor_half,
802                    ]),
803                ));
804
805        let _ = universe.attach(floor_root_t, floor_geom_t);
806        let _ = universe.attach(floor_geom_t, floor_r);
807        let _ = universe.attach(floor_r, floor_color);
808        let _ = universe.attach(floor_geom_t, floor_cn);
809        let _ = universe.attach(floor_cn, floor_shape);
810        universe.add(floor_root_t);
811    }
812
813    // Square boundary walls around the floor edges.
814    // Note: STATIC colliders don't need KineticResponse; they still collide with kinematic/rigged.
815    fn spawn_boundary_wall(
816        universe: &mut engine::Universe,
817        x: f32,
818        y: f32,
819        z: f32,
820        half_extents: [f32; 3],
821        color: [f32; 4],
822    ) {
823        let t = universe.world.add_component(
824            engine::ecs::component::TransformComponent::new()
825                .with_position(x, y, z)
826                .with_scale(
827                    half_extents[0] * 2.0,
828                    half_extents[1] * 2.0,
829                    half_extents[2] * 2.0,
830                ),
831        );
832        let r = universe
833            .world
834            .add_component(engine::ecs::component::RenderableComponent::cube());
835        let c = universe
836            .world
837            .add_component(engine::ecs::component::ColorComponent::rgba(
838                color[0], color[1], color[2], color[3],
839            ));
840
841        let cn = universe
842            .world
843            .add_component(engine::ecs::component::CollisionComponent::STATIC());
844        let shape =
845            universe
846                .world
847                .add_component(engine::ecs::component::CollisionShapeComponent::new(
848                    engine::ecs::component::CollisionShape::cube_half_extents(half_extents),
849                ));
850
851        let _ = universe.attach(t, r);
852        let _ = universe.attach(r, c);
853        let _ = universe.attach(t, cn);
854        let _ = universe.attach(cn, shape);
855        universe.add(t);
856    }
857
858    {
859        let wall_half_height = 2.5;
860        let wall_half_thickness = 0.30;
861        let wall_center_y = wall_half_height; // bottom at y=0
862        let wall_half_len = arena_half;
863
864        // Subtle visible walls.
865        let wall_color = [0.10, 0.10, 0.12, 0.30];
866
867        // +Z / -Z
868        spawn_boundary_wall(
869            &mut universe,
870            0.0,
871            wall_center_y,
872            arena_half,
873            [wall_half_len, wall_half_height, wall_half_thickness],
874            wall_color,
875        );
876        spawn_boundary_wall(
877            &mut universe,
878            0.0,
879            wall_center_y,
880            -arena_half,
881            [wall_half_len, wall_half_height, wall_half_thickness],
882            wall_color,
883        );
884
885        // +X / -X
886        spawn_boundary_wall(
887            &mut universe,
888            arena_half,
889            wall_center_y,
890            0.0,
891            [wall_half_thickness, wall_half_height, wall_half_len],
892            wall_color,
893        );
894        spawn_boundary_wall(
895            &mut universe,
896            -arena_half,
897            wall_center_y,
898            0.0,
899            [wall_half_thickness, wall_half_height, wall_half_len],
900            wall_color,
901        );
902    }
903
904    fn spawn_falling_cube(
905        universe: &mut engine::Universe,
906        parent: engine::ecs::ComponentId,
907        x: f32,
908        y: f32,
909        z: f32,
910        s: f32,
911        r: f32,
912        g: f32,
913        b: f32,
914    ) {
915        let t = universe.world.add_component(
916            engine::ecs::component::TransformComponent::new()
917                .with_position(x, y, z)
918                .with_scale(s, s, s),
919        );
920
921        let renderable = universe
922            .world
923            .add_component(engine::ecs::component::RenderableComponent::cube());
924        let color = universe
925            .world
926            .add_component(engine::ecs::component::ColorComponent::rgba(r, g, b, 1.0));
927
928        let cn = universe
929            .world
930            .add_component(engine::ecs::component::CollisionComponent::KINEMATIC());
931
932        let response = universe.world.add_component({
933            let mut r = engine::ecs::component::KineticResponseComponent::push()
934                .with_push_strength(3.0)
935                .with_friction_y(18.0);
936            // Allow higher fall speeds so gravity coefficients are visible.
937            r.max_speed = 80.0;
938            r
939        });
940
941        let shape =
942            universe
943                .world
944                .add_component(engine::ecs::component::CollisionShapeComponent::new(
945                    engine::ecs::component::CollisionShape::cube_half_extents([
946                        0.5 * s,
947                        0.5 * s,
948                        0.5 * s,
949                    ]),
950                ));
951
952        let _ = universe.attach(t, renderable);
953        let _ = universe.attach(renderable, color);
954
955        let _ = universe.attach(t, cn);
956        let _ = universe.attach(cn, response);
957        let _ = universe.attach(cn, shape);
958
959        let _ = universe.attach(parent, t);
960    }
961
962    // Three gravity fields, each with 20 cubes.
963    let field_low = universe
964        .world
965        .add_component(engine::ecs::component::GravityComponent::new().with_coefficient(0.125));
966    let field_mid = universe
967        .world
968        .add_component(engine::ecs::component::GravityComponent::new().with_coefficient(0.5));
969    let field_high = universe
970        .world
971        .add_component(engine::ecs::component::GravityComponent::new().with_coefficient(1.0));
972
973    universe.add(field_low);
974    universe.add(field_mid);
975    universe.add(field_high);
976
977    fn spawn_field(
978        universe: &mut engine::Universe,
979        field: engine::ecs::ComponentId,
980        base_x: f32,
981        base_z: f32,
982        cube_color: [f32; 3],
983    ) {
984        let spacing = 0.7;
985        let start_x = base_x - 2.0 * spacing;
986        let start_z = base_z - 1.5 * spacing;
987
988        for i in 0..20 {
989            let ix = (i % 5) as f32;
990            let iz = (i / 5) as f32;
991
992            let x = start_x + ix * spacing;
993            let z = start_z + iz * spacing;
994            let y = 2.0 + iz * 0.6 + ix * 0.1;
995
996            spawn_falling_cube(
997                universe,
998                field,
999                x,
1000                y,
1001                z,
1002                0.5,
1003                cube_color[0],
1004                cube_color[1],
1005                cube_color[2],
1006            );
1007        }
1008
1009        // Visual marker for the field center.
1010        let marker_t = universe.world.add_component(
1011            engine::ecs::component::TransformComponent::new()
1012                .with_position(base_x, 0.1, base_z)
1013                .with_scale(0.3, 0.02, 0.3),
1014        );
1015        let marker_r = universe
1016            .world
1017            .add_component(engine::ecs::component::RenderableComponent::cube());
1018        let marker_c = universe
1019            .world
1020            .add_component(engine::ecs::component::ColorComponent::rgba(
1021                cube_color[0],
1022                cube_color[1],
1023                cube_color[2],
1024                1.0,
1025            ));
1026        let _ = universe.attach(marker_t, marker_r);
1027        let _ = universe.attach(marker_r, marker_c);
1028        universe.add(marker_t);
1029    }
1030
1031    spawn_field(&mut universe, field_low, -8.0, 0.0, [0.3, 0.6, 1.0]);
1032    spawn_field(&mut universe, field_mid, 0.0, 0.0, [0.4, 1.0, 0.4]);
1033    spawn_field(&mut universe, field_high, 8.0, 0.0, [1.0, 0.5, 0.2]);
1034
1035    // Group-scoped collision behaviors.
1036    // - Low + High fields: cubes turn white after colliding with another cube.
1037    // - Mid field: cubes lose gravity after colliding with another cube.
1038    universe.add_signal_handler(
1039        engine::ecs::SignalKind::CollisionStarted,
1040        field_low,
1041        on_collision_turn_white,
1042    );
1043    universe.add_signal_handler(
1044        engine::ecs::SignalKind::CollisionStarted,
1045        field_high,
1046        on_collision_turn_white,
1047    );
1048    universe.add_signal_handler(
1049        engine::ecs::SignalKind::CollisionStarted,
1050        field_mid,
1051        on_collision_freeze_gravity,
1052    );
1053
1054    universe.systems.process_commands(
1055        &mut universe.world,
1056        &mut universe.visuals,
1057        &mut universe.render_assets,
1058        &mut universe.command_queue,
1059    );
1060
1061    universe.enable_repl();
1062    engine::Windowing::run_app(universe).expect("Windowing failed");
1063}

Trait Implementations§

Source§

impl Clone for GravityComponent

Source§

fn clone(&self) -> GravityComponent

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Component for GravityComponent

Source§

fn name(&self) -> &'static str

Short debug/type name for this component kind (e.g. “transform”, “camera”).
Source§

fn set_id(&mut self, component: ComponentId)

Source§

fn as_any(&self) -> &dyn Any

Source§

fn as_any_mut(&mut self) -> &mut dyn Any

Source§

fn init(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)

Called when component is added to the World
Source§

fn cleanup(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)

Called when component is removed from the World.
Source§

fn to_mms_ast(&self, _world: &World) -> ComponentExpression

Encode this component as an MMS Component Expression AST node. Read more
Source§

fn encode(&self) -> HashMap<String, Value>

Encode component data to a HashMap for serialization. Read more
Source§

fn decode(&mut self, _data: &HashMap<String, Value>) -> Result<(), String>

Decode component data from a HashMap after deserialization. Read more
Source§

impl Debug for GravityComponent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for GravityComponent

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

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>

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)

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)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<F, T> IntoSample<T> for F
where T: FromSample<F>,

Source§

fn into_sample(self) -> T

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

Source§

fn to_sample_(self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more