sl_types/environment.rs
1//! Extended Environment (EEP) value types: the colour, glow, and cloud
2//! parameters carried by a region's or parcel's sky and water settings.
3//!
4//! These are distinct named types (rather than bare `[f32; N]`) so a colour
5//! cannot be transposed with a position, a direction, a scale, or a rotation —
6//! all of which are also arrays of `f32`.
7
8/// An RGB colour — three `f32` channels (normally `0.0..=1.0`, but HDR
9/// environment colours can exceed `1.0`). A named type so a colour cannot be
10/// transposed with a position, direction, or scale.
11#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
12pub struct Color {
13 /// The red channel.
14 red: f32,
15 /// The green channel.
16 green: f32,
17 /// The blue channel.
18 blue: f32,
19}
20
21impl Color {
22 /// Creates a colour from its red/green/blue channels.
23 #[must_use]
24 pub const fn new(red: f32, green: f32, blue: f32) -> Self {
25 Self { red, green, blue }
26 }
27
28 /// The red channel.
29 #[must_use]
30 pub const fn red(&self) -> f32 {
31 self.red
32 }
33
34 /// The green channel.
35 #[must_use]
36 pub const fn green(&self) -> f32 {
37 self.green
38 }
39
40 /// The blue channel.
41 #[must_use]
42 pub const fn blue(&self) -> f32 {
43 self.blue
44 }
45}
46
47/// An RGBA colour — four `f32` channels (RGB plus an alpha channel). The
48/// alpha-carrying sibling of [`Color`]; a distinct type so it can't be
49/// transposed with a 3-channel colour, a position, or a rotation quaternion
50/// (all of which are also arrays of `f32`). Its one wire user is the windlight
51/// `sunlight_color`. Channels are normally `0.0..=1.0` but HDR values can
52/// exceed `1.0`.
53#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
54pub struct ColorAlpha {
55 /// The red channel.
56 red: f32,
57 /// The green channel.
58 green: f32,
59 /// The blue channel.
60 blue: f32,
61 /// The alpha channel.
62 alpha: f32,
63}
64
65impl ColorAlpha {
66 /// Creates a colour from its red/green/blue/alpha channels.
67 #[must_use]
68 pub const fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
69 Self {
70 red,
71 green,
72 blue,
73 alpha,
74 }
75 }
76
77 /// The red channel.
78 #[must_use]
79 pub const fn red(&self) -> f32 {
80 self.red
81 }
82
83 /// The green channel.
84 #[must_use]
85 pub const fn green(&self) -> f32 {
86 self.green
87 }
88
89 /// The blue channel.
90 #[must_use]
91 pub const fn blue(&self) -> f32 {
92 self.blue
93 }
94
95 /// The alpha channel.
96 #[must_use]
97 pub const fn alpha(&self) -> f32 {
98 self.alpha
99 }
100}
101
102/// A windlight sun/moon **glow** parameter. The wire packs it as a 3-vector
103/// `(size, reserved, focus)` whose middle component is unused/reserved (the
104/// viewer always sends `0`); it is preserved verbatim so a decode/encode round
105/// trip is byte-identical. The meaningful channels are [`size`](Self::size) and
106/// [`focus`](Self::focus).
107#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
108pub struct Glow {
109 /// The glow size.
110 size: f32,
111 /// The unused/reserved middle component (preserved for round-trip).
112 reserved: f32,
113 /// The glow focus.
114 focus: f32,
115}
116
117impl Glow {
118 /// Creates a glow from its wire `(size, reserved, focus)` components.
119 #[must_use]
120 pub const fn new(size: f32, reserved: f32, focus: f32) -> Self {
121 Self {
122 size,
123 reserved,
124 focus,
125 }
126 }
127
128 /// The glow size.
129 #[must_use]
130 pub const fn size(&self) -> f32 {
131 self.size
132 }
133
134 /// The unused/reserved middle component (normally `0`).
135 #[must_use]
136 pub const fn reserved(&self) -> f32 {
137 self.reserved
138 }
139
140 /// The glow focus.
141 #[must_use]
142 pub const fn focus(&self) -> f32 {
143 self.focus
144 }
145}
146
147/// A windlight cloud layer's scroll **position** (X, Y) packed with its
148/// **density** (Z) in one wire 3-vector (the viewer's `cloud_pos_density*`).
149/// The three components are semantically distinct — two are a 2-D scroll offset,
150/// one is a density — so they get named accessors rather than `x`/`y`/`z`, and
151/// this type cannot be confused with a position or direction.
152#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
153pub struct CloudPosDensity {
154 /// The cloud-scroll x position.
155 position_x: f32,
156 /// The cloud-scroll y position.
157 position_y: f32,
158 /// The cloud density.
159 density: f32,
160}
161
162impl CloudPosDensity {
163 /// Creates a value from its wire `(position_x, position_y, density)`
164 /// components.
165 #[must_use]
166 pub const fn new(position_x: f32, position_y: f32, density: f32) -> Self {
167 Self {
168 position_x,
169 position_y,
170 density,
171 }
172 }
173
174 /// The cloud-scroll x position.
175 #[must_use]
176 pub const fn position_x(&self) -> f32 {
177 self.position_x
178 }
179
180 /// The cloud-scroll y position.
181 #[must_use]
182 pub const fn position_y(&self) -> f32 {
183 self.position_y
184 }
185
186 /// The cloud density.
187 #[must_use]
188 pub const fn density(&self) -> f32 {
189 self.density
190 }
191}