pub struct BackgroundComponent {
pub occlusion_and_lighting: bool,
pub ray_casting: bool,
}Fields§
§occlusion_and_lighting: boolIf true, renderables under this node render in the background occluded+lit stage.
This stage is intended to depth-test/write against itself (for self-occlusion) and participate in lighting, while still not occluding the foreground (the renderer clears depth before drawing the foreground).
ray_casting: boolIf true, renderables under this node are eligible for ray casting (BVH insertion).
Default is false because background scene dressing (clouds, skyboxes, etc.) typically should not be hit-testable.
Implementations§
Source§impl BackgroundComponent
impl BackgroundComponent
Sourcepub fn new() -> Self
pub fn new() -> Self
Examples found in repository?
examples/vr-input.rs (line 65)
62fn spawn_sun_background(universe: &mut engine::Universe) {
63 let bg_root = universe
64 .world
65 .add_component(engine::ecs::component::BackgroundComponent::new());
66 universe.add(bg_root);
67
68 let circle_mesh = universe.render_assets.get_mesh(BuiltinMeshType::Circle2D);
69
70 // Big yellow disk.
71 let sun_t = universe.world.add_component(
72 TransformComponent::new()
73 .with_position(2.0, 1.5, -8.0)
74 .with_scale(3.5, 3.5, 3.5),
75 );
76 let sun_r = universe
77 .world
78 .add_component(RenderableComponent::new(Renderable::new(
79 circle_mesh,
80 MaterialHandle::TOON_MESH,
81 )));
82 let sun_color = universe
83 .world
84 .add_component(ColorComponent::rgba(1.0, 0.85, 0.15, 1.0));
85 let sun_emissive = universe.world.add_component(EmissiveComponent::on());
86
87 let _ = universe.attach(bg_root, sun_t);
88 let _ = universe.attach(sun_t, sun_r);
89 let _ = universe.attach(sun_r, sun_color);
90 let _ = universe.attach(sun_r, sun_emissive);
91
92 // Small white highlight disk.
93 let highlight_t = universe.world.add_component(
94 TransformComponent::new()
95 .with_position(-0.35, 0.35, -0.01)
96 .with_scale(0.45, 0.45, 0.45),
97 );
98 let highlight_r = universe
99 .world
100 .add_component(RenderableComponent::new(Renderable::new(
101 circle_mesh,
102 MaterialHandle::TOON_MESH,
103 )));
104 let highlight_color = universe
105 .world
106 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
107 let highlight_emissive = universe.world.add_component(EmissiveComponent::on());
108
109 let _ = universe.attach(sun_t, highlight_t);
110 let _ = universe.attach(highlight_t, highlight_r);
111 let _ = universe.attach(highlight_r, highlight_color);
112 let _ = universe.attach(highlight_r, highlight_emissive);
113}More examples
examples/vtuber-mirror-example.rs (line 110)
77fn main() {
78 mittens_engine::example_support::ensure_model_assets();
79 utils::logger::init();
80
81 let output = scripting::MeowMeowRunner::eval(include_str!("vtuber-mirror-example.mms"));
82
83 for error in &output.errors {
84 eprintln!("[mms] {error}");
85 }
86 println!(
87 "[mms] {} intent(s) from vtuber-mirror-example.mms",
88 output.intents.len()
89 );
90 println!(
91 "[vtuber-mirror-example] expected XR-only views: 2 XR eye views plus mirror-derived views; no desktop scene view unless a Camera3D/Camera2D is added"
92 );
93
94 let world = engine::ecs::World::default();
95 let mut universe = engine::Universe::new(world);
96
97 let scope = engine::ecs::ComponentId::default();
98 for intent in output.intents {
99 universe.command_queue.push_intent_now(scope, intent);
100 }
101
102 universe.systems.process_commands(
103 &mut universe.world,
104 &mut universe.visuals,
105 &mut universe.render_assets,
106 &mut universe.command_queue,
107 );
108
109 let bg_root = universe.world.add_component(
110 engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
111 );
112 universe.add(bg_root);
113
114 let cloud_params = example_util::CloudRingParams {
115 cloud_count: 10,
116 radius: 34.0,
117 center_y: 8.5,
118 puffs_per_cloud: 28,
119 angle_jitter: 0.30,
120 high_y_probability: 0.45,
121 high_y_multiplier: 1.28,
122 seed: 0x57_55_B0_0Au32,
123 };
124 example_util::spawn_cloud_ring(&mut universe, bg_root, cloud_params);
125
126 for kind in [
127 SignalKind::DragStart,
128 SignalKind::DragMove,
129 SignalKind::DragEnd,
130 SignalKind::Click,
131 ] {
132 universe
133 .systems
134 .rx
135 .add_global_handler(kind, on_xr_pointer_event);
136 }
137
138 universe.enable_repl();
139 engine::Windowing::run_app(universe).expect("Windowing failed");
140}examples/vtuber-editor-example.rs (line 111)
77fn main() {
78 mittens_engine::example_support::ensure_model_assets();
79 utils::logger::init();
80
81 let output = scripting::MeowMeowRunner::eval(include_str!("vtuber-editor-example.mms"));
82
83 for error in &output.errors {
84 eprintln!("[mms] {error}");
85 }
86 println!(
87 "[mms] {} intent(s) from vtuber-editor-example.mms",
88 output.intents.len()
89 );
90 println!(
91 "[vtuber-editor-example] expected XR-only views: 2 XR eye views plus mirror-derived views; no desktop scene view unless a Camera3D/Camera2D is added"
92 );
93
94 let world = engine::ecs::World::default();
95
96 let mut universe = engine::Universe::new(world);
97
98 let scope = engine::ecs::ComponentId::default();
99 for intent in output.intents {
100 universe.command_queue.push_intent_now(scope, intent);
101 }
102
103 universe.systems.process_commands(
104 &mut universe.world,
105 &mut universe.visuals,
106 &mut universe.render_assets,
107 &mut universe.command_queue,
108 );
109
110 let bg_root = universe.world.add_component(
111 engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
112 );
113 universe.add(bg_root);
114
115 let cloud_params = example_util::CloudRingParams {
116 cloud_count: 10,
117 radius: 34.0,
118 center_y: 8.5,
119 puffs_per_cloud: 28,
120 angle_jitter: 0.30,
121 high_y_probability: 0.45,
122 high_y_multiplier: 1.28,
123 seed: 0x57_55_B0_0Au32,
124 };
125 example_util::spawn_cloud_ring(&mut universe, bg_root, cloud_params);
126
127 for kind in [
128 SignalKind::DragStart,
129 SignalKind::DragMove,
130 SignalKind::DragEnd,
131 SignalKind::Click,
132 ] {
133 universe
134 .systems
135 .rx
136 .add_global_handler(kind, on_xr_pointer_event);
137 }
138
139 universe.enable_repl();
140 engine::Windowing::run_app(universe).expect("Windowing failed");
141}examples/vtuber-example.rs (line 115)
11fn main() {
12 mittens_engine::example_support::ensure_model_assets();
13 utils::logger::init();
14
15 let world = engine::ecs::World::default();
16 let mut universe = engine::Universe::new(world);
17
18 // Light pink background.
19 let background = universe
20 .world
21 .add_component(BackgroundColorComponent::new());
22 let background_c = universe
23 .world
24 .add_component(ColorComponent::rgba(1.0, 0.82, 0.90, 1.0));
25 let _ = universe.world.add_child(background, background_c);
26 universe.add(background);
27
28 // Small ambient so shadowed areas aren't pure black.
29 let ambient = universe
30 .world
31 .add_component(AmbientLightComponent::rgb(0.10, 0.10, 0.12));
32 universe.add(ambient);
33
34 // --- Camera rig (WASD + mouse) ---
35 // InputComponent is the root, and it owns a Transform (the camera rig).
36 let input = universe
37 .world
38 .add_component(InputComponent::new().with_speed(1.5));
39 let input_mode = universe.world.add_component(
40 InputTransformModeComponent::forward_z()
41 .with_fps_rotation()
42 .with_roll_axis_y(),
43 );
44 let _ = universe.attach(input, input_mode);
45
46 // Start slightly pulled back looking towards the origin.
47 let rig_transform = universe
48 .world
49 .add_component(TransformComponent::new().with_position(0.0, 0.0, 6.0));
50 let _ = universe.attach(input, rig_transform);
51
52 let camera3d = universe.world.add_component(Camera3DComponent::new());
53 let _ = universe.attach(rig_transform, camera3d);
54
55 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
56 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
57
58 universe.add(input);
59
60 // --- lighting ---
61 // Directional key light (slightly down + forward Z).
62 let sun = universe.world.add_component(
63 DirectionalLightComponent::new()
64 .with_intensity(1.2)
65 .with_color(1.0, 0.98, 0.94),
66 );
67 let sun_dir = universe
68 .world
69 .add_component(TransformComponent::new().with_position(0.0, -0.35, 1.0));
70 let _ = universe.attach(sun_dir, sun);
71 universe.add(sun_dir);
72
73 let light_transform = universe.world.add_component(
74 TransformComponent::new()
75 .with_position(1.0, 6.0, 3.0)
76 .with_scale(0.1, 0.1, 0.1),
77 );
78 let light = universe.world.add_component(
79 engine::ecs::component::PointLightComponent::new()
80 .with_distance(120.0)
81 .with_color(1.0, 1.0, 1.0),
82 );
83 let _ = universe.attach(light_transform, light);
84 universe.add(light_transform);
85
86 // --- VTuber model ---
87 let model_root = universe.world.add_component(TransformComponent::new());
88 let model = universe
89 .world
90 .add_component(GLTFComponent::new("assets/models/pc-rei.hoodie.glb"));
91 // emissive for pc-rei
92 let emissive = universe
93 .world
94 .add_component(engine::ecs::component::EmissiveComponent::on());
95
96 let _ = universe.attach(model, emissive);
97
98 let xr_input = universe.world.add_component(InputXRComponent::on());
99 let xr_gamepad = universe
100 .world
101 .add_component(engine::ecs::component::InputXRGamepadComponent::new().speed(1.5));
102 let xr_head = universe.world.add_component(TransformComponent::new());
103 let xr_camera = universe.world.add_component(CameraXRComponent::on());
104 let _ = universe.attach(xr_input, xr_head);
105 let _ = universe.attach(xr_input, xr_gamepad);
106 let _ = universe.attach(xr_head, xr_camera);
107 let _ = universe.attach(xr_head, model_root);
108
109 let _ = universe.attach(model_root, model);
110 universe.add(xr_input);
111 universe.add(model_root);
112
113 // --- Background clouds (occluded + lit) ---
114 let bg_root = universe.world.add_component(
115 engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
116 );
117 universe.add(bg_root);
118 let mut cloud_params = example_util::CloudRingParams::default();
119 cloud_params.cloud_count = 8; // +3 clusters
120 cloud_params.angle_jitter = 0.35;
121 cloud_params.high_y_probability = 0.5;
122 cloud_params.high_y_multiplier = 1.5;
123 cloud_params.seed = 0x57_55_B0_01u32;
124 example_util::spawn_cloud_ring(&mut universe, bg_root, cloud_params);
125
126 // --- Simple environment ---
127 let spawn_cube = |universe: &mut engine::Universe,
128 position: (f32, f32, f32),
129 scale: (f32, f32, f32),
130 color: (f32, f32, f32, f32)| {
131 let transform = universe.world.add_component(
132 TransformComponent::new()
133 .with_position(position.0, position.1, position.2)
134 .with_scale(scale.0, scale.1, scale.2),
135 );
136 let renderable = universe.world.add_component(RenderableComponent::cube());
137 let color = universe
138 .world
139 .add_component(ColorComponent::rgba(color.0, color.1, color.2, color.3));
140
141 let _ = universe.attach(transform, renderable);
142 let _ = universe.attach(renderable, color);
143
144 universe.add(transform);
145 };
146
147 // floor
148 spawn_cube(
149 &mut universe,
150 (0.0, -0.05, 0.0),
151 (10.0, 0.1, 10.0),
152 (0.92, 0.92, 0.92, 1.0),
153 );
154
155 // back wall
156 spawn_cube(
157 &mut universe,
158 (0.0, 1.5, -5.0),
159 (10.0, 3.0, 1.0),
160 (0.95, 0.94, 0.96, 1.0),
161 );
162
163 // desk
164 spawn_cube(
165 &mut universe,
166 (0.0, 0.35, 1.0),
167 (1.0, 0.75, 0.5),
168 (0.75, 0.70, 0.65, 1.0),
169 );
170
171 let xr_root = universe
172 .world
173 .add_component(engine::ecs::component::XrComponent::on());
174 universe.add(xr_root);
175
176 universe.systems.process_commands(
177 &mut universe.world,
178 &mut universe.visuals,
179 &mut universe.render_assets,
180 &mut universe.command_queue,
181 );
182
183 universe.enable_repl();
184 engine::Windowing::run_app(universe).expect("Windowing failed");
185}examples/gestures-and-gizmos.rs (line 69)
13fn build_gestures_and_gizmos_scene(universe: &mut engine::Universe) -> Scene {
14 use engine::ecs::component::{
15 BackgroundColorComponent, BackgroundComponent, Camera3DComponent, ColorComponent,
16 DirectionalLightComponent, InputComponent, InputTransformModeComponent, PointerComponent,
17 RaycastableComponent, RenderableComponent, TransformComponent, TransformGizmoComponent,
18 };
19 use engine::graphics::BuiltinMeshType;
20 use engine::graphics::primitives::{MaterialHandle, Renderable};
21
22 let tri_mesh = universe.render_assets.get_mesh(BuiltinMeshType::Triangle2D);
23 let cube_mesh = universe.render_assets.get_mesh(BuiltinMeshType::Cube);
24 let tetra_mesh = universe
25 .render_assets
26 .get_mesh(BuiltinMeshType::Tetrahedron);
27
28 // BackgroundColor { C.rgba }
29 let bg_color = universe
30 .world
31 .add_component(BackgroundColorComponent::new());
32 let bg_color_c = universe
33 .world
34 .add_component(ColorComponent::rgba(0.90, 0.90, 0.90, 1.0));
35 let _ = universe.world.add_child(bg_color, bg_color_c);
36 universe.add(bg_color);
37
38 // ambient light
39 let ambient = universe
40 .world
41 .add_component(AmbientLightComponent::rgb(0.25, 0.25, 0.25));
42 universe.add(ambient);
43
44 // ground plane
45 let ground_tx = universe.world.add_component(
46 TransformComponent::new()
47 .with_position(0.0, -2.5, 0.0)
48 .with_scale(20.0, 1.0, 20.0),
49 );
50 let ground_r = universe
51 .world
52 .add_component(RenderableComponent::new(Renderable::new(
53 universe.render_assets.get_mesh(BuiltinMeshType::Cube),
54 MaterialHandle::TOON_MESH,
55 )));
56 let ground_c = universe
57 .world
58 .add_component(ColorComponent::rgba(0.75, 0.75, 0.75, 1.0));
59 let _ = universe.attach(ground_tx, ground_r);
60 let _ = universe.attach(ground_r, ground_c);
61 let _ = universe.add(ground_tx);
62
63 // Background {
64 // with_occlusion_and_lighting()
65 // // using the example utils to add clouds to the background
66 // }
67 let bg_root = universe
68 .world
69 .add_component(BackgroundComponent::new().with_occlusion_and_lighting());
70 universe.add(bg_root);
71
72 // DirectionalLight {
73 // T { translate [1, 1, 1] }
74 // }
75 // Directional lights encode their direction in the node's world position.
76 let sun_t = universe
77 .world
78 .add_component(TransformComponent::new().with_position(1.0, 1.0, 1.0));
79 let sun = universe
80 .world
81 .add_component(DirectionalLightComponent::new());
82 let _ = universe.attach(sun_t, sun);
83 universe.add(sun_t);
84
85 // i = input
86 // t = transform
87 // c3d = camera3d
88 //
89 // I {
90 // T {
91 // C3D { with_fps_rotation().with_roll_axis_y() }
92 // }
93 // }
94 let input = universe
95 .world
96 .add_component(InputComponent::new().with_speed(2.5));
97 let input_mode = universe.world.add_component(
98 InputTransformModeComponent::forward_z()
99 .with_fps_rotation()
100 .with_roll_axis_y(),
101 );
102
103 let _ = universe.attach(input, input_mode);
104
105 // Forward is -Z, so put the camera at +Z looking toward the origin.
106 let rig_t = universe
107 .world
108 .add_component(TransformComponent::new().with_position(0.0, 0.0, 3.5));
109 let _ = universe.attach(input, rig_t);
110
111 let cam = universe
112 .world
113 .add_component(Camera3DComponent::new().with_far(600.0).with_fov(70.0));
114 let _ = universe.attach(rig_t, cam);
115
116 // Opt-in: treat this camera rig as a pointer source.
117 let pointer = universe.world.add_component(PointerComponent::new());
118 let _ = universe.attach(cam, pointer);
119
120 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
121 example_util::spawn_desktop_camera_controls_hint(universe, rig_t);
122
123 fn spawn_shape_with_gizmo(
124 universe: &mut engine::Universe,
125 mesh: engine::graphics::primitives::CpuMeshHandle,
126 pos: [f32; 3],
127 scale: [f32; 3],
128 rot_euler: [f32; 3],
129 color: [f32; 4],
130 ) {
131 let t = universe.world.add_component(
132 TransformComponent::new()
133 .with_position(pos[0], pos[1], pos[2])
134 .with_scale(scale[0], scale[1], scale[2])
135 .with_rotation_euler(rot_euler[0], rot_euler[1], rot_euler[2]),
136 );
137 let r = universe
138 .world
139 .add_component(RenderableComponent::new(Renderable::new(
140 mesh,
141 MaterialHandle::TOON_MESH,
142 )));
143 let c = universe
144 .world
145 .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
146 let rc = universe
147 .world
148 .add_component(RaycastableComponent::enabled());
149 let g = universe.world.add_component(TransformGizmoComponent::new());
150
151 let _ = universe.attach(t, r);
152 let _ = universe.attach(r, c);
153 let _ = universe.attach(r, rc);
154 let _ = universe.attach(t, g);
155
156 universe.add(t);
157 }
158
159 fn spawn_shape_raycastable_no_gizmo(
160 universe: &mut engine::Universe,
161 mesh: engine::graphics::primitives::CpuMeshHandle,
162 pos: [f32; 3],
163 scale: [f32; 3],
164 rot_euler: [f32; 3],
165 color: [f32; 4],
166 ) {
167 let t = universe.world.add_component(
168 TransformComponent::new()
169 .with_position(pos[0], pos[1], pos[2])
170 .with_scale(scale[0], scale[1], scale[2])
171 .with_rotation_euler(rot_euler[0], rot_euler[1], rot_euler[2]),
172 );
173 let r = universe
174 .world
175 .add_component(RenderableComponent::new(Renderable::new(
176 mesh,
177 MaterialHandle::TOON_MESH,
178 )));
179 let c = universe
180 .world
181 .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
182 let rc = universe
183 .world
184 .add_component(RaycastableComponent::enabled());
185
186 let _ = universe.attach(t, r);
187 let _ = universe.attach(r, c);
188 let _ = universe.attach(r, rc);
189
190 universe.add(t);
191 }
192
193 spawn_shape_with_gizmo(
194 universe,
195 tri_mesh,
196 [-1.2, 0.0, 0.0],
197 [0.65, 0.65, 0.65],
198 [0.0, 0.0, 0.0],
199 [0.2, 0.9, 0.25, 1.0],
200 );
201 spawn_shape_with_gizmo(
202 universe,
203 cube_mesh,
204 [0.0, 0.0, 0.0],
205 [0.55, 0.55, 0.55],
206 [0.0, 0.0, 0.0],
207 [0.95, 0.25, 0.2, 1.0],
208 );
209 spawn_shape_with_gizmo(
210 universe,
211 tetra_mesh,
212 [1.2, 0.0, 0.0],
213 [0.7, 0.7, 0.7],
214 [0.0, 0.0, 0.0],
215 [0.2, 0.55, 1.0, 1.0],
216 );
217
218 // Standalone tetrahedron: no gizmo, but explicitly raycastable.
219 // This helps isolate tetra picking vs gizmo-handle interception.
220 spawn_shape_raycastable_no_gizmo(
221 universe,
222 tetra_mesh,
223 [2.6, -0.6, 0.0],
224 [0.95, 0.95, 0.95],
225 [0.0, 0.0, 0.0],
226 [0.85, 0.85, 1.0, 1.0],
227 );
228
229 universe.add(input);
230
231 Scene { bg_root }
232}examples/background-example.rs (line 84)
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}Sourcepub fn with_occlusion_and_lighting(self) -> Self
pub fn with_occlusion_and_lighting(self) -> Self
Examples found in repository?
examples/vtuber-mirror-example.rs (line 110)
77fn main() {
78 mittens_engine::example_support::ensure_model_assets();
79 utils::logger::init();
80
81 let output = scripting::MeowMeowRunner::eval(include_str!("vtuber-mirror-example.mms"));
82
83 for error in &output.errors {
84 eprintln!("[mms] {error}");
85 }
86 println!(
87 "[mms] {} intent(s) from vtuber-mirror-example.mms",
88 output.intents.len()
89 );
90 println!(
91 "[vtuber-mirror-example] expected XR-only views: 2 XR eye views plus mirror-derived views; no desktop scene view unless a Camera3D/Camera2D is added"
92 );
93
94 let world = engine::ecs::World::default();
95 let mut universe = engine::Universe::new(world);
96
97 let scope = engine::ecs::ComponentId::default();
98 for intent in output.intents {
99 universe.command_queue.push_intent_now(scope, intent);
100 }
101
102 universe.systems.process_commands(
103 &mut universe.world,
104 &mut universe.visuals,
105 &mut universe.render_assets,
106 &mut universe.command_queue,
107 );
108
109 let bg_root = universe.world.add_component(
110 engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
111 );
112 universe.add(bg_root);
113
114 let cloud_params = example_util::CloudRingParams {
115 cloud_count: 10,
116 radius: 34.0,
117 center_y: 8.5,
118 puffs_per_cloud: 28,
119 angle_jitter: 0.30,
120 high_y_probability: 0.45,
121 high_y_multiplier: 1.28,
122 seed: 0x57_55_B0_0Au32,
123 };
124 example_util::spawn_cloud_ring(&mut universe, bg_root, cloud_params);
125
126 for kind in [
127 SignalKind::DragStart,
128 SignalKind::DragMove,
129 SignalKind::DragEnd,
130 SignalKind::Click,
131 ] {
132 universe
133 .systems
134 .rx
135 .add_global_handler(kind, on_xr_pointer_event);
136 }
137
138 universe.enable_repl();
139 engine::Windowing::run_app(universe).expect("Windowing failed");
140}More examples
examples/vtuber-editor-example.rs (line 111)
77fn main() {
78 mittens_engine::example_support::ensure_model_assets();
79 utils::logger::init();
80
81 let output = scripting::MeowMeowRunner::eval(include_str!("vtuber-editor-example.mms"));
82
83 for error in &output.errors {
84 eprintln!("[mms] {error}");
85 }
86 println!(
87 "[mms] {} intent(s) from vtuber-editor-example.mms",
88 output.intents.len()
89 );
90 println!(
91 "[vtuber-editor-example] expected XR-only views: 2 XR eye views plus mirror-derived views; no desktop scene view unless a Camera3D/Camera2D is added"
92 );
93
94 let world = engine::ecs::World::default();
95
96 let mut universe = engine::Universe::new(world);
97
98 let scope = engine::ecs::ComponentId::default();
99 for intent in output.intents {
100 universe.command_queue.push_intent_now(scope, intent);
101 }
102
103 universe.systems.process_commands(
104 &mut universe.world,
105 &mut universe.visuals,
106 &mut universe.render_assets,
107 &mut universe.command_queue,
108 );
109
110 let bg_root = universe.world.add_component(
111 engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
112 );
113 universe.add(bg_root);
114
115 let cloud_params = example_util::CloudRingParams {
116 cloud_count: 10,
117 radius: 34.0,
118 center_y: 8.5,
119 puffs_per_cloud: 28,
120 angle_jitter: 0.30,
121 high_y_probability: 0.45,
122 high_y_multiplier: 1.28,
123 seed: 0x57_55_B0_0Au32,
124 };
125 example_util::spawn_cloud_ring(&mut universe, bg_root, cloud_params);
126
127 for kind in [
128 SignalKind::DragStart,
129 SignalKind::DragMove,
130 SignalKind::DragEnd,
131 SignalKind::Click,
132 ] {
133 universe
134 .systems
135 .rx
136 .add_global_handler(kind, on_xr_pointer_event);
137 }
138
139 universe.enable_repl();
140 engine::Windowing::run_app(universe).expect("Windowing failed");
141}examples/vtuber-example.rs (line 115)
11fn main() {
12 mittens_engine::example_support::ensure_model_assets();
13 utils::logger::init();
14
15 let world = engine::ecs::World::default();
16 let mut universe = engine::Universe::new(world);
17
18 // Light pink background.
19 let background = universe
20 .world
21 .add_component(BackgroundColorComponent::new());
22 let background_c = universe
23 .world
24 .add_component(ColorComponent::rgba(1.0, 0.82, 0.90, 1.0));
25 let _ = universe.world.add_child(background, background_c);
26 universe.add(background);
27
28 // Small ambient so shadowed areas aren't pure black.
29 let ambient = universe
30 .world
31 .add_component(AmbientLightComponent::rgb(0.10, 0.10, 0.12));
32 universe.add(ambient);
33
34 // --- Camera rig (WASD + mouse) ---
35 // InputComponent is the root, and it owns a Transform (the camera rig).
36 let input = universe
37 .world
38 .add_component(InputComponent::new().with_speed(1.5));
39 let input_mode = universe.world.add_component(
40 InputTransformModeComponent::forward_z()
41 .with_fps_rotation()
42 .with_roll_axis_y(),
43 );
44 let _ = universe.attach(input, input_mode);
45
46 // Start slightly pulled back looking towards the origin.
47 let rig_transform = universe
48 .world
49 .add_component(TransformComponent::new().with_position(0.0, 0.0, 6.0));
50 let _ = universe.attach(input, rig_transform);
51
52 let camera3d = universe.world.add_component(Camera3DComponent::new());
53 let _ = universe.attach(rig_transform, camera3d);
54
55 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
56 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
57
58 universe.add(input);
59
60 // --- lighting ---
61 // Directional key light (slightly down + forward Z).
62 let sun = universe.world.add_component(
63 DirectionalLightComponent::new()
64 .with_intensity(1.2)
65 .with_color(1.0, 0.98, 0.94),
66 );
67 let sun_dir = universe
68 .world
69 .add_component(TransformComponent::new().with_position(0.0, -0.35, 1.0));
70 let _ = universe.attach(sun_dir, sun);
71 universe.add(sun_dir);
72
73 let light_transform = universe.world.add_component(
74 TransformComponent::new()
75 .with_position(1.0, 6.0, 3.0)
76 .with_scale(0.1, 0.1, 0.1),
77 );
78 let light = universe.world.add_component(
79 engine::ecs::component::PointLightComponent::new()
80 .with_distance(120.0)
81 .with_color(1.0, 1.0, 1.0),
82 );
83 let _ = universe.attach(light_transform, light);
84 universe.add(light_transform);
85
86 // --- VTuber model ---
87 let model_root = universe.world.add_component(TransformComponent::new());
88 let model = universe
89 .world
90 .add_component(GLTFComponent::new("assets/models/pc-rei.hoodie.glb"));
91 // emissive for pc-rei
92 let emissive = universe
93 .world
94 .add_component(engine::ecs::component::EmissiveComponent::on());
95
96 let _ = universe.attach(model, emissive);
97
98 let xr_input = universe.world.add_component(InputXRComponent::on());
99 let xr_gamepad = universe
100 .world
101 .add_component(engine::ecs::component::InputXRGamepadComponent::new().speed(1.5));
102 let xr_head = universe.world.add_component(TransformComponent::new());
103 let xr_camera = universe.world.add_component(CameraXRComponent::on());
104 let _ = universe.attach(xr_input, xr_head);
105 let _ = universe.attach(xr_input, xr_gamepad);
106 let _ = universe.attach(xr_head, xr_camera);
107 let _ = universe.attach(xr_head, model_root);
108
109 let _ = universe.attach(model_root, model);
110 universe.add(xr_input);
111 universe.add(model_root);
112
113 // --- Background clouds (occluded + lit) ---
114 let bg_root = universe.world.add_component(
115 engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
116 );
117 universe.add(bg_root);
118 let mut cloud_params = example_util::CloudRingParams::default();
119 cloud_params.cloud_count = 8; // +3 clusters
120 cloud_params.angle_jitter = 0.35;
121 cloud_params.high_y_probability = 0.5;
122 cloud_params.high_y_multiplier = 1.5;
123 cloud_params.seed = 0x57_55_B0_01u32;
124 example_util::spawn_cloud_ring(&mut universe, bg_root, cloud_params);
125
126 // --- Simple environment ---
127 let spawn_cube = |universe: &mut engine::Universe,
128 position: (f32, f32, f32),
129 scale: (f32, f32, f32),
130 color: (f32, f32, f32, f32)| {
131 let transform = universe.world.add_component(
132 TransformComponent::new()
133 .with_position(position.0, position.1, position.2)
134 .with_scale(scale.0, scale.1, scale.2),
135 );
136 let renderable = universe.world.add_component(RenderableComponent::cube());
137 let color = universe
138 .world
139 .add_component(ColorComponent::rgba(color.0, color.1, color.2, color.3));
140
141 let _ = universe.attach(transform, renderable);
142 let _ = universe.attach(renderable, color);
143
144 universe.add(transform);
145 };
146
147 // floor
148 spawn_cube(
149 &mut universe,
150 (0.0, -0.05, 0.0),
151 (10.0, 0.1, 10.0),
152 (0.92, 0.92, 0.92, 1.0),
153 );
154
155 // back wall
156 spawn_cube(
157 &mut universe,
158 (0.0, 1.5, -5.0),
159 (10.0, 3.0, 1.0),
160 (0.95, 0.94, 0.96, 1.0),
161 );
162
163 // desk
164 spawn_cube(
165 &mut universe,
166 (0.0, 0.35, 1.0),
167 (1.0, 0.75, 0.5),
168 (0.75, 0.70, 0.65, 1.0),
169 );
170
171 let xr_root = universe
172 .world
173 .add_component(engine::ecs::component::XrComponent::on());
174 universe.add(xr_root);
175
176 universe.systems.process_commands(
177 &mut universe.world,
178 &mut universe.visuals,
179 &mut universe.render_assets,
180 &mut universe.command_queue,
181 );
182
183 universe.enable_repl();
184 engine::Windowing::run_app(universe).expect("Windowing failed");
185}examples/gestures-and-gizmos.rs (line 69)
13fn build_gestures_and_gizmos_scene(universe: &mut engine::Universe) -> Scene {
14 use engine::ecs::component::{
15 BackgroundColorComponent, BackgroundComponent, Camera3DComponent, ColorComponent,
16 DirectionalLightComponent, InputComponent, InputTransformModeComponent, PointerComponent,
17 RaycastableComponent, RenderableComponent, TransformComponent, TransformGizmoComponent,
18 };
19 use engine::graphics::BuiltinMeshType;
20 use engine::graphics::primitives::{MaterialHandle, Renderable};
21
22 let tri_mesh = universe.render_assets.get_mesh(BuiltinMeshType::Triangle2D);
23 let cube_mesh = universe.render_assets.get_mesh(BuiltinMeshType::Cube);
24 let tetra_mesh = universe
25 .render_assets
26 .get_mesh(BuiltinMeshType::Tetrahedron);
27
28 // BackgroundColor { C.rgba }
29 let bg_color = universe
30 .world
31 .add_component(BackgroundColorComponent::new());
32 let bg_color_c = universe
33 .world
34 .add_component(ColorComponent::rgba(0.90, 0.90, 0.90, 1.0));
35 let _ = universe.world.add_child(bg_color, bg_color_c);
36 universe.add(bg_color);
37
38 // ambient light
39 let ambient = universe
40 .world
41 .add_component(AmbientLightComponent::rgb(0.25, 0.25, 0.25));
42 universe.add(ambient);
43
44 // ground plane
45 let ground_tx = universe.world.add_component(
46 TransformComponent::new()
47 .with_position(0.0, -2.5, 0.0)
48 .with_scale(20.0, 1.0, 20.0),
49 );
50 let ground_r = universe
51 .world
52 .add_component(RenderableComponent::new(Renderable::new(
53 universe.render_assets.get_mesh(BuiltinMeshType::Cube),
54 MaterialHandle::TOON_MESH,
55 )));
56 let ground_c = universe
57 .world
58 .add_component(ColorComponent::rgba(0.75, 0.75, 0.75, 1.0));
59 let _ = universe.attach(ground_tx, ground_r);
60 let _ = universe.attach(ground_r, ground_c);
61 let _ = universe.add(ground_tx);
62
63 // Background {
64 // with_occlusion_and_lighting()
65 // // using the example utils to add clouds to the background
66 // }
67 let bg_root = universe
68 .world
69 .add_component(BackgroundComponent::new().with_occlusion_and_lighting());
70 universe.add(bg_root);
71
72 // DirectionalLight {
73 // T { translate [1, 1, 1] }
74 // }
75 // Directional lights encode their direction in the node's world position.
76 let sun_t = universe
77 .world
78 .add_component(TransformComponent::new().with_position(1.0, 1.0, 1.0));
79 let sun = universe
80 .world
81 .add_component(DirectionalLightComponent::new());
82 let _ = universe.attach(sun_t, sun);
83 universe.add(sun_t);
84
85 // i = input
86 // t = transform
87 // c3d = camera3d
88 //
89 // I {
90 // T {
91 // C3D { with_fps_rotation().with_roll_axis_y() }
92 // }
93 // }
94 let input = universe
95 .world
96 .add_component(InputComponent::new().with_speed(2.5));
97 let input_mode = universe.world.add_component(
98 InputTransformModeComponent::forward_z()
99 .with_fps_rotation()
100 .with_roll_axis_y(),
101 );
102
103 let _ = universe.attach(input, input_mode);
104
105 // Forward is -Z, so put the camera at +Z looking toward the origin.
106 let rig_t = universe
107 .world
108 .add_component(TransformComponent::new().with_position(0.0, 0.0, 3.5));
109 let _ = universe.attach(input, rig_t);
110
111 let cam = universe
112 .world
113 .add_component(Camera3DComponent::new().with_far(600.0).with_fov(70.0));
114 let _ = universe.attach(rig_t, cam);
115
116 // Opt-in: treat this camera rig as a pointer source.
117 let pointer = universe.world.add_component(PointerComponent::new());
118 let _ = universe.attach(cam, pointer);
119
120 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
121 example_util::spawn_desktop_camera_controls_hint(universe, rig_t);
122
123 fn spawn_shape_with_gizmo(
124 universe: &mut engine::Universe,
125 mesh: engine::graphics::primitives::CpuMeshHandle,
126 pos: [f32; 3],
127 scale: [f32; 3],
128 rot_euler: [f32; 3],
129 color: [f32; 4],
130 ) {
131 let t = universe.world.add_component(
132 TransformComponent::new()
133 .with_position(pos[0], pos[1], pos[2])
134 .with_scale(scale[0], scale[1], scale[2])
135 .with_rotation_euler(rot_euler[0], rot_euler[1], rot_euler[2]),
136 );
137 let r = universe
138 .world
139 .add_component(RenderableComponent::new(Renderable::new(
140 mesh,
141 MaterialHandle::TOON_MESH,
142 )));
143 let c = universe
144 .world
145 .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
146 let rc = universe
147 .world
148 .add_component(RaycastableComponent::enabled());
149 let g = universe.world.add_component(TransformGizmoComponent::new());
150
151 let _ = universe.attach(t, r);
152 let _ = universe.attach(r, c);
153 let _ = universe.attach(r, rc);
154 let _ = universe.attach(t, g);
155
156 universe.add(t);
157 }
158
159 fn spawn_shape_raycastable_no_gizmo(
160 universe: &mut engine::Universe,
161 mesh: engine::graphics::primitives::CpuMeshHandle,
162 pos: [f32; 3],
163 scale: [f32; 3],
164 rot_euler: [f32; 3],
165 color: [f32; 4],
166 ) {
167 let t = universe.world.add_component(
168 TransformComponent::new()
169 .with_position(pos[0], pos[1], pos[2])
170 .with_scale(scale[0], scale[1], scale[2])
171 .with_rotation_euler(rot_euler[0], rot_euler[1], rot_euler[2]),
172 );
173 let r = universe
174 .world
175 .add_component(RenderableComponent::new(Renderable::new(
176 mesh,
177 MaterialHandle::TOON_MESH,
178 )));
179 let c = universe
180 .world
181 .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
182 let rc = universe
183 .world
184 .add_component(RaycastableComponent::enabled());
185
186 let _ = universe.attach(t, r);
187 let _ = universe.attach(r, c);
188 let _ = universe.attach(r, rc);
189
190 universe.add(t);
191 }
192
193 spawn_shape_with_gizmo(
194 universe,
195 tri_mesh,
196 [-1.2, 0.0, 0.0],
197 [0.65, 0.65, 0.65],
198 [0.0, 0.0, 0.0],
199 [0.2, 0.9, 0.25, 1.0],
200 );
201 spawn_shape_with_gizmo(
202 universe,
203 cube_mesh,
204 [0.0, 0.0, 0.0],
205 [0.55, 0.55, 0.55],
206 [0.0, 0.0, 0.0],
207 [0.95, 0.25, 0.2, 1.0],
208 );
209 spawn_shape_with_gizmo(
210 universe,
211 tetra_mesh,
212 [1.2, 0.0, 0.0],
213 [0.7, 0.7, 0.7],
214 [0.0, 0.0, 0.0],
215 [0.2, 0.55, 1.0, 1.0],
216 );
217
218 // Standalone tetrahedron: no gizmo, but explicitly raycastable.
219 // This helps isolate tetra picking vs gizmo-handle interception.
220 spawn_shape_raycastable_no_gizmo(
221 universe,
222 tetra_mesh,
223 [2.6, -0.6, 0.0],
224 [0.95, 0.95, 0.95],
225 [0.0, 0.0, 0.0],
226 [0.85, 0.85, 1.0, 1.0],
227 );
228
229 universe.add(input);
230
231 Scene { bg_root }
232}examples/background-occlusion-example.rs (line 121)
19fn main() {
20 mittens_engine::example_support::ensure_model_assets();
21 utils::logger::init();
22
23 let world = engine::ecs::World::default();
24 let mut universe = engine::Universe::new(world);
25
26 // Light purple-red background so the occlusion reads.
27 let clear = universe
28 .world
29 .add_component(engine::ecs::component::BackgroundColorComponent::new());
30 let clear_c = universe
31 .world
32 .add_component(engine::ecs::component::ColorComponent::rgba(
33 0.62, 0.38, 0.56, 1.0,
34 ));
35 let _ = universe.world.add_child(clear, clear_c);
36 universe.add(clear);
37
38 // A bit of ambient so the cluster volume reads.
39 let ambient = universe
40 .world
41 .add_component(engine::ecs::component::AmbientLightComponent::rgb(
42 0.20, 0.15, 0.3,
43 ));
44 universe.add(ambient);
45
46 // --- Camera rig (WASD/QE) ---
47 let input = universe
48 .world
49 .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
50 let input_mode = universe.world.add_component(
51 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
52 );
53 let _ = universe.attach(input, input_mode);
54
55 let rig_transform = universe.world.add_component(
56 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 6.0),
57 );
58 let _ = universe.attach(input, rig_transform);
59
60 let camera3d = universe.world.add_component(
61 engine::ecs::component::Camera3DComponent::new()
62 .with_far(200.0)
63 .with_fov(70.0),
64 );
65 let _ = universe.attach(rig_transform, camera3d);
66
67 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
68 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
69
70 // Key directional light toward the cloud volume.
71 //
72 // Directional lights encode their direction in the node's world position.
73 // The shader normalizes this vector.
74 let sun = universe.world.add_component(
75 engine::ecs::component::DirectionalLightComponent::new()
76 .with_intensity(1.6)
77 .with_color(1.0, 0.98, 0.92),
78 );
79 // Pointing from the clouds back toward the camera a bit (+Z), and slightly from above.
80 let sun_dir = universe.world.add_component(
81 engine::ecs::component::TransformComponent::new().with_position(0.15, 0.65, 0.75),
82 );
83 let _ = universe.attach(sun_dir, sun);
84
85 // A couple point lights to fill/shade the cloud volume.
86 let light_a = universe.world.add_component(
87 engine::ecs::component::PointLightComponent::new()
88 .with_distance(80.0)
89 .with_intensity(3.0)
90 .with_color(0.9, 0.95, 1.0),
91 );
92 let light_a_tx = universe.world.add_component(
93 engine::ecs::component::TransformComponent::new().with_position(8.0, 8.0, 5.0),
94 );
95 let _ = universe.attach(light_a_tx, light_a);
96
97 let light_b = universe.world.add_component(
98 engine::ecs::component::PointLightComponent::new()
99 .with_distance(80.0)
100 .with_intensity(2.2)
101 .with_color(1.0, 0.9, 0.85),
102 );
103 let light_b_tx = universe.world.add_component(
104 engine::ecs::component::TransformComponent::new().with_position(-10.0, 4.0, -6.0),
105 );
106 let _ = universe.attach(light_b_tx, light_b);
107
108 universe.add(input);
109 universe.add(sun_dir);
110 universe.add(light_a_tx);
111 universe.add(light_b_tx);
112
113 let cube_mesh = universe
114 .render_assets
115 .get_mesh(engine::graphics::BuiltinMeshType::Cube);
116
117 // --- Background occluded+lit world ---
118 // Renderables under this node participate in a background stage that depth-writes for
119 // self-occlusion + uses the normal lighting shader inputs.
120 let bg_root = universe.world.add_component(
121 engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
122 );
123 universe.add(bg_root);
124
125 // Create overlapping cube clusters like cloud puffs.
126 // Place them generally in front of the camera (negative Z).
127 // Spawn two groups on opposite X sides.
128 let cluster_count: u32 = 9;
129 for (group_i, group_x) in [-16.0_f32, 16.0_f32].into_iter().enumerate() {
130 let group_tx = universe.world.add_component(
131 engine::ecs::component::TransformComponent::new().with_position(group_x, 0.0, 0.0),
132 );
133 let _ = universe.attach(bg_root, group_tx);
134
135 for cluster_i in 0..cluster_count {
136 let seed = (0xC10u32 ^ (group_i as u32).wrapping_mul(0x9E37_79B9))
137 ^ (cluster_i.wrapping_mul(7919));
138
139 let cx = (rand01(seed ^ 0xA341_316C) - 0.5) * 18.0;
140 let cy = (rand01(seed ^ 0xC801_3EA4) - 0.5) * 10.0 + 2.0;
141 let cz = -18.0 - rand01(seed ^ 0xB529_7A4D) * 26.0;
142
143 let center_tx = universe.world.add_component(
144 engine::ecs::component::TransformComponent::new().with_position(cx, cy, cz),
145 );
146 let _ = universe.attach(group_tx, center_tx);
147
148 let puffs = 18u32;
149 for puff_i in 0..puffs {
150 let puff_seed = seed ^ puff_i.wrapping_mul(1_103_515_245);
151
152 // Slightly ellipsoidal distribution.
153 let ox = (rand01(puff_seed ^ 0x68bc_21eb) - 0.5) * 7.0;
154 let oy = (rand01(puff_seed ^ 0x02e5_be93) - 0.5) * 3.0;
155 let oz = (rand01(puff_seed ^ 0xa1d3_4f2b) - 0.5) * 7.0;
156
157 let base = 0.7 + rand01(puff_seed ^ 0x9e37_79b9) * 2.6;
158 let sx = base * (0.7 + rand01(puff_seed ^ 0x243f_6a88) * 0.8);
159 let sy = base * (0.6 + rand01(puff_seed ^ 0x85a3_08d3) * 0.9);
160 let sz = base * (0.7 + rand01(puff_seed ^ 0x1319_8a2e) * 0.8);
161
162 let tx = universe.world.add_component(
163 engine::ecs::component::TransformComponent::new()
164 .with_position(ox, oy, oz)
165 .with_scale(sx, sy, sz),
166 );
167 let renderable =
168 universe
169 .world
170 .add_component(engine::ecs::component::RenderableComponent::new(
171 engine::graphics::primitives::Renderable::new(
172 cube_mesh,
173 engine::graphics::primitives::MaterialHandle::TOON_MESH,
174 ),
175 ));
176
177 // Slight blue-grey variation.
178 let t = rand01(puff_seed ^ 0x7f4a_7c15);
179 let r = 0.55 + 0.10 * t;
180 let g = 0.58 + 0.10 * t;
181 let b = 0.66 + 0.12 * t;
182 let color = universe
183 .world
184 .add_component(engine::ecs::component::ColorComponent::rgba(r, g, b, 1.0));
185
186 let _ = universe.attach(center_tx, tx);
187 let _ = universe.attach(tx, renderable);
188 let _ = universe.attach(renderable, color);
189 }
190 }
191 }
192
193 // Foreground reference cube (should never be occluded by background depth).
194 let fg_tx = universe.world.add_component(
195 engine::ecs::component::TransformComponent::new()
196 .with_position(0.0, -0.5, -4.0)
197 .with_scale(1.2, 0.8, 1.2),
198 );
199 let fg_renderable =
200 universe
201 .world
202 .add_component(engine::ecs::component::RenderableComponent::new(
203 engine::graphics::primitives::Renderable::new(
204 cube_mesh,
205 engine::graphics::primitives::MaterialHandle::TOON_MESH,
206 ),
207 ));
208 let fg_color = universe
209 .world
210 .add_component(engine::ecs::component::ColorComponent::rgba(
211 0.9, 0.2, 0.2, 1.0,
212 ));
213
214 universe.add(fg_tx);
215 let _ = universe.attach(fg_tx, fg_renderable);
216 let _ = universe.attach(fg_renderable, fg_color);
217
218 // Foreground opaque floor: 16x16 larger white cubes, 10 units below the reference cube.
219 let floor_root = universe.world.add_component(
220 engine::ecs::component::TransformComponent::new().with_position(0.0, -10.5, -10.0),
221 );
222 universe.add(floor_root);
223
224 let spacing = 10_f32;
225 let half = 5_f32;
226 for x in 0..16u32 {
227 for z in 0..64u32 {
228 let px = (x as f32 - half) * spacing;
229 let pz = (z as f32 * -1.0 + half) * spacing;
230 let tx = universe.world.add_component(
231 engine::ecs::component::TransformComponent::new()
232 .with_position(px, -5.0, pz)
233 .with_scale(5.0, 0.5, 5.0),
234 );
235 let renderable =
236 universe
237 .world
238 .add_component(engine::ecs::component::RenderableComponent::new(
239 engine::graphics::primitives::Renderable::new(
240 cube_mesh,
241 engine::graphics::primitives::MaterialHandle::TOON_MESH,
242 ),
243 ));
244 let color = universe
245 .world
246 .add_component(engine::ecs::component::ColorComponent::rgba(
247 1.0, 1.0, 1.0, 1.0,
248 ));
249
250 let _ = universe.attach(floor_root, tx);
251 let _ = universe.attach(tx, renderable);
252 let _ = universe.attach(renderable, color);
253 }
254 }
255
256 universe.systems.process_commands(
257 &mut universe.world,
258 &mut universe.visuals,
259 &mut universe.render_assets,
260 &mut universe.command_queue,
261 );
262
263 universe.enable_repl();
264 engine::Windowing::run_app(universe).expect("Windowing failed");
265}examples/font-example.rs (line 52)
13fn main() {
14 mittens_engine::example_support::ensure_model_assets();
15 utils::logger::init();
16
17 let world = engine::ecs::World::default();
18 let mut universe = engine::Universe::new(world);
19
20 // Dark background so the font texture pops.
21 let background = universe
22 .world
23 .add_component(BackgroundColorComponent::new());
24 let background_c = universe
25 .world
26 .add_component(ColorComponent::rgba(0.20, 0.2, 0.20, 1.0));
27 let _ = universe.world.add_child(background, background_c);
28 universe.add(background);
29
30 // Ambient so text is readable even without explicit lights.
31 let ambient = universe
32 .world
33 .add_component(AmbientLightComponent::rgb(0.50, 0.50, 0.50));
34 universe.add(ambient);
35
36 let directional_tx = universe
37 .world
38 .add_component(TransformComponent::new().with_position(0.0, 0.5, 1.0));
39 let directional_light = universe.world.add_component(
40 engine::ecs::component::DirectionalLightComponent::new()
41 .with_color(1.0, 1.0, 1.0)
42 .with_intensity(0.8),
43 );
44 let _ = universe.attach(directional_tx, directional_light);
45 universe.add(directional_tx);
46
47 // --- background clouds ---
48 // Background stage (occluded + lit) so the cloud volume self-occludes but won't occlude
49 // the foreground text (renderer clears depth before foreground).
50 let bg_root = universe
51 .world
52 .add_component(BackgroundComponent::new().with_occlusion_and_lighting());
53 universe.add(bg_root);
54
55 let mut bg_cloud_params = example_util::CloudRingParams::default();
56 bg_cloud_params.cloud_count = 6;
57 bg_cloud_params.seed = 0xF0_17_C10u32;
58 example_util::spawn_cloud_ring(&mut universe, bg_root, bg_cloud_params);
59
60 // I {
61 // // not fps rotation, just relative rotation
62 // with_forward_z()
63 // with_roll_axis_y()
64 // C3D {}
65 // }
66 let input = universe
67 .world
68 .add_component(InputComponent::new().with_speed(2.0));
69 let input_mode = universe
70 .world
71 .add_component(InputTransformModeComponent::forward_z().with_roll_axis_y());
72 let _ = universe.attach(input, input_mode);
73
74 let rig_transform = universe
75 .world
76 .add_component(TransformComponent::new().with_position(1.8, -0.5, 2.5));
77 let _ = universe.attach(input, rig_transform);
78
79 let camera = universe.world.add_component(Camera3DComponent::new());
80 let _ = universe.attach(rig_transform, camera);
81
82 // Click-to-pick: treat this camera rig as a pointer source.
83 let pointer = universe.world.add_component(PointerComponent::new());
84 let _ = universe.attach(camera, pointer);
85
86 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
87 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
88
89 universe.add(input);
90
91 // --- foreground clouds ---
92 // Normal foreground renderables (not background stage).
93 // Offset the ring forward (negative Z) so several clusters are in view.
94 let fg_cloud_root = universe
95 .world
96 .add_component(TransformComponent::new().with_position(0.0, -6.0, -10.0));
97 universe.add(fg_cloud_root);
98
99 let mut fg_cloud_params = example_util::CloudRingParams::default();
100 fg_cloud_params.cloud_count = 4;
101 fg_cloud_params.radius = 9.0;
102 fg_cloud_params.center_y = 1.0;
103 fg_cloud_params.puffs_per_cloud = 22;
104 fg_cloud_params.angle_jitter = 0.35;
105 fg_cloud_params.high_y_probability = 0.35;
106 fg_cloud_params.high_y_multiplier = 1.4;
107 fg_cloud_params.seed = 0xF0_17_C102u32;
108 example_util::spawn_cloud_ring(&mut universe, fg_cloud_root, fg_cloud_params);
109
110 // T {
111 // with_translation(0,0, -2)
112 // TXT {
113 // "ababaabbaabbaaabbbaaabbbaaaabbbbaaaaabbbbbababababa"
114 // TextureComponent { assets/images/test.font_system.png }
115 // }
116 // }
117 fn estimate_text_height_world(text: &str, scale: f32) -> f32 {
118 let line_count = text.lines().count().max(1) as f32;
119 // Text quads are ~1 unit tall per line in text-space.
120 // Add some padding so blocks don't feel cramped.
121 let pad_lines = 1.25;
122 (line_count + pad_lines) * scale
123 }
124
125 fn spawn_text_block(
126 universe: &mut engine::Universe,
127 position: (f32, f32, f32),
128 scale: f32,
129 text: &str,
130 font_texture_uri: Option<&str>,
131 text_color_rgba: Option<[f32; 4]>,
132 shadow: Option<TextShadowComponent>,
133 filtering: TextureFilteringComponent,
134 ) -> f32 {
135 // T_root { T_scale { [Color] { TXT { shadow, filtering } } } }
136 let text_root = universe.world.add_component(
137 TransformComponent::new().with_position(position.0, position.1, position.2),
138 );
139
140 let text_scale = universe
141 .world
142 .add_component(TransformComponent::new().with_scale(scale, scale, 1.0));
143 let _ = universe.attach(text_root, text_scale);
144
145 let text_parent = if let Some([r, g, b, a]) = text_color_rgba {
146 let color = universe
147 .world
148 .add_component(ColorComponent::rgba(r, g, b, a));
149 let _ = universe.attach(text_scale, color);
150 color
151 } else {
152 text_scale
153 };
154
155 let text_c = universe.world.add_component(TextComponent::new(text));
156 let _ = universe.attach(text_parent, text_c);
157
158 // Explicit opt-in: make the glyph renderables pickable.
159 // TextSystem will propagate this to all spawned glyph quads.
160 let raycastable = universe
161 .world
162 .add_component(RaycastableComponent::enabled());
163 let _ = universe.attach(text_c, raycastable);
164
165 // Route glyph quads into the alpha-to-coverage cutout pass.
166 let cutout = universe
167 .world
168 .add_component(TransparentCutoutComponent::new());
169 let _ = universe.attach(text_c, cutout);
170
171 if let Some(shadow) = shadow {
172 let shadow_id = universe.world.add_component(shadow);
173 let _ = universe.attach(text_c, shadow_id);
174 }
175
176 // Optional: override the font atlas for this text block.
177 // TextSystem will propagate this to all glyph renderables.
178 if let Some(uri) = font_texture_uri {
179 let tex = universe
180 .world
181 .add_component(TextureComponent::with_uri(uri));
182 let _ = universe.attach(text_c, tex);
183 }
184
185 let filtering_id = universe.world.add_component(filtering);
186 let _ = universe.attach(text_c, filtering_id);
187
188 universe.add(text_root);
189
190 estimate_text_height_world(text, scale)
191 }
192
193 // --- text blocks ---
194 // Multi-line samples at different scales.
195 // (Text literals omitted in the README/snippet; see constants below.)
196 const TEXT_BIG: &str = "CAT ENGINE\nfont example\nBIG TEXT";
197 const TEXT_MED: &str = "multi-line\ntext block\nmedium";
198 const TEXT_SMALL: &str = "small\ntext";
199 const TEXT_TINY: &str = "tiny\ntext\n(zoom in)";
200
201 // Stack vertically; advance by (measured height + gap) so big text gets more room.
202 let x = -1.2;
203 let z = -2.0;
204 let mut y = 1.2;
205 let gap = 0.15;
206
207 let shadow_crisp = Some(
208 TextShadowComponent::new()
209 .with_scale(1.35)
210 .with_offset([0.06, -0.06, 0.0015]),
211 );
212
213 y -= spawn_text_block(
214 &mut universe,
215 (x, y, z),
216 0.55,
217 TEXT_BIG,
218 None,
219 Some([1.0, 1.0, 1.0, 1.0]),
220 shadow_crisp,
221 TextureFilteringComponent::nearest_magnification(),
222 ) + gap;
223 y -= spawn_text_block(
224 &mut universe,
225 (x, y, z),
226 0.25,
227 TEXT_MED,
228 None,
229 Some([0.60, 0.95, 1.00, 1.0]),
230 Some(
231 TextShadowComponent::new()
232 .with_rgba([0.0, 0.0, 0.15, 1.0])
233 .with_scale(1.20)
234 .with_offset([0.05, -0.04, 0.0015]),
235 ),
236 TextureFilteringComponent::linear(),
237 ) + gap;
238 y -= spawn_text_block(
239 &mut universe,
240 (x, y, z),
241 0.14,
242 TEXT_SMALL,
243 None,
244 Some([1.0, 0.88, 0.35, 1.0]),
245 Some(
246 TextShadowComponent::new()
247 .with_rgba([0.15, 0.0, 0.0, 1.0])
248 .with_scale(1.55)
249 .with_offset([0.08, -0.08, 0.0015]),
250 ),
251 TextureFilteringComponent::nearest(),
252 ) + gap;
253 let _ = spawn_text_block(
254 &mut universe,
255 (x, y, z),
256 0.08,
257 TEXT_TINY,
258 None,
259 Some([0.90, 1.0, 0.70, 1.0]),
260 shadow_crisp,
261 TextureFilteringComponent::nearest_magnification(),
262 );
263
264 // Left block: explicit multi-line text using the default font_system atlas.
265 const TEXT_LEFT: &str = "even though there's hexes\nto the solar plexus in my lexus\ni'm feelin' reckless,\nwhen i'm eating breakfast";
266 let _ = spawn_text_block(
267 &mut universe,
268 (x - 8.1, 1.1, z),
269 0.22,
270 TEXT_LEFT,
271 Some("assets/textures/font_system.dds"),
272 Some([0.95, 0.95, 0.95, 1.0]),
273 Some(
274 TextShadowComponent::new()
275 .with_scale(1.25)
276 .with_offset([0.05, -0.05, 0.0015]),
277 ),
278 TextureFilteringComponent::nearest_magnification(),
279 );
280
281 // Alt atlas: put it *behind* the original stack (slightly farther from the camera)
282 // and tint it dark grey.
283 let alt_atlas = Some("assets/textures/font_system.0.0.dds");
284 let alt_z = z - 0.05;
285 let alt_grey = Some([0.25, 0.25, 0.25, 1.0]);
286
287 let mut y_alt = 1.2;
288 y_alt -= spawn_text_block(
289 &mut universe,
290 (x, y_alt, alt_z),
291 0.55,
292 TEXT_BIG,
293 alt_atlas,
294 alt_grey,
295 Some(
296 TextShadowComponent::new()
297 .with_rgba([0.0, 0.0, 0.0, 1.0])
298 .with_scale(1.15)
299 .with_offset([0.03, -0.03, 0.0015]),
300 ),
301 TextureFilteringComponent::linear(),
302 ) + gap;
303 y_alt -= spawn_text_block(
304 &mut universe,
305 (x, y_alt, alt_z),
306 0.25,
307 TEXT_MED,
308 alt_atlas,
309 alt_grey,
310 Some(
311 TextShadowComponent::new()
312 .with_rgba([0.0, 0.0, 0.0, 1.0])
313 .with_scale(1.15)
314 .with_offset([0.03, -0.03, 0.0015]),
315 ),
316 TextureFilteringComponent::linear(),
317 ) + gap;
318 y_alt -= spawn_text_block(
319 &mut universe,
320 (x, y_alt, alt_z),
321 0.14,
322 TEXT_SMALL,
323 alt_atlas,
324 alt_grey,
325 Some(
326 TextShadowComponent::new()
327 .with_rgba([0.0, 0.0, 0.0, 1.0])
328 .with_scale(1.15)
329 .with_offset([0.03, -0.03, 0.0015]),
330 ),
331 TextureFilteringComponent::linear(),
332 ) + gap;
333 let _ = spawn_text_block(
334 &mut universe,
335 (x, y_alt, alt_z),
336 0.08,
337 TEXT_TINY,
338 alt_atlas,
339 alt_grey,
340 Some(
341 TextShadowComponent::new()
342 .with_rgba([0.0, 0.0, 0.0, 1.0])
343 .with_scale(1.15)
344 .with_offset([0.03, -0.03, 0.0015]),
345 ),
346 TextureFilteringComponent::linear(),
347 );
348
349 universe.enable_repl();
350
351 let xr_input = universe.world.add_component(InputXRComponent::on());
352 let xr_gamepad = universe
353 .world
354 .add_component(engine::ecs::component::InputXRGamepadComponent::new().speed(1.5));
355 let xr_head = universe.world.add_component(TransformComponent::new());
356 let xr_camera = universe.world.add_component(CameraXRComponent::on());
357 let _ = universe.attach(xr_input, xr_head);
358 let _ = universe.attach(xr_input, xr_gamepad);
359 let _ = universe.attach(xr_head, xr_camera);
360 universe.add(xr_input);
361
362 // Add an OpenXR component so OpenXRSystem initializes and starts polling events.
363 let xr_root = universe
364 .world
365 .add_component(engine::ecs::component::XrComponent::on());
366 universe.add(xr_root);
367
368 // Process init-time registrations (Text expands into glyph subtrees here).
369 universe.systems.process_commands(
370 &mut universe.world,
371 &mut universe.visuals,
372 &mut universe.render_assets,
373 &mut universe.command_queue,
374 );
375
376 engine::Windowing::run_app(universe).expect("Windowing failed");
377}Additional examples can be found in:
pub fn with_ray_casting(self) -> Self
Trait Implementations§
Source§impl Clone for BackgroundComponent
impl Clone for BackgroundComponent
Source§fn clone(&self) -> BackgroundComponent
fn clone(&self) -> BackgroundComponent
Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreSource§impl Component for BackgroundComponent
impl Component for BackgroundComponent
fn as_any(&self) -> &dyn Any
fn as_any_mut(&mut self) -> &mut dyn Any
Source§fn name(&self) -> &'static str
fn name(&self) -> &'static str
Short debug/type name for this component kind (e.g. “transform”, “camera”).
Source§fn to_mms_ast(&self, _world: &World) -> ComponentExpression
fn to_mms_ast(&self, _world: &World) -> ComponentExpression
Encode this component as an MMS Component Expression AST node. Read more
fn set_id(&mut self, _component: ComponentId)
Source§fn init(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)
fn init(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)
Called when component is added to the World
Source§fn cleanup(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)
fn cleanup(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)
Called when component is removed from the World.
impl Copy for BackgroundComponent
Source§impl Debug for BackgroundComponent
impl Debug for BackgroundComponent
Source§impl Default for BackgroundComponent
impl Default for BackgroundComponent
Source§fn default() -> BackgroundComponent
fn default() -> BackgroundComponent
Returns the “default value” for a type. Read more
Auto Trait Implementations§
impl Freeze for BackgroundComponent
impl RefUnwindSafe for BackgroundComponent
impl Send for BackgroundComponent
impl Sync for BackgroundComponent
impl Unpin for BackgroundComponent
impl UnsafeUnpin for BackgroundComponent
impl UnwindSafe for BackgroundComponent
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Convert
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.