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 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
226fn 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}