tawasuyu

tawasuyu/llimphi

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

Rust 614Markdown 274TOML 149RON 310606 Sutichaykuna

⎇ 168cd187519c0ad575d3ce63baee2f164c38d9d1 llimphi-voxel/src/bicho.rs Rust · 831 siq'ikuna · 29.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//! **Seres del bioma (fauna procedural)**: un catálogo de modelitos voxel de
2//! animales (vaca, oveja, ciervo, zorro, oso, caballo…) que **pueblan el mundo
3//! por semilla**, como la flora y las casas. A diferencia de un [`Mueble`](crate::Mueble)
4//! (prop de interior, colocado a mano), un [`Bicho`] lo **esparce el bioma**: el
5//! campo `fauna` del [`Bioma`](crate::Bioma) dice qué animales y con qué densidad,
6//! y [`generate_window`](crate::Bioma::generate_window) los siembra parados sobre
7//! el terreno, orientados al azar.
8//!
9//! Los animales usan **color RGB directo** (no [`Material`](crate::worldgen::Material)):
10//! el rosa del cerdo o el naranja del zorro no viven en la paleta de terreno. Son
11//! mates (sin brillo). Se orientan con `facing` (0=+Z, 1=+X, 2=−Z, 3=−X).
12
13use serde::{Deserialize, Serialize};
14
15use llimphi_3d::VoxelGrid;
16
17/// Catálogo de seres/animales que pueblan un bioma.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub enum Bicho {
20    /// Vaca: cuerpo blanco con manchas negras, cabeza y cuernos.
21    Vaca,
22    /// Oveja: cuerpo lanudo blanco, cara oscura.
23    Oveja,
24    /// Cerdo: cuerpo rosado, hocico y colita.
25    Cerdo,
26    /// Gallina: cuerpo blanco chico, cresta roja y pico.
27    Gallina,
28    /// Ciervo: cuerpo marrón, patas largas y astas.
29    Ciervo,
30    /// Conejo: cuerpo chico con orejas largas.
31    Conejo,
32    /// Zorro: cuerpo naranja, pecho blanco y cola tupida.
33    Zorro,
34    /// Lobo: cuerpo gris alargado, orejas y cola.
35    Lobo,
36    /// Oso: cuerpo marrón voluminoso, cabeza redonda.
37    Oso,
38    /// Caballo: cuerpo marrón alto, cuello, crin y cola.
39    Caballo,
40    /// Pájaro: cuerpo diminuto con alas y pico (posado).
41    Pajaro,
42    /// Pez: cuerpo con aleta caudal (para el agua/orilla).
43    Pez,
44    /// Tortuga: caparazón verde abovedado, cabeza y patas.
45    Tortuga,
46    /// Rana: cuerpo verde agazapado, ojos saltones.
47    Rana,
48    /// Serpiente: cuerpo largo y bajo en zigzag, con cabeza.
49    Serpiente,
50    /// Mariposa: cuerpecito con cuatro alas coloridas (revolotea bajo).
51    Mariposa,
52    // --- Granja / pradera ---
53    /// Pato: cuerpo blanco chico, pico naranja (orilla/pradera).
54    Pato,
55    /// Cabra: cuerpo claro con cuernitos (montaña/granja).
56    Cabra,
57    /// Burro: cuerpo gris con orejas largas (granja).
58    Burro,
59    // --- Bosque ---
60    /// Ardilla: cuerpito con cola tupida enroscada (bosque).
61    Ardilla,
62    /// Búho: cuerpo compacto con ojos grandes, posado (bosque).
63    Buho,
64    /// Erizo: lomo de púas oscuras, cara clara (bosque).
65    Erizo,
66    // --- Desierto ---
67    /// Camello: cuerpo arena con joroba y cuello (desierto).
68    Camello,
69    /// Escorpión: cuerpo bajo con pinzas y cola en alto (desierto).
70    Escorpion,
71    /// Lagarto: cuerpo verde alargado y bajo (desierto/selva).
72    Lagarto,
73    // --- Nieve / agua fría ---
74    /// Pingüino: cuerpo negro con panza blanca, pico naranja (nieve).
75    Pinguino,
76    /// Foca: cuerpo gris tendido con aletas (nieve/agua).
77    Foca,
78    // --- Selva / sabana ---
79    /// Mono: cuerpo marrón con cola larga (selva).
80    Mono,
81    /// Loro: cuerpo verde con pecho rojo y pico (selva).
82    Loro,
83    /// Tigre: cuerpo naranja con rayas negras (selva).
84    Tigre,
85    /// Elefante: cuerpo gris grande con trompa y orejas (sabana).
86    Elefante,
87    /// Cocodrilo: cuerpo verde largo y bajo con fauces (selva/agua).
88    Cocodrilo,
89}
90
91impl Bicho {
92    pub const ALL: [Bicho; 32] = [
93        Bicho::Vaca,
94        Bicho::Oveja,
95        Bicho::Cerdo,
96        Bicho::Gallina,
97        Bicho::Ciervo,
98        Bicho::Conejo,
99        Bicho::Zorro,
100        Bicho::Lobo,
101        Bicho::Oso,
102        Bicho::Caballo,
103        Bicho::Pajaro,
104        Bicho::Pez,
105        Bicho::Tortuga,
106        Bicho::Rana,
107        Bicho::Serpiente,
108        Bicho::Mariposa,
109        Bicho::Pato,
110        Bicho::Cabra,
111        Bicho::Burro,
112        Bicho::Ardilla,
113        Bicho::Buho,
114        Bicho::Erizo,
115        Bicho::Camello,
116        Bicho::Escorpion,
117        Bicho::Lagarto,
118        Bicho::Pinguino,
119        Bicho::Foca,
120        Bicho::Mono,
121        Bicho::Loro,
122        Bicho::Tigre,
123        Bicho::Elefante,
124        Bicho::Cocodrilo,
125    ];
126
127    pub fn label(self) -> &'static str {
128        match self {
129            Bicho::Vaca => "vaca",
130            Bicho::Oveja => "oveja",
131            Bicho::Cerdo => "cerdo",
132            Bicho::Gallina => "gallina",
133            Bicho::Ciervo => "ciervo",
134            Bicho::Conejo => "conejo",
135            Bicho::Zorro => "zorro",
136            Bicho::Lobo => "lobo",
137            Bicho::Oso => "oso",
138            Bicho::Caballo => "caballo",
139            Bicho::Pajaro => "pájaro",
140            Bicho::Pez => "pez",
141            Bicho::Tortuga => "tortuga",
142            Bicho::Rana => "rana",
143            Bicho::Serpiente => "serpiente",
144            Bicho::Mariposa => "mariposa",
145            Bicho::Pato => "pato",
146            Bicho::Cabra => "cabra",
147            Bicho::Burro => "burro",
148            Bicho::Ardilla => "ardilla",
149            Bicho::Buho => "búho",
150            Bicho::Erizo => "erizo",
151            Bicho::Camello => "camello",
152            Bicho::Escorpion => "escorpión",
153            Bicho::Lagarto => "lagarto",
154            Bicho::Pinguino => "pingüino",
155            Bicho::Foca => "foca",
156            Bicho::Mono => "mono",
157            Bicho::Loro => "loro",
158            Bicho::Tigre => "tigre",
159            Bicho::Elefante => "elefante",
160            Bicho::Cocodrilo => "cocodrilo",
161        }
162    }
163
164    pub fn next(self) -> Bicho {
165        let i = Bicho::ALL.iter().position(|&b| b == self).unwrap_or(0);
166        Bicho::ALL[(i + 1) % Bicho::ALL.len()]
167    }
168}
169
170/// **Uso de un ser en un bioma**: qué animal y con qué densidad `[0,1]` aparece
171/// (una candidata por parcela de la grilla de fauna; el bioma lo esparce por
172/// semilla → una fauna procedural, como la flora y las casas).
173#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
174pub struct BichoUso {
175    pub bicho: Bicho,
176    pub densidad: f32,
177}
178
179// Paleta cruda de fauna (colores que la paleta de terreno no tiene).
180const MARRON: [u8; 3] = [120, 84, 50];
181const MARRON_OSC: [u8; 3] = [86, 58, 34];
182const BLANCO: [u8; 3] = [230, 228, 222];
183const NEGRO: [u8; 3] = [42, 42, 46];
184const ROSA: [u8; 3] = [224, 152, 152];
185const NARANJA: [u8; 3] = [206, 108, 46];
186const GRIS: [u8; 3] = [132, 132, 138];
187const VERDE: [u8; 3] = [96, 156, 74];
188const VERDE_OSC: [u8; 3] = [64, 116, 56];
189const ROJO: [u8; 3] = [206, 72, 60];
190const AMARILLO: [u8; 3] = [224, 192, 92];
191const AZUL: [u8; 3] = [72, 116, 186];
192const TAN: [u8; 3] = [204, 172, 120];
193const MARRON_CLR: [u8; 3] = [158, 118, 76];
194const GRIS_OSC: [u8; 3] = [92, 94, 100];
195
196/// Hash barato de la posición → variedad de morfo (color/patrón) por dónde cae el
197/// animal, sin usar la semilla del mundo. Igual que la carrocería de un vehículo o
198/// el tinte del follaje: un rebaño estampado sale VARIADO, determinista por posición.
199#[inline]
200fn morph(x: u32, z: u32) -> u32 {
201    let mut h = x.wrapping_mul(0x9E37_79B9).wrapping_add(z.wrapping_mul(0x85EB_CA77));
202    h ^= h >> 15;
203    h = h.wrapping_mul(0x2C1B_3C6D);
204    (h ^ (h >> 13)) & 0xFFFF
205}
206
207/// Rota un desplazamiento `(dx, dz)` según `facing` (giros de 90°).
208#[inline]
209fn rot(dx: i32, dz: i32, facing: u8) -> (i32, i32) {
210    match facing & 3 {
211        0 => (dx, dz),
212        1 => (-dz, dx),
213        2 => (-dx, -dz),
214        _ => (dz, -dx),
215    }
216}
217
218/// **Detalle sub-voxel de un ser vivo** `(rugosidad, bisel)` — la gramática
219/// cuantizada que rompe la caja rectangular de los animales voxel. Rugosidad suave
220/// (grano de piel/pelaje, escala `VoxelGrid::DETALLE_RUG_MAX`) + bisel alto (redondeo
221/// orgánico de la silueta de sombreado): un ser vivo no es un ladrillo. Es la MISMA
222/// gramática de bisel/campo que el terreno (no una fidelidad distinta → sin valle),
223/// sólo con más redondeo por ser un cuerpo. `generate_window` lo aplica como pincel
224/// de detalle alrededor de cada `stamp_bicho`. El bisel sólo luce si el bisel GLOBAL
225/// de la escena está prendido (activa la isosuperficie); la rugosidad luce siempre.
226pub const DETALLE_SER_VIVO: (f32, f32) = (0.4, 0.85);
227
228/// **Estampa un ser** del `bicho` parado con las patas en `(x, y, z)` (base local
229/// `dy=0` = a ras del suelo), orientado por `facing`. `dz` crece hacia el frente
230/// (hocico), `dx` a lo ancho, `dy` hacia arriba.
231pub fn stamp_bicho(g: &mut VoxelGrid, bicho: Bicho, x: u32, y: u32, z: u32, facing: u8) {
232    let (bx, by, bz) = (x as i32, y as i32, z as i32);
233    let dim = g.dim();
234    let vh = morph(x, z); // morfo de color/patrón por posición (variedad de rebaño)
235    let mut put = |dx: i32, dy: i32, dz: i32, c: [u8; 3]| {
236        let (rx, rz) = rot(dx, dz, facing);
237        let (px, py, pz) = (bx + rx, by + dy, bz + rz);
238        if px >= 0 && py >= 0 && pz >= 0 && (px as u32) < dim[0] && (py as u32) < dim[1] && (pz as u32) < dim[2] {
239            g.set(px as u32, py as u32, pz as u32, c);
240        }
241    };
242
243    match bicho {
244        Bicho::Vaca => {
245            // Morfo de pelaje: holstein / marrón-blanca / negra.
246            let (base, mancha_c) = match vh % 3 {
247                0 => (BLANCO, NEGRO),
248                1 => (BLANCO, MARRON_CLR),
249                _ => (MARRON_CLR, NEGRO),
250            };
251            // Cuerpo (2 de ancho × 3 de largo × 2 de alto) sobre 4 patas.
252            for dx in -1i32..=0 {
253                for dz in -1i32..=1 {
254                    for dy in 2..=3 {
255                        // Manchas en algunas celdas, base en el resto.
256                        let mancha = (dx + dz).rem_euclid(2) == 0 && dz != 1;
257                        put(dx, dy, dz, if mancha { mancha_c } else { base });
258                    }
259                }
260            }
261            for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
262                put(lx, 0, lz, MARRON_OSC);
263                put(lx, 1, lz, MARRON_OSC);
264            }
265            // Cabeza al frente + cuernos.
266            put(0, 3, 2, ROSA);
267            put(-1, 3, 2, ROSA);
268            put(0, 4, 2, base);
269            put(-1, 4, 2, base);
270            put(1, 4, 2, [232, 228, 214]); // cuerno
271            put(-2, 4, 2, [232, 228, 214]); // cuerno
272        }
273        Bicho::Oveja => {
274            // Morfo de lana: blanca / beige / gris.
275            let lana = match vh % 3 {
276                0 => BLANCO,
277                1 => [214, 202, 176],
278                _ => [186, 186, 190],
279            };
280            // Cuerpo lanudo y mullido sobre patas oscuras.
281            for dx in -1..=0 {
282                for dz in -1..=1 {
283                    for dy in 2..=3 {
284                        put(dx, dy, dz, lana);
285                    }
286                }
287            }
288            put(0, 4, 0, lana);
289            put(-1, 4, 0, lana);
290            for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
291                put(lx, 0, lz, NEGRO);
292                put(lx, 1, lz, NEGRO);
293            }
294            // Cara oscura al frente.
295            put(0, 2, 2, NEGRO);
296            put(-1, 2, 2, NEGRO);
297            put(0, 3, 2, lana);
298        }
299        Bicho::Cerdo => {
300            // Cuerpo rosado rechoncho, patas cortas, hocico y colita.
301            for dx in -1..=0 {
302                for dz in -1..=1 {
303                    put(dx, 1, dz, ROSA);
304                    put(dx, 2, dz, ROSA);
305                }
306            }
307            for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
308                put(lx, 0, lz, ROSA);
309            }
310            put(0, 1, 2, ROSA); // hocico
311            put(-1, 1, 2, ROSA);
312            put(0, 2, 2, ROSA);
313            put(-1, 2, 2, ROSA);
314            put(0, 2, 3, [232, 170, 170]); // trompa
315            put(-1, 1, -2, ROSA); // colita
316        }
317        Bicho::Gallina => {
318            // Morfo de plumaje: blanca / marrón / negra (con gallo rojizo).
319            let plum = match vh % 3 {
320                0 => BLANCO,
321                1 => MARRON_CLR,
322                _ => [70, 68, 74],
323            };
324            put(0, 1, 0, plum);
325            put(0, 1, -1, plum);
326            put(0, 2, 0, plum);
327            put(0, 2, 1, plum); // pecho/cabeza
328            put(0, 3, 1, ROJO); // cresta
329            put(0, 2, 2, AMARILLO); // pico
330            put(0, 0, 0, AMARILLO); // patas
331            put(0, 0, -1, AMARILLO);
332            put(0, 1, -2, plum); // cola
333        }
334        Bicho::Ciervo => {
335            // Patas largas, cuerpo marrón, cuello y cabeza al frente, astas.
336            for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
337                put(lx, 0, lz, MARRON_OSC);
338                put(lx, 1, lz, MARRON_OSC);
339            }
340            for dx in -1..=0 {
341                for dz in -1..=1 {
342                    put(dx, 2, dz, MARRON);
343                    put(dx, 3, dz, MARRON);
344                }
345            }
346            // Cuello + cabeza.
347            put(0, 3, 2, MARRON);
348            put(0, 4, 2, MARRON);
349            put(0, 5, 2, MARRON);
350            put(0, 5, 3, MARRON); // hocico
351            // Astas.
352            put(0, 6, 2, MARRON_OSC);
353            put(1, 6, 2, MARRON_OSC);
354            put(-1, 6, 2, MARRON_OSC);
355            put(0, 6, 3, MARRON_OSC);
356        }
357        Bicho::Conejo => {
358            // Morfo de pelaje: blanco / marrón / gris.
359            let pelo = match vh % 3 {
360                0 => BLANCO,
361                1 => MARRON_CLR,
362                _ => GRIS,
363            };
364            // Cuerpo chico, orejas largas, cola de algodón.
365            put(0, 1, 0, pelo);
366            put(0, 1, -1, pelo);
367            put(0, 2, 0, pelo);
368            put(0, 2, 1, pelo); // cabeza al frente
369            put(0, 3, 1, pelo); // oreja
370            put(0, 4, 1, pelo);
371            put(0, 3, 0, pelo); // oreja 2
372            put(0, 4, 0, pelo);
373            put(0, 0, 0, pelo);
374            put(0, 0, -1, pelo);
375            put(0, 2, -1, [240, 240, 236]); // colita
376        }
377        Bicho::Zorro => {
378            // Cuerpo naranja, pecho/cola blancos, orejas puntiagudas.
379            for dz in -1..=1 {
380                put(0, 1, dz, NARANJA);
381                put(0, 2, dz, NARANJA);
382            }
383            put(0, 0, -1, MARRON_OSC); // patas
384            put(0, 0, 1, MARRON_OSC);
385            put(0, 2, 2, NARANJA); // cabeza
386            put(0, 3, 2, NARANJA); // oreja
387            put(0, 2, 3, BLANCO); // hocico
388            put(0, 1, 2, BLANCO); // pecho
389            put(0, 2, -2, BLANCO); // cola
390            put(0, 1, -2, NARANJA);
391        }
392        Bicho::Lobo => {
393            // Como el zorro pero gris y más largo/robusto.
394            for dz in -2..=1 {
395                put(0, 1, dz, GRIS);
396                put(0, 2, dz, GRIS);
397            }
398            for &lz in &[-1, 1] {
399                put(0, 0, lz, [96, 96, 102]);
400            }
401            put(0, 2, 2, GRIS); // cabeza
402            put(0, 3, 2, GRIS); // oreja
403            put(0, 2, 3, [110, 110, 116]); // hocico
404            put(0, 2, -3, GRIS); // cola
405            put(0, 3, -3, GRIS);
406        }
407        Bicho::Oso => {
408            // Cuerpo voluminoso (2×3), cabeza redonda con orejas.
409            for dx in -1..=0 {
410                for dz in -1..=1 {
411                    for dy in 1..=3 {
412                        put(dx, dy, dz, MARRON_OSC);
413                    }
414                }
415            }
416            for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
417                put(lx, 0, lz, MARRON_OSC);
418            }
419            put(0, 3, 2, MARRON); // cabeza
420            put(-1, 3, 2, MARRON);
421            put(0, 4, 2, MARRON);
422            put(-1, 4, 2, MARRON);
423            put(1, 4, 2, MARRON_OSC); // orejas
424            put(-2, 4, 2, MARRON_OSC);
425        }
426        Bicho::Caballo => {
427            // Morfo de capa: alazán / negro / bayo, con crin de contraste.
428            let (capa, crin) = match vh % 3 {
429                0 => (MARRON, MARRON_OSC),
430                1 => ([58, 54, 56], NEGRO),
431                _ => (TAN, MARRON_OSC),
432            };
433            // Alto: patas largas, lomo, cuello erguido, crin y cola.
434            for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
435                put(lx, 0, lz, crin);
436                put(lx, 1, lz, capa);
437                put(lx, 2, lz, capa);
438            }
439            for dx in -1..=0 {
440                for dz in -1..=1 {
441                    put(dx, 3, dz, capa);
442                }
443            }
444            // Cuello + cabeza.
445            put(0, 4, 1, capa);
446            put(0, 5, 1, capa);
447            put(0, 5, 2, capa); // testa
448            put(0, 4, 2, capa);
449            put(0, 6, 1, crin); // crin
450            put(0, 5, 0, crin);
451            put(-1, 4, -1, crin); // cola
452            put(-1, 3, -2, crin);
453        }
454        Bicho::Pajaro => {
455            // Diminuto: cuerpo azul, alas, pico, patitas.
456            put(0, 1, 0, AZUL);
457            put(0, 2, 0, AZUL); // cabeza
458            put(0, 2, 1, AMARILLO); // pico
459            put(1, 1, 0, [96, 140, 210]); // ala
460            put(-1, 1, 0, [96, 140, 210]);
461            put(0, 1, -1, AZUL); // cola
462            put(0, 0, 0, AMARILLO); // pata
463        }
464        Bicho::Pez => {
465            // Cuerpo lateral + aleta caudal (se coloca en/junto al agua).
466            put(0, 1, 0, AZUL);
467            put(0, 1, 1, AZUL);
468            put(0, 2, 0, [110, 150, 210]);
469            put(0, 1, 2, [110, 150, 210]); // morro
470            put(0, 1, -1, [150, 180, 220]); // cola
471            put(0, 2, -1, [150, 180, 220]);
472            put(0, 0, -1, [150, 180, 220]);
473        }
474        Bicho::Tortuga => {
475            // Caparazón verde abovedado + cabeza + 4 patas.
476            for dx in -1..=1 {
477                for dz in -1..=1 {
478                    put(dx, 1, dz, VERDE_OSC);
479                }
480            }
481            put(0, 2, 0, VERDE); // cúpula del caparazón
482            put(0, 1, 2, [180, 200, 120]); // cabeza
483            for &(lx, lz) in &[(-1, -1), (1, -1), (-1, 1), (1, 1)] {
484                put(lx, 0, lz, [180, 200, 120]); // patas
485            }
486        }
487        Bicho::Rana => {
488            // Cuerpo verde agazapado, ojos saltones, patas dobladas.
489            put(0, 1, 0, VERDE);
490            put(-1, 1, 0, VERDE);
491            put(0, 1, 1, VERDE); // frente
492            put(-1, 1, 1, VERDE);
493            put(0, 2, 1, VERDE_OSC); // ojo
494            put(-1, 2, 1, VERDE_OSC);
495            put(0, 0, -1, VERDE); // pata trasera
496            put(-1, 0, -1, VERDE);
497            put(0, 0, 2, [200, 210, 140]); // pata delantera
498        }
499        Bicho::Serpiente => {
500            // Cuerpo largo y bajo en zigzag + cabeza.
501            let cuerpo = [(0, 0), (0, -1), (-1, -1), (-1, -2), (-2, -2), (-2, -3)];
502            for &(sx, sz) in &cuerpo {
503                put(sx, 1, sz, VERDE_OSC);
504            }
505            put(0, 1, 1, VERDE); // cabeza
506            put(0, 1, 2, VERDE);
507            put(0, 2, 1, ROJO); // lengua/ojo
508        }
509        Bicho::Mariposa => {
510            // Morfo de alas: naranja monarca / azul / amarilla.
511            let (ala1, ala2) = match vh % 3 {
512                0 => (AMARILLO, NARANJA),
513                1 => (AZUL, [140, 180, 230]),
514                _ => (AMARILLO, [230, 170, 60]),
515            };
516            put(0, 3, 0, NEGRO); // cuerpo
517            put(0, 2, 0, NEGRO);
518            put(1, 3, 0, ala1); // alas
519            put(1, 3, 1, ala2);
520            put(-1, 3, 0, ala1);
521            put(-1, 3, 1, ala2);
522            put(1, 2, 0, ala2);
523            put(-1, 2, 0, ala2);
524        }
525
526        // ---- Granja / pradera ----
527        Bicho::Pato => {
528            // Cuerpo blanco chico, cuello, pico naranja, cola.
529            put(0, 1, 0, BLANCO);
530            put(0, 1, -1, BLANCO);
531            put(-1, 1, 0, BLANCO);
532            put(0, 2, 1, BLANCO); // cuello/cabeza
533            put(0, 3, 1, BLANCO);
534            put(0, 3, 2, NARANJA); // pico
535            put(0, 2, -1, BLANCO); // cola
536            put(0, 0, 0, NARANJA); // patas
537        }
538        Bicho::Cabra => {
539            // Morfo: blanca / parda. Cuerpo, patas, cabeza barbada y cuernos.
540            let capa = if vh % 2 == 0 { [222, 216, 202] } else { MARRON_CLR };
541            for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
542                put(lx, 0, lz, MARRON_OSC);
543                put(lx, 1, lz, capa);
544            }
545            for dx in -1..=0 {
546                for dz in -1..=1 {
547                    put(dx, 2, dz, capa);
548                }
549            }
550            put(0, 2, 2, capa); // cabeza
551            put(0, 3, 2, capa);
552            put(0, 2, 3, capa); // hocico
553            put(0, 4, 2, [232, 228, 214]); // cuerno
554            put(0, 1, 2, [220, 214, 200]); // barbita
555            put(-1, 2, -2, capa); // cola corta
556        }
557        Bicho::Burro => {
558            // Gris, patas, lomo, cuello y orejas largas.
559            for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
560                put(lx, 0, lz, GRIS_OSC);
561                put(lx, 1, lz, GRIS);
562            }
563            for dx in -1..=0 {
564                for dz in -1..=1 {
565                    put(dx, 2, dz, GRIS);
566                }
567            }
568            put(0, 3, 1, GRIS); // cuello
569            put(0, 3, 2, GRIS); // cabeza
570            put(0, 4, 2, GRIS); // oreja larga
571            put(0, 5, 2, GRIS_OSC);
572            put(-1, 2, -2, GRIS_OSC); // cola
573        }
574
575        // ---- Bosque ----
576        Bicho::Ardilla => {
577            // Cuerpito naranja con cola tupida enroscada hacia arriba.
578            put(0, 1, 0, NARANJA);
579            put(0, 2, 0, NARANJA);
580            put(0, 2, 1, NARANJA); // cabeza
581            put(0, 3, 1, [222, 150, 90]); // oreja
582            put(0, 1, 1, [232, 190, 150]); // pecho claro
583            put(0, 0, 0, MARRON_OSC); // pata
584            put(0, 2, -1, NARANJA); // cola
585            put(0, 3, -1, [222, 150, 90]);
586            put(0, 4, -1, NARANJA);
587            put(0, 4, 0, [222, 150, 90]);
588        }
589        Bicho::Buho => {
590            // Compacto, posado, con ojos grandes y penachos.
591            for dy in 1..=3 {
592                put(0, dy, 0, MARRON);
593                put(-1, dy, 0, MARRON);
594            }
595            put(0, 3, 1, AMARILLO); // ojo
596            put(-1, 3, 1, AMARILLO);
597            put(0, 4, 0, MARRON_OSC); // penacho
598            put(-1, 4, 0, MARRON_OSC);
599            put(0, 2, 1, [206, 180, 140]); // pecho
600            put(-1, 2, 1, [206, 180, 140]);
601            put(0, 0, 0, AMARILLO); // garras
602            put(-1, 0, 0, AMARILLO);
603        }
604        Bicho::Erizo => {
605            // Cuerpo bajo con lomo de púas oscuras y carita clara.
606            for dx in -1..=0 {
607                for dz in -1..=1 {
608                    put(dx, 1, dz, MARRON_OSC);
609                    put(dx, 2, dz, [64, 52, 40]); // púas
610                }
611            }
612            put(0, 2, 2, [222, 198, 160]); // carita
613            put(-1, 2, 2, [222, 198, 160]);
614            put(0, 1, 3, NEGRO); // hocico
615            for &(lx, lz) in &[(-1, -1), (0, 1)] {
616                put(lx, 0, lz, [120, 96, 72]);
617            }
618        }
619
620        // ---- Desierto ----
621        Bicho::Camello => {
622            // Color arena, patas largas, joroba, cuello y cabeza.
623            for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
624                put(lx, 0, lz, [176, 148, 104]);
625                put(lx, 1, lz, TAN);
626                put(lx, 2, lz, TAN);
627            }
628            for dx in -1..=0 {
629                for dz in -1..=1 {
630                    put(dx, 3, dz, TAN);
631                }
632            }
633            put(0, 4, 0, TAN); // joroba
634            put(-1, 4, 0, TAN);
635            put(0, 4, 1, TAN); // cuello
636            put(0, 5, 2, TAN); // cabeza
637            put(0, 5, 3, [176, 148, 104]); // hocico
638            put(-1, 3, -2, TAN); // cola
639        }
640        Bicho::Escorpion => {
641            // Cuerpo bajo oscuro, pinzas al frente, cola curva con aguijón.
642            put(0, 1, 0, [70, 58, 40]);
643            put(0, 1, 1, [70, 58, 40]);
644            put(0, 1, 2, [86, 70, 48]); // pinzas
645            put(1, 1, 2, [86, 70, 48]);
646            put(-1, 1, 2, [86, 70, 48]);
647            put(0, 1, -1, [70, 58, 40]); // cola
648            put(0, 2, -1, [70, 58, 40]);
649            put(0, 2, 0, [70, 58, 40]);
650            put(0, 3, 0, ROJO); // aguijón
651        }
652        Bicho::Lagarto => {
653            // Cuerpo verde alargado y bajo, patitas, cabeza, cola larga.
654            for dz in -2..=1 {
655                put(0, 1, dz, VERDE_OSC);
656            }
657            put(0, 1, 2, VERDE); // cabeza
658            put(0, 1, -3, VERDE_OSC); // cola
659            put(0, 1, -4, [80, 130, 66]);
660            put(1, 0, 0, VERDE_OSC); // patas
661            put(-1, 0, 0, VERDE_OSC);
662            put(1, 0, -1, VERDE_OSC);
663            put(-1, 0, -1, VERDE_OSC);
664        }
665
666        // ---- Nieve / agua fría ----
667        Bicho::Pinguino => {
668            // De pie: espalda negra, panza blanca, pico y patas naranjas.
669            for dy in 1..=3 {
670                put(0, dy, 0, NEGRO); // espalda
671            }
672            put(0, 1, 1, BLANCO); // panza
673            put(0, 2, 1, BLANCO);
674            put(0, 4, 0, NEGRO); // cabeza
675            put(0, 4, 1, NARANJA); // pico
676            put(1, 2, 0, NEGRO); // aleta
677            put(-1, 2, 0, NEGRO);
678            put(0, 0, 1, NARANJA); // patas
679        }
680        Bicho::Foca => {
681            // Cuerpo gris tendido, cabeza levantada, aletas.
682            for dz in -2..=1 {
683                put(0, 1, dz, GRIS);
684                put(-1, 1, dz, GRIS);
685            }
686            put(0, 2, 1, GRIS); // cabeza
687            put(-1, 2, 1, GRIS);
688            put(0, 2, 2, [110, 112, 118]); // hocico
689            put(0, 1, -3, [110, 112, 118]); // cola/aleta
690            put(-1, 1, -3, [110, 112, 118]);
691            put(1, 1, 0, [150, 150, 156]); // aleta lateral
692        }
693
694        // ---- Selva / sabana ----
695        Bicho::Mono => {
696            // Cuerpo marrón sentado, cabeza clara, cola larga curva.
697            put(0, 1, 0, MARRON);
698            put(0, 2, 0, MARRON);
699            put(0, 2, 1, [176, 140, 100]); // cara clara
700            put(0, 3, 1, MARRON); // frente
701            put(0, 1, 1, MARRON); // rodillas
702            put(1, 2, 0, MARRON); // brazo
703            put(-1, 2, 0, MARRON);
704            put(0, 1, -1, MARRON); // cola
705            put(0, 2, -1, MARRON);
706            put(0, 2, -2, MARRON);
707        }
708        Bicho::Loro => {
709            // Cuerpo verde vivo, pecho rojo, pico y cola larga.
710            put(0, 1, 0, [70, 168, 72]);
711            put(0, 2, 0, [70, 168, 72]);
712            put(0, 3, 0, [70, 168, 72]); // cabeza
713            put(0, 3, 1, ROJO); // pico
714            put(0, 1, 1, ROJO); // pecho
715            put(1, 2, 0, [90, 190, 90]); // ala
716            put(0, 1, -1, [220, 200, 60]); // cola
717            put(0, 1, -2, [70, 168, 72]);
718            put(0, 0, 0, [120, 96, 72]); // pata
719        }
720        Bicho::Tigre => {
721            // Cuerpo naranja con rayas negras, patas, cabeza, cola.
722            for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
723                put(lx, 0, lz, NARANJA);
724            }
725            for dx in -1i32..=0 {
726                for dz in -1i32..=1 {
727                    // Rayas: bandas negras alternas a lo largo.
728                    let raya = dz.rem_euclid(2) == 0;
729                    put(dx, 1, dz, if raya { NEGRO } else { NARANJA });
730                }
731            }
732            put(0, 2, 2, NARANJA); // cabeza
733            put(-1, 2, 2, NARANJA);
734            put(0, 1, 2, [232, 200, 150]); // hocico
735            put(0, 2, -2, NARANJA); // cola
736            put(0, 2, -3, NEGRO);
737        }
738        Bicho::Elefante => {
739            // Grande: cuerpo gris (2×3×2), patas, trompa, orejas, colmillos.
740            for dx in -1i32..=0 {
741                for dz in -1..=1 {
742                    for dy in 2..=3 {
743                        put(dx, dy, dz, GRIS);
744                    }
745                }
746            }
747            for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
748                put(lx, 0, lz, [110, 112, 118]);
749                put(lx, 1, lz, GRIS);
750            }
751            put(0, 3, 2, GRIS); // cabeza
752            put(-1, 3, 2, GRIS);
753            put(0, 2, 2, GRIS);
754            put(0, 1, 3, GRIS); // trompa
755            put(0, 0, 3, [110, 112, 118]);
756            put(1, 3, 1, [150, 150, 156]); // oreja
757            put(-2, 3, 1, [150, 150, 156]);
758            put(0, 1, 2, [232, 228, 214]); // colmillo
759        }
760        Bicho::Cocodrilo => {
761            // Cuerpo verde largo y bajo, dorso escamado, fauces, cola.
762            for dz in -3..=2 {
763                put(0, 1, dz, VERDE_OSC);
764                put(-1, 1, dz, VERDE_OSC);
765            }
766            // Dorso escamado (crestas alternas).
767            for dz in -3i32..=1 {
768                if dz.rem_euclid(2) == 0 {
769                    put(0, 2, dz, [72, 128, 60]);
770                }
771            }
772            put(0, 1, 3, VERDE); // hocico
773            put(-1, 1, 3, VERDE);
774            put(0, 2, 3, NEGRO); // ojo/fauces
775            put(0, 1, -4, VERDE_OSC); // cola
776            put(0, 1, -5, [80, 130, 66]);
777            put(1, 0, 0, VERDE_OSC); // patas
778            put(-2, 0, 0, VERDE_OSC);
779        }
780    }
781}
782
783#[cfg(test)]
784mod tests {
785    use super::*;
786
787    fn solids(g: &VoxelGrid, dim: [u32; 3]) -> usize {
788        let mut n = 0;
789        for x in 0..dim[0] {
790            for y in 0..dim[1] {
791                for z in 0..dim[2] {
792                    if g.is_solid(x as i32, y as i32, z as i32) {
793                        n += 1;
794                    }
795                }
796            }
797        }
798        n
799    }
800
801    #[test]
802    fn cada_bicho_estampa_voxels() {
803        let dim = [20u32, 16, 20];
804        for bicho in Bicho::ALL {
805            let mut g = VoxelGrid::new(dim);
806            stamp_bicho(&mut g, bicho, 10, 1, 10, 0);
807            assert!(solids(&g, dim) >= 5, "{} estampó muy poco", bicho.label());
808        }
809    }
810
811    #[test]
812    fn facing_rota_la_silueta() {
813        let dim = [24u32, 16, 24];
814        let mut a = VoxelGrid::new(dim);
815        let mut b = VoxelGrid::new(dim);
816        stamp_bicho(&mut a, Bicho::Ciervo, 12, 1, 12, 0);
817        stamp_bicho(&mut b, Bicho::Ciervo, 12, 1, 12, 1);
818        // Al rotar, la ocupación cambia (el hocico apunta a otro eje).
819        let mut diff = 0;
820        for x in 0..dim[0] {
821            for y in 0..dim[1] {
822                for z in 0..dim[2] {
823                    if a.is_solid(x as i32, y as i32, z as i32) != b.is_solid(x as i32, y as i32, z as i32) {
824                        diff += 1;
825                    }
826                }
827            }
828        }
829        assert!(diff > 0, "facing no rotó la silueta");
830    }
831}