lightmap/
lightmap.rs

1use std::cmp::Ordering;
2use std::f32::consts::PI;
3
4use bottomless_pit::engine_handle::Engine;
5use bottomless_pit::material::{Material, MaterialBuilder};
6use bottomless_pit::render::RenderHandle;
7use bottomless_pit::shader::{Shader, ShaderOptions, UniformData, UniformError};
8use bottomless_pit::texture::UniformTexture;
9use bottomless_pit::{engine_handle::EngineBuilder, *};
10use colour::Colour;
11use encase::ShaderType;
12use resource::LoadingOp;
13use vectors::Vec2;
14
15fn main() {
16    let mut engine = EngineBuilder::new()
17        .set_window_title("Lightmap")
18        .with_resolution((800, 800))
19        .build()
20        .unwrap();
21
22    let uniform_texture = UniformTexture::new(&engine, engine.get_window_size());
23
24    let light = Light {
25        colour: Colour::ORANGE,
26        pos_x: 0.0,
27        pos_y: 0.0,
28        brightness: 0.75,
29        aspect_ratio: 1.0,
30    };
31
32    let light_data = UniformData::new(&light);
33
34    let shader_options = ShaderOptions::with_all(&light_data, &uniform_texture);
35    let light_shader = Shader::new(
36        "examples/light.wgsl",
37        shader_options,
38        &mut engine,
39        LoadingOp::Blocking,
40    );
41
42    let material = MaterialBuilder::new()
43        .set_shader(light_shader)
44        .build(&mut engine);
45
46    let ocluder_material = MaterialBuilder::new().build(&mut engine);
47
48    let rectangles = vec![
49        Rectangle::new(Vec2 { x: 120.0, y: 20.0 }, Vec2 { x: 50.0, y: 50.0 }),
50        Rectangle::new(Vec2 { x: 270.0, y: 70.0 }, Vec2 { x: 50.0, y: 50.0 }),
51        Rectangle::new(Vec2 { x: 130.0, y: 280.0 }, Vec2 { x: 50.0, y: 50.0 }),
52        Rectangle::new(Vec2 { x: 220.0, y: 300.0 }, Vec2 { x: 50.0, y: 50.0 }),
53        Rectangle::new(Vec2 { x: 350.0, y: 350.0 }, Vec2 { x: 100.0, y: 100.0 }),
54    ];
55
56    let s = TextureExample {
57        material,
58        ocluder_material,
59        light,
60        uniform_texture,
61        rectangles,
62        mouse_pos: ZEROS,
63    };
64
65    engine.run(s);
66}
67
68struct TextureExample {
69    material: Material<Light>,
70    ocluder_material: Material,
71    light: Light,
72    uniform_texture: UniformTexture,
73    rectangles: Vec<Rectangle>,
74    mouse_pos: Vec2<f32>,
75}
76
77const ZEROS: Vec2<f32> = Vec2 { x: 0.0, y: 0.0 };
78
79impl Game for TextureExample {
80    fn render<'o>(&'o mut self, mut render_handle: RenderHandle<'o>) {
81        self.create_shadow_map(&mut render_handle);
82
83        let mut p2 = render_handle.begin_pass(Colour::BLACK);
84        let size = p2.get_size();
85        let size = Vec2 {
86            x: size.x as f32,
87            y: size.y as f32,
88        };
89
90        self.material
91            .add_rectangle(Vec2 { x: 0.0, y: 0.0 }, size, Colour::WHITE, &p2);
92        self.material.draw(&mut p2);
93    }
94
95    fn update(&mut self, engine_handle: &mut Engine) {
96        let mouse_pos = engine_handle.get_mouse_position();
97        self.mouse_pos = mouse_pos;
98        let window_size = engine_handle.get_window_size();
99
100        self.light.pos_x = mouse_pos.x / window_size.x as f32;
101        self.light.pos_y = mouse_pos.y / window_size.y as f32;
102        self.material
103            .update_uniform_data(&self.light, &engine_handle)
104            .unwrap();
105
106        self.material
107            .update_uniform_texture(&mut self.uniform_texture, engine_handle)
108            .unwrap_or_else(|_| {});
109    }
110
111    fn on_resize(&mut self, new_size: Vec2<u32>, engine_handle: &mut Engine) {
112        self.light.aspect_ratio = new_size.x as f32 / new_size.y as f32;
113        match self
114            .material
115            .update_uniform_data(&self.light, &engine_handle)
116        {
117            Ok(_) => {}
118            Err(e) => match e {
119                UniformError::NotLoadedYet => {}
120                _ => panic!("{}", e),
121            },
122        }
123
124        match self.material.resize_uniform_texture(
125            &mut self.uniform_texture,
126            new_size,
127            engine_handle,
128        ) {
129            Ok(_) => {}
130            Err(e) => match e {
131                UniformError::NotLoadedYet => {}
132                _ => panic!("{}", e),
133            },
134        }
135    }
136}
137
138impl TextureExample {
139    fn create_shadow_map<'o>(&mut self, render_handle: &mut RenderHandle<'o>) {
140        let mut p1 = render_handle.begin_texture_pass(&mut self.uniform_texture, Colour::WHITE);
141
142        let light_pos = self.mouse_pos;
143
144        for rect in self.rectangles.iter() {
145            for (segment_1, segment_2) in rect.create_segments() {
146                let vert_1 = segment_1;
147                let vert_2 = segment_1
148                    + Vec2 {
149                        x: 300.0 * (segment_1.x - light_pos.x),
150                        y: 300.0 * (segment_1.y - light_pos.y),
151                    };
152                let vert_3 = segment_2;
153                let vert_4 = segment_2
154                    + Vec2 {
155                        x: 300.0 * (segment_2.x - light_pos.x),
156                        y: 300.0 * (segment_2.y - light_pos.y),
157                    };
158
159                let mut arr = [vert_1, vert_2, vert_3, vert_4];
160
161                let center_point = Vec2 {
162                    x: (vert_1.x + vert_2.x + vert_3.x + vert_4.x) / 4.0,
163                    y: (vert_1.y + vert_2.y + vert_3.y + vert_4.y) / 4.0,
164                };
165
166                for point in arr.iter_mut() {
167                    *point = *point - center_point;
168                }
169                arr.sort_by(|left, right| compare_points(left, right));
170                for point in arr.iter_mut() {
171                    *point = *point + center_point;
172                }
173
174                self.ocluder_material
175                    .add_custom(arr, [ZEROS; 4], 0.0, Colour::BLACK, &p1);
176            }
177        }
178
179        // makes sure there is not light in the squares
180        self.rectangles.iter().for_each(|rect| {
181            self.ocluder_material
182                .add_rectangle(rect.pos, rect.size, Colour::BLACK, &mut p1)
183        });
184
185        self.ocluder_material.draw(&mut p1);
186    }
187}
188
189struct Rectangle {
190    pos: Vec2<f32>,
191    size: Vec2<f32>,
192}
193
194impl Rectangle {
195    fn new(pos: Vec2<f32>, size: Vec2<f32>) -> Self {
196        Self { pos, size }
197    }
198
199    fn create_segments(&self) -> [(Vec2<f32>, Vec2<f32>); 4] {
200        let p1 = self.pos;
201        let p2 = Vec2 {
202            x: self.pos.x + self.size.x,
203            y: self.pos.y,
204        };
205        let p3 = Vec2 {
206            x: self.pos.x + self.size.x,
207            y: self.pos.y + self.size.y,
208        };
209        let p4 = Vec2 {
210            x: self.pos.x,
211            y: self.size.y + self.pos.y,
212        };
213        [(p1, p2), (p2, p3), (p3, p4), (p4, p1)]
214    }
215}
216
217#[derive(ShaderType)]
218struct Light {
219    colour: Colour,
220    pos_x: f32,
221    pos_y: f32,
222    brightness: f32,
223    aspect_ratio: f32,
224}
225
226// Convex Hull Algo
227fn compare_points(p1: &Vec2<f32>, p2: &Vec2<f32>) -> Ordering {
228    let angle_one = get_angle(&ZEROS, p1);
229    let angle_two = get_angle(&ZEROS, p2);
230    if angle_one < angle_two {
231        return Ordering::Less;
232    }
233
234    let d1 = get_distance(&ZEROS, p1);
235    let d2 = get_distance(&ZEROS, p2);
236    if (angle_one == angle_two) && (d1 < d2) {
237        return Ordering::Less;
238    }
239
240    Ordering::Greater
241}
242
243fn get_angle(center_point: &Vec2<f32>, point: &Vec2<f32>) -> f32 {
244    let x = point.x - center_point.x;
245    let y = point.y - center_point.y;
246    let mut angle = f32::atan2(y, x);
247    if angle <= 0.0 {
248        angle += 2.0 * PI;
249    }
250
251    angle
252}
253
254fn get_distance(p1: &Vec2<f32>, p2: &Vec2<f32>) -> f32 {
255    let x = p1.x - p2.x;
256    let y = p1.y - p2.y;
257    (x * x + y * y).sqrt()
258}