gioser

tawasuyu/llimphi

Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.

Rust 484Markdown 258TOML 131RON 38118 Sutichaykuna

⎇ main llimphi-voxel/examples/fauna_demo.rs Rust · 160 siq'ikuna · 5.7 KB Ñawpa willay Pi imata llamkarqan ch'uya

chore: refresco desde el monorepo — 27 crates nuevos (anim, lottie, svg, mesh, image, layer, test, wire-view, widgets rag-sidebar/router/lazy-list/rive-button) y sync de los 95 existentes 93253cc22 · Sergio ·

1//! **Fauna (seres) procedural**: los 16 animales del catálogo [`Bicho`] en una
2//! grilla, sobre pasto. `cargo run -p llimphi-voxel --example fauna_demo --release`
3//! → `/tmp/fauna.png`
4
5use std::fs::File;
6use std::io::BufWriter;
7
8use llimphi_3d::glam::Vec3;
9use llimphi_3d::{Atmosphere, Camera3d, Scene3d, VoxelGrid, VoxelRenderer};
10use llimphi_hal::{wgpu, Hal};
11use llimphi_raster::peniko::Color;
12use llimphi_raster::{vello, Renderer};
13use llimphi_voxel::{stamp_bicho, Bicho, Material};
14
15const W: u32 = 1280;
16const H: u32 = 720;
17const FMT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm;
18
19fn main() {
20    let bichos = Bicho::ALL;
21    let cols = 4i32;
22    let step = 10i32;
23    let margin = 8i32;
24    let rows = (bichos.len() as i32 + cols - 1) / cols;
25    let dim = [
26        (cols * step + 2 * margin) as u32,
27        24,
28        (rows * step + 2 * margin) as u32,
29    ];
30    let mut g = VoxelGrid::new(dim);
31
32    // Suelo de pasto con leve grano.
33    for x in 0..dim[0] {
34        for z in 0..dim[2] {
35            let c = Material::Grass.color();
36            let t = ((x * 3 + z * 5) % 9) as i32 - 4;
37            let sh = |v: u8| (v as i32 + t).clamp(0, 255) as u8;
38            g.set(x, 0, z, [sh(c[0]), sh(c[1]), sh(c[2])]);
39        }
40    }
41
42    for (i, &bicho) in bichos.iter().enumerate() {
43        let cx = i as i32 % cols;
44        let cz = i as i32 / cols;
45        let x = margin + cx * step + step / 2;
46        let z = margin + cz * step + step / 2;
47        stamp_bicho(&mut g, bicho, x as u32, 1, z as u32, 0);
48        eprintln!("{}: {}", i, bicho.label());
49    }
50
51    let hal = pollster::block_on(Hal::new(None)).expect("hal");
52    let mut renderer = Renderer::new(&hal).expect("renderer");
53    let mut vr = VoxelRenderer::new(&hal.device, &hal.queue, FMT, &g);
54    vr.sun_dir = [0.42, 0.62, 0.5];
55    vr.atmosphere = Atmosphere {
56        sky_zenith: [96, 146, 210],
57        sky_horizon: [210, 224, 238],
58        fog_density: 0.15 / dim[0] as f32,
59        god_rays: 0.0,
60    };
61    let half = Vec3::new(dim[0] as f32, dim[1] as f32, dim[2] as f32) * 0.5;
62    let look = Vec3::new(dim[0] as f32 * 0.5, 3.0, dim[2] as f32 * 0.5) - half;
63    let camera = Camera3d::orbit(look, 22_f32.to_radians(), 24_f32.to_radians(), dim[0].max(dim[2]) as f32 * 0.82);
64
65    let mut scene = Scene3d::new();
66    let pixels = render(&hal, &mut renderer, &mut scene, &mut vr, &camera);
67    write_png(&pixels, "/tmp/fauna.png");
68    eprintln!("escrito /tmp/fauna.png — {} seres", bichos.len());
69}
70
71fn render(
72    hal: &Hal,
73    renderer: &mut Renderer,
74    scene: &mut Scene3d,
75    vr: &mut VoxelRenderer,
76    camera: &Camera3d,
77) -> Vec<u8> {
78    let inter = hal.device.create_texture(&wgpu::TextureDescriptor {
79        label: Some("inter"),
80        size: wgpu::Extent3d { width: W, height: H, depth_or_array_layers: 1 },
81        mip_level_count: 1,
82        sample_count: 1,
83        dimension: wgpu::TextureDimension::D2,
84        format: FMT,
85        usage: wgpu::TextureUsages::STORAGE_BINDING
86            | wgpu::TextureUsages::TEXTURE_BINDING
87            | wgpu::TextureUsages::RENDER_ATTACHMENT
88            | wgpu::TextureUsages::COPY_SRC,
89        view_formats: &[],
90    });
91    let view = inter.create_view(&wgpu::TextureViewDescriptor::default());
92    renderer
93        .render_to_view(hal, &vello::Scene::new(), &view, W, H, Color::from_rgba8(0, 0, 0, 255))
94        .expect("base");
95    let mut enc = hal
96        .device
97        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("fauna") });
98    scene.render(&hal.device, &hal.queue, &mut enc, &view, (W, H), camera, Some(vr), &[]);
99    hal.queue.submit(std::iter::once(enc.finish()));
100    let _ = hal.device.poll(wgpu::PollType::wait_indefinitely());
101    readback(hal, &inter)
102}
103
104fn readback(hal: &Hal, target: &wgpu::Texture) -> Vec<u8> {
105    let unpadded = (W * 4) as usize;
106    let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
107    let padded = unpadded.div_ceil(align) * align;
108    let buf = hal.device.create_buffer(&wgpu::BufferDescriptor {
109        label: Some("readback"),
110        size: (padded * H as usize) as u64,
111        usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
112        mapped_at_creation: false,
113    });
114    let mut enc = hal
115        .device
116        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
117    enc.copy_texture_to_buffer(
118        wgpu::TexelCopyTextureInfo {
119            texture: target,
120            mip_level: 0,
121            origin: wgpu::Origin3d::ZERO,
122            aspect: wgpu::TextureAspect::All,
123        },
124        wgpu::TexelCopyBufferInfo {
125            buffer: &buf,
126            layout: wgpu::TexelCopyBufferLayout {
127                offset: 0,
128                bytes_per_row: Some(padded as u32),
129                rows_per_image: Some(H),
130            },
131        },
132        wgpu::Extent3d { width: W, height: H, depth_or_array_layers: 1 },
133    );
134    hal.queue.submit(std::iter::once(enc.finish()));
135    let slice = buf.slice(..);
136    let (tx, rx) = std::sync::mpsc::channel();
137    slice.map_async(wgpu::MapMode::Read, move |r| {
138        let _ = tx.send(r);
139    });
140    let _ = hal.device.poll(wgpu::PollType::wait_indefinitely());
141    rx.recv().unwrap().unwrap();
142    let data = slice.get_mapped_range();
143    let mut pixels = Vec::with_capacity((W * H * 4) as usize);
144    for row in 0..H as usize {
145        let s = row * padded;
146        pixels.extend_from_slice(&data[s..s + unpadded]);
147    }
148    drop(data);
149    buf.unmap();
150    pixels
151}
152
153fn write_png(pixels: &[u8], path: &str) {
154    let file = File::create(path).expect("png");
155    let mut enc = png::Encoder::new(BufWriter::new(file), W, H);
156    enc.set_color(png::ColorType::Rgba);
157    enc.set_depth(png::BitDepth::Eight);
158    let mut wtr = enc.write_header().unwrap();
159    wtr.write_image_data(pixels).unwrap();
160}