Skip to main content

enigma_3d/
shadow.rs

1use glium::{Display, Texture2d};
2use glium::framebuffer::DepthRenderBuffer;
3use glium::glutin::surface::WindowSurface;
4use glium::texture::RawImage2d;
5
6pub struct ShadowMaps {
7    pub directional_maps: [Option<Texture2d>; 4],
8    pub point_maps: [Option<Texture2d>; 4],
9    pub light_space_matrices: [[[f32; 4]; 4]; 4],
10    pub point_far_planes: [f32; 4],
11    pub resolution: u32,
12    pub dir_depth_rb: DepthRenderBuffer,
13    pub point_depth_rb: DepthRenderBuffer,
14    pub dummy: Texture2d,
15}
16
17const IDENTITY: [[f32; 4]; 4] = [
18    [1.0, 0.0, 0.0, 0.0],
19    [0.0, 1.0, 0.0, 0.0],
20    [0.0, 0.0, 1.0, 0.0],
21    [0.0, 0.0, 0.0, 1.0],
22];
23
24impl ShadowMaps {
25    pub fn new(display: &Display<WindowSurface>, resolution: u32) -> Self {
26        let dir_depth_rb = DepthRenderBuffer::new(
27            display,
28            glium::texture::DepthFormat::F32,
29            resolution,
30            resolution,
31        ).expect("Failed to create directional shadow depth renderbuffer");
32
33        let point_depth_rb = DepthRenderBuffer::new(
34            display,
35            glium::texture::DepthFormat::F32,
36            resolution * 2,
37            resolution * 3,
38        ).expect("Failed to create point shadow depth renderbuffer");
39
40        let dummy = Texture2d::new(
41            display,
42            RawImage2d::from_raw_rgba_reversed(&[255u8, 0, 0, 255], (1, 1)),
43        ).expect("Failed to create dummy shadow texture");
44
45        Self {
46            directional_maps: [None, None, None, None],
47            point_maps: [None, None, None, None],
48            light_space_matrices: [IDENTITY; 4],
49            point_far_planes: [100.0; 4],
50            resolution,
51            dir_depth_rb,
52            point_depth_rb,
53            dummy,
54        }
55    }
56
57    pub fn clear(&mut self) {
58        for i in 0..4 {
59            self.directional_maps[i] = None;
60            self.point_maps[i] = None;
61        }
62    }
63}
64
65// --- Matrix helpers (column-major, matching camera.rs) ---
66
67pub fn view_matrix(position: &[f32; 3], direction: &[f32; 3], up: &[f32; 3]) -> [[f32; 4]; 4] {
68    let len = (direction[0]*direction[0] + direction[1]*direction[1] + direction[2]*direction[2]).sqrt();
69    let f = [-direction[0]/len, -direction[1]/len, -direction[2]/len];
70
71    let s = [
72        up[1]*f[2] - up[2]*f[1],
73        up[2]*f[0] - up[0]*f[2],
74        up[0]*f[1] - up[1]*f[0],
75    ];
76    let s_len = (s[0]*s[0] + s[1]*s[1] + s[2]*s[2]).sqrt();
77    let s = [s[0]/s_len, s[1]/s_len, s[2]/s_len];
78
79    let u = [
80        f[1]*s[2] - f[2]*s[1],
81        f[2]*s[0] - f[0]*s[2],
82        f[0]*s[1] - f[1]*s[0],
83    ];
84
85    let p = [
86        -position[0]*s[0] - position[1]*s[1] - position[2]*s[2],
87        -position[0]*u[0] - position[1]*u[1] - position[2]*u[2],
88        -position[0]*f[0] - position[1]*f[1] - position[2]*f[2],
89    ];
90
91    [
92        [s[0], u[0], f[0], 0.0],
93        [s[1], u[1], f[1], 0.0],
94        [s[2], u[2], f[2], 0.0],
95        [p[0], p[1], p[2], 1.0],
96    ]
97}
98
99pub fn ortho_matrix(l: f32, r: f32, b: f32, t: f32, near: f32, far: f32) -> [[f32; 4]; 4] {
100    [
101        [2.0/(r-l), 0.0, 0.0, 0.0],
102        [0.0, 2.0/(t-b), 0.0, 0.0],
103        [0.0, 0.0, -2.0/(far-near), 0.0],
104        [-(r+l)/(r-l), -(t+b)/(t-b), -(far+near)/(far-near), 1.0],
105    ]
106}
107
108pub fn perspective_90_matrix(near: f32, far: f32) -> [[f32; 4]; 4] {
109    // f = 1.0/tan(45°) = 1.0, aspect = 1.0
110    [
111        [1.0, 0.0, 0.0, 0.0],
112        [0.0, 1.0, 0.0, 0.0],
113        [0.0, 0.0, (far+near)/(near-far), -1.0],
114        [0.0, 0.0, (2.0*far*near)/(near-far), 0.0],
115    ]
116}
117
118pub fn mat4_mul(a: [[f32;4];4], b: [[f32;4];4]) -> [[f32;4];4] {
119    let mut r = [[0.0f32;4];4];
120    for col in 0..4 {
121        for row in 0..4 {
122            r[col][row] = a[0][row]*b[col][0] + a[1][row]*b[col][1]
123                        + a[2][row]*b[col][2] + a[3][row]*b[col][3];
124        }
125    }
126    r
127}
128
129pub fn directional_light_space_matrix(
130    light_dir: [f32; 3],
131    cam_pos: [f32; 3],
132    half_extent: f32,
133) -> [[f32; 4]; 4] {
134    let len = (light_dir[0]*light_dir[0] + light_dir[1]*light_dir[1] + light_dir[2]*light_dir[2]).sqrt();
135    let dir = [light_dir[0]/len, light_dir[1]/len, light_dir[2]/len];
136    let pos = [
137        cam_pos[0] - dir[0] * half_extent,
138        cam_pos[1] - dir[1] * half_extent,
139        cam_pos[2] - dir[2] * half_extent,
140    ];
141    let up = if dir[1].abs() < 0.99 { [0.0f32, 1.0, 0.0] } else { [1.0f32, 0.0, 0.0] };
142    let view = view_matrix(&pos, &dir, &up);
143    let ortho = ortho_matrix(-half_extent, half_extent, -half_extent, half_extent, 0.1, half_extent * 2.0);
144    mat4_mul(ortho, view)
145}
146
147// Cube face directions and ups, ordered: +X -X +Y -Y +Z -Z
148pub const CUBE_FACE_DIRS: [([f32;3], [f32;3]); 6] = [
149    ([1.0, 0.0, 0.0],  [0.0, -1.0, 0.0]),
150    ([-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]),
151    ([0.0, 1.0, 0.0],  [0.0, 0.0, 1.0]),
152    ([0.0, -1.0, 0.0], [0.0, 0.0, -1.0]),
153    ([0.0, 0.0, 1.0],  [0.0, -1.0, 0.0]),
154    ([0.0, 0.0, -1.0], [0.0, -1.0, 0.0]),
155];
156
157// Atlas viewport for face i in a 2*res x 3*res texture (y=0 at bottom)
158pub fn face_viewport(face: usize, res: u32) -> glium::Rect {
159    let col = (face % 2) as u32;
160    let row = (face / 2) as u32;
161    glium::Rect {
162        left: col * res,
163        bottom: row * res,
164        width: res,
165        height: res,
166    }
167}