Skip to main content

wisp_storybook/stories/
s_sprite_batcher.rs

1//! Story: 100 sprites batched into a single draw call (M0.9).
2
3use glam::Vec2;
4use wisp::application::Application;
5use wisp::{Color, Sprite, Stage, Texture};
6
7use crate::story::Story;
8
9pub fn story() -> Story {
10    Story {
11        id: "sprite-batcher",
12        category: "Scene Graph",
13        title: "Sprite batcher",
14        milestone: "M0.9",
15        writeup: include_str!("writeups/sprite_batcher.md"),
16        build,
17        tick: Some(tick),
18    }
19}
20
21fn build(app: &Application, stage: &mut Stage) {
22    // 4×4 white texture — all 100 sprites share the same Arc, so they batch.
23    let bytes = vec![255u8; 4 * 4 * 4];
24    let texture = Texture::from_rgba(app, 4, 4, &bytes);
25
26    let root = stage.root();
27    for i in 0u16..100 {
28        let f = f32::from(i) / 100.0;
29        let mut sprite = Sprite::from_texture(texture.clone()).with_anchor(Vec2::splat(0.5));
30        sprite.container.transform.scale = Vec2::splat(0.06);
31        // Pseudo-random arrangement on a Lissajous curve for visual interest.
32        let angle = f * std::f32::consts::TAU;
33        let x = (angle * 2.0).sin() * 0.7;
34        let y = (angle * 3.0).sin() * 0.7;
35        sprite.container.transform.position = Vec2::new(x, y);
36        sprite.tint = Color::rgba(
37            0.5 + 0.5 * (angle * 1.0).cos(),
38            0.5 + 0.5 * (angle * 1.5).sin(),
39            0.5 + 0.5 * (angle * 2.0).cos(),
40            1.0,
41        );
42        let _ = stage.add_child(root, sprite);
43    }
44}
45
46fn tick(stage: &mut Stage, t: f32) {
47    let child_ids: Vec<_> = stage
48        .get(stage.root())
49        .map(|root| root.container().children().collect())
50        .unwrap_or_default();
51
52    for (i, id) in child_ids.iter().enumerate().take(100) {
53        let f = u16::try_from(i).map_or(0.0, f32::from) / 100.0;
54        let phase = t + f * std::f32::consts::TAU;
55        let angle = (f * std::f32::consts::TAU) + t * 0.2;
56        let x = (angle * 2.0).sin() * 0.7;
57        let y = (angle * 3.0).sin() * 0.7;
58        if let Some(node) = stage.get_mut(*id) {
59            node.container_mut().transform.position = glam::Vec2::new(x, y);
60            node.container_mut().transform.rotation = phase * 0.3;
61        }
62    }
63}