1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
use crate::model::vs as model_vs;
use cgmath::{Vector3, Zero};

/// A direction lightsource in the world.
///
/// Note: lights coming from the sky are going down, so their direction would be `Vector3::new(0.0,
/// -1.0, 0.0)`
///
/// For more information, see the amazing tutorial at [https://learnopengl.com/Lighting/Colors](https://learnopengl.com/Lighting/Colors)
pub struct DirectionalLight {
    /// The direction of the light source
    pub direction: Vector3<f32>,
    /// The color of the light source.
    pub color: LightColor,
}

impl Default for DirectionalLight {
    fn default() -> Self {
        Self {
            direction: Vector3::zero(),
            color: LightColor::default(),
        }
    }
}

/// A pointlight in the world.
///
/// Note: Not implemented yet
///
/// For more information, see the amazing tutorial at [https://learnopengl.com/Lighting/Colors](https://learnopengl.com/Lighting/Colors)
pub struct PointLight {
    /// The position of the light in the world.
    pub position: Vector3<f32>,
    /// The color of the light in the world.
    pub color: LightColor,

    /// The attenuation of the light, or how much the light decays over a distance.
    /// `PointLightAttenuation` implements `Default` so you can take a good initial value, or you
    /// can tune this until the end of time.
    pub attenuation: PointLightAttenuation,
}

impl Default for PointLight {
    fn default() -> Self {
        Self {
            position: Vector3::zero(),
            color: LightColor::default(),
            attenuation: PointLightAttenuation::default(),
        }
    }
}

/// The color of the light. This is divided in 3 fields: ambient, diffuse and specular. See each field for the definition.
///
/// For more information, see the amazing tutorial at [https://learnopengl.com/Lighting/Colors](https://learnopengl.com/Lighting/Colors)
pub struct LightColor {
    /// Even when it is dark there is usually still some light somewhere in the world (the moon, a distant light) so objects are almost never completely dark.
    /// To simulate this we use an ambient lighting constant that always gives the object some color.
    ///
    /// This will be merged with the ambient factor of the material of your model.
    pub ambient: Vector3<f32>,

    /// Diffuse light simulates the directional impact a light object has on an object.
    /// This is the most visually significant component of the lighting model.
    /// The more a part of an object faces the light source, the brighter it becomes.
    ///
    /// This will be merged with the diffuse factor of the material of your model.
    pub diffuse: Vector3<f32>,

    /// Specular light simulates the bright spot of a light that appears on shiny objects.
    /// Specular highlights are more inclined to the color of the light than the color of the object.
    ///
    /// This will be merged with the specular factor of the material of your model.
    pub specular: Vector3<f32>,
}

impl Default for LightColor {
    fn default() -> Self {
        LightColor {
            ambient: Vector3::zero(),
            diffuse: Vector3::zero(),
            specular: Vector3::zero(),
        }
    }
}

/// The attenuation of the pointlight, or how much the light impacts objects based on their
/// distance.
pub struct PointLightAttenuation {
    /// The constant or base attenuation. This will always reduce the effect of the light source,
    /// regardless on how far away the object is.
    ///
    /// This can also be seen as `brightness`.
    pub constant: f32,

    /// The linear attenuation of the light. This will reduce the effect of the light source if the
    /// model is far away
    pub linear: f32,

    /// The quadratic attenuation of the light. This will greatly reduce the effect of the light
    /// source if the model is far away.
    pub quadratic: f32,
}

impl Default for PointLightAttenuation {
    fn default() -> Self {
        // Values taken from https://learnopengl.com/Lighting/Multiple-lights
        Self {
            constant: 1.0,
            linear: 0.09,
            quadratic: 0.032,
        }
    }
}

