Skip to main content

background_example/
background-example.rs

1use mittens_engine::{engine, utils};
2
3#[path = "example_util/mod.rs"]
4mod example_util;
5
6fn hash_u32(mut x: u32) -> u32 {
7    // A tiny integer hash (deterministic, cheap, less linear than an LCG).
8    x ^= x >> 16;
9    x = x.wrapping_mul(0x7feb_352d);
10    x ^= x >> 15;
11    x = x.wrapping_mul(0x846c_a68b);
12    x ^= x >> 16;
13    x
14}
15
16fn rand01(seed: u32) -> f32 {
17    (hash_u32(seed) as f32) / (u32::MAX as f32)
18}
19
20fn main() {
21    mittens_engine::example_support::ensure_model_assets();
22    utils::logger::init();
23
24    let world = engine::ecs::World::default();
25    let mut universe = engine::Universe::new(world);
26
27    // Dark-ish background clear color so the effect reads.
28    let clear = universe
29        .world
30        .add_component(engine::ecs::component::BackgroundColorComponent::new());
31    let clear_c = universe
32        .world
33        .add_component(engine::ecs::component::ColorComponent::rgba(
34            0.01, 0.01, 0.02, 1.0,
35        ));
36    let _ = universe.world.add_child(clear, clear_c);
37    universe.add(clear);
38
39    // --- Camera rig (WASD/QE) ---
40    let input = universe
41        .world
42        .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
43    let input_mode = universe.world.add_component(
44        engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
45    );
46    let _ = universe.attach(input, input_mode);
47
48    // Start pulled back so both background + foreground are in view.
49    let rig_transform = universe.world.add_component(
50        engine::ecs::component::TransformComponent::new().with_position(0.0, 1.0, 6.0),
51    );
52    let _ = universe.attach(input, rig_transform);
53
54    let camera3d = universe
55        .world
56        .add_component(engine::ecs::component::Camera3DComponent::new());
57    let _ = universe.attach(rig_transform, camera3d);
58
59    // Simple light for toon-shaded foreground.
60    let light = universe.world.add_component(
61        engine::ecs::component::PointLightComponent::new()
62            .with_distance(50.0)
63            .with_color(1.0, 1.0, 1.0),
64    );
65    let light_transform = universe.world.add_component(
66        engine::ecs::component::TransformComponent::new().with_position(0.0, 6.0, 2.0),
67    );
68    let _ = universe.attach(light_transform, light);
69
70    // Topology: I { T { C3D } } — add a small camera-attached controls hint.
71    example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
72
73    universe.add(input);
74    universe.add(light_transform);
75
76    let cube_mesh = universe
77        .render_assets
78        .get_mesh(engine::graphics::BuiltinMeshType::Cube);
79
80    // --- Background world ---
81    // Any renderables under this node will go through the background draw list.
82    let bg_root = universe
83        .world
84        .add_component(engine::ecs::component::BackgroundComponent::new());
85    universe.add(bg_root);
86
87    // A large thin "ground" plane in the background layer.
88    // (Using a scaled cube for now; visually it's a plane.)
89    let ground_tx = universe.world.add_component(
90        engine::ecs::component::TransformComponent::new()
91            .with_position(0.0, -40.0, 0.0)
92            .with_scale(200.0, 1.0, 200.0),
93    );
94    let ground_renderable =
95        universe
96            .world
97            .add_component(engine::ecs::component::RenderableComponent::new(
98                engine::graphics::primitives::Renderable::new(
99                    cube_mesh,
100                    engine::graphics::primitives::MaterialHandle::UNLIT_MESH,
101                ),
102            ));
103    let ground_color = universe
104        .world
105        .add_component(engine::ecs::component::ColorComponent::rgba(
106            0.015, 0.02, 0.03, 1.0,
107        ));
108
109    let _ = universe.attach(bg_root, ground_tx);
110    let _ = universe.attach(ground_tx, ground_renderable);
111    let _ = universe.attach(ground_renderable, ground_color);
112
113    // Add some bright "stars" (small unlit cubes) scattered on a sphere.
114    // Use a grid + jitter + conditional skip so the distribution looks more even.
115    let lat_steps: u32 = 24;
116    let lon_steps: u32 = 48;
117    let density: f32 = 0.14;
118    let radius = 25.0;
119
120    for lat in 0..lat_steps {
121        for lon in 0..lon_steps {
122            let cell = lon + lat * lon_steps;
123            if rand01(cell ^ 0x5a1d_c0de) > density {
124                continue;
125            }
126
127            // Jitter within the cell.
128            let jx = rand01(cell ^ 0xA341_316C) - 0.5;
129            let jy = rand01(cell ^ 0xC801_3EA4) - 0.5;
130
131            // Latitude in [-pi/2, +pi/2], longitude in [0, 2pi).
132            let lat_t = (lat as f32 + 0.5 + 0.9 * jy) / (lat_steps as f32);
133            let lon_t = (lon as f32 + 0.5 + 0.9 * jx) / (lon_steps as f32);
134            let phi = (lat_t - 0.5) * std::f32::consts::PI; // [-pi/2,+pi/2]
135            let theta = lon_t * std::f32::consts::TAU;
136
137            let x = phi.cos() * theta.cos();
138            let y = phi.sin();
139            let z = phi.cos() * theta.sin();
140
141            let px = x * radius;
142            let py = y * radius;
143            let pz = z * radius;
144
145            let scale = 0.12 + rand01(cell.wrapping_add(12345)) * 0.20;
146
147            let tx = universe.world.add_component(
148                engine::ecs::component::TransformComponent::new()
149                    .with_position(px, py, pz)
150                    .with_scale(scale, scale, scale),
151            );
152            let renderable =
153                universe
154                    .world
155                    .add_component(engine::ecs::component::RenderableComponent::new(
156                        engine::graphics::primitives::Renderable::new(
157                            cube_mesh,
158                            engine::graphics::primitives::MaterialHandle::UNLIT_MESH,
159                        ),
160                    ));
161
162            let c = 0.75 + rand01(cell.wrapping_mul(3)) * 0.25;
163            let color = universe
164                .world
165                .add_component(engine::ecs::component::ColorComponent::rgba(c, c, c, 1.0));
166
167            let _ = universe.attach(bg_root, tx);
168            let _ = universe.attach(tx, renderable);
169            let _ = universe.attach(renderable, color);
170        }
171    }
172
173    // --- Foreground world ---
174    // A small cube field that should parallax as you move.
175    let fg_root = universe.world.add_component(
176        engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 0.0),
177    );
178
179    let n: i32 = 8;
180    let step: f32 = 0.8;
181    for z in 0..n {
182        for x in 0..n {
183            let px = (x - n / 2) as f32 * step;
184            let pz = -(z as f32) * step;
185
186            let tx = universe.world.add_component(
187                engine::ecs::component::TransformComponent::new()
188                    .with_position(px, 0.5, pz)
189                    .with_scale(0.25, 0.25, 0.25),
190            );
191            let renderable =
192                universe
193                    .world
194                    .add_component(engine::ecs::component::RenderableComponent::new(
195                        engine::graphics::primitives::Renderable::new(
196                            cube_mesh,
197                            engine::graphics::primitives::MaterialHandle::TOON_MESH,
198                        ),
199                    ));
200
201            let fx = (x as f32) / ((n - 1) as f32);
202            let fz = (z as f32) / ((n - 1) as f32);
203            let color = universe
204                .world
205                .add_component(engine::ecs::component::ColorComponent::rgba(
206                    0.2 + 0.8 * fx,
207                    0.2 + 0.8 * (1.0 - fz),
208                    0.4,
209                    1.0,
210                ));
211
212            let _ = universe.attach(fg_root, tx);
213            let _ = universe.attach(tx, renderable);
214            let _ = universe.attach(renderable, color);
215        }
216    }
217
218    universe.add(fg_root);
219
220    universe.systems.process_commands(
221        &mut universe.world,
222        &mut universe.visuals,
223        &mut universe.render_assets,
224        &mut universe.command_queue,
225    );
226
227    universe.enable_repl();
228    engine::Windowing::run_app(universe).expect("Windowing failed");
229}