Skip to main content

primase_core/
light_falloff.rs

1//! A source-stated distance used by a light's shader model, apart from its emitter radius and range.
2use serde::Serialize;
3
4use crate::{Component, Entity, Geometry, JsonError, World};
5
6/// The normalization distance of a punctual light's source-defined falloff: a source program evaluates its
7/// distance-dependent diffuse wrap and specular width using `distance / self.distance`. This states the input,
8/// not an attenuation function; the source shader defines the function. It is neither the emitter's physical radius
9/// nor the range where lighting stops. Attached to the same entity as [`crate::Light`].
10#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
11pub struct LightFalloff {
12    /// A finite positive distance in the world's length unit, with a finite f32 reciprocal.
13    pub distance: f32,
14}
15
16impl Component for LightFalloff {
17    const NAME: &'static str = "core.light_falloff";
18    const GEOMETRY: Geometry = Geometry::None;
19    fn to_json(&self) -> Result<Option<serde_json::Value>, JsonError> {
20        crate::json::to_value(self).map(Some)
21    }
22    fn verify(&self, _e: Entity, _w: &World) -> Result<(), String> {
23        self.inverse_distance().map(|_| ())
24    }
25}
26
27impl LightFalloff {
28    /// The source-program input; invalid distances are refused rather than clamped or replaced.
29    pub fn inverse_distance(self) -> Result<f32, String> {
30        if !(self.distance.is_finite() && self.distance > 0. && self.distance.recip().is_finite()) {
31            return Err(format!(
32                "light falloff distance {} is not positive with a finite reciprocal",
33                self.distance
34            ));
35        }
36        Ok(self.distance.recip())
37    }
38}