Skip to main content

viewport_lib_wind/
globals.rs

1//! POD uniform layout uploaded to the GPU each frame.
2
3use bytemuck::{Pod, Zeroable};
4
5/// Per-frame wind state, mirroring the WGSL `WindGlobals` struct.
6///
7/// Total size is 64 bytes. Padding fields keep the Rust struct aligned with
8/// the std140 layout the shader expects.
9#[repr(C)]
10#[derive(Copy, Clone, Debug, Pod, Zeroable)]
11pub struct WindGlobals {
12    /// World-space wind direction. Need not be unit length: the shader
13    /// normalises before use, and the magnitude contributes to base strength.
14    pub direction: [f32; 3],
15    /// Steady-state wind strength applied alongside `direction`.
16    pub base_strength: f32,
17    /// Amplitude of the gust envelope.
18    pub gust_strength: f32,
19    /// Cycles per second of the gust envelope.
20    pub gust_frequency: f32,
21    /// Spatial wavelength factor: larger values mean smaller gusts.
22    pub spatial_density: f32,
23    /// Wind clock, advanced by the runtime plugin each frame.
24    pub time: f32,
25    _pad: [f32; 8],
26}
27
28impl WindGlobals {
29    /// Build a value from individual field values. The trailing padding is
30    /// zeroed automatically so the std140 layout stays correct.
31    pub fn new(
32        direction: [f32; 3],
33        base_strength: f32,
34        gust_strength: f32,
35        gust_frequency: f32,
36        spatial_density: f32,
37        time: f32,
38    ) -> Self {
39        Self {
40            direction,
41            base_strength,
42            gust_strength,
43            gust_frequency,
44            spatial_density,
45            time,
46            _pad: [0.0; 8],
47        }
48    }
49
50    /// A still scene: zero direction, zero strength, zero gusts.
51    pub const STILL: Self = Self {
52        direction: [0.0, 0.0, 0.0],
53        base_strength: 0.0,
54        gust_strength: 0.0,
55        gust_frequency: 0.0,
56        spatial_density: 1.0,
57        time: 0.0,
58        _pad: [0.0; 8],
59    };
60
61    /// Size in bytes of the uniform buffer this struct maps to.
62    pub const SIZE: u64 = std::mem::size_of::<Self>() as u64;
63}
64
65impl Default for WindGlobals {
66    fn default() -> Self {
67        Self::STILL
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn layout_is_64_bytes() {
77        assert_eq!(WindGlobals::SIZE, 64);
78        assert_eq!(std::mem::align_of::<WindGlobals>(), 4);
79    }
80
81    #[test]
82    fn still_is_zero_except_density() {
83        let s = WindGlobals::STILL;
84        assert_eq!(s.direction, [0.0; 3]);
85        assert_eq!(s.base_strength, 0.0);
86        assert_eq!(s.gust_strength, 0.0);
87        assert_eq!(s.gust_frequency, 0.0);
88        assert_eq!(s.time, 0.0);
89        assert_eq!(s.spatial_density, 1.0);
90    }
91}