/// The state of the lights in the game. Lights come in two flavors.
///
/// Directional lights: light sources that shine in a certain direction, e.g. the sun.
///
/// Point lights: lights that shine equally in all directions, e.g. a lightbulb.
///
/// Note: lights are limited to 100 of each type. Currently the shaders do not support more than
/// 100 light sources at a time. Please open an issue if you need more light sources.
pub struct LightState {
    /// A `FixedVec` of directional lights
    pub directional: FixedVec<DirectionalLight>,
    /// A `FixedVec` of point lights.
    ///
    /// Note: not implemented yet
    pub point: FixedVec<PointLight>,
}

impl LightState {
    pub(crate) fn new() -> Self {
        Self {
            directional: FixedVec::<DirectionalLight>::new(),
            point: FixedVec::<PointLight>::new(),
        }
    }
}

const LIGHT_COUNT: usize = 100;
/// A fixed vec of light sources. This is limited to 100 entries because of a limitation in the way
/// Crystal's shaders are implemented. Please open an issue if you need more light sources.
///
/// This should mirror most functions that exist on [Vec]. If you're missing a function, feel free to open an issue or PR!
pub struct FixedVec<T> {
    pub(crate) data: [T; LIGHT_COUNT],
    len: usize,
}

impl FixedVec<DirectionalLight> {
    pub(crate) fn to_shader_value(&self) -> (i32, [model_vs::ty::DirectionalLight; LIGHT_COUNT]) {
        let result = array_init::array_init(|i| {
            let light = &self.data[i];
            model_vs::ty::DirectionalLight {
                direction_x: light.direction.x,
                direction_y: light.direction.y,
                direction_z: light.direction.z,
                color_ambient_r: light.color.ambient.x,
                color_ambient_g: light.color.ambient.y,
                color_ambient_b: light.color.ambient.z,
                color_diffuse_r: light.color.diffuse.x,
                color_diffuse_g: light.color.diffuse.x,
                color_diffuse_b: light.color.diffuse.z,
                color_specular_r: light.color.specular.x,
                color_specular_g: light.color.specular.y,
                color_specular_b: light.color.specular.z,
            }
        });
        (self.len() as i32, result)
    }
}

impl<T: Default> FixedVec<T> {
    pub(crate) fn new() -> Self {
        Self {
            // safe because this is a `DirectionalLight` which has a size and is not a reference
            data: array_init::array_init(|_| T::default()),
            len: 0,
        }
    }
}

// Implementation of relevant std::vec::Vec functions
impl<T> FixedVec<T> {
    /// Extracts a slice containing the entire fixed vec.
    ///
    /// Equivalent to `&s[..]`.
    pub fn as_slice(&self) -> &[T] {
        &self.data[..self.len]
    }

    /// Extracts a mutable slice of the entire fixed vec.
    ///
    /// Equivalent to `&mut s[..]`.
    pub fn as_mut_slice(&mut self) -> &mut [T] {
        &mut self.data[..self.len]
    }

    /// Get the amount of lights that are stored in this `FixedVec`.
    ///
    /// Note: this is always 100 or lower.
    pub fn len(&self) -> usize {
        self.len
    }

    /// Returns `true` if this `FixedVec` is empty.
    ///
    /// This is an alias for `self.len() == 0`
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Add a new light to this `FixedVec`.
    ///
    /// This will panic if more than 100 lights are added.
    pub fn push(&mut self, t: T) {
        assert!(self.len() < LIGHT_COUNT);
        self.data[self.len] = t;
        self.len += 1;
    }

    /// Remove the last light source from this `FixedVec`.
    ///
    /// This will panic if the `FixedVec` is empty.
    pub fn pop(&mut self) {
        assert!(self.len > 0);
        self.len -= 1;
    }
}

impl<T> std::ops::Index<usize> for FixedVec<T> {
    type Output = T;
    fn index(&self, index: usize) -> &T {
        assert!(index < self.len());
        &self.data[index]
    }
}

impl<T> std::ops::IndexMut<usize> for FixedVec<T> {
    fn index_mut(&mut self, index: usize) -> &mut T {
        assert!(index < self.len());
        &mut self.data[index]
    }
}