Skip to main content

TextShadowComponent

Struct TextShadowComponent 

Source
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: f32

Shadow 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

Source

pub const DEFAULT_RGBA: [f32; 4]

Source

pub const DEFAULT_SCALE: f32 = 1.25

Source

pub const DEFAULT_OFFSET: [f32; 3]

Source

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
Hide additional 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}
Source

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
Hide additional 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}
Source

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
Hide additional 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}
Source

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
Hide additional 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}
Source

pub fn with_offset_xy(self, offset: [f32; 2]) -> Self

Source

pub fn with_z_offset(self, z_offset: f32) -> Self

Trait Implementations§

Source§

impl Clone for TextShadowComponent

Source§

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)

Performs copy-assignment from source. Read more
Source§

impl Component for TextShadowComponent

Source§

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

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

fn set_id(&mut self, component: ComponentId)

Source§

fn as_any(&self) -> &dyn Any

Source§

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

Source§

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

Called when component is added to the World
Source§

fn 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)

Called when component is removed from the World.
Source§

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

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

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

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

impl Copy for TextShadowComponent

Source§

impl Debug for TextShadowComponent

Source§

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

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

impl Default for TextShadowComponent

Source§

fn default() -> Self

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

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

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

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

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

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

fn as_any(&self) -> &(dyn Any + 'static)

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

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

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

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

Source§

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

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

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

Source§

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

Source§

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

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T> Instrument for T

Source§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

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

Source§

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

Source§

fn into_sample(self) -> T

Source§

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

Source§

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

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

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

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

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

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

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

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

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

Source§

fn to_sample_(self) -> U

Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
Source§

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

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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