tawasuyu

tawasuyu/llimphi

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

Rust 660Markdown 302TOML 157RON 312382 Definiciones

⎇ main llimphi-voxel/examples/room_demo.rs Rust · 168 líneas · 5.9 KB Historia Quién tocó qué crudo

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//! Sala de exposición de los **muebles y objetos de casa**: los 12 modelos
2//! ([`Mueble`]) sobre un piso de baldosas, cada uno estampado con [`stamp_mueble`].
3//!
4//! `cargo run -p llimphi-voxel --example room_demo --release` → `/tmp/room.png`
5
6use std::fs::File;
7use std::io::BufWriter;
8
9use llimphi_3d::glam::Vec3;
10use llimphi_3d::{Atmosphere, Camera3d, Scene3d, VoxelGrid, VoxelRenderer};
11use llimphi_hal::{wgpu, Hal};
12use llimphi_raster::peniko::Color;
13use llimphi_raster::{vello, Renderer};
14use llimphi_voxel::{stamp_mueble, Material, Mueble};
15
16const W: u32 = 1280;
17const H: u32 = 620;
18const FMT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm;
19
20fn main() {
21    let cols = 6i32;
22    let rows = 12i32;
23    let sx = 11i32; // paso en x
24    let sz = 15i32; // paso en z
25    let margin = 6i32;
26    let floor_h = 2i32;
27    let dim = [
28        (cols * sx + 2 * margin) as u32,
29        26,
30        (rows * sz + 2 * margin) as u32,
31    ];
32    let mut g = VoxelGrid::new(dim);
33
34    // Piso de baldosas (cerámica lustrosa + yeso, en damero).
35    for x in 0..dim[0] as i32 {
36        for z in 0..dim[2] as i32 {
37            let tile = ((x / 2 + z / 2) % 2) == 0;
38            let m = if tile { Material::Ceramica } else { Material::Yeso };
39            for y in 0..floor_h {
40                let c = m.color();
41                let a = VoxelGrid::shine_to_alpha(if y == floor_h - 1 { m.brillo() } else { 0.0 });
42                g.set_rgba(x as u32, y as u32, z as u32, c, a);
43            }
44        }
45    }
46
47    // Los 12 muebles en grilla.
48    for (i, m) in Mueble::ALL.iter().enumerate() {
49        let c = i as i32 % cols;
50        let r = i as i32 / cols;
51        let x = margin + c * sx + sx / 2;
52        let z = margin + r * sz + sz / 2;
53        stamp_mueble(&mut g, *m, x as u32, floor_h as u32, z as u32, 0);
54        eprintln!("{:>10} en ({x},{z})", m.label());
55    }
56
57    let hal = pollster::block_on(Hal::new(None)).expect("hal");
58    let mut renderer = Renderer::new(&hal).expect("renderer");
59    let mut vr = VoxelRenderer::new(&hal.device, &hal.queue, FMT, &g);
60    vr.sun_dir = [0.4, 0.72, 0.56];
61    vr.atmosphere = Atmosphere {
62        sky_zenith: [92, 120, 168],
63        sky_horizon: [206, 214, 226],
64        fog_density: 0.0,
65        god_rays: 0.0,
66    };
67    let camera = Camera3d::orbit(
68        Vec3::new(0.0, dim[1] as f32 * 0.02, 0.0),
69        14_f32.to_radians(),
70        40_f32.to_radians(),
71        dim[0].max(dim[2]) as f32 * 0.82,
72    );
73    let mut scene = Scene3d::new();
74    let pixels = render(&hal, &mut renderer, &mut scene, &mut vr, &camera);
75    write_png(&pixels, "/tmp/room.png");
76    eprintln!("escrito /tmp/room.png — 72 muebles: casa, ciudad, edificios y transporte");
77}
78
79fn render(
80    hal: &Hal,
81    renderer: &mut Renderer,
82    scene: &mut Scene3d,
83    vr: &mut VoxelRenderer,
84    camera: &Camera3d,
85) -> Vec<u8> {
86    let inter = hal.device.create_texture(&wgpu::TextureDescriptor {
87        label: Some("inter"),
88        size: wgpu::Extent3d { width: W, height: H, depth_or_array_layers: 1 },
89        mip_level_count: 1,
90        sample_count: 1,
91        dimension: wgpu::TextureDimension::D2,
92        format: FMT,
93        usage: wgpu::TextureUsages::STORAGE_BINDING
94            | wgpu::TextureUsages::TEXTURE_BINDING
95            | wgpu::TextureUsages::RENDER_ATTACHMENT
96            | wgpu::TextureUsages::COPY_SRC,
97        view_formats: &[],
98    });
99    let view = inter.create_view(&wgpu::TextureViewDescriptor::default());
100    renderer
101        .render_to_view(hal, &vello::Scene::new(), &view, W, H, Color::from_rgba8(0, 0, 0, 255))
102        .expect("base");
103    let mut enc = hal
104        .device
105        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("room") });
106    scene.render(&hal.device, &hal.queue, &mut enc, &view, (W, H), camera, Some(vr), &[]);
107    hal.queue.submit(std::iter::once(enc.finish()));
108    let _ = hal.device.poll(wgpu::PollType::wait_indefinitely());
109    readback(hal, &inter)
110}
111
112fn readback(hal: &Hal, target: &wgpu::Texture) -> Vec<u8> {
113    let unpadded = (W * 4) as usize;
114    let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
115    let padded = unpadded.div_ceil(align) * align;
116    let buf = hal.device.create_buffer(&wgpu::BufferDescriptor {
117        label: Some("readback"),
118        size: (padded * H as usize) as u64,
119        usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
120        mapped_at_creation: false,
121    });
122    let mut enc = hal
123        .device
124        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
125    enc.copy_texture_to_buffer(
126        wgpu::TexelCopyTextureInfo {
127            texture: target,
128            mip_level: 0,
129            origin: wgpu::Origin3d::ZERO,
130            aspect: wgpu::TextureAspect::All,
131        },
132        wgpu::TexelCopyBufferInfo {
133            buffer: &buf,
134            layout: wgpu::TexelCopyBufferLayout {
135                offset: 0,
136                bytes_per_row: Some(padded as u32),
137                rows_per_image: Some(H),
138            },
139        },
140        wgpu::Extent3d { width: W, height: H, depth_or_array_layers: 1 },
141    );
142    hal.queue.submit(std::iter::once(enc.finish()));
143    let slice = buf.slice(..);
144    let (tx, rx) = std::sync::mpsc::channel();
145    slice.map_async(wgpu::MapMode::Read, move |r| {
146        let _ = tx.send(r);
147    });
148    let _ = hal.device.poll(wgpu::PollType::wait_indefinitely());
149    rx.recv().unwrap().unwrap();
150    let data = slice.get_mapped_range();
151    let mut pixels = Vec::with_capacity((W * H * 4) as usize);
152    for row in 0..H as usize {
153        let srow = row * padded;
154        pixels.extend_from_slice(&data[srow..srow + unpadded]);
155    }
156    drop(data);
157    buf.unmap();
158    pixels
159}
160
161fn write_png(pixels: &[u8], path: &str) {
162    let file = File::create(path).expect("png");
163    let mut enc = png::Encoder::new(BufWriter::new(file), W, H);
164    enc.set_color(png::ColorType::Rgba);
165    enc.set_depth(png::BitDepth::Eight);
166    let mut wtr = enc.write_header().unwrap();
167    wtr.write_image_data(pixels).unwrap();
168}