pub struct TextShadowComponent {
pub rgba: [f32; 4],
pub scale: f32,
pub offset: [f32; 3],
/* private fields */
}Expand description
Text shadow styling.
If a TextShadowComponent is parented to a TextComponent, TextSystem will spawn additional
shadow renderables for every glyph.
Fields§
§rgba: [f32; 4]Shadow color (RGBA). Default: black.
scale: f32Shadow scale multiplier. Default: 1.25.
offset: [f32; 3]Shadow XYZ offset in glyph-local space.
For Z: TextSystem uses this as a magnitude to nudge the shadow behind the main glyph
to avoid z-fighting.
Default: [0.0, 0.0, 0.001].
Implementations§
Source§impl TextShadowComponent
impl TextShadowComponent
pub const DEFAULT_RGBA: [f32; 4]
pub const DEFAULT_SCALE: f32 = 1.25
pub const DEFAULT_OFFSET: [f32; 3]
Sourcepub fn new() -> Self
pub fn new() -> Self
Examples found in repository?
examples/text-animation.rs (line 82)
6fn main() {
7 mittens_engine::example_support::ensure_model_assets();
8 utils::logger::init();
9
10 let world = engine::ecs::World::default();
11 let mut universe = engine::Universe::new(world);
12
13 // Input-driven camera rig.
14 // Topology: I { T { C3D } }
15 let input = universe
16 .world
17 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
18 let camera3d = universe
19 .world
20 .add_component(engine::ecs::component::Camera3DComponent::new());
21 let rig_transform = universe.world.add_component(
22 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.5),
23 );
24 let input_mode = universe.world.add_component(
25 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
26 );
27 let _ = universe.attach(input, input_mode);
28 let _ = universe.attach(input, rig_transform);
29 let _ = universe.attach(rig_transform, camera3d);
30
31 // Small on-screen help.
32 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
33 universe.add(input);
34
35 // Light so we can see non-emissive materials.
36 let light_transform = universe.world.add_component(
37 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.0),
38 );
39 let light = universe.world.add_component(
40 engine::ecs::component::PointLightComponent::new()
41 .with_distance(25.0)
42 .with_color(1.0, 1.0, 1.0),
43 );
44 let _ = universe.attach(light_transform, light);
45 universe.add(light_transform);
46
47 use engine::ecs::component::{
48 ColorComponent, TextComponent, TextShadowComponent, TextureComponent,
49 TextureFilteringComponent, TransformComponent, TransparentCutoutComponent,
50 };
51
52 // Styled text root.
53 let text_root = universe.world.add_component(
54 TransformComponent::new()
55 .with_position(0.0, 0.0, 0.0)
56 .with_scale(0.25, 0.25, 1.0),
57 );
58
59 // Color must be an ancestor of the glyph renderables.
60 let color_id = universe
61 .world
62 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
63 let _ = universe.attach(text_root, color_id);
64
65 // This is the component we animate via SetText.
66 let text_id = universe.world.add_component(TextComponent::new(">w<"));
67 let _ = universe.attach(color_id, text_id);
68
69 // Route into cutout pass for cleaner edges.
70 let cutout = universe
71 .world
72 .add_component(TransparentCutoutComponent::new());
73 let _ = universe.attach(text_id, cutout);
74
75 // Font atlas.
76 let tex = universe.world.add_component(TextureComponent::with_uri(
77 "assets/textures/font_system.dds",
78 ));
79 let _ = universe.attach(text_id, tex);
80
81 let shadow = universe.world.add_component(
82 TextShadowComponent::new()
83 .with_scale(1.35)
84 .with_offset([0.06, -0.06, 0.0015]),
85 );
86 let filtering = universe
87 .world
88 .add_component(TextureFilteringComponent::nearest_magnification());
89
90 let _ = universe.attach(text_id, shadow);
91 let _ = universe.attach(text_id, filtering);
92
93 universe.add(text_root);
94
95 let clock_component = universe
96 .world
97 .add_component(engine::ecs::component::ClockComponent::new().with_bpm(140.0));
98 let _ = universe.add(clock_component);
99
100 // Looping animation: 4 beats long, 4 keyframes.
101 let anim = universe
102 .world
103 .add_component(engine::ecs::component::AnimationComponent::new());
104
105 let faces = [">w<", "^w^", "-_-", "o_o"];
106 for (i, &face) in faces.iter().enumerate() {
107 let kf = universe
108 .world
109 .add_component(engine::ecs::component::KeyframeComponent::new(i as f64));
110
111 // Each keyframe emits a SetText intent.
112 let action = universe
113 .world
114 .add_component(engine::ecs::component::ActionComponent::new(
115 engine::ecs::IntentValue::SetText {
116 component_ids: vec![text_id],
117 text: face.to_string(),
118 },
119 ));
120
121 let _ = universe.attach(anim, kf);
122 let _ = universe.attach(kf, action);
123 }
124
125 universe.add(anim);
126
127 // Process init-time registrations (Text expands into glyph subtrees here).
128 universe.systems.process_commands(
129 &mut universe.world,
130 &mut universe.visuals,
131 &mut universe.render_assets,
132 &mut universe.command_queue,
133 );
134
135 engine::Windowing::run_app(universe).expect("Windowing failed");
136}More examples
examples/text-example.rs (line 163)
6fn main() {
7 mittens_engine::example_support::ensure_model_assets();
8 utils::logger::init();
9
10 let world = engine::ecs::World::default();
11 let mut universe = engine::Universe::new(world);
12
13 // Input-driven camera rig.
14 // Topology: I { T { C3D } }
15 let input = universe
16 .world
17 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
18 let camera3d = universe
19 .world
20 .add_component(engine::ecs::component::Camera3DComponent::new());
21 let rig_transform = universe.world.add_component(
22 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.5),
23 );
24 let input_mode = universe.world.add_component(
25 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
26 );
27 let _ = universe.attach(input, input_mode);
28 let _ = universe.attach(input, rig_transform);
29 let _ = universe.attach(rig_transform, camera3d);
30
31 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
32 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
33 universe.add(input);
34
35 // Debug square: show the full font texture.
36 let debug_root = universe.world.add_component(
37 engine::ecs::component::TransformComponent::new()
38 .with_position(0.6, -0.2, 0.0)
39 .with_scale(0.8, 0.8, 1.0),
40 );
41 let debug_renderable = universe
42 .world
43 .add_component(engine::ecs::component::RenderableComponent::square());
44 let debug_tex =
45 universe
46 .world
47 .add_component(engine::ecs::component::TextureComponent::with_uri(
48 "assets/textures/font_system.dds",
49 ));
50 let debug_filtering = universe
51 .world
52 .add_component(engine::ecs::component::TextureFilteringComponent::nearest_magnification());
53 let _ = universe.attach(debug_root, debug_renderable);
54 let _ = universe.attach(debug_renderable, debug_tex);
55 let _ = universe.attach(debug_renderable, debug_filtering);
56 universe.add(debug_root);
57
58 // Light so we can actually see non-emissive materials.
59 let light_transform = universe.world.add_component(
60 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.0),
61 );
62 let light = universe.world.add_component(
63 engine::ecs::component::PointLightComponent::new()
64 .with_distance(25.0)
65 .with_color(1.0, 1.0, 1.0),
66 );
67 let _ = universe.attach(light_transform, light);
68 universe.add(light_transform);
69
70 // 4 red cubes around the perimeter of the world (easy visual anchors).
71 fn spawn_red_cube(universe: &mut engine::Universe, x: f32, y: f32, z: f32, s: f32) {
72 let t = universe.world.add_component(
73 engine::ecs::component::TransformComponent::new()
74 .with_position(x, y, z)
75 .with_scale(s, s, s),
76 );
77 let r = universe
78 .world
79 .add_component(engine::ecs::component::RenderableComponent::cube());
80 let c = universe
81 .world
82 .add_component(engine::ecs::component::ColorComponent::rgba(
83 1.0, 0.0, 0.0, 1.0,
84 ));
85 let e = universe
86 .world
87 .add_component(engine::ecs::component::EmissiveComponent::on());
88
89 let _ = universe.attach(t, r);
90 let _ = universe.attach(r, c);
91 let _ = universe.attach(r, e);
92
93 universe.add(t);
94 }
95
96 let p = 1.5;
97 let s = 0.25;
98 spawn_red_cube(&mut universe, -p, -p, 0.0, s);
99 spawn_red_cube(&mut universe, p, -p, 0.0, s);
100 spawn_red_cube(&mut universe, -p, p, 0.0, s);
101 spawn_red_cube(&mut universe, p, p, 0.0, s);
102
103 use engine::ecs::component::{
104 ColorComponent, TextComponent, TextShadowComponent, TextureComponent,
105 TextureFilteringComponent, TransformComponent, TransparentCutoutComponent,
106 };
107
108 fn spawn_text_style(
109 universe: &mut engine::Universe,
110 pos: [f32; 3],
111 scale: f32,
112 text: &str,
113 color: [f32; 4],
114 shadow: TextShadowComponent,
115 filtering: TextureFilteringComponent,
116 ) {
117 let root = universe.world.add_component(
118 TransformComponent::new()
119 .with_position(pos[0], pos[1], pos[2])
120 .with_scale(scale, scale, 1.0),
121 );
122
123 // Color must be an ancestor of the glyph renderables.
124 let color_id = universe
125 .world
126 .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
127 let _ = universe.attach(root, color_id);
128
129 let text_id = universe.world.add_component(TextComponent::new(text));
130 let _ = universe.attach(color_id, text_id);
131
132 // Route into cutout pass for cleaner edges.
133 let cutout = universe
134 .world
135 .add_component(TransparentCutoutComponent::new());
136 let _ = universe.attach(text_id, cutout);
137
138 // Use the same atlas as the debug quad.
139 let tex = universe
140 .world
141 .add_component(TextureComponent::with_uri("assets/textures/font.dds"));
142 let _ = universe.attach(text_id, tex);
143
144 let shadow_id = universe.world.add_component(shadow);
145 let _ = universe.attach(text_id, shadow_id);
146
147 let filtering_id = universe.world.add_component(filtering);
148 let _ = universe.attach(text_id, filtering_id);
149
150 universe.add(root);
151 }
152
153 // Multiple text samples to show:
154 // - different inherited colors
155 // - different shadow settings
156 // - different texture filtering
157 spawn_text_style(
158 &mut universe,
159 [-0.95, 0.45, 0.0],
160 0.12,
161 "NEAREST_MAG\n(crisp)\nAaBbCc 123",
162 [1.0, 1.0, 1.0, 1.0],
163 TextShadowComponent::new()
164 .with_scale(1.35)
165 .with_offset([0.06, -0.06, 0.0015]),
166 TextureFilteringComponent::nearest_magnification(),
167 );
168
169 spawn_text_style(
170 &mut universe,
171 [-0.95, 0.05, 0.0],
172 0.12,
173 "LINEAR\n(softer)\nAaBbCc 123",
174 [0.55, 0.90, 1.0, 1.0],
175 TextShadowComponent::new()
176 .with_rgba([0.0, 0.0, 0.15, 1.0])
177 .with_scale(1.20)
178 .with_offset([0.05, -0.04, 0.0015]),
179 TextureFilteringComponent::linear(),
180 );
181
182 spawn_text_style(
183 &mut universe,
184 [-0.95, -0.35, 0.0],
185 0.12,
186 "NEAREST\n(pixelly)\nAaBbCc 123",
187 [1.0, 0.85, 0.35, 1.0],
188 TextShadowComponent::new()
189 .with_rgba([0.15, 0.0, 0.0, 1.0])
190 .with_scale(1.55)
191 .with_offset([0.08, -0.08, 0.0015]),
192 TextureFilteringComponent::nearest(),
193 );
194
195 // Process init-time registrations (Text expands into glyph subtrees here).
196 universe.systems.process_commands(
197 &mut universe.world,
198 &mut universe.visuals,
199 &mut universe.render_assets,
200 &mut universe.command_queue,
201 );
202
203 engine::Windowing::run_app(universe).expect("Windowing failed");
204}examples/font-example.rs (line 208)
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}Sourcepub fn with_rgba(self, rgba: [f32; 4]) -> Self
pub fn with_rgba(self, rgba: [f32; 4]) -> Self
Examples found in repository?
examples/text-example.rs (line 176)
6fn main() {
7 mittens_engine::example_support::ensure_model_assets();
8 utils::logger::init();
9
10 let world = engine::ecs::World::default();
11 let mut universe = engine::Universe::new(world);
12
13 // Input-driven camera rig.
14 // Topology: I { T { C3D } }
15 let input = universe
16 .world
17 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
18 let camera3d = universe
19 .world
20 .add_component(engine::ecs::component::Camera3DComponent::new());
21 let rig_transform = universe.world.add_component(
22 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.5),
23 );
24 let input_mode = universe.world.add_component(
25 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
26 );
27 let _ = universe.attach(input, input_mode);
28 let _ = universe.attach(input, rig_transform);
29 let _ = universe.attach(rig_transform, camera3d);
30
31 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
32 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
33 universe.add(input);
34
35 // Debug square: show the full font texture.
36 let debug_root = universe.world.add_component(
37 engine::ecs::component::TransformComponent::new()
38 .with_position(0.6, -0.2, 0.0)
39 .with_scale(0.8, 0.8, 1.0),
40 );
41 let debug_renderable = universe
42 .world
43 .add_component(engine::ecs::component::RenderableComponent::square());
44 let debug_tex =
45 universe
46 .world
47 .add_component(engine::ecs::component::TextureComponent::with_uri(
48 "assets/textures/font_system.dds",
49 ));
50 let debug_filtering = universe
51 .world
52 .add_component(engine::ecs::component::TextureFilteringComponent::nearest_magnification());
53 let _ = universe.attach(debug_root, debug_renderable);
54 let _ = universe.attach(debug_renderable, debug_tex);
55 let _ = universe.attach(debug_renderable, debug_filtering);
56 universe.add(debug_root);
57
58 // Light so we can actually see non-emissive materials.
59 let light_transform = universe.world.add_component(
60 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.0),
61 );
62 let light = universe.world.add_component(
63 engine::ecs::component::PointLightComponent::new()
64 .with_distance(25.0)
65 .with_color(1.0, 1.0, 1.0),
66 );
67 let _ = universe.attach(light_transform, light);
68 universe.add(light_transform);
69
70 // 4 red cubes around the perimeter of the world (easy visual anchors).
71 fn spawn_red_cube(universe: &mut engine::Universe, x: f32, y: f32, z: f32, s: f32) {
72 let t = universe.world.add_component(
73 engine::ecs::component::TransformComponent::new()
74 .with_position(x, y, z)
75 .with_scale(s, s, s),
76 );
77 let r = universe
78 .world
79 .add_component(engine::ecs::component::RenderableComponent::cube());
80 let c = universe
81 .world
82 .add_component(engine::ecs::component::ColorComponent::rgba(
83 1.0, 0.0, 0.0, 1.0,
84 ));
85 let e = universe
86 .world
87 .add_component(engine::ecs::component::EmissiveComponent::on());
88
89 let _ = universe.attach(t, r);
90 let _ = universe.attach(r, c);
91 let _ = universe.attach(r, e);
92
93 universe.add(t);
94 }
95
96 let p = 1.5;
97 let s = 0.25;
98 spawn_red_cube(&mut universe, -p, -p, 0.0, s);
99 spawn_red_cube(&mut universe, p, -p, 0.0, s);
100 spawn_red_cube(&mut universe, -p, p, 0.0, s);
101 spawn_red_cube(&mut universe, p, p, 0.0, s);
102
103 use engine::ecs::component::{
104 ColorComponent, TextComponent, TextShadowComponent, TextureComponent,
105 TextureFilteringComponent, TransformComponent, TransparentCutoutComponent,
106 };
107
108 fn spawn_text_style(
109 universe: &mut engine::Universe,
110 pos: [f32; 3],
111 scale: f32,
112 text: &str,
113 color: [f32; 4],
114 shadow: TextShadowComponent,
115 filtering: TextureFilteringComponent,
116 ) {
117 let root = universe.world.add_component(
118 TransformComponent::new()
119 .with_position(pos[0], pos[1], pos[2])
120 .with_scale(scale, scale, 1.0),
121 );
122
123 // Color must be an ancestor of the glyph renderables.
124 let color_id = universe
125 .world
126 .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
127 let _ = universe.attach(root, color_id);
128
129 let text_id = universe.world.add_component(TextComponent::new(text));
130 let _ = universe.attach(color_id, text_id);
131
132 // Route into cutout pass for cleaner edges.
133 let cutout = universe
134 .world
135 .add_component(TransparentCutoutComponent::new());
136 let _ = universe.attach(text_id, cutout);
137
138 // Use the same atlas as the debug quad.
139 let tex = universe
140 .world
141 .add_component(TextureComponent::with_uri("assets/textures/font.dds"));
142 let _ = universe.attach(text_id, tex);
143
144 let shadow_id = universe.world.add_component(shadow);
145 let _ = universe.attach(text_id, shadow_id);
146
147 let filtering_id = universe.world.add_component(filtering);
148 let _ = universe.attach(text_id, filtering_id);
149
150 universe.add(root);
151 }
152
153 // Multiple text samples to show:
154 // - different inherited colors
155 // - different shadow settings
156 // - different texture filtering
157 spawn_text_style(
158 &mut universe,
159 [-0.95, 0.45, 0.0],
160 0.12,
161 "NEAREST_MAG\n(crisp)\nAaBbCc 123",
162 [1.0, 1.0, 1.0, 1.0],
163 TextShadowComponent::new()
164 .with_scale(1.35)
165 .with_offset([0.06, -0.06, 0.0015]),
166 TextureFilteringComponent::nearest_magnification(),
167 );
168
169 spawn_text_style(
170 &mut universe,
171 [-0.95, 0.05, 0.0],
172 0.12,
173 "LINEAR\n(softer)\nAaBbCc 123",
174 [0.55, 0.90, 1.0, 1.0],
175 TextShadowComponent::new()
176 .with_rgba([0.0, 0.0, 0.15, 1.0])
177 .with_scale(1.20)
178 .with_offset([0.05, -0.04, 0.0015]),
179 TextureFilteringComponent::linear(),
180 );
181
182 spawn_text_style(
183 &mut universe,
184 [-0.95, -0.35, 0.0],
185 0.12,
186 "NEAREST\n(pixelly)\nAaBbCc 123",
187 [1.0, 0.85, 0.35, 1.0],
188 TextShadowComponent::new()
189 .with_rgba([0.15, 0.0, 0.0, 1.0])
190 .with_scale(1.55)
191 .with_offset([0.08, -0.08, 0.0015]),
192 TextureFilteringComponent::nearest(),
193 );
194
195 // Process init-time registrations (Text expands into glyph subtrees here).
196 universe.systems.process_commands(
197 &mut universe.world,
198 &mut universe.visuals,
199 &mut universe.render_assets,
200 &mut universe.command_queue,
201 );
202
203 engine::Windowing::run_app(universe).expect("Windowing failed");
204}More examples
examples/font-example.rs (line 232)
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}Sourcepub fn with_scale(self, scale: f32) -> Self
pub fn with_scale(self, scale: f32) -> Self
Examples found in repository?
examples/text-animation.rs (line 83)
6fn main() {
7 mittens_engine::example_support::ensure_model_assets();
8 utils::logger::init();
9
10 let world = engine::ecs::World::default();
11 let mut universe = engine::Universe::new(world);
12
13 // Input-driven camera rig.
14 // Topology: I { T { C3D } }
15 let input = universe
16 .world
17 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
18 let camera3d = universe
19 .world
20 .add_component(engine::ecs::component::Camera3DComponent::new());
21 let rig_transform = universe.world.add_component(
22 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.5),
23 );
24 let input_mode = universe.world.add_component(
25 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
26 );
27 let _ = universe.attach(input, input_mode);
28 let _ = universe.attach(input, rig_transform);
29 let _ = universe.attach(rig_transform, camera3d);
30
31 // Small on-screen help.
32 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
33 universe.add(input);
34
35 // Light so we can see non-emissive materials.
36 let light_transform = universe.world.add_component(
37 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.0),
38 );
39 let light = universe.world.add_component(
40 engine::ecs::component::PointLightComponent::new()
41 .with_distance(25.0)
42 .with_color(1.0, 1.0, 1.0),
43 );
44 let _ = universe.attach(light_transform, light);
45 universe.add(light_transform);
46
47 use engine::ecs::component::{
48 ColorComponent, TextComponent, TextShadowComponent, TextureComponent,
49 TextureFilteringComponent, TransformComponent, TransparentCutoutComponent,
50 };
51
52 // Styled text root.
53 let text_root = universe.world.add_component(
54 TransformComponent::new()
55 .with_position(0.0, 0.0, 0.0)
56 .with_scale(0.25, 0.25, 1.0),
57 );
58
59 // Color must be an ancestor of the glyph renderables.
60 let color_id = universe
61 .world
62 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
63 let _ = universe.attach(text_root, color_id);
64
65 // This is the component we animate via SetText.
66 let text_id = universe.world.add_component(TextComponent::new(">w<"));
67 let _ = universe.attach(color_id, text_id);
68
69 // Route into cutout pass for cleaner edges.
70 let cutout = universe
71 .world
72 .add_component(TransparentCutoutComponent::new());
73 let _ = universe.attach(text_id, cutout);
74
75 // Font atlas.
76 let tex = universe.world.add_component(TextureComponent::with_uri(
77 "assets/textures/font_system.dds",
78 ));
79 let _ = universe.attach(text_id, tex);
80
81 let shadow = universe.world.add_component(
82 TextShadowComponent::new()
83 .with_scale(1.35)
84 .with_offset([0.06, -0.06, 0.0015]),
85 );
86 let filtering = universe
87 .world
88 .add_component(TextureFilteringComponent::nearest_magnification());
89
90 let _ = universe.attach(text_id, shadow);
91 let _ = universe.attach(text_id, filtering);
92
93 universe.add(text_root);
94
95 let clock_component = universe
96 .world
97 .add_component(engine::ecs::component::ClockComponent::new().with_bpm(140.0));
98 let _ = universe.add(clock_component);
99
100 // Looping animation: 4 beats long, 4 keyframes.
101 let anim = universe
102 .world
103 .add_component(engine::ecs::component::AnimationComponent::new());
104
105 let faces = [">w<", "^w^", "-_-", "o_o"];
106 for (i, &face) in faces.iter().enumerate() {
107 let kf = universe
108 .world
109 .add_component(engine::ecs::component::KeyframeComponent::new(i as f64));
110
111 // Each keyframe emits a SetText intent.
112 let action = universe
113 .world
114 .add_component(engine::ecs::component::ActionComponent::new(
115 engine::ecs::IntentValue::SetText {
116 component_ids: vec![text_id],
117 text: face.to_string(),
118 },
119 ));
120
121 let _ = universe.attach(anim, kf);
122 let _ = universe.attach(kf, action);
123 }
124
125 universe.add(anim);
126
127 // Process init-time registrations (Text expands into glyph subtrees here).
128 universe.systems.process_commands(
129 &mut universe.world,
130 &mut universe.visuals,
131 &mut universe.render_assets,
132 &mut universe.command_queue,
133 );
134
135 engine::Windowing::run_app(universe).expect("Windowing failed");
136}More examples
examples/text-example.rs (line 164)
6fn main() {
7 mittens_engine::example_support::ensure_model_assets();
8 utils::logger::init();
9
10 let world = engine::ecs::World::default();
11 let mut universe = engine::Universe::new(world);
12
13 // Input-driven camera rig.
14 // Topology: I { T { C3D } }
15 let input = universe
16 .world
17 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
18 let camera3d = universe
19 .world
20 .add_component(engine::ecs::component::Camera3DComponent::new());
21 let rig_transform = universe.world.add_component(
22 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.5),
23 );
24 let input_mode = universe.world.add_component(
25 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
26 );
27 let _ = universe.attach(input, input_mode);
28 let _ = universe.attach(input, rig_transform);
29 let _ = universe.attach(rig_transform, camera3d);
30
31 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
32 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
33 universe.add(input);
34
35 // Debug square: show the full font texture.
36 let debug_root = universe.world.add_component(
37 engine::ecs::component::TransformComponent::new()
38 .with_position(0.6, -0.2, 0.0)
39 .with_scale(0.8, 0.8, 1.0),
40 );
41 let debug_renderable = universe
42 .world
43 .add_component(engine::ecs::component::RenderableComponent::square());
44 let debug_tex =
45 universe
46 .world
47 .add_component(engine::ecs::component::TextureComponent::with_uri(
48 "assets/textures/font_system.dds",
49 ));
50 let debug_filtering = universe
51 .world
52 .add_component(engine::ecs::component::TextureFilteringComponent::nearest_magnification());
53 let _ = universe.attach(debug_root, debug_renderable);
54 let _ = universe.attach(debug_renderable, debug_tex);
55 let _ = universe.attach(debug_renderable, debug_filtering);
56 universe.add(debug_root);
57
58 // Light so we can actually see non-emissive materials.
59 let light_transform = universe.world.add_component(
60 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.0),
61 );
62 let light = universe.world.add_component(
63 engine::ecs::component::PointLightComponent::new()
64 .with_distance(25.0)
65 .with_color(1.0, 1.0, 1.0),
66 );
67 let _ = universe.attach(light_transform, light);
68 universe.add(light_transform);
69
70 // 4 red cubes around the perimeter of the world (easy visual anchors).
71 fn spawn_red_cube(universe: &mut engine::Universe, x: f32, y: f32, z: f32, s: f32) {
72 let t = universe.world.add_component(
73 engine::ecs::component::TransformComponent::new()
74 .with_position(x, y, z)
75 .with_scale(s, s, s),
76 );
77 let r = universe
78 .world
79 .add_component(engine::ecs::component::RenderableComponent::cube());
80 let c = universe
81 .world
82 .add_component(engine::ecs::component::ColorComponent::rgba(
83 1.0, 0.0, 0.0, 1.0,
84 ));
85 let e = universe
86 .world
87 .add_component(engine::ecs::component::EmissiveComponent::on());
88
89 let _ = universe.attach(t, r);
90 let _ = universe.attach(r, c);
91 let _ = universe.attach(r, e);
92
93 universe.add(t);
94 }
95
96 let p = 1.5;
97 let s = 0.25;
98 spawn_red_cube(&mut universe, -p, -p, 0.0, s);
99 spawn_red_cube(&mut universe, p, -p, 0.0, s);
100 spawn_red_cube(&mut universe, -p, p, 0.0, s);
101 spawn_red_cube(&mut universe, p, p, 0.0, s);
102
103 use engine::ecs::component::{
104 ColorComponent, TextComponent, TextShadowComponent, TextureComponent,
105 TextureFilteringComponent, TransformComponent, TransparentCutoutComponent,
106 };
107
108 fn spawn_text_style(
109 universe: &mut engine::Universe,
110 pos: [f32; 3],
111 scale: f32,
112 text: &str,
113 color: [f32; 4],
114 shadow: TextShadowComponent,
115 filtering: TextureFilteringComponent,
116 ) {
117 let root = universe.world.add_component(
118 TransformComponent::new()
119 .with_position(pos[0], pos[1], pos[2])
120 .with_scale(scale, scale, 1.0),
121 );
122
123 // Color must be an ancestor of the glyph renderables.
124 let color_id = universe
125 .world
126 .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
127 let _ = universe.attach(root, color_id);
128
129 let text_id = universe.world.add_component(TextComponent::new(text));
130 let _ = universe.attach(color_id, text_id);
131
132 // Route into cutout pass for cleaner edges.
133 let cutout = universe
134 .world
135 .add_component(TransparentCutoutComponent::new());
136 let _ = universe.attach(text_id, cutout);
137
138 // Use the same atlas as the debug quad.
139 let tex = universe
140 .world
141 .add_component(TextureComponent::with_uri("assets/textures/font.dds"));
142 let _ = universe.attach(text_id, tex);
143
144 let shadow_id = universe.world.add_component(shadow);
145 let _ = universe.attach(text_id, shadow_id);
146
147 let filtering_id = universe.world.add_component(filtering);
148 let _ = universe.attach(text_id, filtering_id);
149
150 universe.add(root);
151 }
152
153 // Multiple text samples to show:
154 // - different inherited colors
155 // - different shadow settings
156 // - different texture filtering
157 spawn_text_style(
158 &mut universe,
159 [-0.95, 0.45, 0.0],
160 0.12,
161 "NEAREST_MAG\n(crisp)\nAaBbCc 123",
162 [1.0, 1.0, 1.0, 1.0],
163 TextShadowComponent::new()
164 .with_scale(1.35)
165 .with_offset([0.06, -0.06, 0.0015]),
166 TextureFilteringComponent::nearest_magnification(),
167 );
168
169 spawn_text_style(
170 &mut universe,
171 [-0.95, 0.05, 0.0],
172 0.12,
173 "LINEAR\n(softer)\nAaBbCc 123",
174 [0.55, 0.90, 1.0, 1.0],
175 TextShadowComponent::new()
176 .with_rgba([0.0, 0.0, 0.15, 1.0])
177 .with_scale(1.20)
178 .with_offset([0.05, -0.04, 0.0015]),
179 TextureFilteringComponent::linear(),
180 );
181
182 spawn_text_style(
183 &mut universe,
184 [-0.95, -0.35, 0.0],
185 0.12,
186 "NEAREST\n(pixelly)\nAaBbCc 123",
187 [1.0, 0.85, 0.35, 1.0],
188 TextShadowComponent::new()
189 .with_rgba([0.15, 0.0, 0.0, 1.0])
190 .with_scale(1.55)
191 .with_offset([0.08, -0.08, 0.0015]),
192 TextureFilteringComponent::nearest(),
193 );
194
195 // Process init-time registrations (Text expands into glyph subtrees here).
196 universe.systems.process_commands(
197 &mut universe.world,
198 &mut universe.visuals,
199 &mut universe.render_assets,
200 &mut universe.command_queue,
201 );
202
203 engine::Windowing::run_app(universe).expect("Windowing failed");
204}examples/font-example.rs (line 209)
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}Sourcepub fn with_offset(self, offset: [f32; 3]) -> Self
pub fn with_offset(self, offset: [f32; 3]) -> Self
Examples found in repository?
examples/text-animation.rs (line 84)
6fn main() {
7 mittens_engine::example_support::ensure_model_assets();
8 utils::logger::init();
9
10 let world = engine::ecs::World::default();
11 let mut universe = engine::Universe::new(world);
12
13 // Input-driven camera rig.
14 // Topology: I { T { C3D } }
15 let input = universe
16 .world
17 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
18 let camera3d = universe
19 .world
20 .add_component(engine::ecs::component::Camera3DComponent::new());
21 let rig_transform = universe.world.add_component(
22 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.5),
23 );
24 let input_mode = universe.world.add_component(
25 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
26 );
27 let _ = universe.attach(input, input_mode);
28 let _ = universe.attach(input, rig_transform);
29 let _ = universe.attach(rig_transform, camera3d);
30
31 // Small on-screen help.
32 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
33 universe.add(input);
34
35 // Light so we can see non-emissive materials.
36 let light_transform = universe.world.add_component(
37 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.0),
38 );
39 let light = universe.world.add_component(
40 engine::ecs::component::PointLightComponent::new()
41 .with_distance(25.0)
42 .with_color(1.0, 1.0, 1.0),
43 );
44 let _ = universe.attach(light_transform, light);
45 universe.add(light_transform);
46
47 use engine::ecs::component::{
48 ColorComponent, TextComponent, TextShadowComponent, TextureComponent,
49 TextureFilteringComponent, TransformComponent, TransparentCutoutComponent,
50 };
51
52 // Styled text root.
53 let text_root = universe.world.add_component(
54 TransformComponent::new()
55 .with_position(0.0, 0.0, 0.0)
56 .with_scale(0.25, 0.25, 1.0),
57 );
58
59 // Color must be an ancestor of the glyph renderables.
60 let color_id = universe
61 .world
62 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
63 let _ = universe.attach(text_root, color_id);
64
65 // This is the component we animate via SetText.
66 let text_id = universe.world.add_component(TextComponent::new(">w<"));
67 let _ = universe.attach(color_id, text_id);
68
69 // Route into cutout pass for cleaner edges.
70 let cutout = universe
71 .world
72 .add_component(TransparentCutoutComponent::new());
73 let _ = universe.attach(text_id, cutout);
74
75 // Font atlas.
76 let tex = universe.world.add_component(TextureComponent::with_uri(
77 "assets/textures/font_system.dds",
78 ));
79 let _ = universe.attach(text_id, tex);
80
81 let shadow = universe.world.add_component(
82 TextShadowComponent::new()
83 .with_scale(1.35)
84 .with_offset([0.06, -0.06, 0.0015]),
85 );
86 let filtering = universe
87 .world
88 .add_component(TextureFilteringComponent::nearest_magnification());
89
90 let _ = universe.attach(text_id, shadow);
91 let _ = universe.attach(text_id, filtering);
92
93 universe.add(text_root);
94
95 let clock_component = universe
96 .world
97 .add_component(engine::ecs::component::ClockComponent::new().with_bpm(140.0));
98 let _ = universe.add(clock_component);
99
100 // Looping animation: 4 beats long, 4 keyframes.
101 let anim = universe
102 .world
103 .add_component(engine::ecs::component::AnimationComponent::new());
104
105 let faces = [">w<", "^w^", "-_-", "o_o"];
106 for (i, &face) in faces.iter().enumerate() {
107 let kf = universe
108 .world
109 .add_component(engine::ecs::component::KeyframeComponent::new(i as f64));
110
111 // Each keyframe emits a SetText intent.
112 let action = universe
113 .world
114 .add_component(engine::ecs::component::ActionComponent::new(
115 engine::ecs::IntentValue::SetText {
116 component_ids: vec![text_id],
117 text: face.to_string(),
118 },
119 ));
120
121 let _ = universe.attach(anim, kf);
122 let _ = universe.attach(kf, action);
123 }
124
125 universe.add(anim);
126
127 // Process init-time registrations (Text expands into glyph subtrees here).
128 universe.systems.process_commands(
129 &mut universe.world,
130 &mut universe.visuals,
131 &mut universe.render_assets,
132 &mut universe.command_queue,
133 );
134
135 engine::Windowing::run_app(universe).expect("Windowing failed");
136}More examples
examples/text-example.rs (line 165)
6fn main() {
7 mittens_engine::example_support::ensure_model_assets();
8 utils::logger::init();
9
10 let world = engine::ecs::World::default();
11 let mut universe = engine::Universe::new(world);
12
13 // Input-driven camera rig.
14 // Topology: I { T { C3D } }
15 let input = universe
16 .world
17 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
18 let camera3d = universe
19 .world
20 .add_component(engine::ecs::component::Camera3DComponent::new());
21 let rig_transform = universe.world.add_component(
22 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.5),
23 );
24 let input_mode = universe.world.add_component(
25 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
26 );
27 let _ = universe.attach(input, input_mode);
28 let _ = universe.attach(input, rig_transform);
29 let _ = universe.attach(rig_transform, camera3d);
30
31 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
32 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
33 universe.add(input);
34
35 // Debug square: show the full font texture.
36 let debug_root = universe.world.add_component(
37 engine::ecs::component::TransformComponent::new()
38 .with_position(0.6, -0.2, 0.0)
39 .with_scale(0.8, 0.8, 1.0),
40 );
41 let debug_renderable = universe
42 .world
43 .add_component(engine::ecs::component::RenderableComponent::square());
44 let debug_tex =
45 universe
46 .world
47 .add_component(engine::ecs::component::TextureComponent::with_uri(
48 "assets/textures/font_system.dds",
49 ));
50 let debug_filtering = universe
51 .world
52 .add_component(engine::ecs::component::TextureFilteringComponent::nearest_magnification());
53 let _ = universe.attach(debug_root, debug_renderable);
54 let _ = universe.attach(debug_renderable, debug_tex);
55 let _ = universe.attach(debug_renderable, debug_filtering);
56 universe.add(debug_root);
57
58 // Light so we can actually see non-emissive materials.
59 let light_transform = universe.world.add_component(
60 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.0),
61 );
62 let light = universe.world.add_component(
63 engine::ecs::component::PointLightComponent::new()
64 .with_distance(25.0)
65 .with_color(1.0, 1.0, 1.0),
66 );
67 let _ = universe.attach(light_transform, light);
68 universe.add(light_transform);
69
70 // 4 red cubes around the perimeter of the world (easy visual anchors).
71 fn spawn_red_cube(universe: &mut engine::Universe, x: f32, y: f32, z: f32, s: f32) {
72 let t = universe.world.add_component(
73 engine::ecs::component::TransformComponent::new()
74 .with_position(x, y, z)
75 .with_scale(s, s, s),
76 );
77 let r = universe
78 .world
79 .add_component(engine::ecs::component::RenderableComponent::cube());
80 let c = universe
81 .world
82 .add_component(engine::ecs::component::ColorComponent::rgba(
83 1.0, 0.0, 0.0, 1.0,
84 ));
85 let e = universe
86 .world
87 .add_component(engine::ecs::component::EmissiveComponent::on());
88
89 let _ = universe.attach(t, r);
90 let _ = universe.attach(r, c);
91 let _ = universe.attach(r, e);
92
93 universe.add(t);
94 }
95
96 let p = 1.5;
97 let s = 0.25;
98 spawn_red_cube(&mut universe, -p, -p, 0.0, s);
99 spawn_red_cube(&mut universe, p, -p, 0.0, s);
100 spawn_red_cube(&mut universe, -p, p, 0.0, s);
101 spawn_red_cube(&mut universe, p, p, 0.0, s);
102
103 use engine::ecs::component::{
104 ColorComponent, TextComponent, TextShadowComponent, TextureComponent,
105 TextureFilteringComponent, TransformComponent, TransparentCutoutComponent,
106 };
107
108 fn spawn_text_style(
109 universe: &mut engine::Universe,
110 pos: [f32; 3],
111 scale: f32,
112 text: &str,
113 color: [f32; 4],
114 shadow: TextShadowComponent,
115 filtering: TextureFilteringComponent,
116 ) {
117 let root = universe.world.add_component(
118 TransformComponent::new()
119 .with_position(pos[0], pos[1], pos[2])
120 .with_scale(scale, scale, 1.0),
121 );
122
123 // Color must be an ancestor of the glyph renderables.
124 let color_id = universe
125 .world
126 .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
127 let _ = universe.attach(root, color_id);
128
129 let text_id = universe.world.add_component(TextComponent::new(text));
130 let _ = universe.attach(color_id, text_id);
131
132 // Route into cutout pass for cleaner edges.
133 let cutout = universe
134 .world
135 .add_component(TransparentCutoutComponent::new());
136 let _ = universe.attach(text_id, cutout);
137
138 // Use the same atlas as the debug quad.
139 let tex = universe
140 .world
141 .add_component(TextureComponent::with_uri("assets/textures/font.dds"));
142 let _ = universe.attach(text_id, tex);
143
144 let shadow_id = universe.world.add_component(shadow);
145 let _ = universe.attach(text_id, shadow_id);
146
147 let filtering_id = universe.world.add_component(filtering);
148 let _ = universe.attach(text_id, filtering_id);
149
150 universe.add(root);
151 }
152
153 // Multiple text samples to show:
154 // - different inherited colors
155 // - different shadow settings
156 // - different texture filtering
157 spawn_text_style(
158 &mut universe,
159 [-0.95, 0.45, 0.0],
160 0.12,
161 "NEAREST_MAG\n(crisp)\nAaBbCc 123",
162 [1.0, 1.0, 1.0, 1.0],
163 TextShadowComponent::new()
164 .with_scale(1.35)
165 .with_offset([0.06, -0.06, 0.0015]),
166 TextureFilteringComponent::nearest_magnification(),
167 );
168
169 spawn_text_style(
170 &mut universe,
171 [-0.95, 0.05, 0.0],
172 0.12,
173 "LINEAR\n(softer)\nAaBbCc 123",
174 [0.55, 0.90, 1.0, 1.0],
175 TextShadowComponent::new()
176 .with_rgba([0.0, 0.0, 0.15, 1.0])
177 .with_scale(1.20)
178 .with_offset([0.05, -0.04, 0.0015]),
179 TextureFilteringComponent::linear(),
180 );
181
182 spawn_text_style(
183 &mut universe,
184 [-0.95, -0.35, 0.0],
185 0.12,
186 "NEAREST\n(pixelly)\nAaBbCc 123",
187 [1.0, 0.85, 0.35, 1.0],
188 TextShadowComponent::new()
189 .with_rgba([0.15, 0.0, 0.0, 1.0])
190 .with_scale(1.55)
191 .with_offset([0.08, -0.08, 0.0015]),
192 TextureFilteringComponent::nearest(),
193 );
194
195 // Process init-time registrations (Text expands into glyph subtrees here).
196 universe.systems.process_commands(
197 &mut universe.world,
198 &mut universe.visuals,
199 &mut universe.render_assets,
200 &mut universe.command_queue,
201 );
202
203 engine::Windowing::run_app(universe).expect("Windowing failed");
204}examples/font-example.rs (line 210)
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}pub fn with_offset_xy(self, offset: [f32; 2]) -> Self
pub fn with_z_offset(self, z_offset: f32) -> Self
Trait Implementations§
Source§impl Clone for TextShadowComponent
impl Clone for TextShadowComponent
Source§fn clone(&self) -> TextShadowComponent
fn clone(&self) -> TextShadowComponent
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 TextShadowComponent
impl Component for TextShadowComponent
Source§fn name(&self) -> &'static str
fn name(&self) -> &'static str
Short debug/type name for this component kind (e.g. “transform”, “camera”).
fn set_id(&mut self, component: ComponentId)
fn as_any(&self) -> &dyn Any
fn as_any_mut(&mut self) -> &mut dyn Any
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 to_mms_ast(&self, _world: &World) -> ComponentExpression
fn to_mms_ast(&self, _world: &World) -> ComponentExpression
Encode this component as an MMS Component Expression AST node. Read more
Source§fn cleanup(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)
fn cleanup(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)
Called when component is removed from the World.
impl Copy for TextShadowComponent
Source§impl Debug for TextShadowComponent
impl Debug for TextShadowComponent
Auto Trait Implementations§
impl Freeze for TextShadowComponent
impl RefUnwindSafe for TextShadowComponent
impl Send for TextShadowComponent
impl Sync for TextShadowComponent
impl Unpin for TextShadowComponent
impl UnsafeUnpin for TextShadowComponent
impl UnwindSafe for TextShadowComponent
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.