tawasuyu

tawasuyu/llimphi

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

Rust 660Markdown 302TOML 157RON 312382 Definitions

⎇ main llimphi-voxel/examples/roof_demo.rs Rust · 155 lines · 5.7 KB History Who touched what raw

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//! Muestra los **tipos de techo** (plano, tejas, zinc, pizarra, paja) sobre la misma
2//! habitación, en un montaje horizontal. `Cutaway::none` → techo entero.
3//!
4//! `cargo run -p llimphi-voxel --example roof_demo --release` → `/tmp/roof.png`
5
6use std::fs::File;
7use std::io::BufWriter;
8
9use llimphi_3d::glam::Vec3;
10use llimphi_3d::{Atmosphere, Camera3d, Scene3d, VoxelRenderer};
11use llimphi_hal::{wgpu, Hal};
12use llimphi_raster::peniko::Color;
13use llimphi_raster::{vello, Renderer};
14use llimphi_voxel::{Cutaway, Interior, TechoTipo};
15
16const TILE: u32 = 300;
17const H: u32 = 340;
18const FMT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm;
19
20fn main() {
21    let tipos = TechoTipo::ALL;
22    let cols = tipos.len() as u32;
23    let hal = pollster::block_on(Hal::new(None)).expect("hal");
24    let mut renderer = Renderer::new(&hal).expect("renderer");
25    let mut scene = Scene3d::new();
26
27    let dim = [28u32, 40, 24];
28    let mut montage = vec![0u8; (TILE * cols * H * 4) as usize];
29
30    for (i, &techo) in tipos.iter().enumerate() {
31        let mut it = Interior::nuevo(1, "sala");
32        it.ancho = 22;
33        it.fondo = 18;
34        it.techo = techo;
35        let grid = it.build_grid(dim, Cutaway::none());
36
37        let mut vr = VoxelRenderer::new(&hal.device, &hal.queue, FMT, &grid);
38        vr.sun_dir = [0.4, 0.62, 0.5];
39        vr.atmosphere = Atmosphere {
40            sky_zenith: [96, 140, 200],
41            sky_horizon: [206, 220, 234],
42            fog_density: 0.0,
43            god_rays: 0.0,
44        };
45        let half = Vec3::new(dim[0] as f32, dim[1] as f32, dim[2] as f32) * 0.5;
46        // Mirar desde arriba y al costado → se ve la forma del techo.
47        let look = Vec3::new(it.ancho as f32 * 0.5, 8.0, it.fondo as f32 * 0.5) - half;
48        let camera = Camera3d::orbit(look, 32_f32.to_radians(), 30_f32.to_radians(), dim[0] as f32 * 1.15);
49
50        let px = render(&hal, &mut renderer, &mut scene, &mut vr, &camera);
51        // Copiar el tile a su columna del montaje.
52        let x0 = i as u32 * TILE;
53        for y in 0..H {
54            for x in 0..TILE {
55                let src = ((y * TILE + x) * 4) as usize;
56                let dst = ((y * TILE * cols + x0 + x) * 4) as usize;
57                montage[dst..dst + 4].copy_from_slice(&px[src..src + 4]);
58            }
59        }
60        eprintln!("techo {}", techo.label());
61    }
62    write_png(&montage, TILE * cols, H, "/tmp/roof.png");
63    eprintln!("escrito /tmp/roof.png — {cols} tipos de techo");
64}
65
66fn render(
67    hal: &Hal,
68    renderer: &mut Renderer,
69    scene: &mut Scene3d,
70    vr: &mut VoxelRenderer,
71    camera: &Camera3d,
72) -> Vec<u8> {
73    let inter = hal.device.create_texture(&wgpu::TextureDescriptor {
74        label: Some("inter"),
75        size: wgpu::Extent3d { width: TILE, height: H, depth_or_array_layers: 1 },
76        mip_level_count: 1,
77        sample_count: 1,
78        dimension: wgpu::TextureDimension::D2,
79        format: FMT,
80        usage: wgpu::TextureUsages::STORAGE_BINDING
81            | wgpu::TextureUsages::TEXTURE_BINDING
82            | wgpu::TextureUsages::RENDER_ATTACHMENT
83            | wgpu::TextureUsages::COPY_SRC,
84        view_formats: &[],
85    });
86    let view = inter.create_view(&wgpu::TextureViewDescriptor::default());
87    renderer
88        .render_to_view(hal, &vello::Scene::new(), &view, TILE, H, Color::from_rgba8(12, 14, 20, 255))
89        .expect("base");
90    let mut enc = hal
91        .device
92        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("roof") });
93    scene.render(&hal.device, &hal.queue, &mut enc, &view, (TILE, H), camera, Some(vr), &[]);
94    hal.queue.submit(std::iter::once(enc.finish()));
95    let _ = hal.device.poll(wgpu::PollType::wait_indefinitely());
96    readback(hal, &inter)
97}
98
99fn readback(hal: &Hal, target: &wgpu::Texture) -> Vec<u8> {
100    let unpadded = (TILE * 4) as usize;
101    let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
102    let padded = unpadded.div_ceil(align) * align;
103    let buf = hal.device.create_buffer(&wgpu::BufferDescriptor {
104        label: Some("readback"),
105        size: (padded * H as usize) as u64,
106        usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
107        mapped_at_creation: false,
108    });
109    let mut enc = hal
110        .device
111        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
112    enc.copy_texture_to_buffer(
113        wgpu::TexelCopyTextureInfo {
114            texture: target,
115            mip_level: 0,
116            origin: wgpu::Origin3d::ZERO,
117            aspect: wgpu::TextureAspect::All,
118        },
119        wgpu::TexelCopyBufferInfo {
120            buffer: &buf,
121            layout: wgpu::TexelCopyBufferLayout {
122                offset: 0,
123                bytes_per_row: Some(padded as u32),
124                rows_per_image: Some(H),
125            },
126        },
127        wgpu::Extent3d { width: TILE, height: H, depth_or_array_layers: 1 },
128    );
129    hal.queue.submit(std::iter::once(enc.finish()));
130    let slice = buf.slice(..);
131    let (tx, rx) = std::sync::mpsc::channel();
132    slice.map_async(wgpu::MapMode::Read, move |r| {
133        let _ = tx.send(r);
134    });
135    let _ = hal.device.poll(wgpu::PollType::wait_indefinitely());
136    rx.recv().unwrap().unwrap();
137    let data = slice.get_mapped_range();
138    let mut pixels = Vec::with_capacity((TILE * H * 4) as usize);
139    for row in 0..H as usize {
140        let s = row * padded;
141        pixels.extend_from_slice(&data[s..s + unpadded]);
142    }
143    drop(data);
144    buf.unmap();
145    pixels
146}
147
148fn write_png(pixels: &[u8], w: u32, h: u32, path: &str) {
149    let file = File::create(path).expect("png");
150    let mut enc = png::Encoder::new(BufWriter::new(file), w, h);
151    enc.set_color(png::ColorType::Rgba);
152    enc.set_depth(png::BitDepth::Eight);
153    let mut wtr = enc.write_header().unwrap();
154    wtr.write_image_data(pixels).unwrap();
155}