gioser

tawasuyu/llimphi

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

Rust 229TOML 101Markdown 82Shell 24052 Sutichaykuna

⎇ main llimphi-voxel/examples/transport_demo.rs Rust · 160 siq'ikuna · 5.9 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//! 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, Mueble};
15
16const W: u32 = 1280;
17const H: u32 = 620;
18const FMT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm;
19
20fn main() {
21    let fila = [
22        Mueble::Avion, Mueble::Helicoptero, Mueble::Colectivo, Mueble::Camion,
23        Mueble::Camioneta, Mueble::Taxi, Mueble::Ambulancia, Mueble::Tren,
24        Mueble::Vagon, Mueble::Barco, Mueble::Moto, Mueble::Bici,
25    ];
26    let sx = 13i32;
27    let margin = 8i32;
28    let dim = [
29        (fila.len() as i32 * sx + 2 * margin) as u32,
30        20,
31        40,
32    ];
33    let mut g = VoxelGrid::new(dim);
34    let floor_h = 2i32;
35    for x in 0..dim[0] as i32 {
36        for z in 0..dim[2] as i32 {
37            g.set(x as u32, 0, z as u32, [70, 72, 78]);
38            g.set(x as u32, 1, z as u32, [80, 82, 90]);
39        }
40    }
41    // Fila de los 12 tipos de transporte.
42    for (i, m) in fila.iter().enumerate() {
43        let x = margin + i as i32 * sx + sx / 2;
44        stamp_mueble(&mut g, *m, x as u32, floor_h as u32, 28, 0);
45    }
46    // Flota de autos: el mismo Mueble en distintas posiciones → colores variados.
47    for i in 0..fila.len() as i32 {
48        let x = margin + i * sx + sx / 2;
49        stamp_mueble(&mut g, Mueble::Auto, x as u32, floor_h as u32, 12, 1);
50    }
51
52    let hal = pollster::block_on(Hal::new(None)).expect("hal");
53    let mut renderer = Renderer::new(&hal).expect("renderer");
54    let mut vr = VoxelRenderer::new(&hal.device, &hal.queue, FMT, &g);
55    vr.sun_dir = [0.4, 0.66, 0.55];
56    vr.atmosphere = Atmosphere {
57        sky_zenith: [92, 130, 190],
58        sky_horizon: [206, 218, 232],
59        fog_density: 0.0,
60        god_rays: 0.0,
61    };
62    let half = Vec3::new(dim[0] as f32, dim[1] as f32, dim[2] as f32) * 0.5;
63    let look = Vec3::new(dim[0] as f32 * 0.5, 3.0, dim[2] as f32 * 0.5) - half;
64    let camera = Camera3d::orbit(look, 10_f32.to_radians(), 22_f32.to_radians(), dim[0] as f32 * 0.78);
65    let mut scene = Scene3d::new();
66    let pixels = render(&hal, &mut renderer, &mut scene, &mut vr, &camera);
67    write_png(&pixels, "/tmp/transport.png");
68    eprintln!("escrito /tmp/transport.png — 12 transportes + flota de autos (colores por posición)");
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("room") });
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 srow = row * padded;
146        pixels.extend_from_slice(&data[srow..srow + 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}