Skip to main content

wisp_storybook/stories/
s_text_texture.rs

1//! Story: text rendered into a `RenderTexture` and composed into the
2//! scene via a `Sprite` (M-TEXT.5).
3
4use glam::Vec2;
5use wisp::application::Application;
6use wisp::text::{TextTexturePipeline, WispText, WispTextStyle};
7use wisp::{Color, Sprite, Stage, WispFontWeight};
8
9use crate::story::Story;
10
11pub fn story() -> Story {
12    Story {
13        id: "text-texture",
14        category: "Scene Graph",
15        title: "Text → RenderTexture → Sprite",
16        milestone: "M-TEXT.5",
17        writeup: include_str!("writeups/text_texture.md"),
18        build,
19        tick: None,
20    }
21}
22
23fn build(app: &Application, stage: &mut Stage) {
24    // Match the storybook's target format so the sampled view + the
25    // surface gamma agree.
26    let pipeline = TextTexturePipeline::new(app, wgpu::TextureFormat::Rgba8UnormSrgb);
27
28    // Hero line. Pick RT dims that fit "Text → texture" at size_ndc
29    // 0.20 — ≈14 chars × 100 px ≈ 1400 px wide, ~240 px tall.
30    let hero = WispText::new("Text → texture").with_style(
31        WispTextStyle::default()
32            .with_size(0.20)
33            .with_weight(WispFontWeight::Bold)
34            .with_color(Color::rgba(1.0, 0.9, 0.4, 1.0)),
35    );
36
37    let rt = pipeline.render(app, &hero, 1536, 320);
38    let texture = rt.as_texture();
39
40    // 256×256 storybook canvas. Sprite scale.y is negative so the
41    // glyphon-rendered (+y-down) texture displays upright through the
42    // sprite pipeline (+y-up NDC). This is the standard
43    // render-target-as-texture convention.
44    let mut sprite = Sprite::from_texture(texture).with_anchor(Vec2::new(0.5, 0.5));
45    sprite.container.transform.position = Vec2::new(0.0, 0.3);
46    sprite.container.transform.scale = Vec2::new(1.7, -0.85);
47    let _ = stage.add_child(stage.root(), sprite);
48
49    // Render a second piece into another texture to prove the cache
50    // isn't tied to a single output. Different content + dims produces
51    // a distinct cached entry that participates in the same batch.
52    let caption = WispText::new("cached + composed").with_style(
53        WispTextStyle::default()
54            .with_size(0.30)
55            .with_color(Color::rgba(0.55, 0.85, 1.0, 1.0)),
56    );
57    let caption_rt = pipeline.render(app, &caption, 768, 256);
58    let caption_tex = caption_rt.as_texture();
59    let mut caption_sprite = Sprite::from_texture(caption_tex).with_anchor(Vec2::new(0.5, 0.5));
60    caption_sprite.container.transform.position = Vec2::new(0.0, -0.55);
61    caption_sprite.container.transform.scale = Vec2::new(1.5, -0.5);
62    let _ = stage.add_child(stage.root(), caption_sprite);
63}