nightshade/ecs/light/components.rs
1//! Light component definitions.
2
3use serde::{Deserialize, Serialize};
4
5/// Type of light source. Directional, point, and spot follow glTF
6/// KHR_lights_punctual. Area is an emitter with spatial extent shaded with
7/// linearly transformed cosines.
8#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, Deserialize, enum2schema::Schema)]
9#[schema(string_enum)]
10pub enum LightType {
11 /// Infinitely distant light (like the sun). Uses entity's -Z direction.
12 #[default]
13 Directional,
14 /// Omnidirectional point light. Uses entity's position.
15 Point,
16 /// Cone-shaped spotlight. Uses entity's position and -Z direction.
17 Spot,
18 /// Emitter with spatial extent (rectangle, disk, sphere, or tube). The
19 /// extent and shape come from the area fields on [`Light`].
20 Area,
21}
22
23/// Geometric shape of an area light's emitter surface.
24#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, Deserialize, enum2schema::Schema)]
25#[schema(string_enum)]
26pub enum AreaLightShape {
27 /// Flat rectangle oriented in the entity's local XY plane, emitting along
28 /// -Z. Sized by `area_width` and `area_height`. Supports an emissive texture.
29 #[default]
30 Rectangle,
31 /// Flat disk oriented in the entity's local XY plane, emitting along -Z.
32 /// Sized by `area_radius`.
33 Disk,
34 /// Spherical emitter. Sized by `area_radius`. Always faces the shaded point.
35 Sphere,
36 /// Capsule-like tube along the entity's local X axis. `area_width` is the
37 /// length and `area_radius` is the tube radius.
38 Tube,
39}
40
41/// Light source component.
42///
43/// Attach to an entity with a transform to illuminate the scene.
44/// The transform's -Z axis defines the light direction (for directional and spot lights).
45#[derive(Debug, Clone, Serialize, Deserialize, enum2schema::Schema)]
46pub struct Light {
47 /// The type of light (directional, point, spot, or area).
48 pub light_type: LightType,
49 /// Light color as linear RGB.
50 #[schema(type = "array", items = "number", len = 3)]
51 pub color: nalgebra_glm::Vec3,
52 /// Light intensity (lumens for point/spot, lux for directional).
53 pub intensity: f32,
54 /// Maximum range for point/spot lights (0 = unlimited).
55 pub range: f32,
56 /// Inner cone angle in radians for spot lights (full intensity).
57 pub inner_cone_angle: f32,
58 /// Outer cone angle in radians for spot lights (zero intensity).
59 pub outer_cone_angle: f32,
60 /// Whether this light casts shadows.
61 pub cast_shadows: bool,
62 /// Depth bias to reduce shadow acne artifacts.
63 pub shadow_bias: f32,
64 /// Per-light shadow map resolution. 0 falls back to the renderer default.
65 #[serde(default)]
66 pub shadow_resolution: u32,
67 /// Maximum distance at which this light casts shadows. 0 = unlimited.
68 #[serde(default)]
69 pub shadow_distance: f32,
70 /// Optional cookie texture path (IES profile or projected mask).
71 #[serde(default)]
72 pub cookie_texture: Option<String>,
73 /// Emitter shape for `LightType::Area`. Ignored for other light types.
74 #[serde(default)]
75 pub area_shape: AreaLightShape,
76 /// Width of a rectangular area light, or length of a tube light.
77 #[serde(default = "default_area_extent")]
78 pub area_width: f32,
79 /// Height of a rectangular area light.
80 #[serde(default = "default_area_extent")]
81 pub area_height: f32,
82 /// Radius of a disk, sphere, or tube area light.
83 #[serde(default = "default_area_extent")]
84 pub area_radius: f32,
85 /// Whether a rectangular or disk area light emits from both faces.
86 #[serde(default)]
87 pub area_two_sided: bool,
88 /// Optional emissive texture projected across a rectangular area light.
89 #[serde(default)]
90 pub area_emissive_texture: Option<String>,
91}
92
93fn default_area_extent() -> f32 {
94 1.0
95}
96
97impl Default for Light {
98 fn default() -> Self {
99 Self {
100 light_type: LightType::default(),
101 color: nalgebra_glm::Vec3::new(0.0, 0.0, 0.0),
102 intensity: 0.0,
103 range: 0.0,
104 inner_cone_angle: 0.0,
105 outer_cone_angle: 0.0,
106 cast_shadows: false,
107 shadow_bias: 0.0,
108 shadow_resolution: 0,
109 shadow_distance: 0.0,
110 cookie_texture: None,
111 area_shape: AreaLightShape::Rectangle,
112 area_width: default_area_extent(),
113 area_height: default_area_extent(),
114 area_radius: default_area_extent(),
115 area_two_sided: false,
116 area_emissive_texture: None,
117 }
118 }
119}
120
121impl Light {
122 /// Enables shadow casting with the specified depth bias.
123 pub fn with_shadows(mut self, bias: f32) -> Self {
124 self.cast_shadows = true;
125 self.shadow_bias = bias;
126 self
127 }
128}