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/rig_demo.rs Rust · 149 siq'ikuna · 5.6 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//! Demo headless del **rig**: las cuatro morfologías preset (humanoide, cuadrúpedo,
2//! ave, serpiente) en fila, en plena caminata (andar procedural), a un PNG.
3//!
4//! `cargo run -p llimphi-voxel --example rig_demo --release` → `/tmp/rig.png`
5
6use std::fs::File;
7use std::io::BufWriter;
8
9use llimphi_3d::glam::{Mat4, Vec3};
10use llimphi_3d::{Camera3d, Renderer3d, Scene3d};
11use llimphi_hal::{wgpu, Hal};
12use llimphi_raster::peniko::Color;
13use llimphi_raster::{vello, Renderer};
14use llimphi_voxel::{Andar, Rig};
15
16const W: u32 = 1100;
17const H: u32 = 520;
18const FMT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm;
19
20fn main() {
21    let phase: f32 = std::env::args().nth(1).and_then(|s| s.parse().ok()).unwrap_or(1.1);
22
23    let hal = pollster::block_on(Hal::new(None)).expect("hal");
24    let mut renderer = Renderer::new(&hal).expect("renderer");
25
26    // Las cuatro criaturas, con paletas distinguibles (piel, cuerpo, patas).
27    let palettes = [
28        ([0.90, 0.72, 0.58], [0.82, 0.30, 0.28], [0.20, 0.22, 0.34]), // humanoide
29        ([0.78, 0.62, 0.42], [0.55, 0.42, 0.30], [0.40, 0.30, 0.22]), // cuadrúpedo
30        ([0.95, 0.80, 0.30], [0.30, 0.55, 0.85], [0.85, 0.55, 0.20]), // ave
31        ([0.55, 0.80, 0.40], [0.30, 0.62, 0.34], [0.30, 0.62, 0.34]), // serpiente
32    ];
33    let rigs = Rig::presets();
34
35    // Un Renderer3d por criatura, ubicado en su columna (separadas en X).
36    let mut actors: Vec<Renderer3d> = Vec::new();
37    let spacing = 2.6_f32;
38    let x0 = -(rigs.len() as f32 - 1.0) * 0.5 * spacing;
39    for (k, rig) in rigs.iter().enumerate() {
40        let (piel, cuerpo, patas) = palettes[k];
41        let andar = Andar::caminar(rig);
42        let pose = andar.pose(phase);
43        let (v, i) = rig.mesh(&pose, piel, cuerpo, patas);
44        let mut r = Renderer3d::new(&hal.device, FMT);
45        r.set_geometry(&hal.device, &v, &i);
46        r.set_model(Mat4::from_translation(Vec3::new(x0 + k as f32 * spacing, 0.0, 0.0)));
47        actors.push(r);
48    }
49
50    // Cámara: mira al centro de la fila, a la altura del pecho, en 3/4.
51    let camera = Camera3d::orbit(
52        Vec3::new(0.0, 0.7, 0.0),
53        28_f32.to_radians(),
54        18_f32.to_radians(),
55        7.5,
56    );
57
58    let mut scene = Scene3d::new();
59    let target = make_target(&hal);
60    let view = target.create_view(&wgpu::TextureViewDescriptor::default());
61    let sky = Color::from_rgba8(150, 186, 224, 255);
62    renderer.render_to_view(&hal, &vello::Scene::new(), &view, W, H, sky).expect("clear");
63
64    let mut enc = hal
65        .device
66        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("rig") });
67    let refs: Vec<&Renderer3d> = actors.iter().collect();
68    scene.render(&hal.device, &hal.queue, &mut enc, &view, (W, H), &camera, None, &refs);
69    hal.queue.submit(std::iter::once(enc.finish()));
70    let _ = hal.device.poll(wgpu::PollType::wait_indefinitely());
71
72    let pixels = readback(&hal, &target);
73    write_png(&pixels, "/tmp/rig.png");
74    eprintln!("escrito /tmp/rig.png (humanoide · cuadrúpedo · ave · serpiente, fase {phase})");
75}
76
77fn make_target(hal: &Hal) -> wgpu::Texture {
78    hal.device.create_texture(&wgpu::TextureDescriptor {
79        label: Some("rig-target"),
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}
92
93fn readback(hal: &Hal, target: &wgpu::Texture) -> Vec<u8> {
94    let unpadded = (W * 4) as usize;
95    let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
96    let padded = unpadded.div_ceil(align) * align;
97    let buf = hal.device.create_buffer(&wgpu::BufferDescriptor {
98        label: Some("readback"),
99        size: (padded * H as usize) as u64,
100        usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
101        mapped_at_creation: false,
102    });
103    let mut enc = hal
104        .device
105        .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
106    enc.copy_texture_to_buffer(
107        wgpu::TexelCopyTextureInfo {
108            texture: target,
109            mip_level: 0,
110            origin: wgpu::Origin3d::ZERO,
111            aspect: wgpu::TextureAspect::All,
112        },
113        wgpu::TexelCopyBufferInfo {
114            buffer: &buf,
115            layout: wgpu::TexelCopyBufferLayout {
116                offset: 0,
117                bytes_per_row: Some(padded as u32),
118                rows_per_image: Some(H),
119            },
120        },
121        wgpu::Extent3d { width: W, height: H, depth_or_array_layers: 1 },
122    );
123    hal.queue.submit(std::iter::once(enc.finish()));
124    let slice = buf.slice(..);
125    let (tx, rx) = std::sync::mpsc::channel();
126    slice.map_async(wgpu::MapMode::Read, move |r| {
127        let _ = tx.send(r);
128    });
129    let _ = hal.device.poll(wgpu::PollType::wait_indefinitely());
130    rx.recv().unwrap().unwrap();
131    let data = slice.get_mapped_range();
132    let mut pixels = Vec::with_capacity((W * H * 4) as usize);
133    for row in 0..H as usize {
134        let s = row * padded;
135        pixels.extend_from_slice(&data[s..s + unpadded]);
136    }
137    drop(data);
138    buf.unmap();
139    pixels
140}
141
142fn write_png(pixels: &[u8], path: &str) {
143    let file = File::create(path).expect("png");
144    let mut enc = png::Encoder::new(BufWriter::new(file), W, H);
145    enc.set_color(png::ColorType::Rgba);
146    enc.set_depth(png::BitDepth::Eight);
147    let mut wtr = enc.write_header().unwrap();
148    wtr.write_image_data(pixels).unwrap();
149}