-
-
Notifications
You must be signed in to change notification settings - Fork 336
/
mod.rs
354 lines (306 loc) · 10.7 KB
/
mod.rs
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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
//! Contains all structures and methods to create and manage lights.
//!
//! Light sources arte basic building blocks of many scenes in games, it improves
//! perception of scene and makes it look natural. rg3d engine supports three kinds
//! of light sources:
//!
//! 1) Directional - similar to sun in real life, its rays are parallel.
//! 2) Spot - similar to flash light, it has cone light volume and circle spot.
//! 3) Point - similar to light bulb, it has spherical light volume.
//!
//! Each kind of light source is suitable for specific conditions, for example
//! spot light can be used if you have a character with flashlight, point - if
//! you have a character with torch, and finally directional - for outdoor light.
//!
//! Most of light sources supports shadows (via shadows maps) and light scattering,
//! these are common effects for modern games but still can significantly impact
//! performance.
use crate::{
core::{
algebra::Vector3,
color::Color,
define_is_as,
visitor::{Visit, VisitResult, Visitor},
},
scene::{
base::{Base, BaseBuilder},
light::{directional::DirectionalLight, point::PointLight, spot::SpotLight},
},
};
use std::ops::{Deref, DerefMut};
pub mod directional;
pub mod point;
pub mod spot;
/// Default amount of light scattering, it is set to 3% which is fairly
/// significant value and you'll clearly see light volume with such settings.
pub const DEFAULT_SCATTER_R: f32 = 0.03;
/// Default amount of light scattering, it is set to 3% which is fairly
/// significant value and you'll clearly see light volume with such settings.
pub const DEFAULT_SCATTER_G: f32 = 0.03;
/// Default amount of light scattering, it is set to 3% which is fairly
/// significant value and you'll clearly see light volume with such settings.
pub const DEFAULT_SCATTER_B: f32 = 0.03;
/// Engine supports limited amount of light source kinds
#[derive(Debug)]
pub enum Light {
/// See [DirectionalLight](struct.DirectionalLight.html)
Directional(DirectionalLight),
/// See [SpotLight](struct.SpotLight.html)
Spot(SpotLight),
/// See [PointLight](struct.PointLight.html)
Point(PointLight),
}
impl Default for Light {
fn default() -> Self {
Self::Directional(Default::default())
}
}
impl Light {
fn new(id: u32) -> Result<Self, String> {
match id {
0 => Ok(Self::Spot(Default::default())),
1 => Ok(Self::Point(Default::default())),
2 => Ok(Self::Directional(Default::default())),
_ => Err(format!("Invalid light kind {}", id)),
}
}
fn id(&self) -> u32 {
match self {
Self::Spot(_) => 0,
Self::Point(_) => 1,
Self::Directional(_) => 2,
}
}
/// Creates a raw copy of a light node.
pub fn raw_copy(&self) -> Self {
match self {
Light::Directional(v) => Self::Directional(v.raw_copy()),
Light::Spot(v) => Self::Spot(v.raw_copy()),
Light::Point(v) => Self::Point(v.raw_copy()),
}
}
define_is_as!(Light : Directional -> ref DirectionalLight => fn is_directional, fn as_directional, fn as_directional_mut);
define_is_as!(Light : Spot -> ref SpotLight => fn is_spot, fn as_spot, fn as_spot_mut);
define_is_as!(Light : Point -> ref PointLight => fn is_point, fn as_point, fn as_point_mut);
}
impl Deref for Light {
type Target = BaseLight;
fn deref(&self) -> &Self::Target {
match self {
Self::Directional(v) => v.deref(),
Self::Spot(v) => v.deref(),
Self::Point(v) => v.deref(),
}
}
}
impl DerefMut for Light {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
Self::Directional(v) => v.deref_mut(),
Self::Spot(v) => v.deref_mut(),
Self::Point(v) => v.deref_mut(),
}
}
}
impl Visit for Light {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
let mut kind_id = self.id();
kind_id.visit("KindId", visitor)?;
if visitor.is_reading() {
*self = Self::new(kind_id)?;
}
match self {
Self::Spot(spot_light) => spot_light.visit("Data", visitor)?,
Self::Point(point_light) => point_light.visit("Data", visitor)?,
Self::Directional(directional_light) => directional_light.visit("Data", visitor)?,
}
visitor.leave_region()
}
}
/// Light scene node. It contains common properties of light such as color,
/// scattering factor (per color channel) and other useful properties. Exact
/// behavior defined by specific light kind.
#[derive(Debug)]
pub struct BaseLight {
base: Base,
color: Color,
cast_shadows: bool,
scatter: Vector3<f32>,
scatter_enabled: bool,
intensity: f32,
}
impl Deref for BaseLight {
type Target = Base;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for BaseLight {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl Default for BaseLight {
fn default() -> Self {
Self {
base: Default::default(),
color: Color::WHITE,
cast_shadows: true,
scatter: Vector3::new(DEFAULT_SCATTER_R, DEFAULT_SCATTER_G, DEFAULT_SCATTER_B),
scatter_enabled: true,
intensity: 1.0,
}
}
}
impl Visit for BaseLight {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
self.color.visit("Color", visitor)?;
self.base.visit("Base", visitor)?;
self.cast_shadows.visit("CastShadows", visitor)?;
self.scatter.visit("ScatterFactor", visitor)?;
self.scatter_enabled.visit("ScatterEnabled", visitor)?;
let _ = self.intensity.visit("Intensity", visitor); // Backward compatibility.
visitor.leave_region()
}
}
impl BaseLight {
/// Sets color of light, alpha component of color is ignored.
#[inline]
pub fn set_color(&mut self, color: Color) {
self.color = color;
}
/// Returns current color of light source.
#[inline]
pub fn color(&self) -> Color {
self.color
}
/// Enables or disables shadows for light source.
#[inline]
pub fn set_cast_shadows(&mut self, value: bool) {
self.cast_shadows = value;
}
/// Returns true if light is able to cast shadows, false - otherwise.
#[inline]
pub fn is_cast_shadows(&self) -> bool {
self.cast_shadows
}
/// Sets scatter factor per color channel (red, green, blue) in (0..1) range.
/// This parameter defines how "thick" environment is and how much light will
/// be scattered in light volume. Ability to change this parameter per channel
/// allows you simulate Rayleigh scatter if needed - in simple words Rayleigh
/// scatter tells us that blue light waves scatters much better than red ones,
/// this effect makes sky blue. Reasonable value is something near 0.024-0.03
/// per color channel, higher values will cause too "heavy" light scattering
/// as if you light source would be in fog.
#[inline]
pub fn set_scatter(&mut self, f: Vector3<f32>) {
self.scatter = f;
}
/// Returns current scatter factor.
#[inline]
pub fn scatter(&self) -> Vector3<f32> {
self.scatter
}
/// Sets new light intensity. Default is 1.0.
///
/// Intensity is used for very bright light sources in HDR. For examples, sun
/// can be represented as directional light source with very high intensity.
/// Other lights, however, will remain relatively dim.
pub fn set_intensity(&mut self, intensity: f32) {
self.intensity = intensity;
}
/// Returns current intensity of the light.
pub fn intensity(&self) -> f32 {
self.intensity
}
/// Returns current scatter factor in linear color space.
#[inline]
pub fn scatter_linear(&self) -> Vector3<f32> {
self.scatter.map(|v| v.powf(2.2))
}
/// Enables or disables light scattering.
#[inline]
pub fn enable_scatter(&mut self, state: bool) {
self.scatter_enabled = state;
}
/// Returns true if light scattering is enabled, false - otherwise.
#[inline]
pub fn is_scatter_enabled(&self) -> bool {
self.scatter_enabled
}
/// Creates a raw copy of a base light node.
pub fn raw_copy(&self) -> Self {
Self {
base: self.base.raw_copy(),
color: self.color,
cast_shadows: self.cast_shadows,
scatter: self.scatter,
scatter_enabled: self.scatter_enabled,
intensity: self.intensity,
}
}
}
/// Light scene node builder. Provides easy declarative way of creating light scene
/// nodes.
pub struct BaseLightBuilder {
base_builder: BaseBuilder,
color: Color,
cast_shadows: bool,
scatter_factor: Vector3<f32>,
scatter_enabled: bool,
intensity: f32,
}
impl BaseLightBuilder {
/// Creates new instance of light scene node builder, you must pass desired
/// light kind and base scene node builder as parameters. Latter one is needed
/// because engine uses composition and light scene node built on top of base
/// scene node.
pub fn new(base_builder: BaseBuilder) -> Self {
Self {
base_builder,
color: Color::WHITE,
cast_shadows: true,
scatter_factor: Vector3::new(DEFAULT_SCATTER_R, DEFAULT_SCATTER_G, DEFAULT_SCATTER_B),
scatter_enabled: true,
intensity: 1.0,
}
}
/// Sets light color.
pub fn with_color(mut self, color: Color) -> Self {
self.color = color;
self
}
/// Sets whether to casts shadows or not.
pub fn cast_shadows(mut self, cast_shadows: bool) -> Self {
self.cast_shadows = cast_shadows;
self
}
/// Sets light scatter factor per color channel.
pub fn with_scatter_factor(mut self, f: Vector3<f32>) -> Self {
self.scatter_factor = f;
self
}
/// Whether light scatter enabled or not.
pub fn with_scatter_enabled(mut self, state: bool) -> Self {
self.scatter_enabled = state;
self
}
/// Sets desired light intensity.
pub fn with_intensity(mut self, intensity: f32) -> Self {
self.intensity = intensity;
self
}
/// Creates new instance of base light.
pub fn build(self) -> BaseLight {
BaseLight {
base: self.base_builder.build_base(),
color: self.color,
cast_shadows: self.cast_shadows,
scatter: self.scatter_factor,
scatter_enabled: self.scatter_enabled,
intensity: self.intensity,
}
}
}