tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
chore: refresco desde el monorepo — 132 miembros, 79 widgets, la galería vuelve y licencia dual
1//! **Cielo de noche**: estrellas, luna y la luz que dan.
2//!
3//! Vive en la escena y no en el sello de quien filma porque **que sea de noche
4//! lo dice el cuento**: «la princesa vio una estrella» no se puede filmar a
5//! pleno sol, la ruede la marca que la ruede. El sello sigue decidiendo la luz
6//! de día; una escena con cielo de noche la reemplaza por la suya.
7//!
8//! De noche la dirección del sol pasa a ser la de **la luna** (el shader dibuja
9//! el disco pálido ahí y de ahí viene la luz directa, fría y tenue). Por defecto
10//! cae donde mira el plano de contraluz ([`SCENE_SUN`](crate::SCENE_SUN)), así
11//! que ese plano la encuadra.
12
13use llimphi_3d::glam::Vec3;
14use llimphi_3d::{Atmosphere, NightSky, PointLight};
15use serde::{Deserialize, Serialize};
16
17/// El cielo de una escena de noche.
18#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
19pub struct Cielo {
20 /// Color del cielo arriba.
21 pub cenit: [u8; 3],
22 /// Color del cielo en el horizonte; también el de la bruma.
23 pub horizonte: [u8; 3],
24 /// Cuántas estrellas, `0..1`.
25 pub estrellas: f32,
26 /// Dirección **hacia** la luna, sin normalizar. De ahí viene la luz.
27 pub luna: [f32; 3],
28 /// Radio de la luna en radianes; `0` = noche sin luna (la luz igual viene
29 /// de esa dirección, pero no se dibuja el disco).
30 pub luna_radio: f32,
31}
32
33impl Default for Cielo {
34 fn default() -> Self {
35 Self::noche()
36 }
37}
38
39/// La luz que da una pieza de utilería con luz propia, de noche: alcance en
40/// metros (= voxels).
41pub const ALCANCE_LUZ_PROPIA: f32 = 7.0;
42
43impl Cielo {
44 /// Una noche de cuento: azul profundo arriba, horizonte apenas más claro,
45 /// muchas estrellas y una luna grande — se dibuja, no se fotografía.
46 pub fn noche() -> Self {
47 Self {
48 cenit: [12, 18, 46],
49 horizonte: [40, 50, 92],
50 estrellas: 0.8,
51 luna: crate::SCENE_SUN,
52 luna_radio: 0.06,
53 }
54 }
55
56 /// Lo que el renderer necesita para pintarla.
57 pub fn night_sky(&self) -> NightSky {
58 NightSky {
59 amount: 1.0,
60 stars: self.estrellas.clamp(0.0, 1.0),
61 moon: self.luna_radio.max(0.0),
62 }
63 }
64
65 /// La atmósfera de esta noche, con la bruma y los haces que ya traía quien
66 /// filma (son del aire, no de la hora).
67 pub fn atmosfera(&self, fog_density: f32, god_rays: f32) -> Atmosphere {
68 Atmosphere { sky_zenith: self.cenit, sky_horizon: self.horizonte, fog_density, god_rays }
69 }
70
71 /// El color con que limpiar el cuadro antes de componer.
72 pub fn fondo(&self) -> [u8; 4] {
73 [self.horizonte[0], self.horizonte[1], self.horizonte[2], 255]
74 }
75}
76
77/// La luz puntual que da una pieza con luz propia, centrada en ella. `centro`
78/// en coords de grilla; `color` el de la pieza (se normaliza al canal más alto:
79/// lo que alumbra es el tono, no el exceso que la marca como luminosa).
80pub fn luz_de(centro: Vec3, color: [f32; 3], alcance: f32) -> PointLight {
81 let m = color[0].max(color[1]).max(color[2]).max(1e-3);
82 PointLight {
83 pos: centro.to_array(),
84 color: [color[0] / m * 1.1, color[1] / m * 1.1, color[2] / m * 1.1],
85 range: alcance,
86 radius: 0.0,
87 }
88}